@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,224 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# render-audit.sh — projects the orchestrator's own journal into a human-readable audit.md.
|
|
3
|
+
#
|
|
4
|
+
# audit.md is generated, never hand-authored: this script deterministically OVERWRITES it in full
|
|
5
|
+
# from `.doflow/state/orchestration/<slug>.json`'s `history[]` array (WorkflowOrchestrator's own
|
|
6
|
+
# journal — the single source of truth for stage/gate handoffs), one markdown table row per
|
|
7
|
+
# entry. It never appends across runs and never reads the previous audit.md — a stale row can
|
|
8
|
+
# only come from a stale journal, never from this script's own accumulation.
|
|
9
|
+
#
|
|
10
|
+
# Fail-open, matching validate-artifacts.sh's own convention: no active feature, no resolver, or
|
|
11
|
+
# no orchestration run recorded yet are all reported via a note and exit 0 — never a hard error,
|
|
12
|
+
# and no audit.md is written when there is nothing to render.
|
|
13
|
+
#
|
|
14
|
+
# Usage: render-audit.sh [--slug=<slug>] [--json]
|
|
15
|
+
# No --slug → resolve the active feature via do-paths.sh, exactly like validate-artifacts.sh.
|
|
16
|
+
|
|
17
|
+
set -uo pipefail
|
|
18
|
+
|
|
19
|
+
emit_json=false
|
|
20
|
+
slug_override=""
|
|
21
|
+
bad_args=()
|
|
22
|
+
for a in "$@"; do
|
|
23
|
+
case "$a" in
|
|
24
|
+
--json) emit_json=true ;;
|
|
25
|
+
--slug=*) slug_override="${a#--slug=}" ;;
|
|
26
|
+
*) bad_args+=("$a") ;;
|
|
27
|
+
esac
|
|
28
|
+
done
|
|
29
|
+
# An unrecognized argument previously fell through silently — including the space-separated
|
|
30
|
+
# `--slug value` form (only `--slug=value` is accepted), which meant a typo rendered the ACTIVE
|
|
31
|
+
# feature's audit.md instead of the one the caller named, with no signal that anything went wrong.
|
|
32
|
+
if [ "${#bad_args[@]}" -gt 0 ]; then
|
|
33
|
+
args_joined="${bad_args[*]}"
|
|
34
|
+
if [ "$emit_json" = true ]; then
|
|
35
|
+
# Built with jq, not `printf '%s'` interpolation: bad_args is raw argv with no escaping applied
|
|
36
|
+
# to it, and an argument containing a `"` previously produced a document with a duplicate "ok"
|
|
37
|
+
# key that JSON resolves to true — inverting this hard failure into an apparent success for any
|
|
38
|
+
# caller branching on .ok. jq is checked for below the arg-parsing block, not above it, so a
|
|
39
|
+
# minimally-escaped fallback covers the case this runs before that check does.
|
|
40
|
+
if command -v jq >/dev/null 2>&1; then
|
|
41
|
+
jq -nc --arg args "$args_joined" '{"ok":false,"error":"unrecognized-argument","args":$args}' >&2
|
|
42
|
+
else
|
|
43
|
+
esc_args=$(printf '%s' "$args_joined" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
44
|
+
printf '{"ok":false,"error":"unrecognized-argument","args":"%s"}\n' "$esc_args" >&2
|
|
45
|
+
fi
|
|
46
|
+
else
|
|
47
|
+
printf 'render-audit: unrecognized argument(s): %s (use --slug=<value>, not --slug <value>)\n' "$args_joined" >&2
|
|
48
|
+
fi
|
|
49
|
+
exit 2
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
note() {
|
|
53
|
+
if [ "$emit_json" = true ]; then
|
|
54
|
+
printf '{"ok":true,"note":"%s","written":false}\n' "$1"
|
|
55
|
+
else
|
|
56
|
+
printf 'render-audit: %s (audit.md not written)\n' "$1"
|
|
57
|
+
fi
|
|
58
|
+
exit 0
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
command -v jq >/dev/null 2>&1 || note "jq-absent"
|
|
62
|
+
|
|
63
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
64
|
+
RESOLVER="$script_dir/do-paths.sh"
|
|
65
|
+
[ -f "$RESOLVER" ] || RESOLVER="${DOFLOW_CONFIG_DIR:+$DOFLOW_CONFIG_DIR/scripts/doflow/bash/do-paths.sh}"
|
|
66
|
+
[ -f "$RESOLVER" ] || note "resolver-absent"
|
|
67
|
+
|
|
68
|
+
resolver_args=(--json)
|
|
69
|
+
[ -n "$slug_override" ] && resolver_args+=("--slug=$slug_override")
|
|
70
|
+
rc=0; resolved_json=$(bash "$RESOLVER" "${resolver_args[@]}" 2>/dev/null) || rc=$?
|
|
71
|
+
# An unsafe slug is refused, not noted: exit 2 with the resolver's error object (IC-009).
|
|
72
|
+
if [ "$rc" -eq 2 ] && [ "$(printf '%s' "$resolved_json" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
|
|
73
|
+
if [ "$emit_json" = true ]; then printf '%s\n' "$resolved_json"; else printf 'render-audit: %s\n' "$(printf '%s' "$resolved_json" | jq -r '.message')" >&2; fi
|
|
74
|
+
exit 2
|
|
75
|
+
fi
|
|
76
|
+
[ "$rc" -eq 0 ] || note "resolver-error"
|
|
77
|
+
|
|
78
|
+
repo_root=$(printf '%s' "$resolved_json" | jq -r '.repo_root // empty')
|
|
79
|
+
feature_slug=$(printf '%s' "$resolved_json" | jq -r '.feature_slug // empty')
|
|
80
|
+
audit_rel=$(printf '%s' "$resolved_json" | jq -r '.audit // empty')
|
|
81
|
+
[ -n "$feature_slug" ] || note "no-active-feature"
|
|
82
|
+
|
|
83
|
+
# WorkflowOrchestrator's own stateDir/runFile convention (workflow-orchestrator.ts):
|
|
84
|
+
# stateDir = <root>/.doflow/state/orchestration ; runFile = <stateDir>/<taskId>.json
|
|
85
|
+
journal_file="$repo_root/.doflow/state/orchestration/$feature_slug.json"
|
|
86
|
+
[ -f "$journal_file" ] || note "no orchestration run recorded yet"
|
|
87
|
+
|
|
88
|
+
jq -e '.history | type == "array"' "$journal_file" >/dev/null 2>&1 || note "journal-unreadable"
|
|
89
|
+
|
|
90
|
+
# Consumed from the resolver's own `audit` field rather than recomputed by hand — do-paths.sh is
|
|
91
|
+
# the single source of truth for this path. Unlike requirement/design/plan, `audit` is set once,
|
|
92
|
+
# unconditionally, before the legacy/structured layout branch, so it never varies by layout — but
|
|
93
|
+
# reading it from the resolver rather than recomputing it here still means a future resolver change
|
|
94
|
+
# has one place to land, not two that can drift apart.
|
|
95
|
+
[ -n "$audit_rel" ] || note "resolver-missing-audit-path"
|
|
96
|
+
audit_file="$repo_root/$audit_rel"
|
|
97
|
+
|
|
98
|
+
# Render to a temp file first, then move it into place. `>"$audit_file"` truncates the target
|
|
99
|
+
# before the group runs, and with no `-e` a mid-stream jq abort (a malformed or hand-corrupted
|
|
100
|
+
# journal field) would otherwise leave a header-only audit.md behind while this script still
|
|
101
|
+
# reported written:true. Same fail-open philosophy as the notes above: report a clear failure
|
|
102
|
+
# rather than corrupt the target.
|
|
103
|
+
tmp_audit="$(mktemp "${audit_file}.XXXXXX" 2>/dev/null || mktemp)"
|
|
104
|
+
# Every mktemp needs a matching cleanup that runs on the error paths AND on interruption — the
|
|
105
|
+
# explicit `rm -f "$tmp_audit"` below each failure branch does not cover a SIGINT/SIGTERM landing
|
|
106
|
+
# between here and the `mv`, which would otherwise leave a stray `audit.md.XXXXXX` behind inside
|
|
107
|
+
# the feature directory. The trap is a backstop: each failure branch below still removes it
|
|
108
|
+
# immediately on its own path (so a checked exit doesn't wait for the shell to unwind), and this
|
|
109
|
+
# catches everything else. Once the `mv` succeeds, $tmp_audit no longer exists, so a post-mv `rm -f`
|
|
110
|
+
# here is a correctly-silent no-op.
|
|
111
|
+
trap 'rm -f "$tmp_audit"' EXIT
|
|
112
|
+
|
|
113
|
+
# ── Workflow state diagram (034 IC-006) ───────────────────────────────────────────────────────
|
|
114
|
+
# Drawn from the SAME journal the table below is built from -- `program[]` carries the resolved
|
|
115
|
+
# stage/gate sequence with per-node status, and `cursor` marks where the run actually is. Because
|
|
116
|
+
# both come from one source, the picture cannot disagree with the rows beneath it.
|
|
117
|
+
#
|
|
118
|
+
# Stages and gates only, never individual history entries: that bounds the node count by the
|
|
119
|
+
# workflow definition rather than by how long the run has been going, which is what keeps this
|
|
120
|
+
# inside DIAGRAM_GUIDE.md's own stateDiagram-v2 ceiling.
|
|
121
|
+
#
|
|
122
|
+
# Ids are sanitized (`gate-0` -> `gate_0`) because stateDiagram-v2 reads a hyphen as syntax; the
|
|
123
|
+
# human-readable id is restored via the `state : label` form. A journal with no program[] emits
|
|
124
|
+
# nothing at all rather than an empty fence -- an empty capture here is success, not failure.
|
|
125
|
+
#
|
|
126
|
+
# RENDERED HERE, OUTSIDE the brace group below, and its status checked on the spot. Inside that
|
|
127
|
+
# group it could not be checked at all: `rc=$?` after the group observes only the group's LAST
|
|
128
|
+
# command (the history-table jq), so a failure in this one was invisible -- it emitted three lines,
|
|
129
|
+
# aborted, and left an unterminated ```mermaid fence with the entire history table swallowed inside
|
|
130
|
+
# it, while the script reported written:true and exited 0. The `[ ! -s "$tmp_audit" ]` guard did not
|
|
131
|
+
# catch it either, because the table still wrote bytes. Capturing first means a malformed program[]
|
|
132
|
+
# takes the same clear-failure path every other malformed-journal case already takes, which is what
|
|
133
|
+
# this script's own header promises: report a clear failure rather than corrupt the target.
|
|
134
|
+
if ! state_diagram="$(jq -r '
|
|
135
|
+
def sid: gsub("-"; "_");
|
|
136
|
+
(.cursor // -1) as $cur |
|
|
137
|
+
(.program // []) as $p |
|
|
138
|
+
if ($p | length) == 0 then empty else
|
|
139
|
+
"```mermaid",
|
|
140
|
+
"stateDiagram-v2",
|
|
141
|
+
" direction LR",
|
|
142
|
+
" [*] --> " + ($p[0].id | sid),
|
|
143
|
+
( [range(0; ($p|length)-1)] | .[] | " " + ($p[.].id|sid) + " --> " + ($p[.+1].id|sid)
|
|
144
|
+
+ (if $p[.].type == "gate" and ($p[.].decision // "") != "" then " : " + $p[.].decision else "" end) ),
|
|
145
|
+
" " + ($p[-1].id|sid) + " --> [*]",
|
|
146
|
+
"",
|
|
147
|
+
( [range(0; $p|length)] | .[] as $i | $p[$i] |
|
|
148
|
+
" " + (.id|sid) + " : " + .id
|
|
149
|
+
+ (if $i == $cur then " (current)"
|
|
150
|
+
elif .status == "completed" then " (done)"
|
|
151
|
+
elif .status == "approved" then " (approved)"
|
|
152
|
+
elif .status == "rejected" then " (rejected)"
|
|
153
|
+
else " (pending)" end) ),
|
|
154
|
+
"```",
|
|
155
|
+
""
|
|
156
|
+
end
|
|
157
|
+
' "$journal_file" 2>/dev/null)"; then
|
|
158
|
+
rm -f "$tmp_audit"
|
|
159
|
+
if [ "$emit_json" = true ]; then
|
|
160
|
+
printf '{"ok":false,"error":"state-diagram-render-failed","path":"%s"}\n' "$audit_rel" >&2
|
|
161
|
+
else
|
|
162
|
+
printf 'render-audit: failed to render the state diagram for %s (journal program[] may be malformed)\n' "$audit_rel" >&2
|
|
163
|
+
fi
|
|
164
|
+
exit 1
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
{
|
|
168
|
+
printf '# Audit Trail: %s\n\n' "$feature_slug"
|
|
169
|
+
printf '> Generated by `render-audit.sh` from `.doflow/state/orchestration/%s.json`. Do not\n' "$feature_slug"
|
|
170
|
+
printf '> hand-edit — this file is fully regenerated from the orchestration journal on every run.\n\n'
|
|
171
|
+
[ -n "$state_diagram" ] && printf '%s\n\n' "$state_diagram"
|
|
172
|
+
printf '| Date | Stage/Gate | Action | Note / Reason | Flags |\n'
|
|
173
|
+
printf '|---|---|---|---|---|\n'
|
|
174
|
+
jq -r '
|
|
175
|
+
def esc: if . == null then "" else (tostring | gsub("\\\\"; "\\\\") | gsub("\\|"; "\\|") | gsub("\r\n|\n|\r"; " ")) end;
|
|
176
|
+
.history[] |
|
|
177
|
+
(.node // "—") as $stage |
|
|
178
|
+
(if .action == "decide-gate" and (.detail != null and .detail != "")
|
|
179
|
+
then .action + " (" + .detail + ")"
|
|
180
|
+
else .action end) as $action |
|
|
181
|
+
((.note // "") | esc) as $note_raw |
|
|
182
|
+
((.detail // "") | esc) as $detail_raw |
|
|
183
|
+
(if $note_raw != "" then $note_raw
|
|
184
|
+
elif .action == "start" and $detail_raw != "" then $detail_raw
|
|
185
|
+
else "—" end) as $note |
|
|
186
|
+
(if .forced == true then "⚠ FORCED"
|
|
187
|
+
elif .backfilled == true then "⚡ BACKFILLED"
|
|
188
|
+
else "" end) as $flags |
|
|
189
|
+
"| " + (.at | esc) + " | " + ($stage | esc) + " | " + ($action | esc) + " | " + $note + " | " + $flags + " |"
|
|
190
|
+
' "$journal_file"
|
|
191
|
+
} >"$tmp_audit"
|
|
192
|
+
rc=$?
|
|
193
|
+
if [ "$rc" -ne 0 ] || [ ! -s "$tmp_audit" ]; then
|
|
194
|
+
rm -f "$tmp_audit"
|
|
195
|
+
if [ "$emit_json" = true ]; then
|
|
196
|
+
printf '{"ok":false,"error":"render-failed","path":"%s"}\n' "$audit_rel" >&2
|
|
197
|
+
else
|
|
198
|
+
printf 'render-audit: failed to render %s (journal may be malformed)\n' "$audit_rel" >&2
|
|
199
|
+
fi
|
|
200
|
+
exit 1
|
|
201
|
+
fi
|
|
202
|
+
chmod 644 "$tmp_audit" 2>/dev/null || true
|
|
203
|
+
# Unchecked, an mv failure (target dir missing — do-brainstorm's own state before its step 4
|
|
204
|
+
# mkdir, say — or a permissions problem) left the rendered content stranded in $tmp_audit while
|
|
205
|
+
# this script still reported written:true and exit 0: a false success from the one tool whose job
|
|
206
|
+
# is a trustworthy provenance record.
|
|
207
|
+
mv_err="$(mv "$tmp_audit" "$audit_file" 2>&1)"
|
|
208
|
+
mv_rc=$?
|
|
209
|
+
if [ "$mv_rc" -ne 0 ]; then
|
|
210
|
+
rm -f "$tmp_audit"
|
|
211
|
+
if [ "$emit_json" = true ]; then
|
|
212
|
+
printf '{"ok":false,"error":"move-failed","path":"%s"}\n' "$audit_rel" >&2
|
|
213
|
+
else
|
|
214
|
+
printf 'render-audit: failed to write %s (%s)\n' "$audit_rel" "$mv_err" >&2
|
|
215
|
+
fi
|
|
216
|
+
exit 1
|
|
217
|
+
fi
|
|
218
|
+
|
|
219
|
+
if [ "$emit_json" = true ]; then
|
|
220
|
+
printf '{"ok":true,"written":true,"path":"%s"}\n' "$audit_rel"
|
|
221
|
+
else
|
|
222
|
+
printf 'render-audit: wrote %s\n' "$audit_rel"
|
|
223
|
+
fi
|
|
224
|
+
exit 0
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# render-diagrams.sh — renders a chain artifact's fenced mermaid blocks to image files.
|
|
3
|
+
#
|
|
4
|
+
# A FAITHFUL render: the same nodes, the same edges, the same labels. The committed markdown is the
|
|
5
|
+
# only source of truth and is never modified, and no fenced block anywhere carries a theme
|
|
6
|
+
# directive — styling is supplied at render time from assets/mermaid-theme.json, so the source an
|
|
7
|
+
# author wrote is the source a reviewer diffs.
|
|
8
|
+
#
|
|
9
|
+
# Fail-open, matching render-audit.sh's own convention: a missing renderer, no active feature, and
|
|
10
|
+
# an artifact carrying no diagram are each reported via a note and exit 0 — never a hard error. This
|
|
11
|
+
# verb sits OUTSIDE the chain (034 FR-011: user-invoked only, never a stage), so an absent optional
|
|
12
|
+
# capability must never read as a broken run. Exit 1 is reserved for a renderer that ran and failed.
|
|
13
|
+
#
|
|
14
|
+
# Usage: render-diagrams.sh [--slug=<slug>] [--artifact=<path>] [--format=png|svg] [--json]
|
|
15
|
+
# No --slug → resolve the active feature via do-paths.sh, exactly like render-audit.sh.
|
|
16
|
+
# No --artifact → every chain artifact in the feature dir that contains a fenced mermaid block.
|
|
17
|
+
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
emit_json=false
|
|
21
|
+
slug_override=""
|
|
22
|
+
artifact_override=""
|
|
23
|
+
format="png"
|
|
24
|
+
bad_args=()
|
|
25
|
+
for a in "$@"; do
|
|
26
|
+
case "$a" in
|
|
27
|
+
--json) emit_json=true ;;
|
|
28
|
+
--slug=*) slug_override="${a#--slug=}" ;;
|
|
29
|
+
--artifact=*) artifact_override="${a#--artifact=}" ;;
|
|
30
|
+
--format=*) format="${a#--format=}" ;;
|
|
31
|
+
*) bad_args+=("$a") ;;
|
|
32
|
+
esac
|
|
33
|
+
done
|
|
34
|
+
|
|
35
|
+
# An unrecognized argument is a hard failure naming it, never a silent fall-through: the
|
|
36
|
+
# space-separated `--slug value` form would otherwise render the ACTIVE feature instead of the one
|
|
37
|
+
# the caller named, with no signal. Same reasoning, same handling, as render-audit.sh.
|
|
38
|
+
if [ "${#bad_args[@]}" -gt 0 ]; then
|
|
39
|
+
printf 'render-diagrams: unrecognized argument(s): %s\n' "${bad_args[*]}" >&2
|
|
40
|
+
printf 'usage: render-diagrams.sh [--slug=<slug>] [--artifact=<path>] [--format=png|svg] [--json]\n' >&2
|
|
41
|
+
exit 2
|
|
42
|
+
fi
|
|
43
|
+
case "$format" in
|
|
44
|
+
png|svg) ;;
|
|
45
|
+
*) printf 'render-diagrams: --format must be png or svg, got: %s\n' "$format" >&2; exit 2 ;;
|
|
46
|
+
esac
|
|
47
|
+
|
|
48
|
+
# Fail-open note: report and exit 0. Nothing downstream may treat an absent capability as failure.
|
|
49
|
+
note() {
|
|
50
|
+
if [ "$emit_json" = true ]; then
|
|
51
|
+
if command -v jq >/dev/null 2>&1; then
|
|
52
|
+
jq -n --arg n "$1" '{ok:true, written:false, note:$n, rendered:[]}'
|
|
53
|
+
else
|
|
54
|
+
printf '{"ok":true,"written":false,"note":"%s","rendered":[]}\n' "$1"
|
|
55
|
+
fi
|
|
56
|
+
else
|
|
57
|
+
printf 'render-diagrams: %s (nothing rendered)\n' "$1"
|
|
58
|
+
fi
|
|
59
|
+
exit 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
command -v jq >/dev/null 2>&1 || note "jq-absent"
|
|
63
|
+
|
|
64
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
65
|
+
RESOLVER="$script_dir/do-paths.sh"
|
|
66
|
+
[ -f "$RESOLVER" ] || RESOLVER="${DOFLOW_CONFIG_DIR:+$DOFLOW_CONFIG_DIR/scripts/doflow/bash/do-paths.sh}"
|
|
67
|
+
[ -f "$RESOLVER" ] || note "resolver-absent"
|
|
68
|
+
|
|
69
|
+
# The theme sits beside this script's own tree, not in the repo being rendered.
|
|
70
|
+
THEME="$script_dir/../assets/mermaid-theme.json"
|
|
71
|
+
|
|
72
|
+
resolver_args=(--json)
|
|
73
|
+
[ -n "$slug_override" ] && resolver_args+=("--slug=$slug_override")
|
|
74
|
+
rc=0; resolved_json=$(bash "$RESOLVER" "${resolver_args[@]}" 2>/dev/null) || rc=$?
|
|
75
|
+
# An unsafe slug is refused, not noted: exit 2 with the resolver's error object (IC-009).
|
|
76
|
+
if [ "$rc" -eq 2 ] && [ "$(printf '%s' "$resolved_json" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
|
|
77
|
+
if [ "$emit_json" = true ]; then printf '%s\n' "$resolved_json"; else printf 'render-diagrams: %s\n' "$(printf '%s' "$resolved_json" | jq -r '.message')" >&2; fi
|
|
78
|
+
exit 2
|
|
79
|
+
fi
|
|
80
|
+
[ "$rc" -eq 0 ] || note "resolver-error"
|
|
81
|
+
|
|
82
|
+
repo_root=$(printf '%s' "$resolved_json" | jq -r '.repo_root // empty')
|
|
83
|
+
feature_slug=$(printf '%s' "$resolved_json" | jq -r '.feature_slug // empty')
|
|
84
|
+
feature_dir=$(printf '%s' "$resolved_json" | jq -r '.feature_dir // empty')
|
|
85
|
+
[ -n "$feature_slug" ] || note "no-active-feature"
|
|
86
|
+
[ -n "$feature_dir" ] || note "resolver-missing-feature-dir"
|
|
87
|
+
|
|
88
|
+
# The renderer is an OPTIONAL dependency, deliberately absent from package.json (034 FR-012): a
|
|
89
|
+
# user who never renders a diagram never pays for a headless browser download. Its absence is
|
|
90
|
+
# reported with the way to get it, and is not a failure.
|
|
91
|
+
command -v mmdc >/dev/null 2>&1 || note "mermaid renderer not found. Install with: npm install -g @mermaid-js/mermaid-cli (or run: doflow doctor)"
|
|
92
|
+
|
|
93
|
+
# The theme is REQUIRED, not best-effort (034 FR-014): the render must apply the diagram-design
|
|
94
|
+
# roles, so an absent asset is reported rather than silently producing default-styled output. It
|
|
95
|
+
# fails open exactly as the absent renderer above does -- a note and exit 0 -- because this verb
|
|
96
|
+
# sits outside the chain and a missing shipped asset is a broken install, not a broken run. The
|
|
97
|
+
# earlier form appended `-c` only when the file happened to exist, which meant a reinstall gone
|
|
98
|
+
# wrong rendered unthemed images and reported ok:true with failures:0 and nothing on stderr.
|
|
99
|
+
[ -f "$THEME" ] || note "theme asset missing at $THEME. The render would not apply the DoFlow diagram theme, so it was not attempted. Reinstall the scripts tree: npx @khoavu882/doflow install"
|
|
100
|
+
|
|
101
|
+
out_dir="$repo_root/$feature_dir/design/diagrams"
|
|
102
|
+
|
|
103
|
+
# Collect the artifacts to scan. A named --artifact wins; otherwise every markdown file in the
|
|
104
|
+
# feature dir, in a stable order so block indices are reproducible across runs.
|
|
105
|
+
artifacts=()
|
|
106
|
+
if [ -n "$artifact_override" ]; then
|
|
107
|
+
candidate="$artifact_override"
|
|
108
|
+
[ -f "$candidate" ] || candidate="$repo_root/$artifact_override"
|
|
109
|
+
[ -f "$candidate" ] || { printf 'render-diagrams: no such artifact: %s\n' "$artifact_override" >&2; exit 2; }
|
|
110
|
+
artifacts+=("$candidate")
|
|
111
|
+
else
|
|
112
|
+
while IFS= read -r f; do artifacts+=("$f"); done < <(find "$repo_root/$feature_dir" -type f -name '*.md' -not -path '*/diagrams/*' | sort)
|
|
113
|
+
fi
|
|
114
|
+
[ "${#artifacts[@]}" -gt 0 ] || note "no markdown artifacts found under $feature_dir"
|
|
115
|
+
|
|
116
|
+
tmpdir="$(mktemp -d 2>/dev/null)" || note "mktemp-failed"
|
|
117
|
+
trap 'rm -rf "$tmpdir"' EXIT
|
|
118
|
+
|
|
119
|
+
rendered=()
|
|
120
|
+
failures=0
|
|
121
|
+
total_blocks=0
|
|
122
|
+
|
|
123
|
+
for artifact in "${artifacts[@]}"; do
|
|
124
|
+
stem="$(basename "$artifact")"; stem="${stem%.md}"
|
|
125
|
+
|
|
126
|
+
# Extract each fenced mermaid block to its own file, numbered from 01 in DOCUMENT ORDER. Index,
|
|
127
|
+
# not heading text, is the identity: a heading is free prose that can change without the diagram
|
|
128
|
+
# changing, which would silently rename an output file.
|
|
129
|
+
count=$(awk -v outdir="$tmpdir" -v stem="$stem" '
|
|
130
|
+
/^[[:space:]]*```mermaid[[:space:]]*$/ && !inblock { inblock=1; n++; f=sprintf("%s/%s-%02d.mmd", outdir, stem, n); next }
|
|
131
|
+
inblock && /^[[:space:]]*```[[:space:]]*$/ { inblock=0; close(f); next }
|
|
132
|
+
inblock { print >> f }
|
|
133
|
+
END { print n+0 }
|
|
134
|
+
' "$artifact")
|
|
135
|
+
|
|
136
|
+
[ "$count" -gt 0 ] 2>/dev/null || continue
|
|
137
|
+
total_blocks=$((total_blocks + count))
|
|
138
|
+
mkdir -p "$out_dir" || note "cannot create $out_dir"
|
|
139
|
+
|
|
140
|
+
i=1
|
|
141
|
+
while [ "$i" -le "$count" ]; do
|
|
142
|
+
nn=$(printf '%02d' "$i")
|
|
143
|
+
src="$tmpdir/$stem-$nn.mmd"
|
|
144
|
+
dest="$out_dir/$stem-$nn.$format"
|
|
145
|
+
# -s 2 renders PNG at twice device scale so it stays sharp projected; ignored for svg.
|
|
146
|
+
mmdc_args=(-i "$src" -o "$dest" -b "#f5f5f5" -c "$THEME")
|
|
147
|
+
[ "$format" = "png" ] && mmdc_args+=(-s 2)
|
|
148
|
+
if mmdc "${mmdc_args[@]}" >/dev/null 2>&1; then
|
|
149
|
+
rendered+=("$feature_dir/design/diagrams/$stem-$nn.$format")
|
|
150
|
+
else
|
|
151
|
+
failures=$((failures + 1))
|
|
152
|
+
printf 'render-diagrams: renderer failed on %s block %s\n' "$(basename "$artifact")" "$nn" >&2
|
|
153
|
+
fi
|
|
154
|
+
i=$((i + 1))
|
|
155
|
+
done
|
|
156
|
+
done
|
|
157
|
+
|
|
158
|
+
[ "$total_blocks" -gt 0 ] || note "no fenced mermaid block found in the artifacts scanned"
|
|
159
|
+
|
|
160
|
+
# The output directory excludes its own contents, in any host project, whether or not that project
|
|
161
|
+
# ignores agent-docs/. `!.gitignore` keeps the rule itself trackable so it becomes durable once
|
|
162
|
+
# committed. The installer manages no ignore rules, so the verb owns this.
|
|
163
|
+
if [ -d "$out_dir" ] && [ ! -f "$out_dir/.gitignore" ]; then
|
|
164
|
+
printf '*\n!.gitignore\n' > "$out_dir/.gitignore"
|
|
165
|
+
fi
|
|
166
|
+
|
|
167
|
+
if [ "$emit_json" = true ]; then
|
|
168
|
+
printf '%s\n' "${rendered[@]:-}" | jq -R . | jq -s \
|
|
169
|
+
--argjson failures "$failures" --argjson blocks "$total_blocks" --arg dir "$feature_dir/design/diagrams" \
|
|
170
|
+
'map(select(length > 0)) | {ok: ($failures == 0), written: (length > 0), dir: $dir, blocks: $blocks, failures: $failures, rendered: .}'
|
|
171
|
+
else
|
|
172
|
+
printf 'render-diagrams: %s of %s block(s) rendered into %s\n' "${#rendered[@]}" "$total_blocks" "$feature_dir/design/diagrams"
|
|
173
|
+
for r in "${rendered[@]:-}"; do [ -n "$r" ] && printf ' %s\n' "$r"; done
|
|
174
|
+
fi
|
|
175
|
+
|
|
176
|
+
[ "$failures" -eq 0 ] || exit 1
|
|
177
|
+
exit 0
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# render-puml.sh — renders a feature's authored PlantUML sources and projects them into its artifact.
|
|
3
|
+
#
|
|
4
|
+
# ONE verb doing BOTH derived views, deliberately (035 design R6): reading the source once means the
|
|
5
|
+
# committed image and the generated inline block cannot come from different revisions of it. Two
|
|
6
|
+
# verbs could, and that drift is what this whole feature exists to remove.
|
|
7
|
+
#
|
|
8
|
+
# Fail-open, matching render-audit.sh and render-diagrams.sh: no active feature, no design/c4/
|
|
9
|
+
# directory, and no renderer are each reported via a note and exit 0. Authoring C4 in PlantUML is
|
|
10
|
+
# OPT-IN per feature, so a feature without design/c4/ is not a broken feature -- it is the ordinary
|
|
11
|
+
# case, and this verb must say so rather than fail.
|
|
12
|
+
#
|
|
13
|
+
# Writes ONLY into <feature dir>/design/c4/ and one delimited region of one artifact. It never
|
|
14
|
+
# touches design/diagrams/, which is feature 034's ignored render output (035 IC-005).
|
|
15
|
+
#
|
|
16
|
+
# Usage: render-puml.sh [--slug=<slug>] [--check] [--json]
|
|
17
|
+
|
|
18
|
+
set -uo pipefail
|
|
19
|
+
|
|
20
|
+
emit_json=false
|
|
21
|
+
check_only=false
|
|
22
|
+
slug_override=""
|
|
23
|
+
bad_args=()
|
|
24
|
+
for a in "$@"; do
|
|
25
|
+
case "$a" in
|
|
26
|
+
--json) emit_json=true ;;
|
|
27
|
+
--check) check_only=true ;;
|
|
28
|
+
--slug=*) slug_override="${a#--slug=}" ;;
|
|
29
|
+
*) bad_args+=("$a") ;;
|
|
30
|
+
esac
|
|
31
|
+
done
|
|
32
|
+
|
|
33
|
+
if [ "${#bad_args[@]}" -gt 0 ]; then
|
|
34
|
+
printf 'render-puml: unrecognized argument(s): %s\n' "${bad_args[*]}" >&2
|
|
35
|
+
printf 'usage: render-puml.sh [--slug=<slug>] [--check] [--json]\n' >&2
|
|
36
|
+
exit 2
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
note() {
|
|
40
|
+
if [ "$emit_json" = true ]; then
|
|
41
|
+
if command -v jq >/dev/null 2>&1; then jq -n --arg n "$1" '{ok:true, written:false, note:$n, rendered:[]}'
|
|
42
|
+
else printf '{"ok":true,"written":false,"note":"%s","rendered":[]}\n' "$1"; fi
|
|
43
|
+
else
|
|
44
|
+
printf 'render-puml: %s (nothing rendered)\n' "$1"
|
|
45
|
+
fi
|
|
46
|
+
exit 0
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
command -v jq >/dev/null 2>&1 || note "jq-absent"
|
|
50
|
+
|
|
51
|
+
script_dir="$(cd "$(dirname "$0")" && pwd)"
|
|
52
|
+
RESOLVER="$script_dir/do-paths.sh"
|
|
53
|
+
[ -f "$RESOLVER" ] || RESOLVER="${DOFLOW_CONFIG_DIR:-$HOME/.doflow}/scripts/doflow/bash/do-paths.sh"
|
|
54
|
+
[ -f "$RESOLVER" ] || note "resolver-absent"
|
|
55
|
+
# Resolved after repo_root below, since the runtime sits beside the repo root in a checkout and
|
|
56
|
+
# under <config>/runtime/ in an install -- the resolver is the only thing that knows which.
|
|
57
|
+
|
|
58
|
+
resolver_args=(--json)
|
|
59
|
+
[ -n "$slug_override" ] && resolver_args+=("--slug=$slug_override")
|
|
60
|
+
rc=0; resolved_json=$(bash "$RESOLVER" "${resolver_args[@]}" 2>/dev/null) || rc=$?
|
|
61
|
+
# An unsafe slug is refused, not noted: exit 2 with the resolver's error object (IC-009).
|
|
62
|
+
if [ "$rc" -eq 2 ] && [ "$(printf '%s' "$resolved_json" | jq -r '.error // empty' 2>/dev/null)" = "invalid-slug" ]; then
|
|
63
|
+
if [ "$emit_json" = true ]; then printf '%s\n' "$resolved_json"; else printf 'render-puml: %s\n' "$(printf '%s' "$resolved_json" | jq -r '.message')" >&2; fi
|
|
64
|
+
exit 2
|
|
65
|
+
fi
|
|
66
|
+
[ "$rc" -eq 0 ] || note "resolver-error"
|
|
67
|
+
|
|
68
|
+
repo_root=$(printf '%s' "$resolved_json" | jq -r '.repo_root // empty')
|
|
69
|
+
feature_slug=$(printf '%s' "$resolved_json" | jq -r '.feature_slug // empty')
|
|
70
|
+
feature_dir=$(printf '%s' "$resolved_json" | jq -r '.feature_dir // empty')
|
|
71
|
+
|
|
72
|
+
# The projector lives in the JS runtime, not beside the helpers: G12 keeps this repo to one runtime
|
|
73
|
+
# implementation, and Python in core/ is how a shadow tree grew the first time, one individually
|
|
74
|
+
# defensible module at a time. render-audit.sh already shells out to node for the same reason.
|
|
75
|
+
PROJECTOR="$repo_root/src/runtime/c4-project.ts"
|
|
76
|
+
[ -f "$PROJECTOR" ] || PROJECTOR="${DOFLOW_CONFIG_DIR:-$HOME/.doflow}/runtime/src/runtime/c4-project.ts"
|
|
77
|
+
design_rel=$(printf '%s' "$resolved_json" | jq -r '.design // empty')
|
|
78
|
+
[ -n "$feature_slug" ] || note "no-active-feature"
|
|
79
|
+
[ -n "$feature_dir" ] || note "resolver-missing-feature-dir"
|
|
80
|
+
|
|
81
|
+
src_dir="$repo_root/$feature_dir/design/c4"
|
|
82
|
+
# Authoring C4 in PlantUML is opt-in. No directory is the ordinary case, not a failure.
|
|
83
|
+
[ -d "$src_dir" ] || note "no design/c4/ directory in $feature_dir — authoring C4 in PlantUML is opt-in, so there is nothing to render"
|
|
84
|
+
|
|
85
|
+
sources=()
|
|
86
|
+
while IFS= read -r f; do sources+=("$f"); done < <(find "$src_dir" -maxdepth 1 -type f -name '*.puml' | sort)
|
|
87
|
+
[ "${#sources[@]}" -gt 0 ] || note "no .puml source found in $feature_dir/design/c4"
|
|
88
|
+
|
|
89
|
+
# Which source feeds design.md's inline block: container when present, else context. Named once so
|
|
90
|
+
# --check and the render loop cannot disagree about which source the block is supposed to match.
|
|
91
|
+
block_source_for() {
|
|
92
|
+
local pick="" stem
|
|
93
|
+
for f in "$@"; do
|
|
94
|
+
stem="$(basename "${f%.puml}")"
|
|
95
|
+
if [ "$stem" = "container" ]; then pick="$f"; fi
|
|
96
|
+
if [ -z "$pick" ] && [ "$stem" = "context" ]; then pick="$f"; fi
|
|
97
|
+
done
|
|
98
|
+
printf '%s' "$pick"
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
short_hash() { node -e 'const c=require("node:crypto"),f=require("node:fs");process.stdout.write(c.createHash("sha256").update(f.readFileSync(process.argv[1])).digest("hex").slice(0,12))' "$1"; }
|
|
102
|
+
|
|
103
|
+
# ── --check: re-hash and report, rendering and writing nothing (035 IC-004) ──────────────────────
|
|
104
|
+
if [ "$check_only" = true ]; then
|
|
105
|
+
stale=()
|
|
106
|
+
for src in "${sources[@]}"; do
|
|
107
|
+
want="$(short_hash "$src")"
|
|
108
|
+
have=""
|
|
109
|
+
[ -f "${src%.puml}.sha" ] && have="$(cat "${src%.puml}.sha")"
|
|
110
|
+
[ "$want" = "$have" ] || stale+=("$(basename "$src")")
|
|
111
|
+
done
|
|
112
|
+
# IC-004 records the revision in BOTH derived outputs, so checking only the .sha sibling checked
|
|
113
|
+
# only the image. The sibling is written inside the renderer-success branch below; the inline
|
|
114
|
+
# block is written by a separate branch that an absent marker pair can skip. They drift apart, and
|
|
115
|
+
# a check that reads one of them reports the other as current without ever having looked at it.
|
|
116
|
+
block_src="$(block_source_for "${sources[@]}")"
|
|
117
|
+
if [ -n "$block_src" ] && [ -n "$design_rel" ] && [ -f "$repo_root/$design_rel" ]; then
|
|
118
|
+
want="$(short_hash "$block_src")"
|
|
119
|
+
have="$(sed -n 's/.*source-hash: \([0-9a-f]*\).*/\1/p' "$repo_root/$design_rel" | head -1)"
|
|
120
|
+
[ "$want" = "$have" ] || stale+=("$(basename "$block_src") -> inline block in $design_rel")
|
|
121
|
+
fi
|
|
122
|
+
if [ "$emit_json" = true ]; then
|
|
123
|
+
printf '%s\n' "${stale[@]:-}" | jq -R . | jq -s \
|
|
124
|
+
--argjson ok "$([ "${#stale[@]}" -eq 0 ] && echo true || echo false)" \
|
|
125
|
+
'map(select(length>0)) | {ok: $ok, checked:true, stale:.}'
|
|
126
|
+
else
|
|
127
|
+
if [ "${#stale[@]}" -eq 0 ]; then printf 'render-puml: every derived output matches its source\n'
|
|
128
|
+
else printf 'render-puml: stale against their source: %s\n' "${stale[*]}"; fi
|
|
129
|
+
fi
|
|
130
|
+
[ "${#stale[@]}" -eq 0 ] || exit 1
|
|
131
|
+
exit 0
|
|
132
|
+
fi
|
|
133
|
+
|
|
134
|
+
have_renderer=true
|
|
135
|
+
command -v plantuml >/dev/null 2>&1 || have_renderer=false
|
|
136
|
+
|
|
137
|
+
block_src="$(block_source_for "${sources[@]}")"
|
|
138
|
+
rendered=(); failures=0; projected=""
|
|
139
|
+
for src in "${sources[@]}"; do
|
|
140
|
+
stem="$(basename "${src%.puml}")"
|
|
141
|
+
digest="$(short_hash "$src")"
|
|
142
|
+
|
|
143
|
+
if [ "$have_renderer" = true ]; then
|
|
144
|
+
if plantuml -tpng -o "$src_dir" "$src" >/dev/null 2>&1; then
|
|
145
|
+
printf '%s\n' "$digest" > "${src%.puml}.sha"
|
|
146
|
+
rendered+=("$feature_dir/design/c4/$stem.png")
|
|
147
|
+
else
|
|
148
|
+
failures=$((failures + 1))
|
|
149
|
+
printf 'render-puml: renderer failed on %s\n' "$stem.puml" >&2
|
|
150
|
+
fi
|
|
151
|
+
fi
|
|
152
|
+
|
|
153
|
+
# One source feeds design.md §2's inline block; the others render only to images.
|
|
154
|
+
if [ "$src" = "$block_src" ]; then
|
|
155
|
+
# stdout only is the block: the splice writes it into authored prose, so anything the projector
|
|
156
|
+
# (or Node) prints on stderr, an ExperimentalWarning for one, must never become part of it.
|
|
157
|
+
proj_err="$(mktemp)" || note "mktemp-failed"
|
|
158
|
+
if ! block="$(node "$PROJECTOR" "$src" 2>"$proj_err")"; then
|
|
159
|
+
printf 'render-puml: %s%s\n' "$(cat "$proj_err")" "$block" >&2
|
|
160
|
+
rm -f "$proj_err"
|
|
161
|
+
exit 1
|
|
162
|
+
fi
|
|
163
|
+
[ -s "$proj_err" ] && cat "$proj_err" >&2
|
|
164
|
+
rm -f "$proj_err"
|
|
165
|
+
projected="$block"
|
|
166
|
+
fi
|
|
167
|
+
done
|
|
168
|
+
|
|
169
|
+
[ "$have_renderer" = true ] || note "PlantUML not found. Run 'doflow tools' for the install command registered for this machine (on Homebrew: brew install plantuml), or 'doflow doctor'. The sources were left unrendered."
|
|
170
|
+
|
|
171
|
+
# ── Replace the delimited region of the artifact (035 IC-004, RK3) ───────────────────────────────
|
|
172
|
+
# The one operation here that can destroy authored prose. Two guards: an artifact whose markers are
|
|
173
|
+
# absent is REPORTED and left untouched rather than repaired by guess, and the rewrite goes through
|
|
174
|
+
# a temp file moved into place -- the same pattern render-audit.sh uses, for the same reason.
|
|
175
|
+
block_written=false
|
|
176
|
+
if [ -n "$projected" ] && [ -n "$design_rel" ] && [ -f "$repo_root/$design_rel" ]; then
|
|
177
|
+
target="$repo_root/$design_rel"
|
|
178
|
+
if grep -q '<!-- generated from' "$target" && grep -q '<!-- end generated -->' "$target"; then
|
|
179
|
+
tmp="$(mktemp "${target}.XXXXXX")" || note "mktemp-failed"
|
|
180
|
+
trap 'rm -f "$tmp"' EXIT
|
|
181
|
+
# Two corrections a review found here. The end offset added the CLOSE marker's length PLUS a
|
|
182
|
+
# newline unconditionally, so whenever the marker was not followed by one it ate the next
|
|
183
|
+
# character instead -- and because the character it ate first was the newline, every later run
|
|
184
|
+
# ate another. It had already demoted this feature's own "### C4 Level 3" heading to "##" and
|
|
185
|
+
# glued it to the marker. And CLOSE is now searched FROM start, so a close marker sitting before
|
|
186
|
+
# the open one reads as absent rather than duplicating everything between them.
|
|
187
|
+
if node -e '
|
|
188
|
+
const fs = require("node:fs");
|
|
189
|
+
const [target, tmp, block] = process.argv.slice(1);
|
|
190
|
+
const OPEN = "<!-- generated from";
|
|
191
|
+
const CLOSE = "<!-- end generated -->";
|
|
192
|
+
const text = fs.readFileSync(target, "utf8");
|
|
193
|
+
const start = text.indexOf(OPEN);
|
|
194
|
+
const closeAt = start === -1 ? -1 : text.indexOf(CLOSE, start);
|
|
195
|
+
if (closeAt === -1) {
|
|
196
|
+
process.stderr.write("generated-block markers are absent or out of order\n");
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
const afterClose = closeAt + CLOSE.length;
|
|
200
|
+
const end = afterClose + (text[afterClose] === "\n" ? 1 : 0);
|
|
201
|
+
fs.writeFileSync(tmp, text.slice(0, start) + block + text.slice(end));
|
|
202
|
+
' "$target" "$tmp" "$projected" && [ -s "$tmp" ]; then
|
|
203
|
+
mv "$tmp" "$target"; block_written=true
|
|
204
|
+
else
|
|
205
|
+
rm -f "$tmp"
|
|
206
|
+
failures=$((failures + 1))
|
|
207
|
+
printf 'render-puml: rewriting %s failed; it was left untouched\n' "$design_rel" >&2
|
|
208
|
+
fi
|
|
209
|
+
else
|
|
210
|
+
printf 'render-puml: %s carries no generated-block markers; it was left untouched\n' "$design_rel" >&2
|
|
211
|
+
fi
|
|
212
|
+
fi
|
|
213
|
+
|
|
214
|
+
if [ "$emit_json" = true ]; then
|
|
215
|
+
printf '%s\n' "${rendered[@]:-}" | jq -R . | jq -s \
|
|
216
|
+
--argjson failures "$failures" --arg dir "$feature_dir/design/c4" --argjson block "$([ "$block_written" = true ] && echo true || echo false)" \
|
|
217
|
+
'map(select(length>0)) | {ok: ($failures==0), written: (length>0), dir: $dir, blockWritten: $block, failures: $failures, rendered: .}'
|
|
218
|
+
else
|
|
219
|
+
printf 'render-puml: %s source(s) rendered into %s; inline block %s\n' "${#rendered[@]}" "$feature_dir/design/c4" "$([ "$block_written" = true ] && echo written || echo 'not written')"
|
|
220
|
+
fi
|
|
221
|
+
|
|
222
|
+
[ "$failures" -eq 0 ] || exit 1
|
|
223
|
+
exit 0
|