@khoavu882/doflow 0.0.0-stage → 2.0.0-beta.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +132 -2
- package/bin/doflow.js +44 -0
- package/core/.antigravity-plugin/hooks.json +39 -0
- package/core/.antigravity-plugin/mcp_config.json +18 -0
- package/core/.antigravity-plugin/plugin.json +31 -0
- package/core/.claude-plugin/marketplace.json +20 -0
- package/core/.claude-plugin/plugin.json +21 -0
- package/core/.codex-plugin/marketplace.json +20 -0
- package/core/.codex-plugin/plugin.json +35 -0
- package/core/.plugin/plugin.json +19 -0
- package/core/harnesses/antigravity/hooks/pre-implementation-gate.sh +9 -0
- package/core/harnesses/antigravity/hooks/stop-check.sh +10 -0
- package/core/harnesses/claude/hooks/lib.sh +304 -0
- package/core/harnesses/claude/hooks/mcp-tool-guard.sh +20 -0
- package/core/harnesses/claude/hooks/permission-audit.sh +47 -0
- package/core/harnesses/claude/hooks/post-compact.sh +74 -0
- package/core/harnesses/claude/hooks/post-edit-lint.sh +36 -0
- package/core/harnesses/claude/hooks/post-tool-failure.sh +47 -0
- package/core/harnesses/claude/hooks/pre-bash-guard.sh +20 -0
- package/core/harnesses/claude/hooks/pre-compact.sh +43 -0
- package/core/harnesses/claude/hooks/pre-implement-gate.sh +21 -0
- package/core/harnesses/claude/hooks/session-end.sh +89 -0
- package/core/harnesses/claude/hooks/session-start.sh +5 -0
- package/core/harnesses/claude/hooks/skill-config-audit.sh +39 -0
- package/core/harnesses/claude/hooks/stop-check.sh +5 -0
- package/core/harnesses/claude/hooks/subagent-audit.sh +7 -0
- package/core/harnesses/claude/hooks/user-prompt-submit.sh +5 -0
- package/core/harnesses/claude/settings/keybindings.json +20 -0
- package/core/harnesses/claude/settings/settings.json +201 -0
- package/core/harnesses/claude/statusline/doflow-statusline.sh +49 -0
- package/core/harnesses/codex/agents/core-implementer.toml +7 -0
- package/core/harnesses/codex/agents/quality-guardian.toml +8 -0
- package/core/harnesses/codex/agents/research-writer.toml +8 -0
- package/core/harnesses/codex/agents/spec-analyst.toml +8 -0
- package/core/harnesses/codex/agents/system-architect.toml +9 -0
- package/core/harnesses/codex/config/config.toml +10 -0
- package/core/harnesses/codex/hooks/hooks.json +122 -0
- package/core/harnesses/codex/hooks/lib.sh +335 -0
- package/core/harnesses/codex/hooks/mcp-tool-guard.sh +18 -0
- package/core/harnesses/codex/hooks/post-edit-lint.impl.sh +52 -0
- package/core/harnesses/codex/hooks/post-edit-lint.sh +6 -0
- package/core/harnesses/codex/hooks/pre-bash-guard.sh +18 -0
- package/core/harnesses/codex/hooks/pre-compact.impl.sh +46 -0
- package/core/harnesses/codex/hooks/pre-compact.sh +9 -0
- package/core/harnesses/codex/hooks/pre-implement-gate.sh +19 -0
- package/core/harnesses/codex/hooks/session-end.impl.sh +92 -0
- package/core/harnesses/codex/hooks/session-end.sh +5 -0
- package/core/harnesses/codex/hooks/session-start.sh +5 -0
- package/core/harnesses/codex/hooks/stop-check.sh +8 -0
- package/core/harnesses/codex/hooks/subagent-audit.sh +8 -0
- package/core/harnesses/codex/hooks/user-prompt-submit.sh +6 -0
- package/core/harnesses/gemini/hooks/hooks.json +77 -0
- package/core/harnesses/gemini/hooks/lib.sh +302 -0
- package/core/harnesses/gemini/hooks/mcp-tool-guard.sh +21 -0
- package/core/harnesses/gemini/hooks/post-edit-lint.sh +39 -0
- package/core/harnesses/gemini/hooks/pre-bash-guard.sh +21 -0
- package/core/harnesses/gemini/hooks/pre-compact.sh +38 -0
- package/core/harnesses/gemini/hooks/pre-implement-gate.sh +22 -0
- package/core/harnesses/gemini/hooks/session-end.impl.sh +89 -0
- package/core/harnesses/gemini/hooks/session-end.sh +5 -0
- package/core/harnesses/gemini/hooks/session-start.sh +5 -0
- package/core/harnesses/gemini/settings/adapter-defaults.json +4 -0
- package/core/harnesses/kiro/agents/doflow.json +58 -0
- package/core/harnesses/kiro/hooks/doflow.json +53 -0
- package/core/harnesses/kiro/hooks/lib.sh +324 -0
- package/core/harnesses/kiro/hooks/mcp-tool-guard.sh +9 -0
- package/core/harnesses/kiro/hooks/pre-bash-guard.sh +9 -0
- package/core/harnesses/kiro/hooks/pre-implement-gate.sh +9 -0
- package/core/harnesses/kiro/hooks/session-start.sh +9 -0
- package/core/harnesses/kiro/hooks/stop-check.sh +43 -0
- package/core/harnesses/shared/hooks/ENVELOPE_MIGRATION.md +39 -0
- package/core/harnesses/shared/hooks/adapters/antigravity.js +111 -0
- package/core/harnesses/shared/hooks/adapters/arg-fields.js +19 -0
- package/core/harnesses/shared/hooks/adapters/gemini.js +114 -0
- package/core/harnesses/shared/hooks/hooks.json +47 -0
- package/core/harnesses/shared/hooks/policies/blocked-patterns.conf +91 -0
- package/core/harnesses/shared/hooks/policies/capture-failure.sh +97 -0
- package/core/harnesses/shared/hooks/policies/deny-json.sh +29 -0
- package/core/harnesses/shared/hooks/policies/lib.sh +501 -0
- package/core/harnesses/shared/hooks/policies/mcp-policy.conf +21 -0
- package/core/harnesses/shared/hooks/policies/mcp-tool-guard.sh +91 -0
- package/core/harnesses/shared/hooks/policies/pre-bash-guard.sh +328 -0
- package/core/harnesses/shared/hooks/policies/pre-implementation-gate.sh +496 -0
- package/core/harnesses/shared/hooks/policies/prompt-nudge.jq +131 -0
- package/core/harnesses/shared/hooks/policies/session-context.sh +141 -0
- package/core/harnesses/shared/hooks/policies/stop-check.sh +264 -0
- package/core/harnesses/shared/hooks/policies/subagent-audit.sh +46 -0
- package/core/harnesses/shared/hooks/policies/user-prompt-submit.sh +235 -0
- package/core/harnesses/shared/hooks/stream-hook-runner.js +590 -0
- package/core/harnesses/shared/locator/doflow-run +87 -0
- package/core/registry/advice.json +101 -0
- package/core/registry/assets.json +792 -0
- package/core/registry/capabilities.json +120 -0
- package/core/registry/contracts.json +189 -0
- package/core/registry/external-tools.json +191 -0
- package/core/registry/harnesses.json +2062 -0
- package/core/registry/lifecycle.json +231 -0
- package/core/registry/mcp.json +18 -0
- package/core/registry/models.json +79 -0
- package/core/registry/readiness-templates.json +118 -0
- package/core/registry/routes.json +47 -0
- package/core/registry/storage.json +1057 -0
- package/core/registry/verification.json +258 -0
- package/core/registry/workflows.json +790 -0
- package/core/shared/agent-specs/core-implementer.md +73 -0
- package/core/shared/agent-specs/quality-guardian.md +28 -0
- package/core/shared/agent-specs/research-writer.md +31 -0
- package/core/shared/agent-specs/spec-analyst.md +33 -0
- package/core/shared/agent-specs/system-architect.md +29 -0
- package/core/shared/guidance/DOFLOW_CORE.md +8 -0
- package/core/shared/guidance/FLAGS.md +25 -0
- package/core/shared/guidance/PRINCIPLES.md +61 -0
- package/core/shared/guidance/VERSION +1 -0
- package/core/shared/guidance/mcp/MCP_Context7.md +18 -0
- package/core/shared/guidance/mcp/MCP_Sequential.md +17 -0
- package/core/shared/guidance/modes/MODE_Brainstorming.md +46 -0
- package/core/shared/guidance/modes/MODE_DeepResearch.md +69 -0
- package/core/shared/guidance/modes/MODE_Introspection.md +33 -0
- package/core/shared/guidance/modes/MODE_Orchestration.md +57 -0
- package/core/shared/guidance/modes/MODE_Task_Management.md +74 -0
- package/core/shared/guidance/pointers/claude-gemini.md +2 -0
- package/core/shared/guidance/pointers/codex.md +6 -0
- package/core/shared/guidance/references/ARTIFACT_FORMAT.md +373 -0
- package/core/shared/guidance/references/ARTIFACT_VOICE.md +104 -0
- package/core/shared/guidance/references/CONSTITUTION_BASE.md +47 -0
- package/core/shared/guidance/references/DIAGRAM_GUIDE.md +161 -0
- package/core/shared/guidance/references/DOFLOW_CHAIN.md +87 -0
- package/core/shared/guidance/references/EVIDENCE_LEDGER.md +93 -0
- package/core/shared/guidance/references/MODEL_SELECTION.md +74 -0
- package/core/shared/guidance/references/ORCHESTRATION.md +36 -0
- package/core/shared/guidance/references/RESEARCH_CONFIG.md +21 -0
- package/core/shared/guidance/references/WORKFLOW_HANDOFF.md +81 -0
- package/core/shared/guidance/rules/RULE_01_SAFETY.md +25 -0
- package/core/shared/guidance/rules/RULE_02_WORKFLOW.md +23 -0
- package/core/shared/guidance/rules/RULE_03_QUALITY.md +14 -0
- package/core/shared/guidance/rules/RULE_04_QUESTIONS.md +46 -0
- package/core/shared/scripts/doflow/assets/mermaid-theme.json +46 -0
- package/core/shared/scripts/doflow/bash/do-exec-paths.sh +167 -0
- package/core/shared/scripts/doflow/bash/do-git-state.sh +546 -0
- package/core/shared/scripts/doflow/bash/do-parallel-check.sh +208 -0
- package/core/shared/scripts/doflow/bash/do-paths.sh +347 -0
- package/core/shared/scripts/doflow/bash/do-prereqs.sh +66 -0
- package/core/shared/scripts/doflow/bash/do-review-package.sh +127 -0
- package/core/shared/scripts/doflow/bash/do-task-brief.sh +698 -0
- package/core/shared/scripts/doflow/bash/render-audit.sh +224 -0
- package/core/shared/scripts/doflow/bash/render-diagrams.sh +177 -0
- package/core/shared/scripts/doflow/bash/render-puml.sh +223 -0
- package/core/shared/scripts/doflow/bash/sync-context.sh +66 -0
- package/core/shared/scripts/doflow/bash/validate-artifacts.sh +526 -0
- package/core/shared/scripts/doflow/bin/doflow-run +592 -0
- package/core/shared/skills/do/SKILL.md +128 -0
- package/core/shared/skills/do/references/estimation.md +13 -0
- package/core/shared/skills/do/references/pm_routing.md +25 -0
- package/core/shared/skills/do/references/tool_matrix.md +52 -0
- package/core/shared/skills/do-brainstorm/SKILL.md +252 -0
- package/core/shared/skills/do-code-review/README.md +49 -0
- package/core/shared/skills/do-code-review/SKILL.md +333 -0
- package/core/shared/skills/do-code-review/assets/sample_c_clean.c +71 -0
- package/core/shared/skills/do-code-review/assets/sample_c_smells.c +66 -0
- package/core/shared/skills/do-code-review/assets/sample_cpp_shift.cpp +19 -0
- package/core/shared/skills/do-code-review/assets/sample_csharp_clean.cs +100 -0
- package/core/shared/skills/do-code-review/assets/sample_csharp_smells.cs +78 -0
- package/core/shared/skills/do-code-review/assets/sample_java_clean.java +55 -0
- package/core/shared/skills/do-code-review/assets/sample_java_smells.java +56 -0
- package/core/shared/skills/do-code-review/assets/sample_json_clean.json +7 -0
- package/core/shared/skills/do-code-review/assets/sample_markdown_clean.md +30 -0
- package/core/shared/skills/do-code-review/assets/sample_markdown_smells.md +29 -0
- package/core/shared/skills/do-code-review/assets/sample_shell_heredoc.sh +38 -0
- package/core/shared/skills/do-code-review/assets/sample_shell_smells.sh +15 -0
- package/core/shared/skills/do-code-review/assets/sample_typescript_strings.ts +36 -0
- package/core/shared/skills/do-code-review/assets/sample_yaml_smells.yaml +6 -0
- package/core/shared/skills/do-code-review/content-types/config.md +83 -0
- package/core/shared/skills/do-code-review/content-types/markdown.md +132 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_c_clean_quality.json +79 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_c_smells_quality.json +169 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_cpp_shift_quality.json +49 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_clean_quality.json +149 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_csharp_smells_quality.json +157 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_java_clean_quality.json +67 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_java_smells_quality.json +97 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_json_clean_quality.json +33 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_clean_quality.json +1 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_markdown_smells_quality.json +16 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_shell_heredoc_quality.json +54 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_shell_smells_quality.json +91 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_typescript_strings_quality.json +60 -0
- package/core/shared/skills/do-code-review/expected_outputs/sample_yaml_smells_quality.json +46 -0
- package/core/shared/skills/do-code-review/languages/c.md +103 -0
- package/core/shared/skills/do-code-review/languages/cpp.md +103 -0
- package/core/shared/skills/do-code-review/languages/csharp.md +97 -0
- package/core/shared/skills/do-code-review/languages/dart.md +108 -0
- package/core/shared/skills/do-code-review/languages/go.md +92 -0
- package/core/shared/skills/do-code-review/languages/java.md +103 -0
- package/core/shared/skills/do-code-review/languages/kotlin.md +88 -0
- package/core/shared/skills/do-code-review/languages/php.md +109 -0
- package/core/shared/skills/do-code-review/languages/python.md +94 -0
- package/core/shared/skills/do-code-review/languages/ruby.md +105 -0
- package/core/shared/skills/do-code-review/languages/rust.md +99 -0
- package/core/shared/skills/do-code-review/languages/shell.md +90 -0
- package/core/shared/skills/do-code-review/languages/swift.md +88 -0
- package/core/shared/skills/do-code-review/languages/typescript.md +97 -0
- package/core/shared/skills/do-code-review/review-policy.json +47 -0
- package/core/shared/skills/do-code-review/rules/universal.md +49 -0
- package/core/shared/skills/do-code-review/scripts/code_quality_checker.py +1696 -0
- package/core/shared/skills/do-code-review/scripts/doc_quality_checker.py +532 -0
- package/core/shared/skills/do-code-review/scripts/pr_analyzer.py +620 -0
- package/core/shared/skills/do-code-review/scripts/review_report_generator.py +505 -0
- package/core/shared/skills/do-constitution/SKILL.md +72 -0
- package/core/shared/skills/do-design/SKILL.md +212 -0
- package/core/shared/skills/do-diagnose/SKILL.md +156 -0
- package/core/shared/skills/do-diagnose/references/code_audit.md +15 -0
- package/core/shared/skills/do-diagnose/references/refactoring.md +12 -0
- package/core/shared/skills/do-diagnose/references/root_cause.md +17 -0
- package/core/shared/skills/do-document/SKILL.md +81 -0
- package/core/shared/skills/do-document/references/api-reference.md +42 -0
- package/core/shared/skills/do-document/references/deep_research.md +18 -0
- package/core/shared/skills/do-document/references/embedded-research.md +11 -0
- package/core/shared/skills/do-document/references/implementation-flow.md +105 -0
- package/core/shared/skills/do-document/references/index_generator.md +9 -0
- package/core/shared/skills/do-document/references/user-guide.md +22 -0
- package/core/shared/skills/do-execute-plan/SKILL.md +261 -0
- package/core/shared/skills/do-execute-plan/references/handoff_record.md +57 -0
- package/core/shared/skills/do-execute-plan/references/parallel_dispatch.md +92 -0
- package/core/shared/skills/do-execute-plan/references/readiness_gate.md +155 -0
- package/core/shared/skills/do-execute-plan/references/run_position.md +63 -0
- package/core/shared/skills/do-execute-plan/references/scaffold.md +441 -0
- package/core/shared/skills/do-execute-plan/references/task_attempts.md +41 -0
- package/core/shared/skills/do-flow/SKILL.md +186 -0
- package/core/shared/skills/do-flow/references/task_classes.md +134 -0
- package/core/shared/skills/do-git/SKILL.md +115 -0
- package/core/shared/skills/do-git/references/hotfix-ritual.md +44 -0
- package/core/shared/skills/do-git/references/lifecycle-policy.md +54 -0
- package/core/shared/skills/do-git/references/release-ritual.md +64 -0
- package/core/shared/skills/do-implement/SKILL.md +222 -0
- package/core/shared/skills/do-plan/SKILL.md +219 -0
- package/core/shared/skills/do-test/SKILL.md +113 -0
- package/core/shared/templates/doflow/constitution-template.md +28 -0
- package/core/shared/templates/doflow/data-model-template.md +169 -0
- package/core/shared/templates/doflow/design-template.md +193 -0
- package/core/shared/templates/doflow/external-contract-template.md +35 -0
- package/core/shared/templates/doflow/intent-template.md +45 -0
- package/core/shared/templates/doflow/plan-template.md +151 -0
- package/core/shared/templates/doflow/question-log-template.md +51 -0
- package/core/shared/templates/doflow/requirement-template.md +126 -0
- package/core/shared/templates/doflow/specs-template.md +55 -0
- package/core/shared/templates/doflow/state-template.md +59 -0
- package/core/shared/workflows/do-flow-chain.md +17 -0
- package/package.json +46 -4
- package/src/adapters/antigravity/index.ts +631 -0
- package/src/adapters/claude/index.ts +548 -0
- package/src/adapters/claude/mcp.ts +94 -0
- package/src/adapters/codex/agents.ts +208 -0
- package/src/adapters/codex/config.ts +272 -0
- package/src/adapters/codex/hooks.ts +203 -0
- package/src/adapters/codex/index.ts +707 -0
- package/src/adapters/codex/mcp.ts +271 -0
- package/src/adapters/codex/native-projection.ts +34 -0
- package/src/adapters/copilot/index.ts +413 -0
- package/src/adapters/copy-tree.ts +620 -0
- package/src/adapters/gemini/hooks.ts +210 -0
- package/src/adapters/gemini/index.ts +390 -0
- package/src/adapters/hook-commands.ts +60 -0
- package/src/adapters/index.ts +106 -0
- package/src/adapters/kiro/index.ts +310 -0
- package/src/adapters/mcp-entries.ts +331 -0
- package/src/adapters/opencode/index.ts +466 -0
- package/src/adapters/pi/index.ts +279 -0
- package/src/adapters/pi/json-members.ts +185 -0
- package/src/adapters/pi/mcp.ts +373 -0
- package/src/adapters/resolve-source.ts +19 -0
- package/src/cli/commands/install.ts +218 -0
- package/src/cli/commands/list-backups.ts +21 -0
- package/src/cli/commands/reconcile.ts +120 -0
- package/src/cli/commands/remove.ts +82 -0
- package/src/cli/commands/rollback.ts +172 -0
- package/src/cli/commands/self-update.ts +33 -0
- package/src/cli/commands/status.ts +138 -0
- package/src/cli/commands/tools.ts +92 -0
- package/src/cli/commands/update.ts +162 -0
- package/src/cli/index.ts +568 -0
- package/src/cli/runtime-commands.ts +281 -0
- package/src/cli/shared.ts +218 -0
- package/src/helper/bash-helper.ts +34 -0
- package/src/helper/git.ts +24 -0
- package/src/helper/guardrails.ts +49 -0
- package/src/helper/harness-paths.ts +140 -0
- package/src/helper/marker-merge.ts +116 -0
- package/src/helper/prompt.ts +61 -0
- package/src/helper/repo-root.ts +29 -0
- package/src/helper/settings-merge.ts +151 -0
- package/src/helper/settings-scope.ts +33 -0
- package/src/helper/toml.ts +244 -0
- package/src/install/backup-set.ts +149 -0
- package/src/install/backup.ts +724 -0
- package/src/install/context.ts +58 -0
- package/src/install/manifest.ts +145 -0
- package/src/install/mcp.ts +254 -0
- package/src/install/paths.ts +84 -0
- package/src/install/targets.ts +90 -0
- package/src/install/tool-lifecycle.ts +151 -0
- package/src/lifecycle/bash-availability.ts +25 -0
- package/src/lifecycle/index.ts +812 -0
- package/src/lifecycle/mcp-index.ts +28 -0
- package/src/lifecycle/policies.ts +73 -0
- package/src/lifecycle/view.ts +175 -0
- package/src/registry/index.ts +611 -0
- package/src/release/sync-plugin-versions.ts +66 -0
- package/src/runtime/advice/cli.ts +283 -0
- package/src/runtime/advice/client.ts +324 -0
- package/src/runtime/advice/config.ts +189 -0
- package/src/runtime/advice/doctor.ts +169 -0
- package/src/runtime/advice/endpoint-guard.ts +134 -0
- package/src/runtime/advice/kinds.ts +154 -0
- package/src/runtime/advice/providers/ollama-systemone.ts +209 -0
- package/src/runtime/advice/record.ts +166 -0
- package/src/runtime/advice/registry.ts +170 -0
- package/src/runtime/advice/strength.ts +66 -0
- package/src/runtime/brief-tools.ts +342 -0
- package/src/runtime/c4-project.ts +371 -0
- package/src/runtime/capability-router.ts +484 -0
- package/src/runtime/checkouts.ts +170 -0
- package/src/runtime/claims.ts +468 -0
- package/src/runtime/cli-result.ts +55 -0
- package/src/runtime/cli.ts +678 -0
- package/src/runtime/command-detect.ts +516 -0
- package/src/runtime/context-pack.ts +492 -0
- package/src/runtime/decision-register.ts +493 -0
- package/src/runtime/evidence-ledger.ts +338 -0
- package/src/runtime/failure/capture.ts +272 -0
- package/src/runtime/failure/classifier.ts +68 -0
- package/src/runtime/failure/cli.ts +178 -0
- package/src/runtime/failure/home.ts +64 -0
- package/src/runtime/failure/store.ts +226 -0
- package/src/runtime/feature-resolve.ts +79 -0
- package/src/runtime/freshness.ts +177 -0
- package/src/runtime/health.ts +863 -0
- package/src/runtime/history-compactor.ts +237 -0
- package/src/runtime/implementation-gate.ts +321 -0
- package/src/runtime/inventory/coverage.ts +218 -0
- package/src/runtime/inventory/identity.ts +65 -0
- package/src/runtime/inventory/index.ts +705 -0
- package/src/runtime/inventory/precedence.ts +134 -0
- package/src/runtime/inventory/read-scopes.ts +131 -0
- package/src/runtime/inventory/siblings.ts +192 -0
- package/src/runtime/knowledge/chunker.ts +124 -0
- package/src/runtime/knowledge/hybrid.ts +149 -0
- package/src/runtime/knowledge/index-store.ts +145 -0
- package/src/runtime/knowledge/retrieval.ts +194 -0
- package/src/runtime/leak-scan.ts +185 -0
- package/src/runtime/lifecycle/cli.ts +276 -0
- package/src/runtime/lifecycle/event-store.ts +340 -0
- package/src/runtime/lifecycle/fold.ts +353 -0
- package/src/runtime/lifecycle/followup.ts +432 -0
- package/src/runtime/lifecycle/goal.ts +249 -0
- package/src/runtime/lifecycle/intent-writer.ts +86 -0
- package/src/runtime/lifecycle/overview.ts +292 -0
- package/src/runtime/lifecycle/release.ts +241 -0
- package/src/runtime/lifecycle/report-store.ts +221 -0
- package/src/runtime/lifecycle/retention.ts +212 -0
- package/src/runtime/lifecycle/root.ts +71 -0
- package/src/runtime/lifecycle/status.ts +353 -0
- package/src/runtime/lifecycle/store-upkeep.ts +393 -0
- package/src/runtime/locator-resolve.ts +158 -0
- package/src/runtime/mask.ts +210 -0
- package/src/runtime/mcp-consultations.ts +412 -0
- package/src/runtime/mcp-reach.ts +157 -0
- package/src/runtime/model-router.ts +153 -0
- package/src/runtime/outcome.ts +467 -0
- package/src/runtime/reach.ts +148 -0
- package/src/runtime/readiness-record.ts +150 -0
- package/src/runtime/readiness.ts +435 -0
- package/src/runtime/recovery.ts +282 -0
- package/src/runtime/research-request.ts +231 -0
- package/src/runtime/retrieval-plan.ts +578 -0
- package/src/runtime/scaffold/artifacts.ts +217 -0
- package/src/runtime/scaffold/fingerprint.ts +56 -0
- package/src/runtime/scaffold/generate.ts +911 -0
- package/src/runtime/scaffold/languages.ts +245 -0
- package/src/runtime/storage/cli.ts +64 -0
- package/src/runtime/storage/engine.ts +249 -0
- package/src/runtime/storage/registry.ts +130 -0
- package/src/runtime/storage/roots.ts +63 -0
- package/src/runtime/storage/rules.ts +282 -0
- package/src/runtime/storage/upkeep.ts +243 -0
- package/src/runtime/storage/work-status.ts +326 -0
- package/src/runtime/task-attempts.ts +415 -0
- package/src/runtime/task-classifier.ts +550 -0
- package/src/runtime/task-scope.ts +130 -0
- package/src/runtime/task-state.ts +176 -0
- package/src/runtime/trace/indicators.ts +235 -0
- package/src/runtime/trace/ledger.ts +626 -0
- package/src/runtime/trace/render.ts +76 -0
- package/src/runtime/trace/views.ts +459 -0
- package/src/runtime/verification/contract-runner.ts +341 -0
- package/src/runtime/verification/engine.ts +1461 -0
- package/src/runtime/verification/registry.ts +130 -0
- package/src/runtime/verification/scope-bound.ts +418 -0
- package/src/runtime/verification/test-selection.ts +172 -0
- package/src/runtime/workflow-engine.ts +588 -0
- package/src/runtime/workflow-orchestrator.ts +953 -0
- package/src/runtime/worktree.ts +340 -0
- package/src/state/index.ts +193 -0
- package/src/state/lockfile.ts +136 -0
- package/src/state/run-lock.ts +408 -0
- package/types/node-commonjs.d.ts +68 -0
- package/types/runtime.d.ts +91 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# do-git-state.sh — deterministic lifecycle state helper for /do-git.
|
|
3
|
+
#
|
|
4
|
+
# Emits JSON describing the repository's position in the development cycle.
|
|
5
|
+
# This is a deterministic layer component: no prompts, no side effects, just
|
|
6
|
+
# reading git refs and producing structured data.
|
|
7
|
+
|
|
8
|
+
set -uo pipefail
|
|
9
|
+
|
|
10
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
11
|
+
printf '{"error":"jq-not-found","branch_class":null}\n'
|
|
12
|
+
exit 0
|
|
13
|
+
fi
|
|
14
|
+
|
|
15
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
16
|
+
resolved="$(bash "$script_dir/do-paths.sh" --json 2>/dev/null)"
|
|
17
|
+
repo_root="$(printf '%s' "$resolved" | jq -r '.repo_root // empty')"
|
|
18
|
+
# The resolver refuses a branch whose derived slug is unsafe and then prints no repo_root. This
|
|
19
|
+
# helper does not need a feature, only the repository, so it asks git rather than reporting a
|
|
20
|
+
# repository that exists as "not-a-git-repo".
|
|
21
|
+
[ -z "$repo_root" ] && repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
|
|
22
|
+
|
|
23
|
+
trunk_names="main develop"
|
|
24
|
+
feature_prefixes="feat feature"
|
|
25
|
+
fix_prefixes="fix bugfix"
|
|
26
|
+
integration_branch="develop"
|
|
27
|
+
production_branch="master"
|
|
28
|
+
|
|
29
|
+
mode=""
|
|
30
|
+
class_name=""
|
|
31
|
+
slug=""
|
|
32
|
+
|
|
33
|
+
for arg in "$@"; do
|
|
34
|
+
case "$arg" in
|
|
35
|
+
--state) mode="state" ;;
|
|
36
|
+
--next-version) mode="next-version" ;;
|
|
37
|
+
--propagation-targets) mode="propagation-targets" ;;
|
|
38
|
+
--fingerprint) mode="fingerprint" ;;
|
|
39
|
+
--lifecycle) mode="lifecycle" ;;
|
|
40
|
+
--branch-name) mode="branch-name" ;;
|
|
41
|
+
--class=*) class_name="${arg#--class=}" ;;
|
|
42
|
+
--slug=*) slug="${arg#--slug=}" ;;
|
|
43
|
+
*) ;;
|
|
44
|
+
esac
|
|
45
|
+
done
|
|
46
|
+
|
|
47
|
+
[ -z "$mode" ] && mode="state"
|
|
48
|
+
|
|
49
|
+
if [ -z "$repo_root" ]; then
|
|
50
|
+
printf '{"error":"not-a-git-repo"}\n'
|
|
51
|
+
exit 0
|
|
52
|
+
fi
|
|
53
|
+
|
|
54
|
+
cd "$repo_root"
|
|
55
|
+
|
|
56
|
+
current_branch=""
|
|
57
|
+
if git rev-parse --abbrev-ref HEAD >/dev/null 2>&1; then
|
|
58
|
+
current_branch="$(git rev-parse --abbrev-ref HEAD)"
|
|
59
|
+
else
|
|
60
|
+
current_branch=""
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# ── Helper: classify a branch name ────────────────────────────────────────────
|
|
64
|
+
get_class() {
|
|
65
|
+
local b="$1"
|
|
66
|
+
case "$b" in
|
|
67
|
+
"") echo "trunk" ;;
|
|
68
|
+
master|main|develop|HEAD) echo "trunk" ;;
|
|
69
|
+
release/*) echo "release" ;;
|
|
70
|
+
hotfix/*) echo "hotfix" ;;
|
|
71
|
+
feat/*|feature/*) echo "feature" ;;
|
|
72
|
+
fix/*|bugfix/*) echo "fix" ;;
|
|
73
|
+
*/*) echo "other" ;;
|
|
74
|
+
*) echo "other" ;;
|
|
75
|
+
esac
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
get_slug() {
|
|
79
|
+
local b="$1"
|
|
80
|
+
case "$b" in
|
|
81
|
+
"") echo "" ;;
|
|
82
|
+
master|main|develop|HEAD) echo "" ;;
|
|
83
|
+
release/*|hotfix/*) echo "" ;;
|
|
84
|
+
*/*) echo "${b#*/}" ;;
|
|
85
|
+
*) echo "$b" ;;
|
|
86
|
+
esac
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
commits_between() {
|
|
90
|
+
local from="$1"
|
|
91
|
+
local to="$2"
|
|
92
|
+
if git rev-parse "$from" >/dev/null 2>&1 && git rev-parse "$to" >/dev/null 2>&1; then
|
|
93
|
+
local ahead="$(git rev-list --count "${to}..${from}" 2>/dev/null || echo "0")"
|
|
94
|
+
local behind="$(git rev-list --count "${from}..${to}" 2>/dev/null || echo "0")"
|
|
95
|
+
echo "$ahead $behind"
|
|
96
|
+
else
|
|
97
|
+
echo "0 0"
|
|
98
|
+
fi
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
is_dirty() {
|
|
102
|
+
local status="$(git status --short 2>/dev/null | wc -l)"
|
|
103
|
+
[ "$status" -gt 0 ]
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
# Which ref the integration branch resolves to decides whether a distance measured against it
|
|
107
|
+
# means anything. The local branch was used unconditionally, so a develop that had not been pulled
|
|
108
|
+
# made every distance wrong by however stale it was — observed live at 22 reported against 32.
|
|
109
|
+
#
|
|
110
|
+
# The remote-tracking ref is preferred where it exists; the local branch is the fallback for a
|
|
111
|
+
# repository with no remote or one never fetched. Preferring it does not make the answer *fresh* —
|
|
112
|
+
# origin/develop is only as current as the last fetch — so the third field reports how far the
|
|
113
|
+
# local branch trails, which is non-zero exactly when a `git fetch` would change the answer.
|
|
114
|
+
#
|
|
115
|
+
# Echoes: "<ref> <kind> <local-behind-remote>"
|
|
116
|
+
resolve_integration_ref() {
|
|
117
|
+
local branch="$1"
|
|
118
|
+
local ref="$branch" kind="local" behind=0
|
|
119
|
+
if git rev-parse --verify --quiet "refs/remotes/origin/${branch}" >/dev/null 2>&1; then
|
|
120
|
+
ref="origin/${branch}"
|
|
121
|
+
kind="remote-tracking"
|
|
122
|
+
if git rev-parse --verify --quiet "refs/heads/${branch}" >/dev/null 2>&1; then
|
|
123
|
+
behind="$(git rev-list --count "${branch}..origin/${branch}" 2>/dev/null || echo 0)"
|
|
124
|
+
fi
|
|
125
|
+
fi
|
|
126
|
+
printf '%s %s %s\n' "$ref" "$kind" "$behind"
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Commits either side of a comparison, or "0 0" when either ref is missing.
|
|
130
|
+
# Echoes: "<ahead> <behind>"
|
|
131
|
+
distance_from() {
|
|
132
|
+
local branch="$1" against="$2"
|
|
133
|
+
if git rev-parse "refs/heads/${branch}" >/dev/null 2>&1 && \
|
|
134
|
+
git rev-parse --verify --quiet "$against" >/dev/null 2>&1; then
|
|
135
|
+
local counts; counts="$(commits_between "$branch" "$against")"
|
|
136
|
+
printf '%s %s\n' "$(echo "$counts" | awk '{print $1}')" "$(echo "$counts" | awk '{print $2}')"
|
|
137
|
+
else
|
|
138
|
+
printf '0 0\n'
|
|
139
|
+
fi
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
# The single lifecycle word for a branch's position. Ordered so the most actionable state wins:
|
|
143
|
+
# an uncommitted change matters more than any distance, and being behind matters more than ahead.
|
|
144
|
+
lifecycle_position() {
|
|
145
|
+
local ahead="$1" behind="$2" dirty="$3"
|
|
146
|
+
if [ "$dirty" = "true" ]; then printf 'dirty-worktree\n'
|
|
147
|
+
elif [ "$behind" -gt 0 ]; then printf 'behind-integration\n'
|
|
148
|
+
elif [ "$ahead" -gt 0 ]; then printf 'ahead-of-integration\n'
|
|
149
|
+
else printf 'in-sync\n'
|
|
150
|
+
fi
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
do_state() {
|
|
154
|
+
local class="$(get_class "$current_branch")"
|
|
155
|
+
local slugs="$(get_slug "$current_branch")"
|
|
156
|
+
local dirty="false"
|
|
157
|
+
is_dirty && dirty="true"
|
|
158
|
+
|
|
159
|
+
local integration_ref integration_ref_kind integration_local_behind
|
|
160
|
+
read -r integration_ref integration_ref_kind integration_local_behind \
|
|
161
|
+
<<< "$(resolve_integration_ref "$integration_branch")"
|
|
162
|
+
|
|
163
|
+
local ahead behind position
|
|
164
|
+
read -r ahead behind <<< "$(distance_from "$current_branch" "$integration_ref")"
|
|
165
|
+
position="$(lifecycle_position "$ahead" "$behind" "$dirty")"
|
|
166
|
+
|
|
167
|
+
jq -n \
|
|
168
|
+
--arg branch "${current_branch:-null}" \
|
|
169
|
+
--arg class_name "$class" \
|
|
170
|
+
--arg feature_slug "$slugs" \
|
|
171
|
+
--argjson dirty "$dirty" \
|
|
172
|
+
--arg ahead "$ahead" \
|
|
173
|
+
--arg behind "$behind" \
|
|
174
|
+
--arg position "$position" \
|
|
175
|
+
--arg integration_ref "$integration_ref" \
|
|
176
|
+
--arg integration_ref_kind "$integration_ref_kind" \
|
|
177
|
+
--arg integration_local_behind "$integration_local_behind" \
|
|
178
|
+
'{
|
|
179
|
+
branch: (if $branch=="" then null else $branch end),
|
|
180
|
+
class: $class_name,
|
|
181
|
+
feature_slug: (if $feature_slug=="" then null else $feature_slug end),
|
|
182
|
+
dirty: $dirty,
|
|
183
|
+
ahead_of_integration: ($ahead | tonumber),
|
|
184
|
+
behind_integration: ($behind | tonumber),
|
|
185
|
+
integration_ref: $integration_ref,
|
|
186
|
+
integration_ref_kind: $integration_ref_kind,
|
|
187
|
+
integration_local_behind_remote: ($integration_local_behind | tonumber),
|
|
188
|
+
position: $position
|
|
189
|
+
}'
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
# The production branch's refs, for finding release tags that were never merged back: the local
|
|
193
|
+
# branch and its remote-tracking ref, both when both exist, because either may be the one that
|
|
194
|
+
# holds the newest release (a local `main` that was not pulled is stale, a remote-tracking one that
|
|
195
|
+
# was not fetched is too). `main` is the default; `master` is the older default the lifecycle
|
|
196
|
+
# policy still names, used only when no `main` ref exists. Echoes one ref per line, possibly none.
|
|
197
|
+
production_refs() {
|
|
198
|
+
local b ref found
|
|
199
|
+
for b in main master; do
|
|
200
|
+
found=""
|
|
201
|
+
for ref in "refs/heads/${b}" "refs/remotes/origin/${b}"; do
|
|
202
|
+
if git rev-parse --verify --quiet "$ref" >/dev/null 2>&1; then printf '%s\n' "${ref#refs/}"; found=1; fi
|
|
203
|
+
done
|
|
204
|
+
[ -n "$found" ] && return
|
|
205
|
+
done
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
# Splits a semver string into its numeric core and its pre-release label.
|
|
209
|
+
# Echoes: "<major> <minor> <patch> <prerelease-or-empty>"
|
|
210
|
+
parse_semver() {
|
|
211
|
+
local version="$1"
|
|
212
|
+
local core="${version%%-*}"; core="${core%%+*}"
|
|
213
|
+
local pre=""
|
|
214
|
+
case "$version" in
|
|
215
|
+
*-*) pre="${version#*-}"; pre="${pre%%+*}" ;;
|
|
216
|
+
esac
|
|
217
|
+
local major minor patch
|
|
218
|
+
IFS='.' read -r major minor patch <<< "$core" 2>/dev/null || true
|
|
219
|
+
printf '%s %s %s %s\n' "${major:-0}" "${minor:-0}" "${patch:-0}" "$pre"
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
# Reads the commit subjects since a base tag and names the bump they imply.
|
|
223
|
+
# Echoes: "<MAJOR|MINOR|PATCH|INITIAL> <commit-count>"
|
|
224
|
+
classify_bump() {
|
|
225
|
+
local base_tag="$1"
|
|
226
|
+
if [ -z "$base_tag" ]; then
|
|
227
|
+
printf 'INITIAL 0\n'
|
|
228
|
+
return
|
|
229
|
+
fi
|
|
230
|
+
local subjects count kind="PATCH"
|
|
231
|
+
subjects="$(git log --format=%s "${base_tag}..HEAD" 2>/dev/null || true)"
|
|
232
|
+
# `git rev-list --count`, not `printf | wc -l`: the latter sees no trailing newline and so
|
|
233
|
+
# undercounts every range by one.
|
|
234
|
+
count="$(git rev-list --count "${base_tag}..HEAD" 2>/dev/null || echo 0)"
|
|
235
|
+
if printf '%s' "$subjects" | grep -qE 'BREAKING CHANGE|\!:'; then
|
|
236
|
+
kind="MAJOR"
|
|
237
|
+
elif printf '%s' "$subjects" | grep -qE '^feat|^feature'; then
|
|
238
|
+
kind="MINOR"
|
|
239
|
+
fi
|
|
240
|
+
printf '%s %s\n' "$kind" "$count"
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
# The next free number on a pre-release line. Computing label.N+1 arithmetically once proposed a
|
|
244
|
+
# tag that already existed — a well-formed value derived without consulting the thing it describes.
|
|
245
|
+
# `git describe` finds the nearest *reachable* tag, so a pre-release cut on an unmerged branch is
|
|
246
|
+
# invisible to the base-tag lookup and collides here instead. Taken numbers are stepped over rather
|
|
247
|
+
# than stopped on, because the caller wants a usable candidate, and the count of them is reported
|
|
248
|
+
# because the gap is a fact about the tag history. Bounded so a pathological tag set cannot spin.
|
|
249
|
+
# Echoes: "<candidate> <skipped-count>"
|
|
250
|
+
next_free_prerelease() {
|
|
251
|
+
local core="$1" pre="$2"
|
|
252
|
+
local label="${pre%.*}" num="${pre##*.}"
|
|
253
|
+
if ! printf '%s' "$num" | grep -qE '^[0-9]+$'; then
|
|
254
|
+
printf '%s-%s.1 0\n' "$core" "$pre"
|
|
255
|
+
return
|
|
256
|
+
fi
|
|
257
|
+
local candidate=$((num + 1)) skipped=0 guard=0
|
|
258
|
+
while [ "$guard" -lt 100 ]; do
|
|
259
|
+
if ! git rev-parse --verify --quiet "refs/tags/v${core}-${label}.${candidate}" >/dev/null 2>&1; then
|
|
260
|
+
break
|
|
261
|
+
fi
|
|
262
|
+
candidate=$((candidate + 1)); skipped=$((skipped + 1)); guard=$((guard + 1))
|
|
263
|
+
done
|
|
264
|
+
printf '%s-%s.%s %s\n' "$core" "$label" "$candidate" "$skipped"
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
# The newest release tag reachable from HEAD or from the production branch, by version order;
|
|
268
|
+
# empty when there is none. do_next_version bases its proposal on it and the lifecycle output
|
|
269
|
+
# reports it, so the two never disagree. The reasoning behind "newest by version" and "reachable
|
|
270
|
+
# from production too" is in do_next_version's comments below.
|
|
271
|
+
find_base_tag() {
|
|
272
|
+
local base_tag="" eligible="" ref
|
|
273
|
+
eligible="$(git tag --merged HEAD --list 'v*' 2>/dev/null || true)"
|
|
274
|
+
while IFS= read -r ref; do
|
|
275
|
+
[ -z "$ref" ] && continue
|
|
276
|
+
eligible="${eligible}"$'\n'"$(git tag --merged "$ref" --list 'v*' 2>/dev/null || true)"
|
|
277
|
+
done <<< "$(production_refs)"
|
|
278
|
+
eligible="$(printf '%s\n' "$eligible" | sort -u | sed '/^$/d')"
|
|
279
|
+
if [ -n "$eligible" ]; then
|
|
280
|
+
base_tag="$(git tag --list 'v*' --sort=-v:refname 2>/dev/null | grep -Fx -f <(printf '%s\n' "$eligible") | head -1 || true)"
|
|
281
|
+
fi
|
|
282
|
+
printf '%s\n' "$base_tag"
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
do_next_version() {
|
|
286
|
+
# Both start set: `set -u` makes a bare `local base_tag` an unbound variable on bash 4+ whenever
|
|
287
|
+
# no eligible tag exists (a repository with no release tags yet).
|
|
288
|
+
local base_tag="" current_version="0.0.0"
|
|
289
|
+
# `git describe --tags --abbrev=0` answers "nearest reachable tag by commit distance", and every
|
|
290
|
+
# version decision below needs "newest reachable tag by version". Those coincide only while
|
|
291
|
+
# history is linear — and a release ritual merges twice, so the newest tag routinely sits further
|
|
292
|
+
# from HEAD than an older one. On this repository after v1.0.0 shipped, beta.8 was reachable at 33
|
|
293
|
+
# commits and beta.7 at 31, so describe returned beta.7 and every later computation was based on a
|
|
294
|
+
# superseded release.
|
|
295
|
+
#
|
|
296
|
+
# Sorting by version instead. The `v*` filter keeps a non-version tag from winning the sort, and
|
|
297
|
+
# --merged keeps the answer to tags that are actually released work.
|
|
298
|
+
#
|
|
299
|
+
# "Merged" means into HEAD *or into the production branch*. A release ritual tags the merge commit
|
|
300
|
+
# on production, and the integration branch only gets that commit back through a second merge
|
|
301
|
+
# that may not have happened yet, so from the integration branch the newest release tag is
|
|
302
|
+
# unreachable and the proposal was based on an older one (v1.8.3 proposed while v1.12.0 was out).
|
|
303
|
+
# The tags are sorted by git as before; the union only decides which of them are eligible.
|
|
304
|
+
base_tag="$(find_base_tag)"
|
|
305
|
+
[ -n "$base_tag" ] && current_version="${base_tag#v}"
|
|
306
|
+
|
|
307
|
+
# The manifest is what a release publishes. A base tag that disagrees with it means the tag or
|
|
308
|
+
# the manifest is behind, which the caller should see before proposing a version from either.
|
|
309
|
+
local manifest_version="" warning=""
|
|
310
|
+
if [ -n "$base_tag" ] && [ -f package.json ]; then
|
|
311
|
+
manifest_version="$(jq -r '.version // empty' package.json 2>/dev/null || true)"
|
|
312
|
+
if [ -n "$manifest_version" ] && [ "$manifest_version" != "$current_version" ]; then
|
|
313
|
+
warning="manifest version ${manifest_version} differs from base tag ${base_tag}"
|
|
314
|
+
fi
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
local major minor patch prerelease
|
|
318
|
+
read -r major minor patch prerelease <<< "$(parse_semver "$current_version")"
|
|
319
|
+
local version_core="${major}.${minor}.${patch}"
|
|
320
|
+
|
|
321
|
+
local bump_kind commit_count
|
|
322
|
+
read -r bump_kind commit_count <<< "$(classify_bump "$base_tag")"
|
|
323
|
+
|
|
324
|
+
# Semver orders 1.0.0-beta.7 < 1.0.0 < 1.0.1, so a pre-release already sits *before* its own
|
|
325
|
+
# version number: promoting it IS the bump, not an increment on top of it. A bump promotes when
|
|
326
|
+
# the field it would raise is already zero, and otherwise increments with the suffix falling
|
|
327
|
+
# away. These are node-semver's inc() rules reproduced rather than invented.
|
|
328
|
+
local next_major="$major" next_minor="$minor" next_patch="$patch"
|
|
329
|
+
if [ "$bump_kind" = "INITIAL" ]; then
|
|
330
|
+
next_major=1; next_minor=0; next_patch=0
|
|
331
|
+
elif [ -n "$prerelease" ]; then
|
|
332
|
+
case "$bump_kind" in
|
|
333
|
+
MAJOR) [ "$minor" -eq 0 ] && [ "$patch" -eq 0 ] || { next_major=$((major + 1)); next_minor=0; next_patch=0; } ;;
|
|
334
|
+
MINOR) [ "$patch" -eq 0 ] || { next_minor=$((minor + 1)); next_patch=0; } ;;
|
|
335
|
+
esac
|
|
336
|
+
else
|
|
337
|
+
case "$bump_kind" in
|
|
338
|
+
MAJOR) next_major=$((major + 1)); next_minor=0; next_patch=0 ;;
|
|
339
|
+
MINOR) next_minor=$((minor + 1)); next_patch=0 ;;
|
|
340
|
+
*) next_patch=$((patch + 1)) ;;
|
|
341
|
+
esac
|
|
342
|
+
fi
|
|
343
|
+
local next_version="${next_major}.${next_minor}.${next_patch}"
|
|
344
|
+
|
|
345
|
+
# The other honest option for a pre-release base: continue the line rather than promote it.
|
|
346
|
+
# Reported alongside so the release ritual can offer the choice instead of assuming one.
|
|
347
|
+
local next_prerelease="" next_prerelease_skipped=0
|
|
348
|
+
if [ -n "$prerelease" ]; then
|
|
349
|
+
read -r next_prerelease next_prerelease_skipped <<< "$(next_free_prerelease "$version_core" "$prerelease")"
|
|
350
|
+
fi
|
|
351
|
+
|
|
352
|
+
jq -n \
|
|
353
|
+
--arg base_tag "${base_tag:-null}" \
|
|
354
|
+
--arg current_version "$current_version" \
|
|
355
|
+
--arg next_version "$next_version" \
|
|
356
|
+
--arg next_prerelease "$next_prerelease" \
|
|
357
|
+
--arg next_prerelease_skipped "$next_prerelease_skipped" \
|
|
358
|
+
--arg prerelease "$prerelease" \
|
|
359
|
+
--arg bump_kind "$bump_kind" \
|
|
360
|
+
--argjson commit_count "$commit_count" \
|
|
361
|
+
--arg warning "$warning" \
|
|
362
|
+
'{
|
|
363
|
+
base_tag: (if $base_tag=="" then null else $base_tag end),
|
|
364
|
+
current_version: $current_version,
|
|
365
|
+
is_prerelease: ($prerelease != ""),
|
|
366
|
+
next_version: $next_version,
|
|
367
|
+
next_prerelease: (if $next_prerelease=="" then null else $next_prerelease end),
|
|
368
|
+
next_prerelease_skipped: ($next_prerelease_skipped | tonumber),
|
|
369
|
+
bump_kind: $bump_kind,
|
|
370
|
+
commits_count: ($commit_count | tonumber)
|
|
371
|
+
} + (if $warning != "" then {warning: $warning} else {} end)'
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
do_propagation_targets() {
|
|
375
|
+
local targets_json="[]"
|
|
376
|
+
|
|
377
|
+
# Integration branch is always a target
|
|
378
|
+
targets_json="$(echo "$targets_json" | jq --arg k "integration" --arg n "$integration_branch" --argjson hf "true" \
|
|
379
|
+
'. += [{kind: $k, name: $n, has_fix: $hf}]')"
|
|
380
|
+
|
|
381
|
+
# Get all active release branches as targets
|
|
382
|
+
while IFS= read -r rel; do
|
|
383
|
+
[ -z "$rel" ] && continue
|
|
384
|
+
targets_json="$(echo "$targets_json" | jq --arg k "release" --arg n "$rel" --argjson hf "false" \
|
|
385
|
+
'. += [{kind: $k, name: $n, has_fix: $hf}]')"
|
|
386
|
+
done < <(git for-each-ref --format='%(refname:short)' refs/heads/release/* 2>/dev/null | sed 's|refs/heads/release/||')
|
|
387
|
+
|
|
388
|
+
local current_class="$(get_class "$current_branch")"
|
|
389
|
+
|
|
390
|
+
jq -n \
|
|
391
|
+
--arg current "${current_branch:-null}" \
|
|
392
|
+
--arg class "$current_class" \
|
|
393
|
+
'{current: (if $current=="" then null else $current end), class: $class, targets: []}' \
|
|
394
|
+
| jq --argjson t "$targets_json" '.targets = $t'
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
do_fingerprint() {
|
|
398
|
+
local head_sha=""
|
|
399
|
+
head_sha="$(git rev-parse HEAD 2>/dev/null | head -c 40)" || true
|
|
400
|
+
|
|
401
|
+
[ -z "$head_sha" ] && { printf '{"error":"cannot-read-head-sha"}\n'; exit 1; }
|
|
402
|
+
|
|
403
|
+
local ref_list=""
|
|
404
|
+
while IFS= read -r r; do
|
|
405
|
+
ref_list="${ref_list}${r}"$'\n'
|
|
406
|
+
done < <(git for-each-ref --format='%(refname)' refs/heads refs/tags 2>/dev/null | sort)
|
|
407
|
+
|
|
408
|
+
local fp=""
|
|
409
|
+
fp="$(printf '%s\n%s' "$head_sha" "$ref_list" | shasum -a 256 | cut -d' ' -f1)" || true
|
|
410
|
+
|
|
411
|
+
[ -z "$fp" ] && fp="fingerprint-unavailable"
|
|
412
|
+
|
|
413
|
+
jq -n \
|
|
414
|
+
--arg sha "$head_sha" \
|
|
415
|
+
--arg branch "${current_branch:-null}" \
|
|
416
|
+
--arg fingerprint "$fp" \
|
|
417
|
+
'{
|
|
418
|
+
sha: $sha,
|
|
419
|
+
branch: (if $branch=="" then null else $branch end),
|
|
420
|
+
fingerprint: $fingerprint
|
|
421
|
+
}'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
do_branch_name() {
|
|
425
|
+
local class="${class_name:-}"
|
|
426
|
+
local slugs="${slug:-}"
|
|
427
|
+
|
|
428
|
+
[ -z "$class" ] && { printf '{"error":"missing-class"}\n'; exit 2; }
|
|
429
|
+
[ -z "$slugs" ] && { printf '{"error":"missing-slug"}\n'; exit 2; }
|
|
430
|
+
|
|
431
|
+
# A slug becomes a branch name and then a directory under agent-docs/doflow/, so it must not be
|
|
432
|
+
# able to name a path or a flag (IC-009). Same shape do-paths.sh enforces for its own slugs.
|
|
433
|
+
# ASCII ranges whatever the locale: under a UTF-8 collation [A-Za-z] can admit "é".
|
|
434
|
+
if ! (export LC_ALL=C; [[ "$slugs" =~ ^[A-Za-z0-9][A-Za-z0-9._-]*$ ]] && [[ "$slugs" != *..* ]]); then
|
|
435
|
+
jq -n --arg slug "$slugs" '{error: "invalid-slug", message: ("slug \"" + $slug + "\" is not a valid feature slug: use letters, digits, dot, underscore or dash, start with a letter or digit, and no \"..\""), hint: "use letters, digits, dot, underscore or dash, start with a letter or digit, and no \"..\""}'
|
|
436
|
+
exit 2
|
|
437
|
+
fi
|
|
438
|
+
|
|
439
|
+
# Two vocabularies meet here and only one of them is a branch prefix.
|
|
440
|
+
#
|
|
441
|
+
# The arms below are *branch* classes — the same set get_class emits — so --class=fix round-trips
|
|
442
|
+
# and --class=bug did not: it fell through to a catch-all that prepended whatever it was handed,
|
|
443
|
+
# producing bug/<slug>, which get_class then classified as "other". Every DoFlow skill holds a
|
|
444
|
+
# *task* class (bug, refactor, dependency-change, trivial-edit, feature), so the caller most
|
|
445
|
+
# likely to use this verb was the one guaranteed to get an unusable name.
|
|
446
|
+
#
|
|
447
|
+
# Task classes are mapped rather than passed through. In branch terms everything that is not a
|
|
448
|
+
# feature is a fix: the lifecycle policy declares exactly two working prefixes, feat and fix, and
|
|
449
|
+
# inventing refactor/ or trivial-edit/ would widen a vocabulary get_class does not recognise —
|
|
450
|
+
# trading one unclassifiable name for four.
|
|
451
|
+
local branch_class="$class"
|
|
452
|
+
case "$class" in
|
|
453
|
+
bug|refactor|dependency-change|trivial-edit) branch_class="fix" ;;
|
|
454
|
+
esac
|
|
455
|
+
|
|
456
|
+
local branch_name=""
|
|
457
|
+
case "$branch_class" in
|
|
458
|
+
feature) branch_name="feat/${slugs}" ;;
|
|
459
|
+
fix) branch_name="fix/${slugs}" ;;
|
|
460
|
+
release) branch_name="release/${slugs}" ;;
|
|
461
|
+
hotfix) branch_name="hotfix/${slugs}" ;;
|
|
462
|
+
trunk|other)
|
|
463
|
+
printf '{"error":"cannot-create-branch-for-trunk-or-other-class"}\n'
|
|
464
|
+
exit 2
|
|
465
|
+
;;
|
|
466
|
+
# An unrecognised class is refused rather than prepended. The catch-all that used to sit here
|
|
467
|
+
# is what let a task class through and produced a name no verb could classify.
|
|
468
|
+
*)
|
|
469
|
+
printf '{"error":"unknown-class","class":"%s","valid":"feature, fix, release, hotfix, bug, refactor, dependency-change, trivial-edit"}\n' "$class"
|
|
470
|
+
exit 2
|
|
471
|
+
;;
|
|
472
|
+
esac
|
|
473
|
+
|
|
474
|
+
jq -n --arg name "$branch_name" '{name: $name}'
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
# The git facts the lifecycle verbs need, in one place so Node never derives them a second way
|
|
478
|
+
# (feature 046, DEC-044, DEC-035). Read-only.
|
|
479
|
+
# integration_ref develop, else main, else master, else origin/HEAD: the first that resolves, a
|
|
480
|
+
# local branch before its origin/ tracking ref; null when none does. Unlike
|
|
481
|
+
# resolve_integration_ref this never prefers the remote, because the lifecycle
|
|
482
|
+
# views must agree with the release preview about one pinned ref.
|
|
483
|
+
# feature_prefixes the branch prefixes a feature branch is named with
|
|
484
|
+
# integration_local_behind_remote how many commits origin/<integration> is ahead of the local
|
|
485
|
+
# integration branch (0 when it has no origin counterpart or is itself origin/...);
|
|
486
|
+
# the same field --state reports, so a merge that only exists on the remote is
|
|
487
|
+
# visible as the staleness it is. No fetch is run: the answer is as fresh as the
|
|
488
|
+
# last one.
|
|
489
|
+
# release_tags v* tags that match the release-tag pattern (vX.Y.Z with an optional pre-release)
|
|
490
|
+
# base_tag the same base tag --next-version proposes from, or null
|
|
491
|
+
lifecycle_integration_ref() {
|
|
492
|
+
local name
|
|
493
|
+
for name in develop main master; do
|
|
494
|
+
if git rev-parse --verify --quiet "refs/heads/${name}" >/dev/null 2>&1; then printf '%s\n' "$name"; return; fi
|
|
495
|
+
if git rev-parse --verify --quiet "refs/remotes/origin/${name}" >/dev/null 2>&1; then printf 'origin/%s\n' "$name"; return; fi
|
|
496
|
+
done
|
|
497
|
+
if git rev-parse --verify --quiet "refs/remotes/origin/HEAD" >/dev/null 2>&1; then printf 'origin/HEAD\n'; return; fi
|
|
498
|
+
printf '\n'
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
do_lifecycle() {
|
|
502
|
+
local integration="" tags="" base_tag=""
|
|
503
|
+
integration="$(lifecycle_integration_ref)"
|
|
504
|
+
tags="$(git tag --list 'v*' 2>/dev/null | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?$' || true)"
|
|
505
|
+
base_tag="$(find_base_tag)"
|
|
506
|
+
|
|
507
|
+
local behind=0
|
|
508
|
+
case "$integration" in
|
|
509
|
+
""|origin/*) ;;
|
|
510
|
+
*)
|
|
511
|
+
if git rev-parse --verify --quiet "refs/remotes/origin/${integration}" >/dev/null 2>&1; then
|
|
512
|
+
behind="$(git rev-list --count "${integration}..origin/${integration}" 2>/dev/null || echo 0)"
|
|
513
|
+
fi
|
|
514
|
+
;;
|
|
515
|
+
esac
|
|
516
|
+
|
|
517
|
+
local prefixes_json="" tags_json=""
|
|
518
|
+
prefixes_json="$(printf '%s\n' $feature_prefixes | jq -R . | jq -s .)"
|
|
519
|
+
tags_json="$(printf '%s' "$tags" | jq -R -s 'split("\n") | map(select(length > 0))')"
|
|
520
|
+
|
|
521
|
+
jq -n \
|
|
522
|
+
--arg integration "$integration" \
|
|
523
|
+
--arg base_tag "$base_tag" \
|
|
524
|
+
--arg behind "$behind" \
|
|
525
|
+
--argjson prefixes "$prefixes_json" \
|
|
526
|
+
--argjson tags "$tags_json" \
|
|
527
|
+
'{
|
|
528
|
+
integration_ref: (if $integration=="" then null else $integration end),
|
|
529
|
+
integration_local_behind_remote: ($behind | tonumber),
|
|
530
|
+
feature_prefixes: $prefixes,
|
|
531
|
+
release_tags: $tags,
|
|
532
|
+
base_tag: (if $base_tag=="" then null else $base_tag end)
|
|
533
|
+
}'
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
case "$mode" in
|
|
537
|
+
state) do_state ;;
|
|
538
|
+
next-version) do_next_version ;;
|
|
539
|
+
propagation-targets) do_propagation_targets ;;
|
|
540
|
+
fingerprint) do_fingerprint ;;
|
|
541
|
+
lifecycle) do_lifecycle ;;
|
|
542
|
+
branch-name) do_branch_name ;;
|
|
543
|
+
*) printf '{"error":"unknown-mode","mode":"%s"}\n' "$mode"; exit 2 ;;
|
|
544
|
+
esac
|
|
545
|
+
|
|
546
|
+
exit 0
|