@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,208 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# do-parallel-check.sh — verify one phase's [P] siblings really write disjoint files.
|
|
3
|
+
# Form dispatch groups by (phase, owner) and detect union overlaps.
|
|
4
|
+
#
|
|
5
|
+
# `[P]` asserts at plan time that a task's `files:` set does not overlap its phase
|
|
6
|
+
# siblings'. Nothing verified that assertion, so parallel-by-default rested on an
|
|
7
|
+
# unchecked claim: two agents dispatched concurrently onto one file is a lost edit,
|
|
8
|
+
# and the loss is silent. This script turns the claim into a check.
|
|
9
|
+
#
|
|
10
|
+
# Grouping by (phase, owner) lets one agent handle all tasks of the same owner in a phase,
|
|
11
|
+
# reducing dispatch overhead. The script forms groups from `owner:` fields, computes union
|
|
12
|
+
# file sets per group, and reports overlaps across groups (`group_overlaps[]`).
|
|
13
|
+
#
|
|
14
|
+
# Advisory by design: it reports overlaps and grouping decisions; the calling skill decides
|
|
15
|
+
# to serialize or group dispatch. It never edits the plan and never dispatches anything.
|
|
16
|
+
#
|
|
17
|
+
# A task whose `files:` value names no real path (a verification-only task) simply
|
|
18
|
+
# contributes nothing to compare, rather than being treated as conflicting with
|
|
19
|
+
# everything.
|
|
20
|
+
#
|
|
21
|
+
# Usage:
|
|
22
|
+
# do-parallel-check.sh --phase=<X> [--slug=<slug>] [--json]
|
|
23
|
+
|
|
24
|
+
set -uo pipefail
|
|
25
|
+
|
|
26
|
+
phase=""
|
|
27
|
+
slug_override=""
|
|
28
|
+
for arg in "$@"; do
|
|
29
|
+
case "$arg" in
|
|
30
|
+
--phase=*) phase="${arg#--phase=}" ;;
|
|
31
|
+
--slug=*) slug_override="${arg#--slug=}" ;;
|
|
32
|
+
--json) ;;
|
|
33
|
+
*) ;;
|
|
34
|
+
esac
|
|
35
|
+
done
|
|
36
|
+
|
|
37
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
38
|
+
printf '{"error":"jq-not-found","parallel_safe":null}\n'
|
|
39
|
+
exit 0
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
if [ -z "$phase" ]; then
|
|
43
|
+
jq -n '{error:"missing-phase", hint:"pass --phase=<X>, e.g. --phase=A"}'
|
|
44
|
+
exit 2
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
48
|
+
resolved="$(bash "$script_dir/do-paths.sh" --json ${slug_override:+--slug="$slug_override"} 2>/dev/null)"
|
|
49
|
+
resolved_rc=$?
|
|
50
|
+
# An unsafe slug is refused with the resolver's own error object (IC-009), not read as an empty feature.
|
|
51
|
+
if [ "$resolved_rc" -eq 2 ] && [ "$(printf '%s' "$resolved" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
|
|
52
|
+
printf '%s\n' "$resolved"; exit 2
|
|
53
|
+
fi
|
|
54
|
+
repo_root="$(printf '%s' "$resolved" | jq -r '.repo_root // empty')"
|
|
55
|
+
plan_rel="$(printf '%s' "$resolved" | jq -r '.plan // empty')"
|
|
56
|
+
has_plan="$(printf '%s' "$resolved" | jq -r '.has_plan // false')"
|
|
57
|
+
|
|
58
|
+
if [ "$has_plan" != "true" ] || [ -z "$plan_rel" ]; then
|
|
59
|
+
jq -n '{error:"no-plan", hint:"this feature has no plan.md to check"}'
|
|
60
|
+
exit 2
|
|
61
|
+
fi
|
|
62
|
+
plan_abs="$repo_root/$plan_rel"
|
|
63
|
+
|
|
64
|
+
# Pipeline: awk outputs tab-separated values (one per line), jq reads raw lines as strings,
|
|
65
|
+
# aggregates them into an array, then processes each row.
|
|
66
|
+
awk -v want="$phase" '
|
|
67
|
+
/^### Phase / {
|
|
68
|
+
inphase = ($3 == want)
|
|
69
|
+
next
|
|
70
|
+
}
|
|
71
|
+
/^### / { inphase = 0; next }
|
|
72
|
+
!inphase { next }
|
|
73
|
+
/^- \[[ x]\] / {
|
|
74
|
+
line = $0
|
|
75
|
+
if (match(line, /^- \[[ x]\] [^ ]+/) == 0) next
|
|
76
|
+
id = substr(line, RSTART + 6, RLENGTH - 6)
|
|
77
|
+
par = (line ~ /^- \[[ x]\] [^ ]+ \[P\]/) ? "P" : "S"
|
|
78
|
+
# Extract only the portion after em-dash for owner:/files: parsing
|
|
79
|
+
dash_pos = index(line, "—")
|
|
80
|
+
if (dash_pos == 0) {
|
|
81
|
+
print id "\t" par "\t\t"
|
|
82
|
+
next
|
|
83
|
+
}
|
|
84
|
+
field_section = substr(line, dash_pos + length("—"))
|
|
85
|
+
owner = ""
|
|
86
|
+
if (match(field_section, /owner:[^;]*/)) {
|
|
87
|
+
owner = substr(field_section, RSTART + 7, RLENGTH - 7)
|
|
88
|
+
gsub(/^[ \t]+|[ \t]+$/, "", owner)
|
|
89
|
+
}
|
|
90
|
+
if (match(field_section, /files:[^;]*/)) {
|
|
91
|
+
files = substr(field_section, RSTART + 6, RLENGTH - 6)
|
|
92
|
+
n = split(files, parts, ",")
|
|
93
|
+
emitted = 0
|
|
94
|
+
for (i = 1; i <= n; i++) {
|
|
95
|
+
f = parts[i]
|
|
96
|
+
gsub(/^[ \t]+|[ \t]+$/, "", f)
|
|
97
|
+
if (f == "" || f ~ /^none/) continue
|
|
98
|
+
print id "\t" par "\t" owner "\t" f
|
|
99
|
+
emitted = 1
|
|
100
|
+
}
|
|
101
|
+
if (!emitted) print id "\t" par "\t" owner "\t"
|
|
102
|
+
} else {
|
|
103
|
+
print id "\t" par "\t" owner "\t"
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
' "$plan_abs" | jq -R . | jq -s --arg phase "$phase" '
|
|
107
|
+
# Convert array of tab-separated strings to array of objects
|
|
108
|
+
map(split("\t")) |
|
|
109
|
+
map({id: .[0], par: .[1], owner: (.[2] // ""), file: (.[3] // "")}) |
|
|
110
|
+
. as $rows |
|
|
111
|
+
|
|
112
|
+
# Separate by parallel/sequential
|
|
113
|
+
([.[] | select(.par == "P") | .id] | unique) as $par |
|
|
114
|
+
([.[] | select(.par == "S") | .id] | unique) as $seq |
|
|
115
|
+
|
|
116
|
+
# Existing overlaps: per-task file conflicts among [P] tasks
|
|
117
|
+
(
|
|
118
|
+
[.[] | select(.par == "P") | select(.file != "")] |
|
|
119
|
+
group_by(.file) |
|
|
120
|
+
map({path: .[0].file, tasks: ([.[] | .id] | unique)}) |
|
|
121
|
+
map(select(.tasks | length > 1))
|
|
122
|
+
) as $overlaps |
|
|
123
|
+
|
|
124
|
+
# Unowned tasks: those with empty owner
|
|
125
|
+
([.[] | select(.owner == "") | .id] | unique) as $unowned_ids |
|
|
126
|
+
|
|
127
|
+
# Form groups preserving first task appearance order
|
|
128
|
+
(
|
|
129
|
+
reduce $rows[] as $row (
|
|
130
|
+
[];
|
|
131
|
+
if $row.id == "" then .
|
|
132
|
+
elif $row.owner == "" then
|
|
133
|
+
# Check if unowned task already added
|
|
134
|
+
if any(.[]; .id == ($phase + ":" + $row.id)) then
|
|
135
|
+
map(if .id == ($phase + ":" + $row.id) then
|
|
136
|
+
.files = (.files + (if $row.file == "" then [] else [$row.file] end) | unique)
|
|
137
|
+
else . end)
|
|
138
|
+
else
|
|
139
|
+
. + [{
|
|
140
|
+
id: ($phase + ":" + $row.id),
|
|
141
|
+
owner: null,
|
|
142
|
+
tasks: [$row.id],
|
|
143
|
+
files: (if $row.file == "" then [] else [$row.file] end)
|
|
144
|
+
}]
|
|
145
|
+
end
|
|
146
|
+
else
|
|
147
|
+
($row.owner) as $own |
|
|
148
|
+
($phase + ":" + $own) as $gid |
|
|
149
|
+
if any(.[]; .id == $gid) then
|
|
150
|
+
map(if .id == $gid then
|
|
151
|
+
.tasks = (if (.tasks | index($row.id)) then .tasks else (.tasks + [$row.id]) end) |
|
|
152
|
+
.files = (.files + (if $row.file == "" then [] else [$row.file] end) | unique)
|
|
153
|
+
else . end)
|
|
154
|
+
else
|
|
155
|
+
. + [{
|
|
156
|
+
id: $gid,
|
|
157
|
+
owner: $own,
|
|
158
|
+
tasks: [$row.id],
|
|
159
|
+
files: (if $row.file == "" then [] else [$row.file] end)
|
|
160
|
+
}]
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
)
|
|
164
|
+
) as $final_groups |
|
|
165
|
+
|
|
166
|
+
# Create a mapping from task_id to group_id for lookup
|
|
167
|
+
([$final_groups[] | .tasks[] as $t | {key: $t, value: .id}] | from_entries) as $task_to_group |
|
|
168
|
+
|
|
169
|
+
# Group overlaps: paths appearing in tasks assigned to more than one group
|
|
170
|
+
(
|
|
171
|
+
$rows |
|
|
172
|
+
[.[] | select(.par != "") | select(.file != "")] |
|
|
173
|
+
group_by(.file) |
|
|
174
|
+
map({
|
|
175
|
+
path: .[0].file,
|
|
176
|
+
groups: ([.[] | .id as $tid | $task_to_group[$tid]] | unique)
|
|
177
|
+
}) |
|
|
178
|
+
map(select((.groups | length) > 1))
|
|
179
|
+
) as $final_group_overlaps |
|
|
180
|
+
|
|
181
|
+
# Serialize list = union of group ids from overlaps
|
|
182
|
+
([$final_group_overlaps[].groups[]] | unique) as $group_serialize |
|
|
183
|
+
|
|
184
|
+
{
|
|
185
|
+
phase: $phase,
|
|
186
|
+
parallel_tasks: $par,
|
|
187
|
+
sequential_tasks: $seq,
|
|
188
|
+
overlaps: $overlaps,
|
|
189
|
+
# `null` when the caller named a phase and nothing parsed, true only when BOTH the task-level
|
|
190
|
+
# and the group-level collision sets are empty.
|
|
191
|
+
#
|
|
192
|
+
# This field was `(($overlaps|length) == 0)`, which was wrong twice. A plan whose tasks are not
|
|
193
|
+
# under a `### Phase <X>` heading parses to zero tasks, so zero overlaps, so "safe" — the flag
|
|
194
|
+
# said a collision-free phase where it had read no phase at all. And `$overlaps` counts only
|
|
195
|
+
# `[P]`-to-`[P]` collisions, so a plan could report `parallel_safe: true` in the same object as
|
|
196
|
+
# a non-empty `group_overlaps` naming the very file two groups both write. `do-execute-plan`
|
|
197
|
+
# tells the reader to branch on this field and never on the exit code, so either shape
|
|
198
|
+
# authorises concurrent writes to one file.
|
|
199
|
+
parallel_safe: (if (($par | length) == 0 and ($seq | length) == 0) then null
|
|
200
|
+
else (($overlaps | length) == 0 and ($final_group_overlaps | length) == 0) end),
|
|
201
|
+
serialize: ([$overlaps[] | .tasks[]] | unique),
|
|
202
|
+
groups: $final_groups,
|
|
203
|
+
group_overlaps: $final_group_overlaps,
|
|
204
|
+
group_serialize: $group_serialize,
|
|
205
|
+
unowned_tasks: $unowned_ids
|
|
206
|
+
}
|
|
207
|
+
'
|
|
208
|
+
exit 0
|
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# do-paths.sh — deterministic path/feature resolver for the doflow chain.
|
|
3
|
+
#
|
|
4
|
+
# Emits a single JSON object describing the ACTIVE feature so doflow skills
|
|
5
|
+
# never compute paths, feature numbers, or existence themselves (the
|
|
6
|
+
# deterministic/generative split — design §4). In a git repo, the active
|
|
7
|
+
# feature is derived from the branch (branch-coupled, FR-6): `feat/<NNN-slug>`
|
|
8
|
+
# → feature dir `agent-docs/doflow/<NNN-slug>/`. On a trunk branch
|
|
9
|
+
# (master/main/develop) the slug is null and the caller must prompt for one.
|
|
10
|
+
# Outside a git repo (e.g. doflow installed at a multi-service container root,
|
|
11
|
+
# above the actual git sub-repos) there is no branch to key off at all, so
|
|
12
|
+
# resolution falls back to scanning `agent-docs/doflow/` directly — see the
|
|
13
|
+
# "feature slug" section below and `candidate_slugs` in the emitted JSON.
|
|
14
|
+
#
|
|
15
|
+
# Self-contained by design: it does NOT source hooks/lib.sh, because this
|
|
16
|
+
# script and lib.sh sit at different relative depths depending on install
|
|
17
|
+
# location (global vs project-scoped). It needs only `git` + `jq`.
|
|
18
|
+
#
|
|
19
|
+
# Fail-open: on any error it still emits best-effort JSON and exits 0, so a
|
|
20
|
+
# skill is never hard-blocked by path resolution. The one non-zero exit is the
|
|
21
|
+
# explicit `--require feature` gate (exit 2 when no active feature).
|
|
22
|
+
#
|
|
23
|
+
# Usage:
|
|
24
|
+
# do-paths.sh [--json] # default — full resolver JSON
|
|
25
|
+
# do-paths.sh --paths-only # skip the next_number scan (cheap)
|
|
26
|
+
# do-paths.sh --require feature # exit 2 + error JSON if no active feature
|
|
27
|
+
# do-paths.sh --slug=<slug> # force resolution to this slug, skipping branch/dir detection
|
|
28
|
+
# # (used by a skill after it has already disambiguated via
|
|
29
|
+
# # AskUserQuestion — see "non-git root" note below)
|
|
30
|
+
|
|
31
|
+
set -uo pipefail # deliberately NOT -e: we handle errors and still emit JSON
|
|
32
|
+
|
|
33
|
+
mode="json"
|
|
34
|
+
require=""
|
|
35
|
+
slug_override=""
|
|
36
|
+
prev=""
|
|
37
|
+
for arg in "$@"; do
|
|
38
|
+
case "$arg" in
|
|
39
|
+
--json) mode="json" ;;
|
|
40
|
+
--paths-only) mode="paths-only" ;;
|
|
41
|
+
--require) require="pending" ;; # value follows in next arg
|
|
42
|
+
--require=*) require="${arg#--require=}" ;;
|
|
43
|
+
--slug=*) slug_override="${arg#--slug=}" ;;
|
|
44
|
+
feature) [ "$prev" = "--require" ] && require="feature" ;;
|
|
45
|
+
*) ;;
|
|
46
|
+
esac
|
|
47
|
+
prev="$arg"
|
|
48
|
+
done
|
|
49
|
+
[ "$require" = "pending" ] && require="feature"
|
|
50
|
+
|
|
51
|
+
# jq is mandatory to emit JSON; fail-open with a diagnostic object if absent.
|
|
52
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
53
|
+
printf '{"error":"jq-not-found","feature_slug":null}\n'
|
|
54
|
+
exit 0
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
# ── repo root & branch (degrade gracefully outside a git repo) ────────────────
|
|
58
|
+
is_git_repo=false
|
|
59
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)"
|
|
60
|
+
if [ -n "$repo_root" ]; then
|
|
61
|
+
is_git_repo=true
|
|
62
|
+
else
|
|
63
|
+
repo_root="$(pwd)"
|
|
64
|
+
fi
|
|
65
|
+
branch=""
|
|
66
|
+
[ "$is_git_repo" = true ] && branch="$(git -C "$repo_root" rev-parse --abbrev-ref HEAD 2>/dev/null || echo "")"
|
|
67
|
+
|
|
68
|
+
specs_rel="agent-docs/doflow"
|
|
69
|
+
specs_dir="$repo_root/$specs_rel"
|
|
70
|
+
|
|
71
|
+
# ── numeric-prefixed feature dirs under agent-docs/doflow/ (shared by the non-git candidate-slug
|
|
72
|
+
# fallback below and the next_number scan further down — one scan, not two; applies the same
|
|
73
|
+
# numeric-prefix filter next_number always used, so a stray non-numeric dir — notes/, .archive/,
|
|
74
|
+
# a manual-cleanup leftover — can never masquerade as a feature candidate) ────────────────────
|
|
75
|
+
numbered_dirs=()
|
|
76
|
+
if { [ "$is_git_repo" = false ] || [ "$mode" != "paths-only" ]; } && [ -d "$specs_dir" ]; then
|
|
77
|
+
for d in "$specs_dir"/*/; do
|
|
78
|
+
[ -d "$d" ] || continue
|
|
79
|
+
n="$(basename "$d")"
|
|
80
|
+
case "${n%%-*}" in ''|*[!0-9]*) continue ;; esac
|
|
81
|
+
numbered_dirs+=("$n")
|
|
82
|
+
done
|
|
83
|
+
fi
|
|
84
|
+
|
|
85
|
+
# ── feature slug: branch-derived in a git repo; directory-scan otherwise ──────
|
|
86
|
+
# A non-git root (doflow installed above the actual git repos — e.g. a multi-service
|
|
87
|
+
# container workspace) has no branch to key off at all, so branch parsing doesn't apply here —
|
|
88
|
+
# fall back to scanning agent-docs/doflow/ directly (numbered_dirs above). Exactly one candidate
|
|
89
|
+
# dir is unambiguous and gets auto-selected (no prompt needed — nothing to decide). Zero
|
|
90
|
+
# candidates is genuinely "no active feature." Two or more is a real decision this deterministic
|
|
91
|
+
# script cannot make on its own; it surfaces every candidate via `candidate_slugs` instead of
|
|
92
|
+
# guessing, so the calling skill (do-execute-plan / do-flow, which has AskUserQuestion access)
|
|
93
|
+
# can ask the user, then re-invoke with `--slug=<chosen>` to force resolution.
|
|
94
|
+
feature_slug=""
|
|
95
|
+
candidate_slugs_json="[]"
|
|
96
|
+
# Initialized here, not only inside the git arm below: a non-git root never runs the branch
|
|
97
|
+
# `case`, and the emit block reads this unconditionally under `set -u`. Leaving it unset made
|
|
98
|
+
# the resolver die with "branch_class: unbound variable" at exactly the container-root layout
|
|
99
|
+
# the directory-scan fallback exists to support.
|
|
100
|
+
branch_class=""
|
|
101
|
+
if [ "$is_git_repo" = true ]; then
|
|
102
|
+
case "$branch" in
|
|
103
|
+
"") feature_slug=""; branch_class="trunk" ;;
|
|
104
|
+
master|main|develop|HEAD) feature_slug=""; branch_class="trunk" ;;
|
|
105
|
+
release/*) feature_slug=""; branch_class="release" ;;
|
|
106
|
+
hotfix/*) feature_slug=""; branch_class="hotfix" ;;
|
|
107
|
+
feat/*|feature/*) feature_slug="${branch#*/}"; branch_class="feature" ;;
|
|
108
|
+
fix/*|bugfix/*) feature_slug="${branch#*/}"; branch_class="fix" ;;
|
|
109
|
+
*/*) feature_slug="${branch#*/}"; branch_class="other" ;;
|
|
110
|
+
*) feature_slug="$branch"; branch_class="other" ;;
|
|
111
|
+
esac
|
|
112
|
+
# Flatten any separator the strip above left behind. Two reasons this is not cosmetic:
|
|
113
|
+
# `feature_dir` is `agent-docs/doflow/<slug>`, and a slug with a `/` nests it a level deeper than
|
|
114
|
+
# every other feature — deep enough that the non-git directory scan above can never find it,
|
|
115
|
+
# since that scan globs one level and then drops names without a numeric prefix. And
|
|
116
|
+
# `assertSafeTaskId` in src/runtime/evidence-ledger.js rejects path separators outright, so
|
|
117
|
+
# `doflow evidence` and `doflow claim` throw for the whole session.
|
|
118
|
+
#
|
|
119
|
+
# The arm above used to read `*/)`, which matches only a name ending in `/` — something git
|
|
120
|
+
# refuses to create — so it never once fired, and every prefix outside the six named here fell
|
|
121
|
+
# to `*)` and kept it. `feat/`, `feature/`, `fix/` and `bugfix/` were unaffected because they
|
|
122
|
+
# have their own arms; `task/`, `chore/`, `refactor/` and the rest were not.
|
|
123
|
+
feature_slug="$(printf '%s' "$feature_slug" | tr '/' '-')"
|
|
124
|
+
else
|
|
125
|
+
case "${#numbered_dirs[@]}" in
|
|
126
|
+
0) : ;;
|
|
127
|
+
1) feature_slug="${numbered_dirs[0]}" ;;
|
|
128
|
+
*) candidate_slugs_json="$(printf '%s\n' "${numbered_dirs[@]}" | jq -R . | jq -s .)" ;;
|
|
129
|
+
esac
|
|
130
|
+
fi
|
|
131
|
+
|
|
132
|
+
# An explicit --slug always wins (post-disambiguation re-resolution, or a deliberate override).
|
|
133
|
+
if [ -n "$slug_override" ]; then
|
|
134
|
+
feature_slug="$slug_override"
|
|
135
|
+
candidate_slugs_json="[]"
|
|
136
|
+
fi
|
|
137
|
+
|
|
138
|
+
# The slug becomes a directory name under agent-docs/doflow/ and a key in per-task state, so it must
|
|
139
|
+
# not be able to name a path or a flag (IC-009): letters, digits, dot, underscore and dash, a letter
|
|
140
|
+
# or digit first, and no "..". Refused before any path is built, whether it came from --slug or was
|
|
141
|
+
# derived from the branch. Exit 2 like the other refusals; the message is also in `hint`, which is
|
|
142
|
+
# the field every caller of the other error objects already reads.
|
|
143
|
+
if [ -n "$feature_slug" ]; then
|
|
144
|
+
slug_ok=true
|
|
145
|
+
# The ranges are ASCII whatever the caller's locale: under a UTF-8 collation [A-Za-z] can admit
|
|
146
|
+
# letters such as "é", which the rule exists to refuse.
|
|
147
|
+
(export LC_ALL=C; [[ "$feature_slug" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] && [[ "$feature_slug" != *..* ]]) || slug_ok=false
|
|
148
|
+
if [ "$slug_ok" = false ]; then
|
|
149
|
+
rule='use letters, digits, dot, underscore or dash, start with a letter or digit, and no ".."'
|
|
150
|
+
# A slug the caller did not type is traced to where it came from, so a branch name is not blamed
|
|
151
|
+
# on a flag nobody passed.
|
|
152
|
+
if [ -z "$slug_override" ] && [ "$is_git_repo" = true ] && [ -n "$branch" ]; then
|
|
153
|
+
subject="branch '$branch' gives slug '$feature_slug', which is not a valid feature slug"
|
|
154
|
+
else
|
|
155
|
+
subject="slug \"$feature_slug\" is not a valid feature slug"
|
|
156
|
+
fi
|
|
157
|
+
jq -n --arg subject "$subject" --arg rule "$rule" \
|
|
158
|
+
'{error:"invalid-slug", message:($subject + ": " + $rule), hint:$rule}'
|
|
159
|
+
exit 2
|
|
160
|
+
fi
|
|
161
|
+
fi
|
|
162
|
+
|
|
163
|
+
feature_dir=""; requirement=""; design=""; specs=""; data_model=""; plan=""; state=""; audit=""
|
|
164
|
+
decisions=""; decisions_register=""; has_decisions=false
|
|
165
|
+
has_requirement=false; has_design=false; has_plan=false; has_specs=false; has_data_model=false
|
|
166
|
+
layout="legacy"
|
|
167
|
+
intention_next_round=""; design_next_round=""; plan_next_round=""
|
|
168
|
+
if [ -n "$feature_slug" ]; then
|
|
169
|
+
feature_dir="$specs_rel/$feature_slug"
|
|
170
|
+
abs="$repo_root/$feature_dir"
|
|
171
|
+
state="$feature_dir/state.md"; audit="$feature_dir/audit.md"
|
|
172
|
+
decisions="$feature_dir/decisions.md"; decisions_register="$feature_dir/decisions/register.json"
|
|
173
|
+
[ -f "$abs/decisions/register.json" ] && has_decisions=true
|
|
174
|
+
# ── layout: computed ONCE from a single file's presence, then used to pick
|
|
175
|
+
# every path field below — never detected per-field (design.md R4). This is
|
|
176
|
+
# what makes a self-contradictory mix of old/new paths for the same feature
|
|
177
|
+
# dir unreachable by construction.
|
|
178
|
+
[ -f "$abs/intention/requirement.md" ] && layout="structured"
|
|
179
|
+
if [ "$layout" = "structured" ]; then
|
|
180
|
+
requirement="$feature_dir/intention/requirement.md"
|
|
181
|
+
design="$feature_dir/design/design.md"
|
|
182
|
+
specs="$feature_dir/design/specs.md"
|
|
183
|
+
data_model="$feature_dir/design/data-model.md"
|
|
184
|
+
plan="$feature_dir/plan.md"
|
|
185
|
+
[ -f "$abs/intention/requirement.md" ] && has_requirement=true
|
|
186
|
+
[ -f "$abs/design/design.md" ] && has_design=true
|
|
187
|
+
[ -f "$abs/design/specs.md" ] && has_specs=true
|
|
188
|
+
[ -f "$abs/design/data-model.md" ] && has_data_model=true
|
|
189
|
+
[ -f "$abs/plan.md" ] && has_plan=true
|
|
190
|
+
else
|
|
191
|
+
requirement="$feature_dir/requirement.md"; design="$feature_dir/design.md"
|
|
192
|
+
plan="$feature_dir/plan.md"
|
|
193
|
+
[ -f "$abs/requirement.md" ] && has_requirement=true
|
|
194
|
+
[ -f "$abs/design.md" ] && has_design=true
|
|
195
|
+
[ -f "$abs/plan.md" ] && has_plan=true
|
|
196
|
+
fi
|
|
197
|
+
|
|
198
|
+
# ── per-stage question-log round scan (structured layout only has files to find,
|
|
199
|
+
# but the scan itself is layout-agnostic — an empty/absent subdir just floors at 1).
|
|
200
|
+
# Skipped in --paths-only mode like the next_number scan below; the three variables
|
|
201
|
+
# stay "" (emitted as null, never a numeric 1) so "not computed in this cheap mode"
|
|
202
|
+
# can never be mistaken for "computed, no rounds yet" ──
|
|
203
|
+
if [ "$mode" != "paths-only" ]; then
|
|
204
|
+
max=0
|
|
205
|
+
if [ -d "$abs/intention" ]; then
|
|
206
|
+
for f in "$abs/intention"/brainstorm-*-question.md; do
|
|
207
|
+
[ -f "$f" ] || continue
|
|
208
|
+
num="$(basename "$f")"; num="${num#brainstorm-}"; num="${num%-question.md}"
|
|
209
|
+
case "$num" in ''|*[!0-9]*) continue ;; esac
|
|
210
|
+
num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
|
|
211
|
+
done
|
|
212
|
+
fi
|
|
213
|
+
intention_next_round=$((max + 1))
|
|
214
|
+
|
|
215
|
+
max=0
|
|
216
|
+
if [ -d "$abs/design" ]; then
|
|
217
|
+
for f in "$abs/design"/design-*-question.md; do
|
|
218
|
+
[ -f "$f" ] || continue
|
|
219
|
+
num="$(basename "$f")"; num="${num#design-}"; num="${num%-question.md}"
|
|
220
|
+
case "$num" in ''|*[!0-9]*) continue ;; esac
|
|
221
|
+
num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
|
|
222
|
+
done
|
|
223
|
+
fi
|
|
224
|
+
design_next_round=$((max + 1))
|
|
225
|
+
|
|
226
|
+
max=0
|
|
227
|
+
if [ -d "$abs" ]; then
|
|
228
|
+
for f in "$abs"/plan-*-question.md; do
|
|
229
|
+
[ -f "$f" ] || continue
|
|
230
|
+
num="$(basename "$f")"; num="${num#plan-}"; num="${num%-question.md}"
|
|
231
|
+
case "$num" in ''|*[!0-9]*) continue ;; esac
|
|
232
|
+
num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
|
|
233
|
+
done
|
|
234
|
+
fi
|
|
235
|
+
plan_next_round=$((max + 1))
|
|
236
|
+
fi
|
|
237
|
+
fi
|
|
238
|
+
|
|
239
|
+
# ── next number: max(existing spec dirs, numbered branches) + 1 ───────────────
|
|
240
|
+
# Base-10 forced (10#) so leading zeros never trigger octal parsing.
|
|
241
|
+
next_number="001"
|
|
242
|
+
if [ "$mode" != "paths-only" ]; then
|
|
243
|
+
max=0
|
|
244
|
+
if [ "${#numbered_dirs[@]}" -gt 0 ]; then
|
|
245
|
+
for n in "${numbered_dirs[@]}"; do
|
|
246
|
+
nn=$((10#${n%%-*})); [ "$nn" -gt "$max" ] && max="$nn"
|
|
247
|
+
done
|
|
248
|
+
fi
|
|
249
|
+
while IFS= read -r b; do
|
|
250
|
+
seg="${b##*/}"; num="${seg%%-*}"
|
|
251
|
+
case "$num" in ''|*[!0-9]*) continue ;; esac
|
|
252
|
+
num=$((10#$num)); [ "$num" -gt "$max" ] && max="$num"
|
|
253
|
+
done < <(git -C "$repo_root" branch --format='%(refname:short)' 2>/dev/null || true)
|
|
254
|
+
next_number="$(printf '%03d' "$((max + 1))")"
|
|
255
|
+
fi
|
|
256
|
+
|
|
257
|
+
# ── constitution paths (two-tier — design §6) ─────────────────────────────────
|
|
258
|
+
constitution_local="agent-docs/constitution.md" # tier-2, per-repo (may not exist)
|
|
259
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
260
|
+
constitution_base=""
|
|
261
|
+
for c in \
|
|
262
|
+
"$script_dir/../../../guidance/references/CONSTITUTION_BASE.md" \
|
|
263
|
+
"$HOME/.doflow/guidance/references/CONSTITUTION_BASE.md"; do
|
|
264
|
+
if [ -f "$c" ]; then
|
|
265
|
+
constitution_base="$(cd "$(dirname "$c")" && pwd)/CONSTITUTION_BASE.md"
|
|
266
|
+
break
|
|
267
|
+
fi
|
|
268
|
+
done
|
|
269
|
+
# Existence of tier-2 is repo-scoped, not feature-scoped, so it is computed here rather than in the
|
|
270
|
+
# has_requirement/has_design/has_plan block above — a constitution is present or absent regardless
|
|
271
|
+
# of whether any feature is active. It also stays outside the --paths-only skip, like the two paths
|
|
272
|
+
# above, because callers using that cheap mode still need it.
|
|
273
|
+
has_constitution_local=false
|
|
274
|
+
[ -f "$repo_root/$constitution_local" ] && has_constitution_local=true
|
|
275
|
+
|
|
276
|
+
# ── --require feature gate (the one non-zero exit) ────────────────────────────
|
|
277
|
+
if [ "$require" = "feature" ] && [ -z "$feature_slug" ]; then
|
|
278
|
+
if [ "$candidate_slugs_json" != "[]" ]; then
|
|
279
|
+
jq -n --arg branch "$branch" --argjson candidate_slugs "$candidate_slugs_json" \
|
|
280
|
+
'{error:"ambiguous-feature",
|
|
281
|
+
branch:(if $branch=="" then null else $branch end),
|
|
282
|
+
candidate_slugs:$candidate_slugs,
|
|
283
|
+
hint:"multiple agent-docs/doflow/ feature dirs exist and no git branch disambiguates them — ask which one via AskUserQuestion, then re-run with --slug=<chosen>"}'
|
|
284
|
+
else
|
|
285
|
+
jq -n --arg branch "$branch" \
|
|
286
|
+
'{error:"no-active-feature",
|
|
287
|
+
branch:(if $branch=="" then null else $branch end),
|
|
288
|
+
hint:"checkout a feat/<NNN-slug> branch or run /do-brainstorm to start one"}'
|
|
289
|
+
fi
|
|
290
|
+
exit 2
|
|
291
|
+
fi
|
|
292
|
+
|
|
293
|
+
# ── emit resolver JSON ────────────────────────────────────────────────────────
|
|
294
|
+
jq -n \
|
|
295
|
+
--arg repo_root "$repo_root" \
|
|
296
|
+
--argjson is_git_repo "$is_git_repo" \
|
|
297
|
+
--arg branch "$branch" \
|
|
298
|
+
--arg feature_slug "$feature_slug" \
|
|
299
|
+
--arg feature_dir "$feature_dir" \
|
|
300
|
+
--argjson candidate_slugs "$candidate_slugs_json" \
|
|
301
|
+
--arg requirement "$requirement" --arg design "$design" --arg specs "$specs" --arg data_model "$data_model" --arg plan "$plan" \
|
|
302
|
+
--arg state "$state" --arg audit "$audit" \
|
|
303
|
+
--arg decisions "$decisions" --arg decisions_register "$decisions_register" --argjson has_decisions "$has_decisions" \
|
|
304
|
+
--argjson has_requirement "$has_requirement" --argjson has_design "$has_design" --argjson has_plan "$has_plan" \
|
|
305
|
+
--argjson has_specs "$has_specs" --argjson has_data_model "$has_data_model" \
|
|
306
|
+
--arg layout "$layout" \
|
|
307
|
+
--arg intention_next_round "$intention_next_round" \
|
|
308
|
+
--arg design_next_round "$design_next_round" \
|
|
309
|
+
--arg plan_next_round "$plan_next_round" \
|
|
310
|
+
--arg next_number "$next_number" \
|
|
311
|
+
--arg constitution_base "$constitution_base" \
|
|
312
|
+
--arg constitution_local "$constitution_local" \
|
|
313
|
+
--argjson has_constitution_local "$has_constitution_local" \
|
|
314
|
+
--arg branch_class "$branch_class" \
|
|
315
|
+
'{
|
|
316
|
+
repo_root: $repo_root,
|
|
317
|
+
is_git_repo: $is_git_repo,
|
|
318
|
+
branch: (if $branch=="" then null else $branch end),
|
|
319
|
+
feature_slug: (if $feature_slug=="" then null else $feature_slug end),
|
|
320
|
+
feature_dir: (if $feature_dir=="" then null else $feature_dir end),
|
|
321
|
+
candidate_slugs: $candidate_slugs,
|
|
322
|
+
layout: $layout,
|
|
323
|
+
requirement: (if $requirement=="" then null else $requirement end),
|
|
324
|
+
design: (if $design=="" then null else $design end),
|
|
325
|
+
specs: (if $specs=="" then null else $specs end),
|
|
326
|
+
data_model: (if $data_model=="" then null else $data_model end),
|
|
327
|
+
plan: (if $plan=="" then null else $plan end),
|
|
328
|
+
state: (if $state=="" then null else $state end),
|
|
329
|
+
audit: (if $audit=="" then null else $audit end),
|
|
330
|
+
decisions: (if $decisions=="" then null else $decisions end),
|
|
331
|
+
decisions_register: (if $decisions_register=="" then null else $decisions_register end),
|
|
332
|
+
has_decisions: $has_decisions,
|
|
333
|
+
has_requirement: $has_requirement,
|
|
334
|
+
has_design: $has_design,
|
|
335
|
+
has_specs: $has_specs,
|
|
336
|
+
has_data_model: $has_data_model,
|
|
337
|
+
has_plan: $has_plan,
|
|
338
|
+
intention_next_round: (if $intention_next_round=="" then null else ($intention_next_round|tonumber) end),
|
|
339
|
+
design_next_round: (if $design_next_round=="" then null else ($design_next_round|tonumber) end),
|
|
340
|
+
plan_next_round: (if $plan_next_round=="" then null else ($plan_next_round|tonumber) end),
|
|
341
|
+
next_number: $next_number,
|
|
342
|
+
constitution_base: (if $constitution_base=="" then null else $constitution_base end),
|
|
343
|
+
constitution_local: $constitution_local,
|
|
344
|
+
has_constitution_local: $has_constitution_local,
|
|
345
|
+
branch_class: (if $branch_class=="" then null else $branch_class end)
|
|
346
|
+
}'
|
|
347
|
+
exit 0
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# do-prereqs.sh — doflow implement-phase prerequisite gate (prompt-level PRIMARY).
|
|
3
|
+
#
|
|
4
|
+
# Called as step 1 of /do-execute-plan. With --require-plan it exits 2 (and prints an
|
|
5
|
+
# error JSON) when the active feature lacks requirement.md, design.md, or plan.md — the
|
|
6
|
+
# "no implementing before you've planned" rule. Self-contained; fail-open for everything
|
|
7
|
+
# EXCEPT the explicit gate failure (the one intentional non-zero exit).
|
|
8
|
+
#
|
|
9
|
+
# Usage: do-prereqs.sh --require-plan [--slug=<slug>]
|
|
10
|
+
# exit 0 = ok, exit 2 = missing requirement/design/plan (or ambiguous/no active feature)
|
|
11
|
+
# --slug=<slug> forwards to do-paths.sh — pass this after a caller has already disambiguated
|
|
12
|
+
# an "ambiguous-feature" result (non-git root, 2+ candidates) via AskUserQuestion.
|
|
13
|
+
|
|
14
|
+
set -uo pipefail
|
|
15
|
+
|
|
16
|
+
require_plan=false
|
|
17
|
+
slug_override=""
|
|
18
|
+
for a in "$@"; do
|
|
19
|
+
case "$a" in
|
|
20
|
+
--require-plan) require_plan=true ;;
|
|
21
|
+
--slug=*) slug_override="${a#--slug=}" ;;
|
|
22
|
+
esac
|
|
23
|
+
done
|
|
24
|
+
|
|
25
|
+
command -v jq >/dev/null 2>&1 || { echo '{"ok":true,"note":"jq-absent-skip-gate"}'; exit 0; }
|
|
26
|
+
|
|
27
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
28
|
+
RESOLVER="$script_dir/do-paths.sh"
|
|
29
|
+
[ -f "$RESOLVER" ] || RESOLVER="${DOFLOW_CONFIG_DIR:+$DOFLOW_CONFIG_DIR/scripts/doflow/bash/do-paths.sh}"
|
|
30
|
+
[ -f "$RESOLVER" ] || { echo '{"ok":true,"note":"resolver-absent-skip-gate"}'; exit 0; }
|
|
31
|
+
|
|
32
|
+
RESOLVER_ARGS=(--json)
|
|
33
|
+
[ -n "$slug_override" ] && RESOLVER_ARGS+=("--slug=$slug_override")
|
|
34
|
+
rc=0; json=$("$RESOLVER" "${RESOLVER_ARGS[@]}" 2>/dev/null) || rc=$?
|
|
35
|
+
# A slug that could name a path is the caller's mistake, not an unrunnable resolver: the gate refuses
|
|
36
|
+
# it with the resolver's own error rather than skipping itself (IC-009).
|
|
37
|
+
if [ "$rc" -eq 2 ] && [ "$(printf '%s' "$json" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
|
|
38
|
+
printf '%s\n' "$json"; exit 2
|
|
39
|
+
fi
|
|
40
|
+
[ "$rc" -eq 0 ] || { echo '{"ok":true,"note":"resolver-error-skip-gate"}'; exit 0; }
|
|
41
|
+
slug=$(echo "$json" | jq -r '.feature_slug // empty')
|
|
42
|
+
candidate_slugs=$(echo "$json" | jq -c '.candidate_slugs // []')
|
|
43
|
+
has_requirement=$(echo "$json" | jq -r '.has_requirement')
|
|
44
|
+
has_design=$(echo "$json" | jq -r '.has_design')
|
|
45
|
+
has_plan=$(echo "$json" | jq -r '.has_plan')
|
|
46
|
+
|
|
47
|
+
if [ "$require_plan" = true ]; then
|
|
48
|
+
if [ -z "$slug" ]; then
|
|
49
|
+
if [ "$candidate_slugs" != "[]" ]; then
|
|
50
|
+
echo "$json" | jq '{ok:false, error:"ambiguous-feature", candidate_slugs:.candidate_slugs,
|
|
51
|
+
hint:"multiple agent-docs/doflow/ feature dirs exist and no git branch disambiguates them — ask which one via AskUserQuestion, then re-run do-prereqs.sh --require-plan --slug=<chosen>"}'
|
|
52
|
+
else
|
|
53
|
+
echo "$json" | jq '{ok:false, error:"no-active-feature", hint:"run /do-brainstorm to start a feature"}'
|
|
54
|
+
fi
|
|
55
|
+
exit 2
|
|
56
|
+
fi
|
|
57
|
+
if [ "$has_requirement" != "true" ] || [ "$has_design" != "true" ] || [ "$has_plan" != "true" ]; then
|
|
58
|
+
echo "$json" | jq '{ok:false, error:"missing-prereqs", has_requirement:.has_requirement,
|
|
59
|
+
has_design:.has_design, has_plan:.has_plan,
|
|
60
|
+
hint:"run /do-design and /do-plan before /do-execute-plan"}'
|
|
61
|
+
exit 2
|
|
62
|
+
fi
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
echo "$json" | jq '{ok:true, feature_slug:.feature_slug, has_requirement:.has_requirement, has_design:.has_design, has_plan:.has_plan}'
|
|
66
|
+
exit 0
|