@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.4
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 +1061 -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 +48 -4
- package/src/adapters/antigravity/index.js +631 -0
- package/src/adapters/claude/index.js +548 -0
- package/src/adapters/claude/mcp.js +94 -0
- package/src/adapters/codex/agents.js +208 -0
- package/src/adapters/codex/config.js +272 -0
- package/src/adapters/codex/hooks.js +203 -0
- package/src/adapters/codex/index.js +707 -0
- package/src/adapters/codex/mcp.js +271 -0
- package/src/adapters/codex/native-projection.js +34 -0
- package/src/adapters/copilot/index.js +413 -0
- package/src/adapters/copy-tree.js +620 -0
- package/src/adapters/gemini/hooks.js +210 -0
- package/src/adapters/gemini/index.js +390 -0
- package/src/adapters/hook-commands.js +60 -0
- package/src/adapters/index.js +106 -0
- package/src/adapters/kiro/index.js +310 -0
- package/src/adapters/mcp-entries.js +331 -0
- package/src/adapters/opencode/index.js +466 -0
- package/src/adapters/pi/index.js +279 -0
- package/src/adapters/pi/json-members.js +185 -0
- package/src/adapters/pi/mcp.js +373 -0
- package/src/adapters/resolve-source.js +19 -0
- package/src/cli/commands/install.js +218 -0
- package/src/cli/commands/list-backups.js +21 -0
- package/src/cli/commands/reconcile.js +120 -0
- package/src/cli/commands/remove.js +82 -0
- package/src/cli/commands/rollback.js +172 -0
- package/src/cli/commands/self-update.js +33 -0
- package/src/cli/commands/status.js +138 -0
- package/src/cli/commands/tools.js +92 -0
- package/src/cli/commands/update.js +162 -0
- package/src/cli/index.js +568 -0
- package/src/cli/runtime-commands.js +281 -0
- package/src/cli/shared.js +218 -0
- package/src/helper/bash-helper.js +34 -0
- package/src/helper/git.js +24 -0
- package/src/helper/guardrails.js +49 -0
- package/src/helper/harness-paths.js +140 -0
- package/src/helper/marker-merge.js +116 -0
- package/src/helper/prompt.js +61 -0
- package/src/helper/repo-root.js +29 -0
- package/src/helper/settings-merge.js +151 -0
- package/src/helper/settings-scope.js +33 -0
- package/src/helper/toml.js +244 -0
- package/src/install/backup-set.js +149 -0
- package/src/install/backup.js +724 -0
- package/src/install/context.js +58 -0
- package/src/install/manifest.js +145 -0
- package/src/install/mcp.js +254 -0
- package/src/install/paths.js +84 -0
- package/src/install/targets.js +90 -0
- package/src/install/tool-lifecycle.js +151 -0
- package/src/lifecycle/bash-availability.js +25 -0
- package/src/lifecycle/index.js +812 -0
- package/src/lifecycle/mcp-index.js +28 -0
- package/src/lifecycle/policies.js +73 -0
- package/src/lifecycle/view.js +175 -0
- package/src/registry/index.js +611 -0
- package/src/release/build-package.js +91 -0
- package/src/release/changelog-section.js +70 -0
- package/src/release/sync-plugin-versions.js +66 -0
- package/src/runtime/advice/cli.js +283 -0
- package/src/runtime/advice/client.js +324 -0
- package/src/runtime/advice/config.js +189 -0
- package/src/runtime/advice/doctor.js +169 -0
- package/src/runtime/advice/endpoint-guard.js +134 -0
- package/src/runtime/advice/kinds.js +154 -0
- package/src/runtime/advice/providers/ollama-systemone.js +209 -0
- package/src/runtime/advice/record.js +166 -0
- package/src/runtime/advice/registry.js +170 -0
- package/src/runtime/advice/strength.js +66 -0
- package/src/runtime/brief-tools.js +342 -0
- package/src/runtime/c4-project.js +371 -0
- package/src/runtime/capability-router.js +484 -0
- package/src/runtime/checkouts.js +170 -0
- package/src/runtime/claims.js +468 -0
- package/src/runtime/cli-result.js +55 -0
- package/src/runtime/cli.js +678 -0
- package/src/runtime/command-detect.js +516 -0
- package/src/runtime/context-pack.js +492 -0
- package/src/runtime/decision-register.js +493 -0
- package/src/runtime/evidence-ledger.js +338 -0
- package/src/runtime/failure/capture.js +272 -0
- package/src/runtime/failure/classifier.js +68 -0
- package/src/runtime/failure/cli.js +178 -0
- package/src/runtime/failure/home.js +64 -0
- package/src/runtime/failure/store.js +226 -0
- package/src/runtime/feature-resolve.js +79 -0
- package/src/runtime/freshness.js +177 -0
- package/src/runtime/health.js +863 -0
- package/src/runtime/history-compactor.js +237 -0
- package/src/runtime/implementation-gate.js +321 -0
- package/src/runtime/inventory/coverage.js +218 -0
- package/src/runtime/inventory/identity.js +65 -0
- package/src/runtime/inventory/index.js +705 -0
- package/src/runtime/inventory/precedence.js +134 -0
- package/src/runtime/inventory/read-scopes.js +131 -0
- package/src/runtime/inventory/siblings.js +192 -0
- package/src/runtime/knowledge/chunker.js +124 -0
- package/src/runtime/knowledge/hybrid.js +149 -0
- package/src/runtime/knowledge/index-store.js +145 -0
- package/src/runtime/knowledge/retrieval.js +194 -0
- package/src/runtime/leak-scan.js +185 -0
- package/src/runtime/lifecycle/cli.js +276 -0
- package/src/runtime/lifecycle/event-store.js +340 -0
- package/src/runtime/lifecycle/fold.js +353 -0
- package/src/runtime/lifecycle/followup.js +432 -0
- package/src/runtime/lifecycle/goal.js +249 -0
- package/src/runtime/lifecycle/intent-writer.js +86 -0
- package/src/runtime/lifecycle/overview.js +292 -0
- package/src/runtime/lifecycle/release.js +241 -0
- package/src/runtime/lifecycle/report-store.js +221 -0
- package/src/runtime/lifecycle/retention.js +212 -0
- package/src/runtime/lifecycle/root.js +71 -0
- package/src/runtime/lifecycle/status.js +353 -0
- package/src/runtime/lifecycle/store-upkeep.js +393 -0
- package/src/runtime/locator-resolve.js +158 -0
- package/src/runtime/mask.js +210 -0
- package/src/runtime/mcp-consultations.js +412 -0
- package/src/runtime/mcp-reach.js +157 -0
- package/src/runtime/model-router.js +153 -0
- package/src/runtime/outcome.js +467 -0
- package/src/runtime/reach.js +148 -0
- package/src/runtime/readiness-record.js +150 -0
- package/src/runtime/readiness.js +435 -0
- package/src/runtime/recovery.js +282 -0
- package/src/runtime/research-request.js +231 -0
- package/src/runtime/retrieval-plan.js +578 -0
- package/src/runtime/scaffold/artifacts.js +217 -0
- package/src/runtime/scaffold/fingerprint.js +56 -0
- package/src/runtime/scaffold/generate.js +911 -0
- package/src/runtime/scaffold/languages.js +245 -0
- package/src/runtime/storage/cli.js +64 -0
- package/src/runtime/storage/engine.js +249 -0
- package/src/runtime/storage/registry.js +130 -0
- package/src/runtime/storage/roots.js +63 -0
- package/src/runtime/storage/rules.js +282 -0
- package/src/runtime/storage/upkeep.js +243 -0
- package/src/runtime/storage/work-status.js +326 -0
- package/src/runtime/task-attempts.js +415 -0
- package/src/runtime/task-classifier.js +550 -0
- package/src/runtime/task-scope.js +130 -0
- package/src/runtime/task-state.js +176 -0
- package/src/runtime/trace/indicators.js +235 -0
- package/src/runtime/trace/ledger.js +626 -0
- package/src/runtime/trace/render.js +76 -0
- package/src/runtime/trace/views.js +459 -0
- package/src/runtime/verification/contract-runner.js +341 -0
- package/src/runtime/verification/engine.js +1461 -0
- package/src/runtime/verification/registry.js +130 -0
- package/src/runtime/verification/scope-bound.js +418 -0
- package/src/runtime/verification/test-selection.js +172 -0
- package/src/runtime/workflow-engine.js +588 -0
- package/src/runtime/workflow-orchestrator.js +953 -0
- package/src/runtime/worktree.js +340 -0
- package/src/state/index.js +193 -0
- package/src/state/lockfile.js +136 -0
- package/src/state/run-lock.js +408 -0
- package/types/node-commonjs.d.ts +68 -0
- package/types/runtime.d.ts +91 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# lib.sh — shared constants and helpers for session lifecycle hooks
|
|
3
|
+
#
|
|
4
|
+
# Usage: source "$(dirname "$0")/lib.sh"
|
|
5
|
+
#
|
|
6
|
+
# All hooks must:
|
|
7
|
+
# 1. source this file at the top
|
|
8
|
+
# 2. call require_jq immediately after
|
|
9
|
+
# 3. capture stdin once: INPUT=$(cat)
|
|
10
|
+
# 4. use: json_field "$INPUT" ".field_name"
|
|
11
|
+
#
|
|
12
|
+
# Deployment:
|
|
13
|
+
# This file is installed by the doflow CLI and overwritten on update — do
|
|
14
|
+
# not edit an installed copy directly.
|
|
15
|
+
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
# ── State directories ────────────────────────────────────────────────────────
|
|
19
|
+
|
|
20
|
+
# XDG-compliant, agent-agnostic store shared by Claude Code, Codex, Gemini, etc.
|
|
21
|
+
DOFLOW_HOME="${XDG_CONFIG_HOME:-$HOME/.config}/doflow"
|
|
22
|
+
STATE_DIR="$DOFLOW_HOME/session-env"
|
|
23
|
+
SESSION_DIR="$STATE_DIR/sessions"
|
|
24
|
+
PROJECTS_DIR="$STATE_DIR/projects"
|
|
25
|
+
# shellcheck disable=SC2034 # used by session-start.sh and session-end.sh which source this file
|
|
26
|
+
SESSIONS_LOG="$DOFLOW_HOME/sessions.log"
|
|
27
|
+
|
|
28
|
+
# Identifies which agent is running. Override via env var for non-Claude agents.
|
|
29
|
+
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude-code}"
|
|
30
|
+
|
|
31
|
+
# ── canonicalize_path ─────────────────────────────────────────────────────────
|
|
32
|
+
|
|
33
|
+
# Print an absolute, symlink-resolved form of <path> to stdout when <path>
|
|
34
|
+
# exists; otherwise print <path> unchanged.
|
|
35
|
+
#
|
|
36
|
+
# Replaces `realpath -e "$1" 2>/dev/null || echo "$1"`, which already fails
|
|
37
|
+
# silently on stock macOS (BSD realpath has no -e flag there) and falls back
|
|
38
|
+
# to an uncanonicalized path. Uses only primitives present on every target
|
|
39
|
+
# (cd, pwd -P, dirname, basename) — no GNU-only flag, no assumed Homebrew
|
|
40
|
+
# coreutils.
|
|
41
|
+
#
|
|
42
|
+
# Hardening notes:
|
|
43
|
+
# - `CDPATH=` resets CDPATH for the internal `cd` so a user's exported
|
|
44
|
+
# CDPATH can't redirect it to an unrelated directory of the same name
|
|
45
|
+
# (and can't make `cd` echo a stray "found via CDPATH" line to stdout).
|
|
46
|
+
# - `--` guards `dirname`/`basename`/`cd` against a path that begins with
|
|
47
|
+
# `-` being parsed as an option.
|
|
48
|
+
# - if the internal `cd` fails for any reason, `||` falls through to the
|
|
49
|
+
# uncanonicalized-path branch instead of aborting under this file's
|
|
50
|
+
# `set -e` (matching the old `realpath ... || echo "$1"` degrade path).
|
|
51
|
+
# - a root-level parent directory ("/") is special-cased so the result
|
|
52
|
+
# never gets a leading `//`, which POSIX leaves undefined and which
|
|
53
|
+
# Cygwin/MSYS2 (a DoFlow target platform) interprets as a UNC path.
|
|
54
|
+
canonicalize_path() {
|
|
55
|
+
local path="$1" dir base
|
|
56
|
+
if [[ -e "$path" ]] \
|
|
57
|
+
&& dir=$(CDPATH= cd -P -- "$(dirname -- "$path")" 2>/dev/null && pwd -P); then
|
|
58
|
+
base=$(basename -- "$path")
|
|
59
|
+
if [[ "$dir" == "/" ]]; then
|
|
60
|
+
printf '/%s\n' "$base"
|
|
61
|
+
else
|
|
62
|
+
printf '%s/%s\n' "$dir" "$base"
|
|
63
|
+
fi
|
|
64
|
+
else
|
|
65
|
+
printf '%s\n' "$path"
|
|
66
|
+
fi
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# ── cwd_hash ─────────────────────────────────────────────────────────────────
|
|
70
|
+
|
|
71
|
+
# Derive a stable 16-char hash of an absolute directory path.
|
|
72
|
+
# Used to namespace per-project state (compact summaries, warnings).
|
|
73
|
+
# Normalizes symlinks and ../ components so equivalent paths hash identically.
|
|
74
|
+
#
|
|
75
|
+
# For a path that is (or resolves to) an existing directory — the case this
|
|
76
|
+
# function exists for — fully resolves it, including a symlink at the path's
|
|
77
|
+
# own leaf component, via `(cd "$1" && pwd -P)`, so equivalent directories
|
|
78
|
+
# always hash identically. Callers that ever pass a non-directory or
|
|
79
|
+
# nonexistent path fall back to the general-purpose canonicalize_path, which
|
|
80
|
+
# — being usable by any file or missing path, not just directories — only
|
|
81
|
+
# resolves symlinks in the parent chain, not the leaf itself.
|
|
82
|
+
#
|
|
83
|
+
# Hash fallback chain: sha256sum (Linux, Git Bash) -> shasum -a 256 (macOS,
|
|
84
|
+
# every release since 10.6) -> cksum (last resort). The cksum branch is
|
|
85
|
+
# normalized via printf '%016x' to the same 16-lowercase-hex-char contract
|
|
86
|
+
# every other branch provides (raw cksum output is decimal and shorter).
|
|
87
|
+
# Collision risk from the cksum fallback is acceptable here — this is only a
|
|
88
|
+
# cache-key namespace, not security-relevant.
|
|
89
|
+
cwd_hash() {
|
|
90
|
+
local canonical
|
|
91
|
+
if [ -d "$1" ]; then
|
|
92
|
+
canonical=$(cd "$1" && pwd -P)
|
|
93
|
+
else
|
|
94
|
+
canonical=$(canonicalize_path "$1")
|
|
95
|
+
fi
|
|
96
|
+
if command -v sha256sum &>/dev/null; then
|
|
97
|
+
echo "$canonical" | sha256sum | cut -c1-16
|
|
98
|
+
elif command -v shasum &>/dev/null; then
|
|
99
|
+
echo "$canonical" | shasum -a 256 | cut -c1-16
|
|
100
|
+
else
|
|
101
|
+
printf '%016x\n' "$(echo "$canonical" | cksum | cut -d' ' -f1)"
|
|
102
|
+
fi
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
# ── with_file_lock / release_file_lock ────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
# Cross-platform mutual exclusion via atomic `mkdir` (atomic on every
|
|
108
|
+
# filesystem DoFlow runs on, including NTFS via MSYS2). Replaces
|
|
109
|
+
# `flock -x -w 5 200`, which is entirely absent on stock macOS (util-linux
|
|
110
|
+
# only, causing the session-end log-trimming block to silently no-op today).
|
|
111
|
+
#
|
|
112
|
+
# Usage (caller acquires, then is responsible for releasing — including on
|
|
113
|
+
# failure paths, ideally via `trap`):
|
|
114
|
+
#
|
|
115
|
+
# if with_file_lock "$LOCK_FILE" 5; then
|
|
116
|
+
# trap 'release_file_lock "$LOCK_FILE"' EXIT
|
|
117
|
+
# ...critical section...
|
|
118
|
+
# release_file_lock "$LOCK_FILE"
|
|
119
|
+
# fi
|
|
120
|
+
#
|
|
121
|
+
# Polls for the lock (creating "<lockfile>.d" as the lock token) until
|
|
122
|
+
# acquired or until <timeout_seconds> elapses. Returns 0 once acquired
|
|
123
|
+
# (lock held), or 1 on timeout — callers should treat a timeout the same
|
|
124
|
+
# way the old `flock -w 5 200 || exit 0` did: skip the guarded section
|
|
125
|
+
# rather than block indefinitely.
|
|
126
|
+
#
|
|
127
|
+
# Stale-lock recovery: unlike `flock`, an `mkdir` lock is not released
|
|
128
|
+
# automatically if the holder is killed or crashes — a leftover lockdir
|
|
129
|
+
# would otherwise wedge every future acquisition permanently. To guard
|
|
130
|
+
# against that, the holder's PID is written to "<lockfile>.d/pid" at
|
|
131
|
+
# acquire time; when this function finds the lockdir already taken, it
|
|
132
|
+
# checks whether that PID is still alive (`kill -0`) and, if not, treats
|
|
133
|
+
# the lock as abandoned and reclaims it immediately instead of waiting out
|
|
134
|
+
# the full timeout. This is a best-effort check (a PID can in principle be
|
|
135
|
+
# reused by an unrelated process before we look), not a hard guarantee —
|
|
136
|
+
# acceptable here because the guarded sections this protects are idempotent
|
|
137
|
+
# log/state maintenance, not a correctness-critical resource.
|
|
138
|
+
with_file_lock() {
|
|
139
|
+
local lockfile="$1"
|
|
140
|
+
local timeout_seconds="$2"
|
|
141
|
+
local lockdir="${lockfile}.d"
|
|
142
|
+
local pidfile="${lockdir}/pid"
|
|
143
|
+
local waited=0 holder_pid
|
|
144
|
+
while true; do
|
|
145
|
+
if mkdir "$lockdir" 2>/dev/null; then
|
|
146
|
+
echo "$$" >"$pidfile" 2>/dev/null || true
|
|
147
|
+
return 0
|
|
148
|
+
fi
|
|
149
|
+
# Someone else holds the lock (or a stale one was left behind) — check
|
|
150
|
+
# whether the recorded holder is still alive before waiting on it.
|
|
151
|
+
holder_pid=$(cat "$pidfile" 2>/dev/null || echo "")
|
|
152
|
+
if [[ -n "$holder_pid" ]] && ! kill -0 "$holder_pid" 2>/dev/null; then
|
|
153
|
+
rm -rf "$lockdir" 2>/dev/null || true
|
|
154
|
+
continue
|
|
155
|
+
fi
|
|
156
|
+
if (( waited >= timeout_seconds )); then
|
|
157
|
+
return 1
|
|
158
|
+
fi
|
|
159
|
+
sleep 1
|
|
160
|
+
waited=$((waited + 1))
|
|
161
|
+
done
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
# Companion release for with_file_lock. Idempotent — safe to call even if
|
|
165
|
+
# the lock was never acquired or was already released.
|
|
166
|
+
release_file_lock() {
|
|
167
|
+
local lockfile="$1"
|
|
168
|
+
local lockdir="${lockfile}.d"
|
|
169
|
+
rm -f "${lockdir}/pid" 2>/dev/null || true
|
|
170
|
+
rmdir "$lockdir" 2>/dev/null || true
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
# ── run_with_timeout ─────────────────────────────────────────────────────────
|
|
174
|
+
|
|
175
|
+
# Run <command...>, terminating it if it exceeds <seconds>. When no
|
|
176
|
+
# GNU-compatible timeout-enforcing binary (`timeout`/`gtimeout`) is on
|
|
177
|
+
# PATH — stock macOS lacks GNU coreutils' `timeout` unless Homebrew is
|
|
178
|
+
# installed — runs <command...> directly with no enforced budget rather
|
|
179
|
+
# than failing (a soft safety net, not a correctness requirement; NFR-002
|
|
180
|
+
# forbids requiring a new dependency).
|
|
181
|
+
#
|
|
182
|
+
# GNU-ness check: on Git Bash, PATH includes Windows' System32, which ships
|
|
183
|
+
# its own `timeout.exe` — an interactive countdown/delay command with
|
|
184
|
+
# incompatible syntax, not GNU coreutils' `timeout`. If it resolved first
|
|
185
|
+
# and were trusted blindly, `timeout 5 git status` would fail on argument
|
|
186
|
+
# parsing and the wrapped command would never run at all — strictly worse
|
|
187
|
+
# than the no-timeout-found fallback below. So a candidate binary is only
|
|
188
|
+
# trusted after `<bin> --version` succeeds (GNU coreutils understands
|
|
189
|
+
# `--version`; Windows' `timeout.exe` does not and errors out), otherwise
|
|
190
|
+
# the next candidate is tried and, failing that, the command runs directly.
|
|
191
|
+
#
|
|
192
|
+
# Usage: run_with_timeout 5 -- git status
|
|
193
|
+
run_with_timeout() {
|
|
194
|
+
local seconds="$1"
|
|
195
|
+
shift
|
|
196
|
+
if [[ "${1:-}" == "--" ]]; then
|
|
197
|
+
shift
|
|
198
|
+
fi
|
|
199
|
+
local timeout_bin=""
|
|
200
|
+
if command -v timeout &>/dev/null && timeout --version >/dev/null 2>&1; then
|
|
201
|
+
timeout_bin="timeout"
|
|
202
|
+
elif command -v gtimeout &>/dev/null && gtimeout --version >/dev/null 2>&1; then
|
|
203
|
+
timeout_bin="gtimeout"
|
|
204
|
+
fi
|
|
205
|
+
if [[ -n "$timeout_bin" ]]; then
|
|
206
|
+
"$timeout_bin" "$seconds" "$@"
|
|
207
|
+
else
|
|
208
|
+
"$@"
|
|
209
|
+
fi
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
# ── Git state helpers ────────────────────────────────────────────────────────
|
|
213
|
+
#
|
|
214
|
+
# Thin wrappers around `git -C <cwd> ...` with the project's standard 1s
|
|
215
|
+
# timeout. Each echoes git's own output (or nothing/0 on failure) and leaves
|
|
216
|
+
# stderr suppressed -- callers keep their own `|| echo <fallback>` around a
|
|
217
|
+
# call for a script-specific default, exactly as the inline git calls these
|
|
218
|
+
# replace did (022-hooks-remaining-duplication). Kept in this per-harness
|
|
219
|
+
# copy rather than moved to the canonical lib.sh's sourcing path: this
|
|
220
|
+
# script's own with_file_lock/other APIs have not been reconciled with the
|
|
221
|
+
# canonical copy, so repointing the `source` line is a separate, larger
|
|
222
|
+
# change this refactor does not make.
|
|
223
|
+
|
|
224
|
+
is_git_worktree() {
|
|
225
|
+
run_with_timeout 1 -- git -C "$1" rev-parse --is-inside-work-tree &>/dev/null
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
git_branch_of() {
|
|
229
|
+
run_with_timeout 1 -- git -C "$1" branch --show-current 2>/dev/null
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
git_short_sha_of() {
|
|
233
|
+
run_with_timeout 1 -- git -C "$1" rev-parse --short HEAD 2>/dev/null
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
git_uncommitted_count_of() {
|
|
237
|
+
run_with_timeout 1 -- git -C "$1" status --porcelain 2>/dev/null | wc -l | tr -d ' '
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
has_uncommitted_changes() {
|
|
241
|
+
run_with_timeout 1 -- git -C "$1" status --porcelain 2>/dev/null | grep -q .
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
# ── Directory helpers ─────────────────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
# Create and return the session-scoped state directory for a given session_id.
|
|
247
|
+
# Safe to call multiple times (mkdir -p is idempotent).
|
|
248
|
+
ensure_session_dir() {
|
|
249
|
+
local session_id="$1"
|
|
250
|
+
mkdir -p "$SESSION_DIR/$session_id"
|
|
251
|
+
echo "$SESSION_DIR/$session_id"
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
# Create and return the project-scoped state directory for a given cwd.
|
|
255
|
+
# Shared across all sessions in the same directory.
|
|
256
|
+
ensure_project_dir() {
|
|
257
|
+
local cwd="$1"
|
|
258
|
+
local hash
|
|
259
|
+
hash=$(cwd_hash "$cwd")
|
|
260
|
+
mkdir -p "$PROJECTS_DIR/$hash"
|
|
261
|
+
echo "$PROJECTS_DIR/$hash"
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
# ── JSON helpers ──────────────────────────────────────────────────────────────
|
|
265
|
+
|
|
266
|
+
# Extract a field from a JSON string.
|
|
267
|
+
# Usage: json_field "$INPUT" ".field_name"
|
|
268
|
+
# Returns empty string if field is null or jq fails.
|
|
269
|
+
json_field() {
|
|
270
|
+
local json="$1"
|
|
271
|
+
local query="$2"
|
|
272
|
+
echo "$json" | jq -r "$query // empty" 2>/dev/null || echo ""
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
# ── Dependency guard ──────────────────────────────────────────────────────────
|
|
276
|
+
|
|
277
|
+
# Verify jq is available at runtime. If absent, emit a diagnostic to stderr
|
|
278
|
+
# and exit 0 (never block Claude Code — degraded operation is preferable to failure).
|
|
279
|
+
require_jq() {
|
|
280
|
+
if ! command -v jq &>/dev/null; then
|
|
281
|
+
echo "[hooks] jq not found — install jq to enable session lifecycle hooks (apt install jq / brew install jq)" >&2
|
|
282
|
+
exit 0
|
|
283
|
+
fi
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
# ── Session role ──────────────────────────────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
# Print `main` or `worker` for this session, read from DOFLOW_SESSION_ROLE (matched
|
|
289
|
+
# case-insensitively). Unset or empty is `main`; a value that is neither `main` nor
|
|
290
|
+
# `worker` is `worker` and writes one line to stderr. A worker never writes or claims the
|
|
291
|
+
# project's compact summary or warning. Always returns 0. bash 3.2 safe (no ${var,,}).
|
|
292
|
+
doflow_session_role() {
|
|
293
|
+
local role
|
|
294
|
+
role=$(printf '%s' "${DOFLOW_SESSION_ROLE:-}" | tr '[:upper:]' '[:lower:]') || role=""
|
|
295
|
+
case "$role" in
|
|
296
|
+
""|main) printf 'main\n' ;;
|
|
297
|
+
worker) printf 'worker\n' ;;
|
|
298
|
+
*)
|
|
299
|
+
echo "[doflow] unknown DOFLOW_SESSION_ROLE; treating session as worker" >&2
|
|
300
|
+
printf 'worker\n'
|
|
301
|
+
;;
|
|
302
|
+
esac
|
|
303
|
+
return 0
|
|
304
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude front door: delegates the mcp-tool-guard decision to the Canonical Policy
|
|
3
|
+
# Library, then translates it into Claude Code's verified deny contract.
|
|
4
|
+
#
|
|
5
|
+
# Schema verified (Phase 8.3, 2026-04-17): hookSpecificOutput.permissionDecision
|
|
6
|
+
# "deny" is correctly recognized by Claude Code and blocks execution — exit code
|
|
7
|
+
# alone is not sufficient here, so this front door is a translator, not a bare
|
|
8
|
+
# exec (unlike session-context/stop-check, which never need this translation).
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude}"
|
|
11
|
+
|
|
12
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
13
|
+
source "$(dirname "$0")/../../.doflow/shared/hooks/policies/deny-json.sh"
|
|
14
|
+
|
|
15
|
+
POLICY="$(dirname "$0")/../../.doflow/shared/hooks/policies/mcp-tool-guard.sh"
|
|
16
|
+
REASON=$(bash "$POLICY" 2>&1 >/dev/null)
|
|
17
|
+
CODE=$?
|
|
18
|
+
|
|
19
|
+
[ "$CODE" -ne 0 ] && emit_pretooluse_deny_nested "${REASON:-MCP tool call blocked by mcp-tool-guard}"
|
|
20
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# permission-audit.sh — PermissionDenied hook (pure observability, no deny path)
|
|
3
|
+
#
|
|
4
|
+
# The decision has already been made by the time this fires; there is nothing to
|
|
5
|
+
# block. Its purpose is completeness of the audit trail: without it the record
|
|
6
|
+
# shows only what was permitted, which makes "this agent never tried X" and "this
|
|
7
|
+
# agent was refused X" indistinguishable after the fact.
|
|
8
|
+
#
|
|
9
|
+
# subagent-audit.sh deliberately does NOT serve this event: it records
|
|
10
|
+
# agent_type/agent_id, which a permission decision does not carry, and would
|
|
11
|
+
# write empty rows into the log it exists to keep meaningful.
|
|
12
|
+
#
|
|
13
|
+
# Appends {timestamp, session_id, event, tool_name, reason} to a session-scoped
|
|
14
|
+
# log. Fields beyond the common envelope are best-effort — an absent key yields
|
|
15
|
+
# empty rather than failing the hook.
|
|
16
|
+
#
|
|
17
|
+
# Multi-session safe: writes only to sessions/{session_id}/permission-audit.log.
|
|
18
|
+
# Must complete in <20ms. Must never exit non-zero.
|
|
19
|
+
|
|
20
|
+
set -euo pipefail
|
|
21
|
+
# shellcheck source=lib.sh
|
|
22
|
+
source "$(dirname "$0")/lib.sh"
|
|
23
|
+
require_jq
|
|
24
|
+
|
|
25
|
+
INPUT=$(cat)
|
|
26
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
27
|
+
|
|
28
|
+
# Guard: if we can't determine the session, nothing to do
|
|
29
|
+
[[ -z "$SESSION_ID" ]] && exit 0
|
|
30
|
+
|
|
31
|
+
EVENT=$(json_field "$INPUT" ".hook_event_name")
|
|
32
|
+
TOOL_NAME=$(json_field "$INPUT" ".tool_name")
|
|
33
|
+
REASON=$(json_field "$INPUT" ".permission_decision_reason")
|
|
34
|
+
TIMESTAMP=$(date -u +%FT%TZ)
|
|
35
|
+
|
|
36
|
+
SESSION_PATH=$(ensure_session_dir "$SESSION_ID")
|
|
37
|
+
|
|
38
|
+
jq -nc \
|
|
39
|
+
--arg ts "$TIMESTAMP" \
|
|
40
|
+
--arg session "$SESSION_ID" \
|
|
41
|
+
--arg event "$EVENT" \
|
|
42
|
+
--arg tool "$TOOL_NAME" \
|
|
43
|
+
--arg reason "$REASON" \
|
|
44
|
+
'{timestamp: $ts, session_id: $session, event: $event, tool_name: $tool, reason: $reason}' \
|
|
45
|
+
>> "$SESSION_PATH/permission-audit.log" 2>/dev/null || true
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# post-compact.sh — PostCompact hook
|
|
3
|
+
#
|
|
4
|
+
# Saves the AI-generated compact_summary (from stdin JSON) to a project-scoped
|
|
5
|
+
# file. user-prompt-submit.sh reads and injects it directly on the next session's
|
|
6
|
+
# first prompt — no manual restore command required.
|
|
7
|
+
#
|
|
8
|
+
# Atomic write: mktemp → populate → mv ensures readers never see partial content.
|
|
9
|
+
# Multi-session safe: project directory is keyed by cwd hash, not session_id.
|
|
10
|
+
# Must complete in <200ms. Must never exit non-zero or block.
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
# shellcheck source=lib.sh
|
|
14
|
+
source "$(dirname "$0")/lib.sh"
|
|
15
|
+
require_jq
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
|
|
19
|
+
# A worker session never writes the project's summary or metadata: they belong to the main session.
|
|
20
|
+
if [[ "$(doflow_session_role || echo main)" == "worker" ]]; then
|
|
21
|
+
exit 0
|
|
22
|
+
fi
|
|
23
|
+
|
|
24
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
25
|
+
CWD=$(json_field "$INPUT" ".cwd")
|
|
26
|
+
TRIGGER=$(json_field "$INPUT" ".trigger")
|
|
27
|
+
COMPACT_SUMMARY=$(json_field "$INPUT" ".compact_summary")
|
|
28
|
+
|
|
29
|
+
# Guard: nothing useful to save if we don't know where we are
|
|
30
|
+
[[ -z "$CWD" ]] && exit 0
|
|
31
|
+
|
|
32
|
+
# Guard: an empty summary would write a frontmatter-only file that would be
|
|
33
|
+
# injected as empty context on the next session's first prompt — useless noise, skip it.
|
|
34
|
+
[[ -z "$COMPACT_SUMMARY" ]] && exit 0
|
|
35
|
+
|
|
36
|
+
PROJECT_DIR=$(ensure_project_dir "$CWD")
|
|
37
|
+
|
|
38
|
+
# Read current branch for frontmatter metadata (best-effort, empty is fine)
|
|
39
|
+
BRANCH=$(run_with_timeout 5 -- git -C "$CWD" branch --show-current 2>/dev/null || echo "")
|
|
40
|
+
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
|
|
41
|
+
|
|
42
|
+
# Single cleanup function covers both temp files — a second trap() call would
|
|
43
|
+
# silently replace the first, leaving the earlier temp file unguarded on failure.
|
|
44
|
+
TMP=""
|
|
45
|
+
META_TMP=""
|
|
46
|
+
_cleanup() { rm -f "$TMP" "$META_TMP" 2>/dev/null || true; }
|
|
47
|
+
trap _cleanup EXIT
|
|
48
|
+
|
|
49
|
+
# Atomic write: write to a temp file then rename.
|
|
50
|
+
TMP=$(mktemp "$PROJECT_DIR/.last-compact-summary.XXXXXX")
|
|
51
|
+
cat > "$TMP" <<EOF
|
|
52
|
+
---
|
|
53
|
+
compacted_at: ${TIMESTAMP}
|
|
54
|
+
session_id: ${SESSION_ID:-unknown}
|
|
55
|
+
trigger: ${TRIGGER:-unknown}
|
|
56
|
+
branch: ${BRANCH:-unknown}
|
|
57
|
+
role: main
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
${COMPACT_SUMMARY}
|
|
61
|
+
EOF
|
|
62
|
+
mv "$TMP" "$PROJECT_DIR/last-compact-summary.md"
|
|
63
|
+
TMP="" # already moved; _cleanup should not attempt to rm it
|
|
64
|
+
|
|
65
|
+
# Update meta.json compacted_at (field-merge — do not overwrite other fields)
|
|
66
|
+
if [[ -f "$PROJECT_DIR/meta.json" ]]; then
|
|
67
|
+
META_TMP=$(mktemp "$PROJECT_DIR/.meta.XXXXXX")
|
|
68
|
+
if jq --arg ts "$TIMESTAMP" '.compacted_at = $ts' "$PROJECT_DIR/meta.json" > "$META_TMP"; then
|
|
69
|
+
mv "$META_TMP" "$PROJECT_DIR/meta.json"
|
|
70
|
+
META_TMP="" # already moved; _cleanup should not attempt to rm it
|
|
71
|
+
fi
|
|
72
|
+
fi
|
|
73
|
+
|
|
74
|
+
exit 0
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# post-edit-lint.sh — PostToolUse(Edit|Write) hook
|
|
3
|
+
#
|
|
4
|
+
# Pure collector: appends the edited/written file_path to a session-scoped
|
|
5
|
+
# list. The actual lint work happens in stop-check.sh at the end of each
|
|
6
|
+
# Claude turn, where all paths can be batched by extension in one pass.
|
|
7
|
+
#
|
|
8
|
+
# Multi-session safe: writes only to sessions/{session_id}/edited-files.txt.
|
|
9
|
+
# Must complete in <20ms. Must never exit non-zero or produce unexpected output.
|
|
10
|
+
|
|
11
|
+
set -euo pipefail
|
|
12
|
+
# shellcheck source=lib.sh
|
|
13
|
+
source "$(dirname "$0")/lib.sh"
|
|
14
|
+
require_jq
|
|
15
|
+
|
|
16
|
+
INPUT=$(cat)
|
|
17
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
18
|
+
TOOL_NAME=$(json_field "$INPUT" ".tool_name")
|
|
19
|
+
|
|
20
|
+
# Only handle Edit and Write events
|
|
21
|
+
[[ "$TOOL_NAME" != "Edit" && "$TOOL_NAME" != "Write" ]] && exit 0
|
|
22
|
+
|
|
23
|
+
# Guard: if we can't determine the session, nothing to do
|
|
24
|
+
[[ -z "$SESSION_ID" ]] && exit 0
|
|
25
|
+
|
|
26
|
+
FILE_PATH=$(json_field "$INPUT" ".tool_input.file_path")
|
|
27
|
+
|
|
28
|
+
# Skip if no file_path (e.g., field absent from input)
|
|
29
|
+
[[ -z "$FILE_PATH" ]] && exit 0
|
|
30
|
+
|
|
31
|
+
SESSION_PATH=$(ensure_session_dir "$SESSION_ID")
|
|
32
|
+
|
|
33
|
+
# Append path (one per line) — stop-check.sh reads this list atomically
|
|
34
|
+
echo "$FILE_PATH" >> "$SESSION_PATH/edited-files.txt"
|
|
35
|
+
|
|
36
|
+
exit 0
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# post-tool-failure.sh — PostToolUse*Failure* hook (pure observability, no deny path)
|
|
3
|
+
#
|
|
4
|
+
# A failed tool call is the one outcome no other hook records. post-edit-lint.sh
|
|
5
|
+
# deliberately does NOT serve this event: it queues a path for end-of-turn linting,
|
|
6
|
+
# and on a failure the file was never written — queuing it would lint content that
|
|
7
|
+
# does not exist. This hook is the separate handler that failure case needs.
|
|
8
|
+
#
|
|
9
|
+
# Appends {timestamp, session_id, event, tool_name, file_path, error} to a
|
|
10
|
+
# session-scoped log. Fields beyond the common envelope (session_id,
|
|
11
|
+
# hook_event_name, tool_name) are best-effort: json_field yields empty for any
|
|
12
|
+
# absent key, so a payload shape change degrades to a thinner record rather than
|
|
13
|
+
# a broken hook.
|
|
14
|
+
#
|
|
15
|
+
# Multi-session safe: writes only to sessions/{session_id}/tool-failures.log.
|
|
16
|
+
# Must complete in <20ms. Must never exit non-zero or block the turn.
|
|
17
|
+
|
|
18
|
+
set -euo pipefail
|
|
19
|
+
# shellcheck source=lib.sh
|
|
20
|
+
source "$(dirname "$0")/lib.sh"
|
|
21
|
+
require_jq
|
|
22
|
+
|
|
23
|
+
INPUT=$(cat)
|
|
24
|
+
SESSION_ID=$(json_field "$INPUT" ".session_id")
|
|
25
|
+
|
|
26
|
+
# Guard: if we can't determine the session, nothing to do
|
|
27
|
+
[[ -z "$SESSION_ID" ]] && exit 0
|
|
28
|
+
|
|
29
|
+
EVENT=$(json_field "$INPUT" ".hook_event_name")
|
|
30
|
+
TOOL_NAME=$(json_field "$INPUT" ".tool_name")
|
|
31
|
+
FILE_PATH=$(json_field "$INPUT" ".tool_input.file_path")
|
|
32
|
+
ERROR=$(json_field "$INPUT" ".tool_response.error")
|
|
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 tool "$TOOL_NAME" \
|
|
42
|
+
--arg file "$FILE_PATH" \
|
|
43
|
+
--arg error "$ERROR" \
|
|
44
|
+
'{timestamp: $ts, session_id: $session, event: $event, tool_name: $tool, file_path: $file, error: $error}' \
|
|
45
|
+
>> "$SESSION_PATH/tool-failures.log" 2>/dev/null || true
|
|
46
|
+
|
|
47
|
+
exit 0
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude front door: delegates the pre-bash-guard decision to the Canonical Policy
|
|
3
|
+
# Library, then translates it into Claude Code's verified deny contract.
|
|
4
|
+
#
|
|
5
|
+
# Schema verified (Phase 8.3, 2026-04-17): hookSpecificOutput.permissionDecision
|
|
6
|
+
# "deny" is correctly recognized by Claude Code and blocks execution — exit code
|
|
7
|
+
# alone is not sufficient here, so this front door is a translator, not a bare
|
|
8
|
+
# exec (unlike session-context/stop-check, which never need this translation).
|
|
9
|
+
set -uo pipefail
|
|
10
|
+
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude}"
|
|
11
|
+
|
|
12
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
13
|
+
source "$(dirname "$0")/../../.doflow/shared/hooks/policies/deny-json.sh"
|
|
14
|
+
|
|
15
|
+
POLICY="$(dirname "$0")/../../.doflow/shared/hooks/policies/pre-bash-guard.sh"
|
|
16
|
+
REASON=$(bash "$POLICY" 2>&1 >/dev/null)
|
|
17
|
+
CODE=$?
|
|
18
|
+
|
|
19
|
+
[ "$CODE" -ne 0 ] && emit_pretooluse_deny_nested "${REASON:-Command blocked by pre-bash-guard}"
|
|
20
|
+
exit 0
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre-compact.sh — PreCompact hook
|
|
3
|
+
#
|
|
4
|
+
# Outputs a plain string to stdout that Claude Code passes as custom_instructions
|
|
5
|
+
# to the compaction LLM call. This enriches the compact summary with git state
|
|
6
|
+
# so the summary preserves branch/commit context for the next session.
|
|
7
|
+
#
|
|
8
|
+
# Output is a plain string — NOT JSON.
|
|
9
|
+
# Must be under ~500 chars (Claude Code may truncate longer strings).
|
|
10
|
+
# Must never exit non-zero or block.
|
|
11
|
+
|
|
12
|
+
set -euo pipefail
|
|
13
|
+
# shellcheck source=lib.sh
|
|
14
|
+
source "$(dirname "$0")/lib.sh"
|
|
15
|
+
require_jq
|
|
16
|
+
|
|
17
|
+
INPUT=$(cat)
|
|
18
|
+
CWD=$(json_field "$INPUT" ".cwd")
|
|
19
|
+
|
|
20
|
+
BRANCH=""
|
|
21
|
+
SHA=""
|
|
22
|
+
UNCOMMITTED=0
|
|
23
|
+
|
|
24
|
+
if [[ -n "$CWD" ]] && is_git_worktree "$CWD"; then
|
|
25
|
+
BRANCH=$(git_branch_of "$CWD" || echo "")
|
|
26
|
+
SHA=$(git_short_sha_of "$CWD" || echo "")
|
|
27
|
+
UNCOMMITTED=$(git_uncommitted_count_of "$CWD" || echo "0")
|
|
28
|
+
|
|
29
|
+
# Last 2 commits (short SHA only for brevity) — keeps output under 500 chars
|
|
30
|
+
RECENT=$(run_with_timeout 1 -- git -C "$CWD" log --format="%h %s" -2 2>/dev/null \
|
|
31
|
+
| paste -sd ' | ' - || echo "")
|
|
32
|
+
|
|
33
|
+
# Build output and hard-cap at 490 chars to stay within Claude Code's limit
|
|
34
|
+
OUT=$(printf 'Include in compact summary:\n- git branch: %s sha: %s\n- recent commits: %s\n- uncommitted files: %s\n- cwd: %s\nPreserve: decisions made, files modified, open questions, planned next steps.' \
|
|
35
|
+
"${BRANCH:-unknown}" "${SHA:-unknown}" "${RECENT:-none}" "$UNCOMMITTED" "${CWD:-unknown}")
|
|
36
|
+
printf '%s' "${OUT:0:490}"
|
|
37
|
+
else
|
|
38
|
+
OUT=$(printf 'Include in compact summary:\n- cwd: %s (not a git repository)\nPreserve: decisions made, files modified, open questions, planned next steps.' \
|
|
39
|
+
"${CWD:-unknown}")
|
|
40
|
+
printf '%s' "${OUT:0:490}"
|
|
41
|
+
fi
|
|
42
|
+
|
|
43
|
+
exit 0
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude front door: delegates the pre-implementation-gate decision to the
|
|
3
|
+
# Canonical Policy Library, then translates it into Claude Code's verified deny
|
|
4
|
+
# contract.
|
|
5
|
+
#
|
|
6
|
+
# Schema verified (Phase 8.3, 2026-04-17): hookSpecificOutput.permissionDecision
|
|
7
|
+
# "deny" is correctly recognized by Claude Code and blocks execution — exit code
|
|
8
|
+
# alone is not sufficient here, so this front door is a translator, not a bare
|
|
9
|
+
# exec (unlike session-context/stop-check, which never need this translation).
|
|
10
|
+
set -uo pipefail
|
|
11
|
+
export DOFLOW_AGENT="${DOFLOW_AGENT:-claude}"
|
|
12
|
+
|
|
13
|
+
command -v jq >/dev/null 2>&1 || exit 0
|
|
14
|
+
source "$(dirname "$0")/../../.doflow/shared/hooks/policies/deny-json.sh"
|
|
15
|
+
|
|
16
|
+
POLICY="$(dirname "$0")/../../.doflow/shared/hooks/policies/pre-implementation-gate.sh"
|
|
17
|
+
REASON=$(bash "$POLICY" 2>&1 >/dev/null)
|
|
18
|
+
CODE=$?
|
|
19
|
+
|
|
20
|
+
[ "$CODE" -ne 0 ] && emit_pretooluse_deny_nested "${REASON:-Implementation requires an approved plan.md}"
|
|
21
|
+
exit 0
|