@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -2
- package/bin/doflow.js +44 -0
- package/core/.antigravity-plugin/hooks.json +39 -0
- package/core/.antigravity-plugin/mcp_config.json +18 -0
- package/core/.antigravity-plugin/plugin.json +31 -0
- package/core/.claude-plugin/marketplace.json +20 -0
- package/core/.claude-plugin/plugin.json +21 -0
- package/core/.codex-plugin/marketplace.json +20 -0
- package/core/.codex-plugin/plugin.json +35 -0
- package/core/.plugin/plugin.json +19 -0
- package/core/harnesses/antigravity/hooks/pre-implementation-gate.sh +9 -0
- package/core/harnesses/antigravity/hooks/stop-check.sh +10 -0
- package/core/harnesses/claude/hooks/lib.sh +304 -0
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +20 -0
- package/core/harnesses/claude/hooks/permission-audit.sh +47 -0
- package/core/harnesses/claude/hooks/post-compact.sh +74 -0
- package/core/harnesses/claude/hooks/post-edit-lint.sh +36 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +47 -0
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +20 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +43 -0
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +21 -0
- package/core/harnesses/claude/hooks/session-end.sh +89 -0
- package/core/harnesses/claude/hooks/session-start.sh +5 -0
- package/core/harnesses/claude/hooks/skill-config-audit.sh +39 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -0
- package/core/harnesses/claude/hooks/subagent-audit.sh +7 -0
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -0
- package/core/harnesses/claude/settings/keybindings.json +20 -0
- package/core/harnesses/claude/settings/settings.json +201 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +49 -0
- package/core/harnesses/codex/agents/core-implementer.toml +7 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +8 -0
- package/core/harnesses/codex/agents/research-writer.toml +8 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +8 -0
- package/core/harnesses/codex/agents/system-architect.toml +9 -0
- package/core/harnesses/codex/config/config.toml +10 -0
- package/core/harnesses/codex/hooks/hooks.json +122 -0
- package/core/harnesses/codex/hooks/lib.sh +335 -0
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +18 -0
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +52 -0
- package/core/harnesses/codex/hooks/post-edit-lint.sh +6 -0
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +18 -0
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +46 -0
- package/core/harnesses/codex/hooks/pre-compact.sh +9 -0
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +19 -0
- package/core/harnesses/codex/hooks/session-end.impl.sh +92 -0
- package/core/harnesses/codex/hooks/session-end.sh +5 -0
- package/core/harnesses/codex/hooks/session-start.sh +5 -0
- package/core/harnesses/codex/hooks/stop-check.sh +8 -0
- package/core/harnesses/codex/hooks/subagent-audit.sh +8 -0
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +6 -0
- package/core/harnesses/gemini/hooks/hooks.json +77 -0
- package/core/harnesses/gemini/hooks/lib.sh +302 -0
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +21 -0
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +39 -0
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +21 -0
- package/core/harnesses/gemini/hooks/pre-compact.sh +38 -0
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +22 -0
- package/core/harnesses/gemini/hooks/session-end.impl.sh +89 -0
- package/core/harnesses/gemini/hooks/session-end.sh +5 -0
- package/core/harnesses/gemini/hooks/session-start.sh +5 -0
- package/core/harnesses/gemini/settings/adapter-defaults.json +4 -0
- package/core/harnesses/kiro/agents/doflow.json +58 -0
- package/core/harnesses/kiro/hooks/doflow.json +53 -0
- package/core/harnesses/kiro/hooks/lib.sh +324 -0
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +9 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +9 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +9 -0
- package/core/harnesses/kiro/hooks/session-start.sh +9 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +43 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +39 -0
- package/core/harnesses/shared/hooks/adapters/antigravity.js +111 -0
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +19 -0
- package/core/harnesses/shared/hooks/adapters/gemini.js +114 -0
- package/core/harnesses/shared/hooks/hooks.json +47 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +91 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +97 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +29 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +501 -0
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +21 -0
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +91 -0
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +328 -0
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +496 -0
- package/core/harnesses/shared/hooks/policies/prompt-nudge.jq +131 -0
- package/core/harnesses/shared/hooks/policies/session-context.sh +141 -0
- package/core/harnesses/shared/hooks/policies/stop-check.sh +264 -0
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +46 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +235 -0
- package/core/harnesses/shared/hooks/stream-hook-runner.js +590 -0
- package/core/harnesses/shared/locator/doflow-run +87 -0
- package/core/registry/advice.json +101 -0
- package/core/registry/assets.json +792 -0
- package/core/registry/capabilities.json +120 -0
- package/core/registry/contracts.json +189 -0
- package/core/registry/external-tools.json +191 -0
- package/core/registry/harnesses.json +2062 -0
- package/core/registry/lifecycle.json +231 -0
- package/core/registry/mcp.json +18 -0
- package/core/registry/models.json +79 -0
- package/core/registry/readiness-templates.json +118 -0
- package/core/registry/routes.json +47 -0
- package/core/registry/storage.json +1057 -0
- package/core/registry/verification.json +258 -0
- package/core/registry/workflows.json +790 -0
- package/core/shared/agent-specs/core-implementer.md +73 -0
- package/core/shared/agent-specs/quality-guardian.md +28 -0
- package/core/shared/agent-specs/research-writer.md +31 -0
- package/core/shared/agent-specs/spec-analyst.md +33 -0
- package/core/shared/agent-specs/system-architect.md +29 -0
- package/core/shared/guidance/DOFLOW_CORE.md +8 -0
- package/core/shared/guidance/FLAGS.md +25 -0
- package/core/shared/guidance/PRINCIPLES.md +61 -0
- package/core/shared/guidance/VERSION +1 -0
- package/core/shared/guidance/mcp/MCP_Context7.md +18 -0
- package/core/shared/guidance/mcp/MCP_Sequential.md +17 -0
- package/core/shared/guidance/modes/MODE_Brainstorming.md +46 -0
- package/core/shared/guidance/modes/MODE_DeepResearch.md +69 -0
- package/core/shared/guidance/modes/MODE_Introspection.md +33 -0
- package/core/shared/guidance/modes/MODE_Orchestration.md +57 -0
- package/core/shared/guidance/modes/MODE_Task_Management.md +74 -0
- package/core/shared/guidance/pointers/claude-gemini.md +2 -0
- package/core/shared/guidance/pointers/codex.md +6 -0
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +373 -0
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +104 -0
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +47 -0
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +161 -0
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +87 -0
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +93 -0
- package/core/shared/guidance/references/MODEL_SELECTION.md +74 -0
- package/core/shared/guidance/references/ORCHESTRATION.md +36 -0
- package/core/shared/guidance/references/RESEARCH_CONFIG.md +21 -0
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +81 -0
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +25 -0
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +23 -0
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +14 -0
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +46 -0
- package/core/shared/scripts/doflow/assets/mermaid-theme.json +46 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +167 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +546 -0
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +208 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +347 -0
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +66 -0
- package/core/shared/scripts/doflow/bash/do-review-package.sh +127 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +698 -0
- package/core/shared/scripts/doflow/bash/render-audit.sh +224 -0
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +177 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +223 -0
- package/core/shared/scripts/doflow/bash/sync-context.sh +66 -0
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +526 -0
- package/core/shared/scripts/doflow/bin/doflow-run +592 -0
- package/core/shared/skills/do/SKILL.md +128 -0
- package/core/shared/skills/do/references/estimation.md +13 -0
- package/core/shared/skills/do/references/pm_routing.md +25 -0
- package/core/shared/skills/do/references/tool_matrix.md +52 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +252 -0
- package/core/shared/skills/do-code-review/README.md +49 -0
- package/core/shared/skills/do-code-review/SKILL.md +333 -0
- package/core/shared/skills/do-code-review/assets/sample_c_clean.c +71 -0
- package/core/shared/skills/do-code-review/assets/sample_c_smells.c +66 -0
- package/core/shared/skills/do-code-review/assets/sample_cpp_shift.cpp +19 -0
- package/core/shared/skills/do-code-review/assets/sample_csharp_clean.cs +100 -0
- package/core/shared/skills/do-code-review/assets/sample_csharp_smells.cs +78 -0
- package/core/shared/skills/do-code-review/assets/sample_java_clean.java +55 -0
- package/core/shared/skills/do-code-review/assets/sample_java_smells.java +56 -0
- package/core/shared/skills/do-code-review/assets/sample_json_clean.json +7 -0
- package/core/shared/skills/do-code-review/assets/sample_markdown_clean.md +30 -0
- package/core/shared/skills/do-code-review/assets/sample_markdown_smells.md +29 -0
- package/core/shared/skills/do-code-review/assets/sample_shell_heredoc.sh +38 -0
- package/core/shared/skills/do-code-review/assets/sample_shell_smells.sh +15 -0
- package/core/shared/skills/do-code-review/assets/sample_typescript_strings.ts +36 -0
- package/core/shared/skills/do-code-review/assets/sample_yaml_smells.yaml +6 -0
- package/core/shared/skills/do-code-review/content-types/config.md +83 -0
- package/core/shared/skills/do-code-review/content-types/markdown.md +132 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_c_clean_quality.json +79 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_c_smells_quality.json +169 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_cpp_shift_quality.json +49 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_clean_quality.json +149 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_smells_quality.json +157 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_java_clean_quality.json +67 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_java_smells_quality.json +97 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_json_clean_quality.json +33 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_clean_quality.json +1 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_smells_quality.json +16 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_shell_heredoc_quality.json +54 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_shell_smells_quality.json +91 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_typescript_strings_quality.json +60 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_yaml_smells_quality.json +46 -0
- package/core/shared/skills/do-code-review/languages/c.md +103 -0
- package/core/shared/skills/do-code-review/languages/cpp.md +103 -0
- package/core/shared/skills/do-code-review/languages/csharp.md +97 -0
- package/core/shared/skills/do-code-review/languages/dart.md +108 -0
- package/core/shared/skills/do-code-review/languages/go.md +92 -0
- package/core/shared/skills/do-code-review/languages/java.md +103 -0
- package/core/shared/skills/do-code-review/languages/kotlin.md +88 -0
- package/core/shared/skills/do-code-review/languages/php.md +109 -0
- package/core/shared/skills/do-code-review/languages/python.md +94 -0
- package/core/shared/skills/do-code-review/languages/ruby.md +105 -0
- package/core/shared/skills/do-code-review/languages/rust.md +99 -0
- package/core/shared/skills/do-code-review/languages/shell.md +90 -0
- package/core/shared/skills/do-code-review/languages/swift.md +88 -0
- package/core/shared/skills/do-code-review/languages/typescript.md +97 -0
- package/core/shared/skills/do-code-review/review-policy.json +47 -0
- package/core/shared/skills/do-code-review/rules/universal.md +49 -0
- package/core/shared/skills/do-code-review/scripts/code_quality_checker.py +1696 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +532 -0
- package/core/shared/skills/do-code-review/scripts/pr_analyzer.py +620 -0
- package/core/shared/skills/do-code-review/scripts/review_report_generator.py +505 -0
- package/core/shared/skills/do-constitution/SKILL.md +72 -0
- package/core/shared/skills/do-design/SKILL.md +212 -0
- package/core/shared/skills/do-diagnose/SKILL.md +156 -0
- package/core/shared/skills/do-diagnose/references/code_audit.md +15 -0
- package/core/shared/skills/do-diagnose/references/refactoring.md +12 -0
- package/core/shared/skills/do-diagnose/references/root_cause.md +17 -0
- package/core/shared/skills/do-document/SKILL.md +81 -0
- package/core/shared/skills/do-document/references/api-reference.md +42 -0
- package/core/shared/skills/do-document/references/deep_research.md +18 -0
- package/core/shared/skills/do-document/references/embedded-research.md +11 -0
- package/core/shared/skills/do-document/references/implementation-flow.md +105 -0
- package/core/shared/skills/do-document/references/index_generator.md +9 -0
- package/core/shared/skills/do-document/references/user-guide.md +22 -0
- package/core/shared/skills/do-execute-plan/SKILL.md +261 -0
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +57 -0
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +92 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +155 -0
- package/core/shared/skills/do-execute-plan/references/run_position.md +63 -0
- package/core/shared/skills/do-execute-plan/references/scaffold.md +441 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +41 -0
- package/core/shared/skills/do-flow/SKILL.md +186 -0
- package/core/shared/skills/do-flow/references/task_classes.md +134 -0
- package/core/shared/skills/do-git/SKILL.md +115 -0
- package/core/shared/skills/do-git/references/hotfix-ritual.md +44 -0
- package/core/shared/skills/do-git/references/lifecycle-policy.md +54 -0
- package/core/shared/skills/do-git/references/release-ritual.md +64 -0
- package/core/shared/skills/do-implement/SKILL.md +222 -0
- package/core/shared/skills/do-plan/SKILL.md +219 -0
- package/core/shared/skills/do-test/SKILL.md +113 -0
- package/core/shared/templates/doflow/constitution-template.md +28 -0
- package/core/shared/templates/doflow/data-model-template.md +169 -0
- package/core/shared/templates/doflow/design-template.md +193 -0
- package/core/shared/templates/doflow/external-contract-template.md +35 -0
- package/core/shared/templates/doflow/intent-template.md +45 -0
- package/core/shared/templates/doflow/plan-template.md +151 -0
- package/core/shared/templates/doflow/question-log-template.md +51 -0
- package/core/shared/templates/doflow/requirement-template.md +126 -0
- package/core/shared/templates/doflow/specs-template.md +55 -0
- package/core/shared/templates/doflow/state-template.md +59 -0
- package/core/shared/workflows/do-flow-chain.md +17 -0
- package/package.json +46 -4
- package/src/adapters/antigravity/index.ts +631 -0
- package/src/adapters/claude/index.ts +548 -0
- package/src/adapters/claude/mcp.ts +94 -0
- package/src/adapters/codex/agents.ts +208 -0
- package/src/adapters/codex/config.ts +272 -0
- package/src/adapters/codex/hooks.ts +203 -0
- package/src/adapters/codex/index.ts +707 -0
- package/src/adapters/codex/mcp.ts +271 -0
- package/src/adapters/codex/native-projection.ts +34 -0
- package/src/adapters/copilot/index.ts +413 -0
- package/src/adapters/copy-tree.ts +620 -0
- package/src/adapters/gemini/hooks.ts +210 -0
- package/src/adapters/gemini/index.ts +390 -0
- package/src/adapters/hook-commands.ts +60 -0
- package/src/adapters/index.ts +106 -0
- package/src/adapters/kiro/index.ts +310 -0
- package/src/adapters/mcp-entries.ts +331 -0
- package/src/adapters/opencode/index.ts +466 -0
- package/src/adapters/pi/index.ts +279 -0
- package/src/adapters/pi/json-members.ts +185 -0
- package/src/adapters/pi/mcp.ts +373 -0
- package/src/adapters/resolve-source.ts +19 -0
- package/src/cli/commands/install.ts +218 -0
- package/src/cli/commands/list-backups.ts +21 -0
- package/src/cli/commands/reconcile.ts +120 -0
- package/src/cli/commands/remove.ts +82 -0
- package/src/cli/commands/rollback.ts +172 -0
- package/src/cli/commands/self-update.ts +33 -0
- package/src/cli/commands/status.ts +138 -0
- package/src/cli/commands/tools.ts +92 -0
- package/src/cli/commands/update.ts +162 -0
- package/src/cli/index.ts +568 -0
- package/src/cli/runtime-commands.ts +281 -0
- package/src/cli/shared.ts +218 -0
- package/src/helper/bash-helper.ts +34 -0
- package/src/helper/git.ts +24 -0
- package/src/helper/guardrails.ts +49 -0
- package/src/helper/harness-paths.ts +140 -0
- package/src/helper/marker-merge.ts +116 -0
- package/src/helper/prompt.ts +61 -0
- package/src/helper/repo-root.ts +29 -0
- package/src/helper/settings-merge.ts +151 -0
- package/src/helper/settings-scope.ts +33 -0
- package/src/helper/toml.ts +244 -0
- package/src/install/backup-set.ts +149 -0
- package/src/install/backup.ts +724 -0
- package/src/install/context.ts +58 -0
- package/src/install/manifest.ts +145 -0
- package/src/install/mcp.ts +254 -0
- package/src/install/paths.ts +84 -0
- package/src/install/targets.ts +90 -0
- package/src/install/tool-lifecycle.ts +151 -0
- package/src/lifecycle/bash-availability.ts +25 -0
- package/src/lifecycle/index.ts +812 -0
- package/src/lifecycle/mcp-index.ts +28 -0
- package/src/lifecycle/policies.ts +73 -0
- package/src/lifecycle/view.ts +175 -0
- package/src/registry/index.ts +611 -0
- package/src/release/sync-plugin-versions.ts +66 -0
- package/src/runtime/advice/cli.ts +283 -0
- package/src/runtime/advice/client.ts +324 -0
- package/src/runtime/advice/config.ts +189 -0
- package/src/runtime/advice/doctor.ts +169 -0
- package/src/runtime/advice/endpoint-guard.ts +134 -0
- package/src/runtime/advice/kinds.ts +154 -0
- package/src/runtime/advice/providers/ollama-systemone.ts +209 -0
- package/src/runtime/advice/record.ts +166 -0
- package/src/runtime/advice/registry.ts +170 -0
- package/src/runtime/advice/strength.ts +66 -0
- package/src/runtime/brief-tools.ts +342 -0
- package/src/runtime/c4-project.ts +371 -0
- package/src/runtime/capability-router.ts +484 -0
- package/src/runtime/checkouts.ts +170 -0
- package/src/runtime/claims.ts +468 -0
- package/src/runtime/cli-result.ts +55 -0
- package/src/runtime/cli.ts +678 -0
- package/src/runtime/command-detect.ts +516 -0
- package/src/runtime/context-pack.ts +492 -0
- package/src/runtime/decision-register.ts +493 -0
- package/src/runtime/evidence-ledger.ts +338 -0
- package/src/runtime/failure/capture.ts +272 -0
- package/src/runtime/failure/classifier.ts +68 -0
- package/src/runtime/failure/cli.ts +178 -0
- package/src/runtime/failure/home.ts +64 -0
- package/src/runtime/failure/store.ts +226 -0
- package/src/runtime/feature-resolve.ts +79 -0
- package/src/runtime/freshness.ts +177 -0
- package/src/runtime/health.ts +863 -0
- package/src/runtime/history-compactor.ts +237 -0
- package/src/runtime/implementation-gate.ts +321 -0
- package/src/runtime/inventory/coverage.ts +218 -0
- package/src/runtime/inventory/identity.ts +65 -0
- package/src/runtime/inventory/index.ts +705 -0
- package/src/runtime/inventory/precedence.ts +134 -0
- package/src/runtime/inventory/read-scopes.ts +131 -0
- package/src/runtime/inventory/siblings.ts +192 -0
- package/src/runtime/knowledge/chunker.ts +124 -0
- package/src/runtime/knowledge/hybrid.ts +149 -0
- package/src/runtime/knowledge/index-store.ts +145 -0
- package/src/runtime/knowledge/retrieval.ts +194 -0
- package/src/runtime/leak-scan.ts +185 -0
- package/src/runtime/lifecycle/cli.ts +276 -0
- package/src/runtime/lifecycle/event-store.ts +340 -0
- package/src/runtime/lifecycle/fold.ts +353 -0
- package/src/runtime/lifecycle/followup.ts +432 -0
- package/src/runtime/lifecycle/goal.ts +249 -0
- package/src/runtime/lifecycle/intent-writer.ts +86 -0
- package/src/runtime/lifecycle/overview.ts +292 -0
- package/src/runtime/lifecycle/release.ts +241 -0
- package/src/runtime/lifecycle/report-store.ts +221 -0
- package/src/runtime/lifecycle/retention.ts +212 -0
- package/src/runtime/lifecycle/root.ts +71 -0
- package/src/runtime/lifecycle/status.ts +353 -0
- package/src/runtime/lifecycle/store-upkeep.ts +393 -0
- package/src/runtime/locator-resolve.ts +158 -0
- package/src/runtime/mask.ts +210 -0
- package/src/runtime/mcp-consultations.ts +412 -0
- package/src/runtime/mcp-reach.ts +157 -0
- package/src/runtime/model-router.ts +153 -0
- package/src/runtime/outcome.ts +467 -0
- package/src/runtime/reach.ts +148 -0
- package/src/runtime/readiness-record.ts +150 -0
- package/src/runtime/readiness.ts +435 -0
- package/src/runtime/recovery.ts +282 -0
- package/src/runtime/research-request.ts +231 -0
- package/src/runtime/retrieval-plan.ts +578 -0
- package/src/runtime/scaffold/artifacts.ts +217 -0
- package/src/runtime/scaffold/fingerprint.ts +56 -0
- package/src/runtime/scaffold/generate.ts +911 -0
- package/src/runtime/scaffold/languages.ts +245 -0
- package/src/runtime/storage/cli.ts +64 -0
- package/src/runtime/storage/engine.ts +249 -0
- package/src/runtime/storage/registry.ts +130 -0
- package/src/runtime/storage/roots.ts +63 -0
- package/src/runtime/storage/rules.ts +282 -0
- package/src/runtime/storage/upkeep.ts +243 -0
- package/src/runtime/storage/work-status.ts +326 -0
- package/src/runtime/task-attempts.ts +415 -0
- package/src/runtime/task-classifier.ts +550 -0
- package/src/runtime/task-scope.ts +130 -0
- package/src/runtime/task-state.ts +176 -0
- package/src/runtime/trace/indicators.ts +235 -0
- package/src/runtime/trace/ledger.ts +626 -0
- package/src/runtime/trace/render.ts +76 -0
- package/src/runtime/trace/views.ts +459 -0
- package/src/runtime/verification/contract-runner.ts +341 -0
- package/src/runtime/verification/engine.ts +1461 -0
- package/src/runtime/verification/registry.ts +130 -0
- package/src/runtime/verification/scope-bound.ts +418 -0
- package/src/runtime/verification/test-selection.ts +172 -0
- package/src/runtime/workflow-engine.ts +588 -0
- package/src/runtime/workflow-orchestrator.ts +953 -0
- package/src/runtime/worktree.ts +340 -0
- package/src/state/index.ts +193 -0
- package/src/state/lockfile.ts +136 -0
- package/src/state/run-lock.ts +408 -0
- package/types/node-commonjs.d.ts +68 -0
- package/types/runtime.d.ts +91 -0
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# session-context.sh — Canonical Policy Library: SessionStart guard policy
|
|
3
|
+
#
|
|
4
|
+
# Fires when a harness starts a new session. Captures git state into a
|
|
5
|
+
# session-scoped file so a harness's user-prompt-submit hook can inject it on
|
|
6
|
+
# the first prompt.
|
|
7
|
+
#
|
|
8
|
+
# Cannot inject into the LLM context from this event — side effects only.
|
|
9
|
+
# Must complete in <200ms. Must never exit non-zero or produce unexpected stderr.
|
|
10
|
+
#
|
|
11
|
+
# Canonical Policy Script Contract (design.md §4):
|
|
12
|
+
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional here; DOFLOW_AGENT
|
|
13
|
+
# only affects the "last_agent" field recorded in meta.json)
|
|
14
|
+
# stdin the harness's native SessionStart JSON payload — must expose at
|
|
15
|
+
# least ".session_id" and ".cwd"
|
|
16
|
+
# exit this policy never denies — it always exits 0 (side-effect only)
|
|
17
|
+
#
|
|
18
|
+
# Depends on lib.sh (require_jq, json_field, ensure_session_dir,
|
|
19
|
+
# ensure_project_dir, cwd_hash, run_with_timeout, $SESSIONS_LOG) being
|
|
20
|
+
# discoverable next to this script when it runs — a front door invoking this
|
|
21
|
+
# canonical script is responsible for making lib.sh resolvable via
|
|
22
|
+
# "$(dirname "$0")/lib.sh", exactly as every harness's own SessionStart script
|
|
23
|
+
# already required before this policy moved to a shared location.
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
# shellcheck source=lib.sh
|
|
27
|
+
source "$(dirname "$0")/lib.sh"
|
|
28
|
+
require_jq
|
|
29
|
+
|
|
30
|
+
INPUT=$(cat)
|
|
31
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
32
|
+
CWD=$(json_field "$INPUT" ".cwd")
|
|
33
|
+
|
|
34
|
+
# Guard: if we can't determine the session, nothing to do
|
|
35
|
+
[[ -z "$SESSION_ID" ]] && exit 0
|
|
36
|
+
|
|
37
|
+
SESSION_PATH=$(ensure_session_dir "$SESSION_ID")
|
|
38
|
+
|
|
39
|
+
# Clean up stale injected flag from a prior crashed session
|
|
40
|
+
rm -f "$SESSION_PATH/injected"
|
|
41
|
+
|
|
42
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
43
|
+
|
|
44
|
+
# ── Git state capture ─────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
IS_GIT_REPO=false
|
|
47
|
+
BRANCH=""
|
|
48
|
+
SHA=""
|
|
49
|
+
COMMITS_JSON="[]"
|
|
50
|
+
UNCOMMITTED=0
|
|
51
|
+
STASH_COUNT=0
|
|
52
|
+
UPSTREAM_BEHIND=0
|
|
53
|
+
|
|
54
|
+
if [[ -n "$CWD" ]] && is_git_worktree "$CWD"; then
|
|
55
|
+
IS_GIT_REPO=true
|
|
56
|
+
|
|
57
|
+
BRANCH=$(git_branch_of "$CWD" || echo "")
|
|
58
|
+
SHA=$(git_short_sha_of "$CWD" || echo "")
|
|
59
|
+
|
|
60
|
+
# Last 5 commits as a JSON array of one-liner strings.
|
|
61
|
+
# Capture git's raw output first (never fatal under pipefail), then build
|
|
62
|
+
# the JSON array from that captured text in a single jq call whose failure
|
|
63
|
+
# replaces COMMITS_JSON via assignment rather than appending via echo —
|
|
64
|
+
# a trailing `|| echo "[]"` on the pipe would double-emit when git log
|
|
65
|
+
# fails (e.g. zero-commit repo) but jq still prints "[]" downstream.
|
|
66
|
+
COMMITS_RAW=$(run_with_timeout 1 -- git -C "$CWD" log --oneline -5 2>/dev/null || true)
|
|
67
|
+
COMMITS_JSON=$(printf '%s' "$COMMITS_RAW" | jq -Rs 'split("\n") | map(select(length > 0))' 2>/dev/null) || COMMITS_JSON="[]"
|
|
68
|
+
|
|
69
|
+
# Count uncommitted (staged + unstaged) files
|
|
70
|
+
UNCOMMITTED=$(git_uncommitted_count_of "$CWD" || echo "0")
|
|
71
|
+
|
|
72
|
+
# Count stash entries
|
|
73
|
+
STASH_COUNT=$(run_with_timeout 1 -- git -C "$CWD" stash list 2>/dev/null | wc -l | tr -d ' ' || echo "0")
|
|
74
|
+
|
|
75
|
+
# How far behind upstream (0 if no upstream configured)
|
|
76
|
+
# shellcheck disable=SC1083 # @{u} is git upstream ref syntax, not a shell brace expression
|
|
77
|
+
UPSTREAM_BEHIND=$(run_with_timeout 1 -- git -C "$CWD" rev-list --count HEAD..@{u} 2>/dev/null || echo "0")
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# ── Write git-context.json ────────────────────────────────────────────────────
|
|
81
|
+
|
|
82
|
+
# Use jq -n for full construction — avoids heredoc variable interpolation
|
|
83
|
+
# which can produce invalid JSON if numeric fields contain unexpected output.
|
|
84
|
+
jq -n \
|
|
85
|
+
--arg branch "$BRANCH" \
|
|
86
|
+
--arg sha "$SHA" \
|
|
87
|
+
--argjson commits "$COMMITS_JSON" \
|
|
88
|
+
--argjson uncommitted "$UNCOMMITTED" \
|
|
89
|
+
--argjson stash "$STASH_COUNT" \
|
|
90
|
+
--argjson behind "$UPSTREAM_BEHIND" \
|
|
91
|
+
--argjson is_git "$IS_GIT_REPO" \
|
|
92
|
+
--arg cwd "$CWD" \
|
|
93
|
+
--arg captured_at "$TIMESTAMP" \
|
|
94
|
+
'{
|
|
95
|
+
branch: $branch,
|
|
96
|
+
sha: $sha,
|
|
97
|
+
commits: $commits,
|
|
98
|
+
uncommitted_count: $uncommitted,
|
|
99
|
+
stash_count: $stash,
|
|
100
|
+
upstream_behind: $behind,
|
|
101
|
+
is_git_repo: $is_git,
|
|
102
|
+
cwd: $cwd,
|
|
103
|
+
captured_at: $captured_at
|
|
104
|
+
}' > "$SESSION_PATH/git-context.json"
|
|
105
|
+
|
|
106
|
+
# ── Write meta.json ───────────────────────────────────────────────────────────
|
|
107
|
+
|
|
108
|
+
if [[ -n "$CWD" ]]; then
|
|
109
|
+
PROJECT_DIR=$(ensure_project_dir "$CWD")
|
|
110
|
+
# Preserve compacted_at from the prior session if present
|
|
111
|
+
PRIOR_COMPACTED_AT=""
|
|
112
|
+
if [[ -f "$PROJECT_DIR/meta.json" ]]; then
|
|
113
|
+
PRIOR_COMPACTED_AT=$(jq -r '.compacted_at // empty' "$PROJECT_DIR/meta.json" 2>/dev/null || echo "")
|
|
114
|
+
fi
|
|
115
|
+
META_TMP=$(mktemp "$PROJECT_DIR/.meta.XXXXXX")
|
|
116
|
+
trap 'rm -f "$META_TMP" 2>/dev/null || true' EXIT
|
|
117
|
+
jq -n \
|
|
118
|
+
--arg cwd "$CWD" \
|
|
119
|
+
--arg cwd_hash "$(cwd_hash "$CWD")" \
|
|
120
|
+
--arg last_agent "$DOFLOW_AGENT" \
|
|
121
|
+
--arg last_active "$TIMESTAMP" \
|
|
122
|
+
--arg compacted_at "${PRIOR_COMPACTED_AT:-}" \
|
|
123
|
+
--arg branch "${BRANCH:-}" \
|
|
124
|
+
'{
|
|
125
|
+
cwd: $cwd,
|
|
126
|
+
cwd_hash: $cwd_hash,
|
|
127
|
+
last_agent: $last_agent,
|
|
128
|
+
last_active: $last_active,
|
|
129
|
+
compacted_at: (if $compacted_at == "" then null else $compacted_at end),
|
|
130
|
+
branch: $branch
|
|
131
|
+
}' > "$META_TMP" && mv "$META_TMP" "$PROJECT_DIR/meta.json" \
|
|
132
|
+
|| rm -f "$META_TMP" 2>/dev/null || true
|
|
133
|
+
fi
|
|
134
|
+
|
|
135
|
+
# ── Append to sessions.log ────────────────────────────────────────────────────
|
|
136
|
+
|
|
137
|
+
mkdir -p "$(dirname "$SESSIONS_LOG")"
|
|
138
|
+
echo "$TIMESTAMP START $SESSION_ID ${CWD:-unknown}" >> "$SESSIONS_LOG"
|
|
139
|
+
|
|
140
|
+
storage_upkeep_async "$CWD"
|
|
141
|
+
exit 0
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# stop-check.sh — Canonical Policy Library: Stop hook
|
|
3
|
+
#
|
|
4
|
+
# Two responsibilities:
|
|
5
|
+
# 1. Batch lint dispatch: reads the list of files edited this turn
|
|
6
|
+
# (written by a harness's own post-edit-lint hook), dispatches
|
|
7
|
+
# formatters async and linters sync with a 2s timeout, then clears the
|
|
8
|
+
# list. Only runs when the incoming payload carries a session_id — a
|
|
9
|
+
# harness with no edited-files queue of its own (e.g. Antigravity, which
|
|
10
|
+
# wires no PostToolUse editor hook yet) simply has nothing to drain here.
|
|
11
|
+
# 2. Stub detection: takes the payload's last_assistant_message, or else the
|
|
12
|
+
# last assistant message in the JSONL transcript (Claude Code, Codex and
|
|
13
|
+
# flat {role,content} shapes), then checks for unfinished-work markers.
|
|
14
|
+
# Exits non-zero if stubs are found (blocks the session from stopping).
|
|
15
|
+
# Runs independently of (1) — it needs only the payload field or a
|
|
16
|
+
# transcript path, not a session_id, so it still fires for a harness that
|
|
17
|
+
# has no edited-files queue. A given message blocks once: harnesses without
|
|
18
|
+
# stop_hook_active (Antigravity, Kiro) have no other loop guard.
|
|
19
|
+
# 3. Process-leak scan: DoFlow's own identifiers (FR-###, agent-docs/, chain
|
|
20
|
+
# artifact names) reaching files that ship. Warns, never blocks.
|
|
21
|
+
#
|
|
22
|
+
# Multi-session safe: reads/writes only from sessions/{session_id}/.
|
|
23
|
+
# Async lint uses </dev/null >/dev/null to avoid holding stdin fd.
|
|
24
|
+
#
|
|
25
|
+
# Canonical Policy Script Contract (design.md §4):
|
|
26
|
+
# env DOFLOW_PROJECT_DIR, DOFLOW_AGENT (both optional here)
|
|
27
|
+
# stdin a Stop-shaped JSON payload. session_id and transcript path field
|
|
28
|
+
# names both vary by harness (transcript_path vs. Antigravity's
|
|
29
|
+
# camelCase transcriptPath) — this script reads the union of known
|
|
30
|
+
# field names rather than assuming one, since a native front door
|
|
31
|
+
# (Claude/Codex/Kiro) execs this script directly with no payload
|
|
32
|
+
# translation of its own.
|
|
33
|
+
# exit 0 = allow the stop, non-zero = deny (block the stop) with the
|
|
34
|
+
# reason on stderr.
|
|
35
|
+
#
|
|
36
|
+
# Depends on lib.sh (require_jq, json_field, ensure_session_dir,
|
|
37
|
+
# run_with_timeout) being discoverable next to this script when it runs — see
|
|
38
|
+
# session-context.sh's header note; the same front-door responsibility
|
|
39
|
+
# applies here.
|
|
40
|
+
#
|
|
41
|
+
# Reconciliation note (022-normalize-hooks, Phase A): claude's own copy of
|
|
42
|
+
# this policy additionally ran a process-leak scan (step 3) that
|
|
43
|
+
# codex/kiro's copies did not have; that step is folded in here as the more
|
|
44
|
+
# complete, already-proven behavior (D3) rather than dropped. Antigravity's
|
|
45
|
+
# copy runs stub detection only (no lint dispatch, no leak scan — it has no
|
|
46
|
+
# edited-files queue to drain), gated on session_id being absent being a
|
|
47
|
+
# no-op rather than an early exit, so stub detection still fires for it.
|
|
48
|
+
|
|
49
|
+
set -euo pipefail
|
|
50
|
+
# shellcheck source=lib.sh
|
|
51
|
+
source "$(dirname "$0")/lib.sh"
|
|
52
|
+
require_jq
|
|
53
|
+
|
|
54
|
+
INPUT=$(cat)
|
|
55
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
56
|
+
|
|
57
|
+
TRANSCRIPT_PATH=$(json_field "$INPUT" ".transcript_path")
|
|
58
|
+
[ -z "$TRANSCRIPT_PATH" ] && TRANSCRIPT_PATH=$(json_field "$INPUT" ".transcriptPath")
|
|
59
|
+
|
|
60
|
+
# ── 1. Lint dispatch (only when we have a session-scoped edited-files queue) ──
|
|
61
|
+
|
|
62
|
+
if [ -n "$SESSION_ID" ]; then
|
|
63
|
+
SESSION_PATH=$(ensure_session_dir "$SESSION_ID")
|
|
64
|
+
EDITED_FILES="$SESSION_PATH/edited-files.txt"
|
|
65
|
+
PROC_FILE="$SESSION_PATH/edited-files.txt.proc"
|
|
66
|
+
|
|
67
|
+
# Atomic read-and-clear: mv before reading so concurrent PostToolUse writes
|
|
68
|
+
# go to a fresh edited-files.txt, not the file we're about to process.
|
|
69
|
+
mv "$EDITED_FILES" "$PROC_FILE" 2>/dev/null || true
|
|
70
|
+
|
|
71
|
+
if [ -f "$PROC_FILE" ] && [ -s "$PROC_FILE" ]; then
|
|
72
|
+
# Partition paths by extension
|
|
73
|
+
py_files=()
|
|
74
|
+
js_files=()
|
|
75
|
+
go_files=()
|
|
76
|
+
leak_files=()
|
|
77
|
+
java_has_files=false
|
|
78
|
+
|
|
79
|
+
# The resolver for the leak-scan verb. Absent install -> the scan is
|
|
80
|
+
# skipped, like every other tool below: this hook has never required
|
|
81
|
+
# anything to be present.
|
|
82
|
+
DOFLOW_RUN=""
|
|
83
|
+
for candidate in \
|
|
84
|
+
"${CLAUDE_CONFIG_DIR:-$HOME/.claude}/scripts/doflow/bin/doflow-run" \
|
|
85
|
+
"$HOME/.doflow/scripts/doflow/bin/doflow-run"
|
|
86
|
+
do
|
|
87
|
+
[ -x "$candidate" ] && { DOFLOW_RUN="$candidate"; break; }
|
|
88
|
+
done
|
|
89
|
+
|
|
90
|
+
while IFS= read -r path; do
|
|
91
|
+
[ -z "$path" ] && continue
|
|
92
|
+
case "$path" in
|
|
93
|
+
*.py) py_files+=("$path") ;;
|
|
94
|
+
*.ts|*.tsx|*.js|*.jsx) js_files+=("$path") ;;
|
|
95
|
+
*.go) go_files+=("$path") ;;
|
|
96
|
+
*.java) java_has_files=true ;;
|
|
97
|
+
esac
|
|
98
|
+
# Every edited path is a leak-scan candidate regardless of language;
|
|
99
|
+
# the verb decides what it can read and reports the rest as unscanned.
|
|
100
|
+
case "$path" in
|
|
101
|
+
*/agent-docs/*|agent-docs/*) ;; # correct usage there; the verb excludes it too
|
|
102
|
+
*) leak_files+=("$path") ;;
|
|
103
|
+
esac
|
|
104
|
+
done < "$PROC_FILE"
|
|
105
|
+
|
|
106
|
+
# Python: async format, sync check (output goes to stderr: on Codex, text on stdout
|
|
107
|
+
# before the {} reply is invalid hook output)
|
|
108
|
+
if [ ${#py_files[@]} -gt 0 ]; then
|
|
109
|
+
if command -v ruff &>/dev/null; then
|
|
110
|
+
nohup ruff format "${py_files[@]}" </dev/null >/dev/null 2>&1 &
|
|
111
|
+
run_with_timeout 2 -- ruff check "${py_files[@]}" >&2 || true
|
|
112
|
+
fi
|
|
113
|
+
fi
|
|
114
|
+
|
|
115
|
+
# JS/TS: async fix only (eslint --fix rarely needs the caller's immediate attention)
|
|
116
|
+
if [ ${#js_files[@]} -gt 0 ]; then
|
|
117
|
+
if command -v eslint &>/dev/null; then
|
|
118
|
+
nohup eslint --fix "${js_files[@]}" </dev/null >/dev/null 2>&1 &
|
|
119
|
+
fi
|
|
120
|
+
fi
|
|
121
|
+
|
|
122
|
+
# Go: sync format (gofmt is fast, <100ms for typical files)
|
|
123
|
+
if [ ${#go_files[@]} -gt 0 ]; then
|
|
124
|
+
if command -v gofmt &>/dev/null; then
|
|
125
|
+
run_with_timeout 2 -- gofmt -w "${go_files[@]}" >&2 || true
|
|
126
|
+
fi
|
|
127
|
+
fi
|
|
128
|
+
|
|
129
|
+
# Java: async spotlessApply (slow — only if gradlew exists in cwd)
|
|
130
|
+
if [ "$java_has_files" = "true" ]; then
|
|
131
|
+
if [ -x "./gradlew" ]; then
|
|
132
|
+
nohup ./gradlew spotlessApply </dev/null >/dev/null 2>&1 &
|
|
133
|
+
fi
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Process-leak scan: DoFlow's own identifiers (FR-###, agent-docs/, chain
|
|
137
|
+
# artifact names) reaching files that ship. Warns, never blocks — a
|
|
138
|
+
# legitimate occurrence exists in docs *about* DoFlow, and a false
|
|
139
|
+
# positive that failed a turn would cost more than the leak it prevented.
|
|
140
|
+
if [ ${#leak_files[@]} -gt 0 ]; then
|
|
141
|
+
leak_args=()
|
|
142
|
+
for p in "${leak_files[@]}"; do leak_args+=(--path "$p"); done
|
|
143
|
+
if [ -n "$DOFLOW_RUN" ]; then
|
|
144
|
+
leak_out="$(run_with_timeout 2 -- "$DOFLOW_RUN" leak-scan "${leak_args[@]}" 2>/dev/null || true)"
|
|
145
|
+
if [ -n "$leak_out" ] && printf '%s' "$leak_out" | grep -q ':[0-9]'; then
|
|
146
|
+
printf 'doflow: internal identifiers found in shipped files (warning, not a block):\n%s\n' \
|
|
147
|
+
"$leak_out" >&2
|
|
148
|
+
fi
|
|
149
|
+
fi
|
|
150
|
+
fi
|
|
151
|
+
|
|
152
|
+
rm -f "$PROC_FILE"
|
|
153
|
+
fi
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
# ── 2. Stub detection ─────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
# A stop that continues an earlier block has already been asked to finish once;
|
|
159
|
+
# re-blocking it would loop, so only the lint queue above runs for it.
|
|
160
|
+
[ "$(printf '%s' "$INPUT" | jq -r '.stop_hook_active // false' 2>/dev/null)" = "true" ] && exit 0
|
|
161
|
+
|
|
162
|
+
# The payload's own last_assistant_message is the response being stopped on. A present key
|
|
163
|
+
# (even null or empty) is authoritative: the transcript is only read when the key is absent.
|
|
164
|
+
# jq filter for the transcript fallback: one JSON object per line, assistant text from three
|
|
165
|
+
# shapes — Claude Code (.message with text blocks), Codex rollout (response_item .payload with
|
|
166
|
+
# output_text blocks) and the flat {role,content} line. A partial or foreign line yields "".
|
|
167
|
+
read -r -d '' LAST_ASSISTANT_JQ <<'JQ' || true
|
|
168
|
+
def text: if type == "string" then .
|
|
169
|
+
elif type == "array" then [ .[]? | objects | select(.type == "text" or .type == "output_text") | .text | strings ] | join("\n")
|
|
170
|
+
else "" end;
|
|
171
|
+
[ inputs | fromjson? | objects
|
|
172
|
+
| if ((.message | objects | .role) // null) == "assistant" then .message.content
|
|
173
|
+
elif .type == "response_item" and ((.payload | objects | .role) // null) == "assistant" then .payload.content
|
|
174
|
+
elif .role == "assistant" then .content
|
|
175
|
+
else empty end
|
|
176
|
+
| text | select(length > 0) ] | last // ""
|
|
177
|
+
JQ
|
|
178
|
+
|
|
179
|
+
if printf '%s' "$INPUT" | jq -e 'has("last_assistant_message")' >/dev/null 2>&1; then
|
|
180
|
+
LAST_ASSISTANT_CONTENT=$(printf '%s' "$INPUT" | jq -r '.last_assistant_message // ""' 2>/dev/null || true)
|
|
181
|
+
else
|
|
182
|
+
# Only parse transcript if we have a path to it
|
|
183
|
+
[ -z "$TRANSCRIPT_PATH" ] && exit 0
|
|
184
|
+
[ -f "$TRANSCRIPT_PATH" ] || exit 0
|
|
185
|
+
|
|
186
|
+
# tail-scan is O(constant) regardless of transcript size — the last assistant
|
|
187
|
+
# entry is always near the end of the file. 200 lines covers any realistic
|
|
188
|
+
# single response without loading the entire (potentially multi-MB) transcript.
|
|
189
|
+
LAST_ASSISTANT_CONTENT=$(tail -n 200 "$TRANSCRIPT_PATH" | jq -Rnr "$LAST_ASSISTANT_JQ" 2>/dev/null || true)
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
[ -z "$LAST_ASSISTANT_CONTENT" ] && exit 0
|
|
193
|
+
|
|
194
|
+
# Text that only resembles a marker is cleaned before the match, outside ``` fences only, except
|
|
195
|
+
# URLs: a markdown heading loses its leading hashes (## TODO list, # Todo) so a real marker later on
|
|
196
|
+
# the same line still counts; "the // TODO comment" mentions (a marker, comment(s) or marker(s), then
|
|
197
|
+
# the end of the sentence) are cut out; a URL, up to a space, quote, ) or //, is cut out
|
|
198
|
+
# (http://todo-app.example.com). The text is lowercased, which the case-insensitive match ignores.
|
|
199
|
+
# Every step is linear in the line length: a line with no "://" is not touched by the URL step,
|
|
200
|
+
# and the URL step splits on "://" and trims each scheme (at most 32 characters) backwards.
|
|
201
|
+
# Ceiling: an unfenced title-case `# Todo`/`# Fixme` at the start of a line reads as a heading, and an
|
|
202
|
+
# unfenced `## TODO: x` as a heading, not a Python comment.
|
|
203
|
+
STUB_TEXT=$(printf '%s\n' "$LAST_ASSISTANT_CONTENT" | awk '
|
|
204
|
+
function strip_urls(s, n, parts, k, cur, out, L, m, frag) {
|
|
205
|
+
if (index(s, "://") == 0) return s
|
|
206
|
+
n = split(s, parts, "://")
|
|
207
|
+
cur = parts[1]; out = ""
|
|
208
|
+
for (k = 2; k <= n; k++) {
|
|
209
|
+
L = length(cur); m = 0
|
|
210
|
+
while (m < 32 && m < L && substr(cur, L - m, 1) ~ /[A-Za-z0-9+.-]/) m++
|
|
211
|
+
while (m > 0 && substr(cur, L - m + 1, 1) !~ /[A-Za-z]/) m--
|
|
212
|
+
if (m > 0) {
|
|
213
|
+
out = out substr(cur, 1, L - m)
|
|
214
|
+
frag = parts[k]
|
|
215
|
+
cur = match(frag, urlend) ? substr(frag, RSTART) : ""
|
|
216
|
+
} else {
|
|
217
|
+
out = out cur "://"
|
|
218
|
+
cur = parts[k]
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
return out cur
|
|
222
|
+
}
|
|
223
|
+
BEGIN {
|
|
224
|
+
q = "\047"
|
|
225
|
+
urlend = "[[:space:]\"" q "`)]|//"
|
|
226
|
+
mention = "(#|//)[[:space:]]*(todo|fixme)s?[[:space:]]+(comments?|markers?)[[:space:]]*([.,;!?)]|$)"
|
|
227
|
+
}
|
|
228
|
+
/^[[:space:]]*(```|~~~)/ { fence = !fence }
|
|
229
|
+
{
|
|
230
|
+
if (!fence) {
|
|
231
|
+
if ($0 ~ /^ ? ? ?###*[[:space:]]+/) sub(/^ ? ? ?#+[[:space:]]+/, "")
|
|
232
|
+
else if ($0 ~ /^ ? ? ?#[[:space:]]+(Todo|Fixme)([^[:alnum:]_]|$)/) sub(/^ ? ? ?#[[:space:]]+/, "")
|
|
233
|
+
}
|
|
234
|
+
line = tolower($0)
|
|
235
|
+
line = strip_urls(line)
|
|
236
|
+
if (!fence) gsub(mention, " ", line)
|
|
237
|
+
print line
|
|
238
|
+
}') || STUB_TEXT=$LAST_ASSISTANT_CONTENT
|
|
239
|
+
|
|
240
|
+
# Search extracted content for unfinished-work markers
|
|
241
|
+
# Match stubs only inside code comment context to avoid false positives from
|
|
242
|
+
# explanatory prose (e.g. "I removed the TODO comment" should not trigger).
|
|
243
|
+
# POSIX ERE has no \b; require what follows TODO/FIXME/stub to be a
|
|
244
|
+
# non-word character or end-of-string instead, so "TODOX" doesn't match.
|
|
245
|
+
STUB_PATTERN='(#|//)[[:space:]]*(TODO|FIXME)([^[:alnum:]_]|$)|raise NotImplementedError|throw new Error\(.*[Nn]ot [Ii]mplemented|(#|//)[[:space:]]*stub([^[:alnum:]_]|$)'
|
|
246
|
+
|
|
247
|
+
if grep -qiE -- "$STUB_PATTERN" <<<"$STUB_TEXT" 2>/dev/null; then
|
|
248
|
+
# Block a given message once: the continuation that follows is judged on new text, and a
|
|
249
|
+
# harness with no stop_hook_active would otherwise be blocked on this one forever. State is
|
|
250
|
+
# per session, or per transcript when the harness sends no session_id; any failure here
|
|
251
|
+
# leaves the block in force only for this call.
|
|
252
|
+
GUARD_KEY="$SESSION_ID"
|
|
253
|
+
[ -z "$GUARD_KEY" ] && GUARD_KEY="t$(printf '%s' "$TRANSCRIPT_PATH" | cksum | cut -d' ' -f1)"
|
|
254
|
+
GUARD_FILE="$(ensure_session_dir "$GUARD_KEY" 2>/dev/null)/stop-last-blocked" || GUARD_FILE=""
|
|
255
|
+
MESSAGE_SUM=$(printf '%s' "$LAST_ASSISTANT_CONTENT" | cksum 2>/dev/null) || MESSAGE_SUM=""
|
|
256
|
+
if [ -n "$MESSAGE_SUM" ] && [ -n "$GUARD_FILE" ] && [ "$(cat "$GUARD_FILE" 2>/dev/null)" = "$MESSAGE_SUM" ]; then
|
|
257
|
+
exit 0
|
|
258
|
+
fi
|
|
259
|
+
[ -n "$MESSAGE_SUM" ] && [ -n "$GUARD_FILE" ] && { printf '%s\n' "$MESSAGE_SUM" > "$GUARD_FILE"; } 2>/dev/null || true
|
|
260
|
+
echo "[stop-check] The last response contains an unfinished-work marker (TODO/FIXME/stub comment or a not-implemented raise/throw)." >&2
|
|
261
|
+
exit 2
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
exit 0
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# subagent-audit.sh — Canonical Policy Library: SubagentStart + SubagentStop hook
|
|
3
|
+
# (pure observability, no deny path)
|
|
4
|
+
#
|
|
5
|
+
# 022-hooks-remaining-duplication: claude and codex's copies were byte-identical — moved here as
|
|
6
|
+
# the single canonical copy. Gemini has no SubagentStart/SubagentStop-equivalent event.
|
|
7
|
+
#
|
|
8
|
+
# Appends {timestamp, session_id, event, agent_type, agent_id} to a session-scoped audit log.
|
|
9
|
+
# Today there is zero record of which of the installed agents/ specialists actually run, how often, or
|
|
10
|
+
# whether one is invoked outside its intended use (e.g. everything routing through general-purpose
|
|
11
|
+
# instead of do-code-review/security-engineer) — this hook is the evidence base a future policy
|
|
12
|
+
# hook (e.g. an agent-aware pre-bash-guard extension) would need before writing rules on a guess.
|
|
13
|
+
# Per agent-docs/research/hook-governance-agent-tool-mcp-skill.md §3.1: pure logging, no deny path,
|
|
14
|
+
# safe to ship immediately, and the recommended first step before any tighter agent governance.
|
|
15
|
+
#
|
|
16
|
+
# Multi-session safe: writes only to sessions/{session_id}/subagent-audit.log.
|
|
17
|
+
# Must never exit non-zero or block a subagent from starting/stopping.
|
|
18
|
+
|
|
19
|
+
set -euo pipefail
|
|
20
|
+
# shellcheck source=lib.sh
|
|
21
|
+
source "$(dirname "$0")/lib.sh"
|
|
22
|
+
require_jq
|
|
23
|
+
|
|
24
|
+
INPUT=$(cat)
|
|
25
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
26
|
+
|
|
27
|
+
# Guard: if we can't determine the session, nothing to do
|
|
28
|
+
[[ -z "$SESSION_ID" ]] && exit 0
|
|
29
|
+
|
|
30
|
+
EVENT=$(json_field "$INPUT" ".hook_event_name")
|
|
31
|
+
AGENT_TYPE=$(json_field "$INPUT" ".agent_type")
|
|
32
|
+
AGENT_ID=$(json_field "$INPUT" ".agent_id")
|
|
33
|
+
TIMESTAMP=$(date -u +%FT%TZ)
|
|
34
|
+
|
|
35
|
+
SESSION_PATH=$(ensure_session_dir "$SESSION_ID")
|
|
36
|
+
|
|
37
|
+
jq -nc \
|
|
38
|
+
--arg ts "$TIMESTAMP" \
|
|
39
|
+
--arg session "$SESSION_ID" \
|
|
40
|
+
--arg event "$EVENT" \
|
|
41
|
+
--arg agent_type "$AGENT_TYPE" \
|
|
42
|
+
--arg agent_id "$AGENT_ID" \
|
|
43
|
+
'{timestamp: $ts, session_id: $session, event: $event, agent_type: $agent_type, agent_id: $agent_id}' \
|
|
44
|
+
>> "$SESSION_PATH/subagent-audit.log" 2>/dev/null || true
|
|
45
|
+
|
|
46
|
+
exit 0
|