@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,91 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Build the JavaScript form published to npm in an isolated staging directory. The checkout stays
|
|
4
|
+
// TypeScript so local development and the source CLI keep using Node's native type stripping.
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { stripTypeScriptTypes } = require('node:module');
|
|
8
|
+
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
9
|
+
|
|
10
|
+
const TEXT_EXTENSIONS = new Set(['.js', '.json', '.sh', '.md', '.yaml', '.yml']);
|
|
11
|
+
|
|
12
|
+
function walkFiles(root ) {
|
|
13
|
+
const files = [];
|
|
14
|
+
for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
|
|
15
|
+
const file = path.join(root, entry.name);
|
|
16
|
+
if (entry.isDirectory()) files.push(...walkFiles(file));
|
|
17
|
+
else if (entry.isFile()) files.push(file);
|
|
18
|
+
}
|
|
19
|
+
return files;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function buildPackage(outDir , { repoRoot = REPO_ROOT } = {}) {
|
|
23
|
+
if (typeof stripTypeScriptTypes !== 'function') {
|
|
24
|
+
throw new Error('Node does not provide node:module.stripTypeScriptTypes');
|
|
25
|
+
}
|
|
26
|
+
const packageJson = JSON.parse(fs.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
27
|
+
const output = path.resolve(outDir);
|
|
28
|
+
fs.rmSync(output, { recursive: true, force: true });
|
|
29
|
+
fs.mkdirSync(output, { recursive: true });
|
|
30
|
+
|
|
31
|
+
for (const rel of packageJson.files || []) {
|
|
32
|
+
const source = path.join(repoRoot, rel);
|
|
33
|
+
if (fs.existsSync(source)) fs.cpSync(source, path.join(output, rel), { recursive: true });
|
|
34
|
+
}
|
|
35
|
+
for (const name of ['package.json', 'README.md', 'LICENSE', 'CHANGELOG.md']) {
|
|
36
|
+
const source = path.join(repoRoot, name);
|
|
37
|
+
if (fs.existsSync(source)) fs.copyFileSync(source, path.join(output, name));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const converted = new Set ();
|
|
41
|
+
for (const file of walkFiles(path.join(output, 'src'))) {
|
|
42
|
+
if (!file.endsWith('.ts') || file.endsWith('.d.ts')) continue;
|
|
43
|
+
const relative = path.relative(output, file).split(path.sep).join('/');
|
|
44
|
+
const jsRelative = relative.slice(0, -3) + '.js';
|
|
45
|
+
const code = fs.readFileSync(file, 'utf8');
|
|
46
|
+
fs.writeFileSync(path.join(output, jsRelative), stripTypeScriptTypes(code, { mode: 'strip' }));
|
|
47
|
+
fs.rmSync(file);
|
|
48
|
+
converted.add(relative);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (const file of walkFiles(output)) {
|
|
52
|
+
if (!TEXT_EXTENSIONS.has(path.extname(file))) continue;
|
|
53
|
+
const relative = path.relative(output, file).split(path.sep).join('/');
|
|
54
|
+
let content = fs.readFileSync(file, 'utf8');
|
|
55
|
+
content = content.replace(/(['"`])((?:\.\.?\/)[^'"`\n]*?\.ts)\1/g, (whole , quote , specifier ) => {
|
|
56
|
+
const resolved = path.posix.normalize(path.posix.join(path.posix.dirname(relative), specifier));
|
|
57
|
+
if (!converted.has(resolved)) return whole;
|
|
58
|
+
return `${quote}${specifier.slice(0, -3)}.js${quote}`;
|
|
59
|
+
});
|
|
60
|
+
for (const rel of [...converted].sort((a, b) => b.length - a.length)) {
|
|
61
|
+
content = content.split(rel).join(rel.slice(0, -3) + '.js');
|
|
62
|
+
}
|
|
63
|
+
fs.writeFileSync(file, content);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const remaining = walkFiles(output);
|
|
67
|
+
const runtimeFiles = ['src', 'bin'].flatMap((directory) => {
|
|
68
|
+
const root = path.join(output, directory);
|
|
69
|
+
return fs.existsSync(root) ? walkFiles(root) : [];
|
|
70
|
+
});
|
|
71
|
+
const nonDeclarationTs = runtimeFiles.filter((file ) => file.endsWith('.ts') && !file.endsWith('.d.ts'));
|
|
72
|
+
if (nonDeclarationTs.length) throw new Error(`staged package retains TypeScript files: ${nonDeclarationTs.join(', ')}`);
|
|
73
|
+
for (const file of remaining) {
|
|
74
|
+
if (!TEXT_EXTENSIONS.has(path.extname(file))) continue;
|
|
75
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
76
|
+
const unresolved = [...converted].filter((rel) => content.includes(rel));
|
|
77
|
+
if (unresolved.length) throw new Error(`${path.relative(output, file)} still references converted TypeScript paths: ${unresolved.join(', ')}`);
|
|
78
|
+
}
|
|
79
|
+
return output;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (require.main === module) {
|
|
83
|
+
const outDir = process.argv[2];
|
|
84
|
+
if (!outDir) {
|
|
85
|
+
process.stderr.write('Usage: node src/release/build-package.js <outDir>\n');
|
|
86
|
+
process.exit(2);
|
|
87
|
+
}
|
|
88
|
+
buildPackage(outDir);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = { buildPackage };
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
// ── One release version, one CHANGELOG section, one GitHub Release body ─────────────────────────
|
|
6
|
+
//
|
|
7
|
+
// `gh release create --generate-notes` lists merged pull requests, and this repository merges
|
|
8
|
+
// locally, so every Release the workflow created carried only a compare link. CHANGELOG.md is
|
|
9
|
+
// where the release text is already written (Keep a Changelog, one `## [X.Y.Z]` heading per
|
|
10
|
+
// version), so the release workflow extracts the tagged version's section from it and passes that
|
|
11
|
+
// as the Release body. A tag whose section is missing or empty fails the job instead of
|
|
12
|
+
// publishing an empty Release.
|
|
13
|
+
//
|
|
14
|
+
// Usage: node src/release/changelog-section.js <version|tag> [CHANGELOG path]
|
|
15
|
+
|
|
16
|
+
const fs = require('node:fs');
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
19
|
+
|
|
20
|
+
function escapeRegExp(text ) {
|
|
21
|
+
return text.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Returns the body of a version's section: the lines after its `## [version]` heading up to the
|
|
26
|
+
* next `## [` heading, trimmed. The closing `]` after the version makes 1.2.1 and 1.2.10 distinct.
|
|
27
|
+
* A leading `v` on the version is ignored. Returns null when the heading is absent or the body is
|
|
28
|
+
* empty.
|
|
29
|
+
* @param {string} changelogText
|
|
30
|
+
* @param {string} version
|
|
31
|
+
* @returns {string|null}
|
|
32
|
+
*/
|
|
33
|
+
function changelogSection(changelogText , version ) {
|
|
34
|
+
const wanted = String(version).trim().replace(/^v/, '');
|
|
35
|
+
if (wanted === '') return null;
|
|
36
|
+
const lines = changelogText.split(/\r?\n/);
|
|
37
|
+
const heading = new RegExp(`^##\\s+\\[${escapeRegExp(wanted)}\\](\\s|$)`);
|
|
38
|
+
const start = lines.findIndex((line) => heading.test(line));
|
|
39
|
+
if (start === -1) return null;
|
|
40
|
+
let end = lines.length;
|
|
41
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
42
|
+
if (/^##\s+\[/.test(lines[i])) { end = i; break; }
|
|
43
|
+
}
|
|
44
|
+
const body = lines.slice(start + 1, end).join('\n').trim();
|
|
45
|
+
return body === '' ? null : body;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (require.main === module) {
|
|
49
|
+
const version = process.argv[2];
|
|
50
|
+
const file = process.argv[3] || path.join(REPO_ROOT, 'CHANGELOG.md');
|
|
51
|
+
if (!version) {
|
|
52
|
+
console.error('usage: node src/release/changelog-section.js <version|tag> [CHANGELOG path]');
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
let text ;
|
|
56
|
+
try {
|
|
57
|
+
text = fs.readFileSync(file, 'utf8');
|
|
58
|
+
} catch (err) {
|
|
59
|
+
console.error(`cannot read ${file}: ${(err ).message}`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
const body = changelogSection(text, version);
|
|
63
|
+
if (body === null) {
|
|
64
|
+
console.error(`no non-empty CHANGELOG section for ${String(version).replace(/^v/, '')} in ${file}`);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
process.stdout.write(`${body}\n`);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = { changelogSection };
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
// ── One release version, projected into every distribution manifest ─────────────────────────────
|
|
6
|
+
//
|
|
7
|
+
// package.json is the single source of the release version. The three plugin manifests (Codex,
|
|
8
|
+
// Claude, Copilot) each repeat it, and v1.2.1 shipped with all three still reading 1.2.0 — the
|
|
9
|
+
// guard tests comparing them were failing correctly (architecture review R8). Hand-editing four
|
|
10
|
+
// files in step is exactly the drift class this repo removes elsewhere by generation, so the
|
|
11
|
+
// npm `version` lifecycle hook runs this script: `npm version <bump>` rewrites the manifests and
|
|
12
|
+
// stages them into the same release commit. The equality guards in test/install/doflow.test.js
|
|
13
|
+
// stay as the independent check that the projection actually happened.
|
|
14
|
+
|
|
15
|
+
const fs = require('node:fs');
|
|
16
|
+
const path = require('node:path');
|
|
17
|
+
const { REPO_ROOT } = require('../helper/repo-root.js');
|
|
18
|
+
|
|
19
|
+
/** Every distribution manifest that repeats the release version, relative to the repo root. */
|
|
20
|
+
const PLUGIN_MANIFESTS = [
|
|
21
|
+
'core/.codex-plugin/plugin.json',
|
|
22
|
+
'core/.claude-plugin/plugin.json',
|
|
23
|
+
'core/.plugin/plugin.json',
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Rewrites each plugin manifest's version to package.json's, preserving formatting by targeted
|
|
28
|
+
* string replacement rather than re-serializing the whole file.
|
|
29
|
+
* @param {Object} [options]
|
|
30
|
+
* @param {string} [options.repoRoot]
|
|
31
|
+
* @param {Object} [options.fsImpl]
|
|
32
|
+
* @returns {{version: string, updated: Array<string>, unchanged: Array<string>}}
|
|
33
|
+
*/
|
|
34
|
+
function syncPluginVersions({ repoRoot = REPO_ROOT, fsImpl = fs } = {}) {
|
|
35
|
+
const pkg = JSON.parse(fsImpl.readFileSync(path.join(repoRoot, 'package.json'), 'utf8'));
|
|
36
|
+
if (typeof pkg.version !== 'string' || pkg.version.trim() === '') {
|
|
37
|
+
throw new Error('package.json declares no version to project');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
const updated = [];
|
|
41
|
+
const unchanged = [];
|
|
42
|
+
for (const rel of PLUGIN_MANIFESTS) {
|
|
43
|
+
const file = path.join(repoRoot, rel);
|
|
44
|
+
const text = fsImpl.readFileSync(file, 'utf8');
|
|
45
|
+
const manifest = JSON.parse(text);
|
|
46
|
+
if (manifest.version === pkg.version) {
|
|
47
|
+
unchanged.push(rel);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const next = text.replace(/"version":\s*"[^"]*"/, `"version": "${pkg.version}"`);
|
|
51
|
+
if (!/"version":\s*"/.test(text)) {
|
|
52
|
+
throw new Error(`${rel} has no version field to rewrite — the manifest shape changed; update PLUGIN_MANIFESTS`);
|
|
53
|
+
}
|
|
54
|
+
fsImpl.writeFileSync(file, next, 'utf8');
|
|
55
|
+
updated.push(rel);
|
|
56
|
+
}
|
|
57
|
+
return { version: pkg.version, updated, unchanged };
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (require.main === module) {
|
|
61
|
+
const result = syncPluginVersions();
|
|
62
|
+
for (const rel of result.updated) console.log(`synced ${rel} -> ${result.version}`);
|
|
63
|
+
if (result.updated.length === 0) console.log(`all plugin manifests already at ${result.version}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
module.exports = { syncPluginVersions, PLUGIN_MANIFESTS };
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// TypeScript migration (agent-docs/BRIEF_GOAL_DOFLOW.md): run directly by Node native type-stripping.
|
|
4
|
+
|
|
5
|
+
const fs = require('node:fs');
|
|
6
|
+
const path = require('node:path');
|
|
7
|
+
const { finishRuntime, usageError } = require('../cli-result.js');
|
|
8
|
+
const { REPO_ROOT } = require('../../helper/repo-root.js');
|
|
9
|
+
const { loadAdviceRegistry } = require('./registry.js');
|
|
10
|
+
const { loadStorageRegistry } = require('../storage/registry.js');
|
|
11
|
+
const { resolveRoots } = require('../storage/roots.js');
|
|
12
|
+
const { resolveSetting, writeSetting } = require('./config.js');
|
|
13
|
+
const { createAdviceClient } = require('./client.js');
|
|
14
|
+
const { materializeKind, validateQuestionSet, validateStaticAnswer } = require('./kinds.js');
|
|
15
|
+
const { findPick, appendOutcome } = require('./record.js');
|
|
16
|
+
const { strengthWords } = require('./strength.js');
|
|
17
|
+
|
|
18
|
+
function loadSources(repoRoot ) {
|
|
19
|
+
const read = (name ) => JSON.parse(fs.readFileSync(path.join(repoRoot, 'core/registry', name), 'utf8'));
|
|
20
|
+
return {
|
|
21
|
+
workflows: read('workflows.json'),
|
|
22
|
+
verification: read('verification.json'),
|
|
23
|
+
mcp: read('mcp.json'),
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function loadRuntime({ projectRoot, global = false } = {}) {
|
|
28
|
+
const advice = loadAdviceRegistry({ repoRoot: REPO_ROOT });
|
|
29
|
+
const storage = loadStorageRegistry({ repoRoot: REPO_ROOT });
|
|
30
|
+
const roots = resolveRoots({ registry: storage, projectRoot, global });
|
|
31
|
+
const sources = loadSources(REPO_ROOT);
|
|
32
|
+
const adviceRegistry = advice.ok ? advice.registry : null;
|
|
33
|
+
return { adviceRegistry, adviceError: advice.ok ? null : advice.detail, storage, roots, sources };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function emitResult(result , { json, kind } ) {
|
|
37
|
+
if (result.reason === 'over-limit') {
|
|
38
|
+
return usageError('advise', result.detail || 'request exceeds an advice limit', json);
|
|
39
|
+
}
|
|
40
|
+
if (json) {
|
|
41
|
+
console.log(JSON.stringify(result, null, 2));
|
|
42
|
+
} else if (result.status === 'answered') {
|
|
43
|
+
for (const answer of result.answers) {
|
|
44
|
+
const staticText = answer.staticAnswer === null ? '' : ` Static answer: ${answer.staticAnswer}.`;
|
|
45
|
+
console.log(`Advice (${kind}): ${answer.pick}, ${strengthWords(answer.strength)}.${staticText}`);
|
|
46
|
+
}
|
|
47
|
+
} else {
|
|
48
|
+
console.log(`Advice (${kind}): none; the static answer stands.`);
|
|
49
|
+
}
|
|
50
|
+
return finishRuntime(0);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function isPresent(value ) {
|
|
54
|
+
return value !== undefined && value !== null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function readState({ state, stateFile, json } ) {
|
|
58
|
+
if (isPresent(state) === isPresent(stateFile)) {
|
|
59
|
+
usageError('advise', 'provide exactly one of --state or --state-file', json);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
if (isPresent(state)) return state;
|
|
63
|
+
try {
|
|
64
|
+
const content = fs.readFileSync(stateFile, 'utf8');
|
|
65
|
+
if (!content.length) throw new Error('file is empty');
|
|
66
|
+
return content;
|
|
67
|
+
} catch (error ) {
|
|
68
|
+
usageError('advise', `cannot read state file ${stateFile}: ${error.message}`, json);
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function readQuestions(file , json ) {
|
|
74
|
+
try {
|
|
75
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
76
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed) || !Array.isArray(parsed.questions)) {
|
|
77
|
+
throw new Error('expected an object with a questions array');
|
|
78
|
+
}
|
|
79
|
+
return parsed.questions;
|
|
80
|
+
} catch (error ) {
|
|
81
|
+
usageError('advise', `cannot read questions file ${file}: ${error.message}`, json);
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseAskInput(options ) {
|
|
87
|
+
const hasKind = typeof options.kind === 'string' && options.kind.length > 0;
|
|
88
|
+
const hasQuestionsFile = isPresent(options.questionsFile);
|
|
89
|
+
if (hasKind === hasQuestionsFile) {
|
|
90
|
+
usageError('advise', 'provide exactly one of --kind or --questions-file', options.json);
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
const state = readState({ state: options.state, stateFile: options.stateFile, json: options.json });
|
|
94
|
+
if (state === null) return null;
|
|
95
|
+
if (options.server !== undefined && !hasKind) {
|
|
96
|
+
usageError('advise', '--server is only valid with --kind mcp-need', options.json);
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
if (options.staticAnswer !== undefined && !hasKind) {
|
|
100
|
+
usageError('advise', '--static is only valid with a named --kind', options.json);
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
return { state, hasKind, hasQuestionsFile };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function endpointSource(userSetting , endpoint ) {
|
|
107
|
+
if (userSetting?.decidedBy?.user) {
|
|
108
|
+
try {
|
|
109
|
+
const file = JSON.parse(fs.readFileSync(userSetting.decidedBy.user, 'utf8'));
|
|
110
|
+
if (file.endpoint === endpoint) return userSetting.decidedBy.user;
|
|
111
|
+
} catch { /* an invalid file will be reported by its resolver/writer */ }
|
|
112
|
+
}
|
|
113
|
+
return 'provider default';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function inspectFailure(inspected , { endpoint, model, provider } ) {
|
|
117
|
+
if (inspected.endpoint.state === 'refused') return inspected.endpoint.message;
|
|
118
|
+
if (inspected.endpoint.state !== 'reached') {
|
|
119
|
+
return `advise: Ollama did not answer at ${endpoint}; start it yourself and re-run. Nothing was written.`;
|
|
120
|
+
}
|
|
121
|
+
if (!inspected.endpoint.versionOk) {
|
|
122
|
+
return `advise: Ollama ${inspected.endpoint.version || 'unknown'} at ${endpoint} is older than ${provider.minVersion}. Nothing was written.`;
|
|
123
|
+
}
|
|
124
|
+
if (!inspected.model.installed) {
|
|
125
|
+
return `advise: model ${model} is not installed; run 'ollama pull ${model}' yourself. Nothing was written.`;
|
|
126
|
+
}
|
|
127
|
+
if (!inspected.model.decisionCapable) {
|
|
128
|
+
const capable = inspected.models
|
|
129
|
+
.filter((entry ) => Array.isArray(entry.capabilities) && entry.capabilities.includes(provider.capability))
|
|
130
|
+
.map((entry ) => entry.name);
|
|
131
|
+
return `advise: model ${model} is not decision-capable; decision-capable here: ${capable.length ? capable.join(', ') : 'none'}. Nothing was written.`;
|
|
132
|
+
}
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function writeFailure(message , json ) {
|
|
137
|
+
if (json) console.log(JSON.stringify({ ok: false, error: 'write-failed', message }, null, 2));
|
|
138
|
+
else if (message.startsWith('decision model: endpoint ') || message.startsWith('advise:')) console.error(message);
|
|
139
|
+
else console.error(`advise: ${message}`);
|
|
140
|
+
return finishRuntime(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function handleConfigure(options , runtime ) {
|
|
144
|
+
const { adviceRegistry, adviceError, roots, sources } = runtime;
|
|
145
|
+
const { json, project, global, force } = options;
|
|
146
|
+
const hasModel = typeof options.model === 'string' && options.model.length > 0;
|
|
147
|
+
const hasOff = options.off === true;
|
|
148
|
+
if (hasModel && hasOff) return usageError('advise', 'cannot combine --model and --off', json);
|
|
149
|
+
if (!hasModel && !hasOff) return usageError('advise', 'provide exactly one of --model or --off', json);
|
|
150
|
+
if (project && options.endpoint !== undefined) return usageError('advise', '--endpoint cannot be used with --project', json);
|
|
151
|
+
if (hasOff && options.endpoint !== undefined) return usageError('advise', '--endpoint cannot be used with --off', json);
|
|
152
|
+
if (project && (global || !options.projectRoot)) return usageError('advise', '--project requires a project root', json);
|
|
153
|
+
if (adviceError || !adviceRegistry) return writeFailure(`advice registry is invalid: ${adviceError || 'missing registry'}`, json);
|
|
154
|
+
|
|
155
|
+
const file = project
|
|
156
|
+
? path.join(roots['project-doflow'], 'decision-model.json')
|
|
157
|
+
: path.join(roots['config-home'], 'decision-model.json');
|
|
158
|
+
const fsImpl = fs;
|
|
159
|
+
if (hasOff) {
|
|
160
|
+
const setting = project ? { schema: 1, enabled: false } : { schema: 1, enabled: false };
|
|
161
|
+
const written = writeSetting({ file, level: project ? 'project' : 'user', setting, force, fsImpl });
|
|
162
|
+
if (!written.ok) return writeFailure(written.detail, json);
|
|
163
|
+
const effective = resolveSetting({ adviceRegistry, roots, fsImpl });
|
|
164
|
+
if (json) console.log(JSON.stringify({ written: file, setting: effective }, null, 2));
|
|
165
|
+
else console.log(project ? `Decision model for this project: off (wrote ${file}).` : `Decision model off (wrote ${file}).`);
|
|
166
|
+
return finishRuntime(0);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let userRoots = roots;
|
|
170
|
+
if (project) userRoots = { ...roots, 'project-doflow': null };
|
|
171
|
+
const userSetting = resolveSetting({ adviceRegistry, roots: userRoots, fsImpl });
|
|
172
|
+
if (project && userSetting.state !== 'on') {
|
|
173
|
+
return writeFailure('turn the decision model on at user level first; a project cannot enable it', json);
|
|
174
|
+
}
|
|
175
|
+
const providerId = userSetting.provider || 'ollama';
|
|
176
|
+
const provider = adviceRegistry.providers.find((entry ) => entry.id === providerId);
|
|
177
|
+
if (!provider) return writeFailure(`provider '${providerId}' is not in core/registry/advice.json`, json);
|
|
178
|
+
const endpoint = project
|
|
179
|
+
? userSetting.endpoint
|
|
180
|
+
: options.endpoint || userSetting.endpoint || provider.defaultEndpoint;
|
|
181
|
+
const endpointFile = options.endpoint ? '--endpoint' : endpointSource(userSetting, endpoint);
|
|
182
|
+
const client = createAdviceClient({ adviceRegistry, roots, sources });
|
|
183
|
+
const inspected = await client.inspect({ endpoint, model: options.model, provider: provider.id, minVersion: provider.minVersion, file: endpointFile });
|
|
184
|
+
const failure = inspectFailure(inspected, { endpoint, model: options.model, provider });
|
|
185
|
+
if (failure) return writeFailure(failure, json);
|
|
186
|
+
|
|
187
|
+
const setting = project
|
|
188
|
+
? { schema: 1, model: options.model }
|
|
189
|
+
: { schema: 1, enabled: true, provider: provider.id, endpoint, model: options.model };
|
|
190
|
+
const written = writeSetting({ file, level: project ? 'project' : 'user', setting, force, fsImpl });
|
|
191
|
+
if (!written.ok) return writeFailure(written.detail, json);
|
|
192
|
+
const effective = resolveSetting({ adviceRegistry, roots, fsImpl });
|
|
193
|
+
if (json) console.log(JSON.stringify({ written: file, setting: effective }, null, 2));
|
|
194
|
+
else console.log(project
|
|
195
|
+
? `Decision model for this project: ${options.model} (wrote ${file}).`
|
|
196
|
+
: `Decision model on: ${options.model} at ${endpoint} (wrote ${file}).`);
|
|
197
|
+
return finishRuntime(0);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function handleAsk(options , runtime ) {
|
|
201
|
+
const parsed = parseAskInput(options);
|
|
202
|
+
if (!parsed) return finishRuntime(2);
|
|
203
|
+
const { adviceRegistry, adviceError, roots, sources } = runtime;
|
|
204
|
+
if (adviceError || !adviceRegistry) {
|
|
205
|
+
const result = {
|
|
206
|
+
status: 'off', reason: 'config-invalid', detail: adviceError || 'advice registry is missing',
|
|
207
|
+
provider: null, model: null, endpoint: null, decidedBy: { user: null, project: null }, durationMs: 0, answers: [],
|
|
208
|
+
};
|
|
209
|
+
return emitResult(result, { json: options.json, kind: options.kind || 'adhoc' });
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
let kind = 'adhoc';
|
|
213
|
+
let questionSet;
|
|
214
|
+
let questionError = null;
|
|
215
|
+
if (parsed.hasKind) {
|
|
216
|
+
kind = options.kind;
|
|
217
|
+
const built = materializeKind(kind, { adviceRegistry, sources, server: options.server });
|
|
218
|
+
if (!built.ok && built.reason === 'usage-error') return usageError('advise', built.detail, options.json);
|
|
219
|
+
if (built.ok) questionSet = { state: parsed.state, questions: [built.question] };
|
|
220
|
+
else {
|
|
221
|
+
questionSet = { state: parsed.state, questions: [] };
|
|
222
|
+
questionError = built.detail;
|
|
223
|
+
}
|
|
224
|
+
if (options.staticAnswer !== undefined && built.ok) {
|
|
225
|
+
const validation = validateStaticAnswer(built.question, options.staticAnswer);
|
|
226
|
+
if (!validation.ok) return usageError('advise', validation.detail, options.json);
|
|
227
|
+
}
|
|
228
|
+
} else {
|
|
229
|
+
const questions = readQuestions(options.questionsFile, options.json);
|
|
230
|
+
if (!questions) return finishRuntime(2);
|
|
231
|
+
questionSet = { state: parsed.state, questions };
|
|
232
|
+
const validation = validateQuestionSet(questionSet, { adviceRegistry });
|
|
233
|
+
if (!validation.ok) return usageError('advise', validation.detail, options.json);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const client = createAdviceClient({ adviceRegistry, roots, sources });
|
|
237
|
+
const result = await client.ask({
|
|
238
|
+
questionSet, kind, staticAnswer: options.staticAnswer, caller: 'advise', taskId: options.taskId,
|
|
239
|
+
record: true,
|
|
240
|
+
});
|
|
241
|
+
// Preserve the kind materialiser's source-specific diagnostic while the client owns miss recording.
|
|
242
|
+
if (questionError && result.reason === 'config-invalid') result.detail = questionError;
|
|
243
|
+
return emitResult(result, { json: options.json, kind });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function handleOutcome(options , runtime ) {
|
|
247
|
+
const { json } = options;
|
|
248
|
+
if (typeof options.pickId !== 'string' || !options.pickId) return usageError('advise', '--pick-id is required', json);
|
|
249
|
+
if (typeof options.actual !== 'string' || !options.actual) return usageError('advise', '--actual is required', json);
|
|
250
|
+
const found = findPick({ pickId: options.pickId, roots: runtime.roots });
|
|
251
|
+
if (!found) {
|
|
252
|
+
if (json) console.log(JSON.stringify({ ok: false, error: 'not-found', message: `no pick ${options.pickId} in the retained record` }, null, 2));
|
|
253
|
+
else console.error(`advise: no pick ${options.pickId} in the retained record`);
|
|
254
|
+
return finishRuntime(1);
|
|
255
|
+
}
|
|
256
|
+
if (!found.pick.options?.includes(options.actual)) {
|
|
257
|
+
return usageError('advise', `--actual must be one of: ${found.pick.options.join(', ')}`, json);
|
|
258
|
+
}
|
|
259
|
+
const written = appendOutcome({
|
|
260
|
+
pickId: options.pickId, actual: options.actual, source: 'caller', roots: runtime.roots, store: found.store,
|
|
261
|
+
});
|
|
262
|
+
if (!written) return writeFailure(`could not record outcome for ${options.pickId}`, json);
|
|
263
|
+
if (json) console.log(JSON.stringify({ pickId: options.pickId, actual: options.actual, recorded: true, file: written.file }, null, 2));
|
|
264
|
+
else console.log(`Recorded outcome ${options.actual} for ${options.pickId}.`);
|
|
265
|
+
return finishRuntime(0);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
async function handleAdviseCommand(options = {}) {
|
|
269
|
+
const action = options.actionGiven ? options.action : 'ask';
|
|
270
|
+
if (!['ask', 'outcome', 'configure'].includes(action)) return usageError('advise', `unknown action '${action}'`, options.json);
|
|
271
|
+
try {
|
|
272
|
+
const projectRoot = options.global ? null : path.resolve(options.projectRoot || process.cwd());
|
|
273
|
+
const runtime = loadRuntime({ projectRoot, global: options.global });
|
|
274
|
+
const shaped = { ...options, projectRoot };
|
|
275
|
+
if (action === 'configure') return await handleConfigure(shaped, runtime);
|
|
276
|
+
if (action === 'outcome') return handleOutcome(shaped, runtime);
|
|
277
|
+
return await handleAsk(shaped, runtime);
|
|
278
|
+
} catch (error ) {
|
|
279
|
+
return writeFailure(error?.message || 'advice command failed', options.json);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
module.exports = { handleAdviseCommand };
|