@kontourai/flow-agents 0.1.1

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.
Files changed (418) hide show
  1. package/.githooks/pre-push +11 -0
  2. package/.github/workflows/ci.yml +210 -0
  3. package/.github/workflows/docs-pages.yml +52 -0
  4. package/.github/workflows/publish-npm.yml +104 -0
  5. package/AGENTS.md +26 -0
  6. package/CHANGELOG.md +66 -0
  7. package/CODE_OF_CONDUCT.md +25 -0
  8. package/CONTEXT.md +300 -0
  9. package/CONTRIBUTING.md +44 -0
  10. package/LICENSE +201 -0
  11. package/README.md +129 -0
  12. package/SECURITY.md +33 -0
  13. package/agent-cards/dev.json +19 -0
  14. package/agents/dev.json +127 -0
  15. package/agents/tool-code-reviewer.json +61 -0
  16. package/agents/tool-dependencies-updater.json +118 -0
  17. package/agents/tool-explore-config.json +92 -0
  18. package/agents/tool-explore-deps.json +92 -0
  19. package/agents/tool-explore-entry.json +92 -0
  20. package/agents/tool-explore-patterns.json +92 -0
  21. package/agents/tool-explore-structure.json +92 -0
  22. package/agents/tool-explore-tests.json +92 -0
  23. package/agents/tool-planner.json +57 -0
  24. package/agents/tool-playwright.json +145 -0
  25. package/agents/tool-security-reviewer.json +56 -0
  26. package/agents/tool-verifier.json +61 -0
  27. package/agents/tool-worker.json +58 -0
  28. package/build/src/cli/console-learning-projection.js +123 -0
  29. package/build/src/cli/docs-preview.js +39 -0
  30. package/build/src/cli/effective-backlog-settings.js +102 -0
  31. package/build/src/cli/export-bookmarks.js +38 -0
  32. package/build/src/cli/fixture-retirement-audit.js +140 -0
  33. package/build/src/cli/flow-kit.js +138 -0
  34. package/build/src/cli/import-bookmarks.js +50 -0
  35. package/build/src/cli/init.js +239 -0
  36. package/build/src/cli/instinct-cli.js +93 -0
  37. package/build/src/cli/promote-workflow-artifact.js +63 -0
  38. package/build/src/cli/publish-change-helper.js +154 -0
  39. package/build/src/cli/pull-work-provider.js +469 -0
  40. package/build/src/cli/runtime-adapter.js +23 -0
  41. package/build/src/cli/telemetry-doctor.js +221 -0
  42. package/build/src/cli/usage-feedback.js +443 -0
  43. package/build/src/cli/validate-hook-influence.js +152 -0
  44. package/build/src/cli/validate-source-tree.js +31 -0
  45. package/build/src/cli/validate-workflow-artifacts.js +486 -0
  46. package/build/src/cli/veritas-governance.js +262 -0
  47. package/build/src/cli/workflow-artifact-cleanup-audit.js +272 -0
  48. package/build/src/cli/workflow-sidecar.js +816 -0
  49. package/build/src/cli.js +89 -0
  50. package/build/src/flow-kit/validate.js +75 -0
  51. package/build/src/lib/args.js +45 -0
  52. package/build/src/lib/fs.js +62 -0
  53. package/build/src/lib/workflow-learning-projection.js +334 -0
  54. package/build/src/runtime-adapters.js +146 -0
  55. package/build/src/tools/build-universal-bundles.js +397 -0
  56. package/build/src/tools/common.js +56 -0
  57. package/build/src/tools/filter-installed-packs.js +132 -0
  58. package/build/src/tools/generate-context-map.js +198 -0
  59. package/build/src/tools/validate-package.js +64 -0
  60. package/build/src/tools/validate-source-tree.js +622 -0
  61. package/console.telemetry.json +176 -0
  62. package/context/base-rules.md +17 -0
  63. package/context/code-review-standards.md +62 -0
  64. package/context/coding-standards.md +42 -0
  65. package/context/common/orchestrators.md +12 -0
  66. package/context/common/subagents.md +28 -0
  67. package/context/contracts/artifact-contract.md +182 -0
  68. package/context/contracts/builder-kit-workflow-state-contract.md +319 -0
  69. package/context/contracts/delivery-contract.md +69 -0
  70. package/context/contracts/execution-contract.md +53 -0
  71. package/context/contracts/governance-adapter-contract.md +67 -0
  72. package/context/contracts/planning-contract.md +85 -0
  73. package/context/contracts/review-contract.md +104 -0
  74. package/context/contracts/sandbox-policy.md +52 -0
  75. package/context/contracts/verification-contract.md +134 -0
  76. package/context/contracts/work-item-contract.md +215 -0
  77. package/context/deferred/demo-mode.md +33 -0
  78. package/context/deferred/languages/go.md +31 -0
  79. package/context/deferred/languages/python.md +31 -0
  80. package/context/deferred/languages/typescript.md +34 -0
  81. package/context/deferred/parallelization.md +35 -0
  82. package/context/deferred/worktree-isolation.md +24 -0
  83. package/context/development-workflow.md +50 -0
  84. package/context/scripts/context-budget/budget-scan.sh +166 -0
  85. package/context/scripts/detect-tools.sh +3 -0
  86. package/context/scripts/discover-agents.sh +28 -0
  87. package/context/scripts/git-status.sh +49 -0
  88. package/context/scripts/hooks/config-protection.js +79 -0
  89. package/context/scripts/hooks/desktop-notify.sh +39 -0
  90. package/context/scripts/hooks/governance-audit.sh +135 -0
  91. package/context/scripts/hooks/lib/audit-transport.sh +40 -0
  92. package/context/scripts/hooks/lib/hook-flags.js +49 -0
  93. package/context/scripts/hooks/lib/patterns.sh +57 -0
  94. package/context/scripts/hooks/lib/resolve-formatter.js +80 -0
  95. package/context/scripts/hooks/post-edit-accumulator.js +66 -0
  96. package/context/scripts/hooks/pre-commit-quality.js +194 -0
  97. package/context/scripts/hooks/quality-gate.js +93 -0
  98. package/context/scripts/hooks/report-only-guard.js +21 -0
  99. package/context/scripts/hooks/run-hook.js +136 -0
  100. package/context/scripts/hooks/stop-format-typecheck.js +141 -0
  101. package/context/scripts/hooks/stop-goal-fit.js +337 -0
  102. package/context/scripts/hooks/workflow-steering.js +250 -0
  103. package/context/scripts/telemetry/console-presets.sh +14 -0
  104. package/context/scripts/telemetry/install-console-config.sh +214 -0
  105. package/context/scripts/telemetry/lib/config.sh +85 -0
  106. package/context/scripts/telemetry/lib/enrich.sh +115 -0
  107. package/context/scripts/telemetry/lib/redact.sh +22 -0
  108. package/context/scripts/telemetry/lib/session.sh +63 -0
  109. package/context/scripts/telemetry/lib/transport.sh +183 -0
  110. package/context/scripts/telemetry/lib/usage.sh +29 -0
  111. package/context/scripts/telemetry/sync-agents.sh +173 -0
  112. package/context/scripts/telemetry/telemetry.conf +23 -0
  113. package/context/scripts/telemetry/telemetry.sh +387 -0
  114. package/context/scripts/validate-package.sh +89 -0
  115. package/context/settings/backlog-provider-settings.json +54 -0
  116. package/context/templates/core/identity.md +26 -0
  117. package/context/templates/core/user.md +15 -0
  118. package/docs/_config.yml +15 -0
  119. package/docs/_layouts/default.html +87 -0
  120. package/docs/adr/0001-flow-agents-consumes-flow.md +77 -0
  121. package/docs/adr/0002-flow-kits-as-extension-unit.md +13 -0
  122. package/docs/adr/0003-flow-agents-coordinates-kits-and-adapters.md +13 -0
  123. package/docs/adr/0004-gates-expect-surface-claims.md +15 -0
  124. package/docs/adr/0005-kubernetes-inspired-resource-contracts.md +48 -0
  125. package/docs/adr/0006-typescript-first-source-policy.md +98 -0
  126. package/docs/agent-system-guidebook.md +391 -0
  127. package/docs/agent-usage-feedback-loop.md +351 -0
  128. package/docs/assets/favicon.svg +13 -0
  129. package/docs/assets/og-image.png +0 -0
  130. package/docs/assets/site.css +774 -0
  131. package/docs/assets/site.js +139 -0
  132. package/docs/configurable-workflow-routing.md +174 -0
  133. package/docs/context-map.md +145 -0
  134. package/docs/developer-architecture.md +145 -0
  135. package/docs/developer-hook-setup.md +61 -0
  136. package/docs/fixture-ownership.md +44 -0
  137. package/docs/flow-kit-repository-contract.md +180 -0
  138. package/docs/index.md +129 -0
  139. package/docs/kontour-resource-contract.md +358 -0
  140. package/docs/migrations.md +64 -0
  141. package/docs/north-star.md +322 -0
  142. package/docs/operating-layers.md +110 -0
  143. package/docs/repository-structure.md +132 -0
  144. package/docs/sandbox-policy.md +56 -0
  145. package/docs/skills-map.md +203 -0
  146. package/docs/standards-register.md +96 -0
  147. package/docs/veritas-integration.md +165 -0
  148. package/docs/work-item-adapters.md +72 -0
  149. package/docs/workflow-artifact-lifecycle.md +141 -0
  150. package/docs/workflow-eval-strategy.md +295 -0
  151. package/docs/workflow-shared-contracts.md +51 -0
  152. package/docs/workflow-usage-guide.md +443 -0
  153. package/evals/ARCHITECTURE.md +143 -0
  154. package/evals/CONVENTIONS.md +58 -0
  155. package/evals/README.md +128 -0
  156. package/evals/acceptance/run.sh +29 -0
  157. package/evals/acceptance/test_claude_harness.sh +242 -0
  158. package/evals/acceptance/test_codex_harness.sh +108 -0
  159. package/evals/acceptance/test_kiro_harness.sh +128 -0
  160. package/evals/cases/dev/404.html +97 -0
  161. package/evals/cases/dev/code-review.yaml +44 -0
  162. package/evals/cases/dev/dashboard.html +300 -0
  163. package/evals/cases/dev/deliver.yaml +66 -0
  164. package/evals/cases/dev/dependency-update.yaml +16 -0
  165. package/evals/cases/dev/explore.yaml +20 -0
  166. package/evals/cases/dev/index.html +370 -0
  167. package/evals/cases/dev/package-lock.json +28 -0
  168. package/evals/cases/dev/package.json +16 -0
  169. package/evals/cases/dev/plan-work.yaml +20 -0
  170. package/evals/cases/dev/promptfooconfig.yaml +666 -0
  171. package/evals/cases/dev/search-first.yaml +20 -0
  172. package/evals/cases/dev/tdd-workflow.yaml +48 -0
  173. package/evals/cases/dev/verify-work.yaml +44 -0
  174. package/evals/cases/dev/workflow.yaml +34 -0
  175. package/evals/ci/run-baseline.sh +283 -0
  176. package/evals/fixtures/backlog-provider-settings/global-default.json +44 -0
  177. package/evals/fixtures/backlog-provider-settings/project-override.json +53 -0
  178. package/evals/fixtures/builder-kit-workflow-state/baseline-freshness-resolution-hint.json +139 -0
  179. package/evals/fixtures/builder-kit-workflow-state/direct-primitive-stop.json +59 -0
  180. package/evals/fixtures/builder-kit-workflow-state/empty-board-route-shape.json +55 -0
  181. package/evals/fixtures/builder-kit-workflow-state/happy-path.json +71 -0
  182. package/evals/fixtures/builder-kit-workflow-state/mid-work-resume.json +80 -0
  183. package/evals/fixtures/builder-kit-workflow-state/missing-prestep-recovery.json +65 -0
  184. package/evals/fixtures/builder-kit-workflow-state/product-build-chaining.json +60 -0
  185. package/evals/fixtures/builder-kit-workflow-state/stale-continuation-requires-new-probe.json +57 -0
  186. package/evals/fixtures/console-learning-projection/artifacts/console-learning-correction/learning.json +50 -0
  187. package/evals/fixtures/console-learning-projection/artifacts/console-learning-open-route/learning.json +41 -0
  188. package/evals/fixtures/flow-kit-repository/invalid-absolute-path/kit.json +8 -0
  189. package/evals/fixtures/flow-kit-repository/invalid-asset-section/flows/review.flow.json +6 -0
  190. package/evals/fixtures/flow-kit-repository/invalid-asset-section/kit.json +11 -0
  191. package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/flows/review.flow.json +6 -0
  192. package/evals/fixtures/flow-kit-repository/invalid-duplicate-flow/kit.json +9 -0
  193. package/evals/fixtures/flow-kit-repository/invalid-id/flows/review.flow.json +6 -0
  194. package/evals/fixtures/flow-kit-repository/invalid-id/kit.json +8 -0
  195. package/evals/fixtures/flow-kit-repository/invalid-malformed-json/kit.json +8 -0
  196. package/evals/fixtures/flow-kit-repository/invalid-missing-flow/kit.json +8 -0
  197. package/evals/fixtures/flow-kit-repository/invalid-missing-id/flows/review.flow.json +6 -0
  198. package/evals/fixtures/flow-kit-repository/invalid-missing-id/kit.json +7 -0
  199. package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/flows/review.flow.json +6 -0
  200. package/evals/fixtures/flow-kit-repository/invalid-missing-schema-version/kit.json +7 -0
  201. package/evals/fixtures/flow-kit-repository/invalid-name/flows/review.flow.json +6 -0
  202. package/evals/fixtures/flow-kit-repository/invalid-name/kit.json +8 -0
  203. package/evals/fixtures/flow-kit-repository/invalid-schema-version/flows/review.flow.json +6 -0
  204. package/evals/fixtures/flow-kit-repository/invalid-schema-version/kit.json +8 -0
  205. package/evals/fixtures/flow-kit-repository/invalid-traversal/kit.json +8 -0
  206. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/adapters/example.json +3 -0
  207. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/assets/example.txt +1 -0
  208. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/docs/README.md +3 -0
  209. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/flows/runtime.flow.json +26 -0
  210. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-evals/example.json +3 -0
  211. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit-skills/mixed/SKILL.md +3 -0
  212. package/evals/fixtures/flow-kit-repository/mixed-runtime-kit/kit.json +44 -0
  213. package/evals/fixtures/flow-kit-repository/valid-local-kit/docs/README.md +3 -0
  214. package/evals/fixtures/flow-kit-repository/valid-local-kit/flows/review.flow.json +26 -0
  215. package/evals/fixtures/flow-kit-repository/valid-local-kit/kit.json +20 -0
  216. package/evals/fixtures/hook-influence/cases.json +336 -0
  217. package/evals/fixtures/pull-work-provider/github-issues.json +170 -0
  218. package/evals/fixtures/pull-work-wip-shepherding/global-wip-informs.json +43 -0
  219. package/evals/fixtures/pull-work-wip-shepherding/personal-wip-blocks.json +42 -0
  220. package/evals/fixtures/surface-trust/accepted-claim-trust-report.json +31 -0
  221. package/evals/fixtures/surface-trust/artifact-absent.json +19 -0
  222. package/evals/fixtures/surface-trust/integrity-mismatch-trust-report.json +32 -0
  223. package/evals/fixtures/surface-trust/missing-authority-trust-report.json +27 -0
  224. package/evals/fixtures/surface-trust/provider-absent.json +19 -0
  225. package/evals/fixtures/surface-trust/rejected-claim-trust-report.json +30 -0
  226. package/evals/fixtures/surface-trust/stale-claim-trust-snapshot.json +31 -0
  227. package/evals/fixtures/usage-feedback/sample-full.jsonl +11 -0
  228. package/evals/fixtures/usage-feedback/sample-outcomes.jsonl +1 -0
  229. package/evals/fixtures/veritas-governance-adapter/fake-veritas-pass.sh +18 -0
  230. package/evals/fixtures/veritas-governance-adapter/fake-veritas-secret-fail.sh +10 -0
  231. package/evals/fixtures/veritas-governance-adapter/fake-veritas-unconfigured.sh +4 -0
  232. package/evals/integration/test_bundle_install.sh +541 -0
  233. package/evals/integration/test_console_learning_projection.sh +192 -0
  234. package/evals/integration/test_context_map.sh +65 -0
  235. package/evals/integration/test_effective_backlog_settings.sh +58 -0
  236. package/evals/integration/test_fixture_retirement_audit.sh +58 -0
  237. package/evals/integration/test_flow_agents_statusline.sh +93 -0
  238. package/evals/integration/test_flow_kit_repository.sh +90 -0
  239. package/evals/integration/test_goal_fit_hook.sh +482 -0
  240. package/evals/integration/test_hook_category_behaviors.sh +190 -0
  241. package/evals/integration/test_hook_influence_cases.sh +69 -0
  242. package/evals/integration/test_local_flow_kit_install.sh +145 -0
  243. package/evals/integration/test_publish_change_helper.sh +176 -0
  244. package/evals/integration/test_pull_work_provider.sh +140 -0
  245. package/evals/integration/test_runtime_adapter_activation.sh +106 -0
  246. package/evals/integration/test_telemetry.sh +485 -0
  247. package/evals/integration/test_telemetry_doctor.sh +193 -0
  248. package/evals/integration/test_usage_feedback_dashboard.sh +169 -0
  249. package/evals/integration/test_usage_feedback_global.sh +117 -0
  250. package/evals/integration/test_usage_feedback_import.sh +227 -0
  251. package/evals/integration/test_usage_feedback_outcomes.sh +165 -0
  252. package/evals/integration/test_usage_feedback_report.sh +263 -0
  253. package/evals/integration/test_veritas_governance_adapter.sh +235 -0
  254. package/evals/integration/test_workflow_artifact_cleanup_audit.sh +287 -0
  255. package/evals/integration/test_workflow_artifacts.sh +1247 -0
  256. package/evals/integration/test_workflow_sidecar_writer.sh +2112 -0
  257. package/evals/integration/test_workflow_steering_hook.sh +337 -0
  258. package/evals/lib/assertions/delegated-to.js +40 -0
  259. package/evals/lib/assertions/max-tool-calls.js +15 -0
  260. package/evals/lib/assertions/no-write-tools.js +27 -0
  261. package/evals/lib/assertions/pass-at-k.js +39 -0
  262. package/evals/lib/assertions/telemetry-utils.js +105 -0
  263. package/evals/lib/assertions/tool-called.js +39 -0
  264. package/evals/lib/assertions/verify-after-fix.js +61 -0
  265. package/evals/lib/claude-judge.sh +40 -0
  266. package/evals/lib/claude-provider.sh +74 -0
  267. package/evals/lib/codex-judge.sh +39 -0
  268. package/evals/lib/codex-provider.sh +81 -0
  269. package/evals/lib/eval-dev.sh +5 -0
  270. package/evals/lib/eval-judge.sh +22 -0
  271. package/evals/lib/eval-provider.sh +26 -0
  272. package/evals/lib/eval-report.sh +73 -0
  273. package/evals/lib/kiro-dev.sh +4 -0
  274. package/evals/lib/kiro-judge.sh +17 -0
  275. package/evals/lib/kiro-provider.sh +62 -0
  276. package/evals/lib/node.sh +111 -0
  277. package/evals/promptfooconfig.yaml +70 -0
  278. package/evals/run.sh +309 -0
  279. package/evals/static/test_evidence_refs.sh +141 -0
  280. package/evals/static/test_package.sh +407 -0
  281. package/evals/static/test_repo_hooks.sh +68 -0
  282. package/evals/static/test_universal_bundles.sh +274 -0
  283. package/evals/static/test_workflow_skills.sh +1207 -0
  284. package/install.sh +64 -0
  285. package/integrations/veritas/flow-agents.adapter.json +138 -0
  286. package/integrations/veritas/flow-agents.authority-settings.json +26 -0
  287. package/integrations/veritas/flow-agents.repo-standards.json +82 -0
  288. package/kits/builder/flows/build.flow.json +218 -0
  289. package/kits/builder/flows/shape.flow.json +127 -0
  290. package/kits/builder/kit.json +19 -0
  291. package/kits/catalog.json +11 -0
  292. package/package.json +130 -0
  293. package/packaging/README.md +60 -0
  294. package/packaging/manifest.json +173 -0
  295. package/packaging/packs.json +69 -0
  296. package/powers/dependency-checker/POWER.md +20 -0
  297. package/powers/dependency-checker/mcp.json +20 -0
  298. package/powers/playwright/POWER.md +25 -0
  299. package/powers/playwright/mcp.json +12 -0
  300. package/prompts/code-audit.md +123 -0
  301. package/prompts/kcommit.md +88 -0
  302. package/schemas/backlog-provider-settings.schema.json +138 -0
  303. package/schemas/workflow-acceptance.schema.json +216 -0
  304. package/schemas/workflow-critique.schema.json +113 -0
  305. package/schemas/workflow-evidence.schema.json +357 -0
  306. package/schemas/workflow-handoff.schema.json +52 -0
  307. package/schemas/workflow-learning.schema.json +223 -0
  308. package/schemas/workflow-release.schema.json +172 -0
  309. package/schemas/workflow-state.schema.json +80 -0
  310. package/scripts/README.md +111 -0
  311. package/scripts/build-universal-bundles.js +3 -0
  312. package/scripts/check-content-boundary.cjs +99 -0
  313. package/scripts/context-budget/budget-scan.sh +166 -0
  314. package/scripts/detect-tools.sh +3 -0
  315. package/scripts/discover-agents.sh +28 -0
  316. package/scripts/effective-backlog-settings.js +2 -0
  317. package/scripts/filter-installed-packs.js +2 -0
  318. package/scripts/flow-kit.js +2 -0
  319. package/scripts/generate-context-map.js +2 -0
  320. package/scripts/git-status.sh +49 -0
  321. package/scripts/hooks/claude-hook-adapter.js +174 -0
  322. package/scripts/hooks/claude-telemetry-hook.js +115 -0
  323. package/scripts/hooks/codex-hook-adapter.js +176 -0
  324. package/scripts/hooks/codex-telemetry-hook.js +95 -0
  325. package/scripts/hooks/config-protection.js +79 -0
  326. package/scripts/hooks/desktop-notify.sh +39 -0
  327. package/scripts/hooks/governance-audit.sh +135 -0
  328. package/scripts/hooks/lib/audit-transport.sh +40 -0
  329. package/scripts/hooks/lib/hook-flags.js +49 -0
  330. package/scripts/hooks/lib/patterns.sh +57 -0
  331. package/scripts/hooks/lib/resolve-formatter.js +80 -0
  332. package/scripts/hooks/post-edit-accumulator.js +66 -0
  333. package/scripts/hooks/pre-commit-quality.js +194 -0
  334. package/scripts/hooks/quality-gate.js +93 -0
  335. package/scripts/hooks/report-only-guard.js +21 -0
  336. package/scripts/hooks/run-hook.js +136 -0
  337. package/scripts/hooks/stop-format-typecheck.js +141 -0
  338. package/scripts/hooks/stop-goal-fit.js +337 -0
  339. package/scripts/hooks/workflow-steering.js +250 -0
  340. package/scripts/install-codex-home.sh +106 -0
  341. package/scripts/package.json +3 -0
  342. package/scripts/promote-workflow-artifact.js +2 -0
  343. package/scripts/publish-change-helper.js +2 -0
  344. package/scripts/pull-work-provider.js +2 -0
  345. package/scripts/setup-repo-hooks.sh +8 -0
  346. package/scripts/statusline/flow-agents-statusline.js +157 -0
  347. package/scripts/telemetry/console-presets.sh +14 -0
  348. package/scripts/telemetry/install-console-config.sh +214 -0
  349. package/scripts/telemetry/lib/config.sh +85 -0
  350. package/scripts/telemetry/lib/enrich.sh +115 -0
  351. package/scripts/telemetry/lib/redact.sh +22 -0
  352. package/scripts/telemetry/lib/session.sh +63 -0
  353. package/scripts/telemetry/lib/transport.sh +183 -0
  354. package/scripts/telemetry/lib/usage.sh +29 -0
  355. package/scripts/telemetry/sync-agents.sh +173 -0
  356. package/scripts/telemetry/telemetry.conf +23 -0
  357. package/scripts/telemetry/telemetry.sh +387 -0
  358. package/scripts/usage-feedback.js +2 -0
  359. package/scripts/validate-hook-influence-cases.js +2 -0
  360. package/scripts/validate-package.sh +89 -0
  361. package/scripts/validate-source-tree.js +9 -0
  362. package/skills/agentic-engineering/SKILL.md +62 -0
  363. package/skills/browser-test/SKILL.md +51 -0
  364. package/skills/builder-shape/SKILL.md +76 -0
  365. package/skills/context-budget/SKILL.md +40 -0
  366. package/skills/deliver/SKILL.md +241 -0
  367. package/skills/dependency-update/SKILL.md +68 -0
  368. package/skills/design-probe/SKILL.md +107 -0
  369. package/skills/eval-rebuild/SKILL.md +39 -0
  370. package/skills/evidence-gate/SKILL.md +186 -0
  371. package/skills/execute-plan/SKILL.md +110 -0
  372. package/skills/explore/SKILL.md +137 -0
  373. package/skills/feedback-loop/SKILL.md +87 -0
  374. package/skills/fix-bug/SKILL.md +133 -0
  375. package/skills/frontend-design/SKILL.md +80 -0
  376. package/skills/github-cli/SKILL.md +63 -0
  377. package/skills/idea-to-backlog/SKILL.md +267 -0
  378. package/skills/knowledge-capture/SKILL.md +55 -0
  379. package/skills/learning-review/SKILL.md +115 -0
  380. package/skills/pickup-probe/SKILL.md +114 -0
  381. package/skills/plan-work/SKILL.md +176 -0
  382. package/skills/pull-work/SKILL.md +309 -0
  383. package/skills/release-readiness/SKILL.md +121 -0
  384. package/skills/review-work/SKILL.md +161 -0
  385. package/skills/search-first/SKILL.md +66 -0
  386. package/skills/tdd-workflow/SKILL.md +140 -0
  387. package/skills/verify-work/SKILL.md +109 -0
  388. package/src/cli/console-learning-projection.ts +140 -0
  389. package/src/cli/effective-backlog-settings.ts +99 -0
  390. package/src/cli/fixture-retirement-audit.ts +154 -0
  391. package/src/cli/flow-kit.ts +139 -0
  392. package/src/cli/init.ts +248 -0
  393. package/src/cli/promote-workflow-artifact.ts +64 -0
  394. package/src/cli/publish-change-helper.ts +143 -0
  395. package/src/cli/pull-work-provider.ts +481 -0
  396. package/src/cli/runtime-adapter.ts +24 -0
  397. package/src/cli/telemetry-doctor.ts +243 -0
  398. package/src/cli/usage-feedback.ts +418 -0
  399. package/src/cli/validate-hook-influence.ts +119 -0
  400. package/src/cli/validate-source-tree.ts +30 -0
  401. package/src/cli/validate-workflow-artifacts.ts +411 -0
  402. package/src/cli/veritas-governance.ts +322 -0
  403. package/src/cli/workflow-artifact-cleanup-audit.ts +281 -0
  404. package/src/cli/workflow-sidecar.ts +676 -0
  405. package/src/cli.ts +95 -0
  406. package/src/flow-kit/validate.ts +74 -0
  407. package/src/lib/args.ts +43 -0
  408. package/src/lib/fs.ts +62 -0
  409. package/src/lib/workflow-learning-projection.ts +491 -0
  410. package/src/runtime-adapters.ts +154 -0
  411. package/src/tools/build-universal-bundles.ts +366 -0
  412. package/src/tools/common.ts +61 -0
  413. package/src/tools/filter-installed-packs.ts +129 -0
  414. package/src/tools/generate-context-map.ts +199 -0
  415. package/src/tools/validate-package.ts +57 -0
  416. package/src/tools/validate-source-tree.ts +488 -0
  417. package/tsconfig.json +19 -0
  418. package/veritas.claims.json +6 -0
@@ -0,0 +1,622 @@
1
+ #!/usr/bin/env node
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { loadJson, readText, rel, root, walkFiles } from "./common.js";
6
+ class Reporter {
7
+ errors = [];
8
+ fail(message) { this.errors.push(message); }
9
+ check(condition, message) { if (!condition)
10
+ this.fail(message); }
11
+ }
12
+ const manifestPath = path.join(root, "packaging/manifest.json");
13
+ const packsPath = path.join(root, "packaging/packs.json");
14
+ const kitsCatalogPath = path.join(root, "kits/catalog.json");
15
+ const flowRoot = process.env.FLOW_CLI_ROOT ? path.resolve(process.env.FLOW_CLI_ROOT) : "";
16
+ const flowSchemaPath = flowRoot ? path.join(flowRoot, "schemas", "flow-definition.schema.json") : "";
17
+ const flowCliPath = flowRoot ? ["dist/cli.js", "src/cli.js"].map((candidate) => path.join(flowRoot, candidate)).find((candidate) => fs.existsSync(candidate)) ?? path.join(flowRoot, "dist/cli.js") : "";
18
+ const kitIdRe = /^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)*$/;
19
+ const kitAssetSections = new Set(["skills", "docs", "adapters", "evals", "assets"]);
20
+ const kitTopLevelKeys = new Set(["schema_version", "id", "name", "product_name", "description", "flows", ...kitAssetSections]);
21
+ const textRefExtensions = new Set([".md", ".yaml", ".yml", ".json", ".sh", ".js", ".toml"]);
22
+ const ignoredRefDirs = new Set(["node_modules", "__pycache__", ".pytest_cache", ".cache"]);
23
+ const legacyRefRe = /(?<![A-Za-z0-9_.-])(?:agents|agent-cards|context|evals|lib|powers|prompts|scripts|skills)\/[A-Za-z0-9_./@:+-]+/g;
24
+ const mirroredFiles = new Map([
25
+ ["scripts/telemetry/telemetry.sh", { mirror: "context/scripts/telemetry/telemetry.sh", allowedDifferences: [] }],
26
+ ["scripts/telemetry/lib/config.sh", { mirror: "context/scripts/telemetry/lib/config.sh", allowedDifferences: [] }],
27
+ ["scripts/telemetry/telemetry.conf", { mirror: "context/scripts/telemetry/telemetry.conf", allowedDifferences: [] }],
28
+ ["scripts/telemetry/console-presets.sh", { mirror: "context/scripts/telemetry/console-presets.sh", allowedDifferences: [] }],
29
+ ["scripts/telemetry/install-console-config.sh", { mirror: "context/scripts/telemetry/install-console-config.sh", allowedDifferences: [] }],
30
+ ["scripts/discover-agents.sh", { mirror: "context/scripts/discover-agents.sh", allowedDifferences: [['ROOT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"', 'ROOT_DIR="$(cd "$SCRIPT_DIR/../.." && pwd)"']] }],
31
+ ]);
32
+ const publicScriptWrappers = new Map([
33
+ ["scripts/build-universal-bundles.js", { target: "../build/src/tools/build-universal-bundles.js", significantLines: [
34
+ "// Supports FLOW_AGENTS_PACKS through the TypeScript bundle builder.",
35
+ 'import("../build/src/tools/build-universal-bundles.js").then(({ main }) => process.exit(main()));',
36
+ ] }],
37
+ ["scripts/filter-installed-packs.js", { target: "../build/src/tools/filter-installed-packs.js", significantLines: ['import("../build/src/tools/filter-installed-packs.js").then(({ main }) => process.exit(main(process.argv.slice(2))));'] }],
38
+ ["scripts/generate-context-map.js", { target: "../build/src/tools/generate-context-map.js", significantLines: ['import("../build/src/tools/generate-context-map.js").then(({ main }) => process.exit(main(process.argv.slice(2))));'] }],
39
+ ["scripts/flow-kit.js", { target: "../build/src/cli/flow-kit.js", significantLines: ['import("../build/src/cli/flow-kit.js").then(({ main }) => process.exit(main()));'] }],
40
+ ["scripts/pull-work-provider.js", { target: "../build/src/cli/pull-work-provider.js", significantLines: ['import("../build/src/cli/pull-work-provider.js").then(({ main }) => process.exit(main()));'] }],
41
+ ["scripts/effective-backlog-settings.js", { target: "../build/src/cli/effective-backlog-settings.js", significantLines: ['import("../build/src/cli/effective-backlog-settings.js").then(({ main }) => process.exit(main()));'] }],
42
+ ["scripts/publish-change-helper.js", { target: "../build/src/cli/publish-change-helper.js", significantLines: ['import("../build/src/cli/publish-change-helper.js").then(({ main }) => process.exit(main()));'] }],
43
+ ["scripts/promote-workflow-artifact.js", { target: "../build/src/cli/promote-workflow-artifact.js", significantLines: ['import("../build/src/cli/promote-workflow-artifact.js").then(({ main }) => process.exit(main()));'] }],
44
+ ["scripts/usage-feedback.js", { target: "../build/src/cli/usage-feedback.js", significantLines: ['import("../build/src/cli/usage-feedback.js").then(({ main }) => process.exit(main()));'] }],
45
+ ["scripts/validate-hook-influence-cases.js", { target: "../build/src/cli/validate-hook-influence.js", significantLines: ['import("../build/src/cli/validate-hook-influence.js").then(({ main }) => process.exit(main(process.argv.slice(2))));'] }],
46
+ ["scripts/validate-source-tree.js", { target: "validate:source", significantLines: [
47
+ 'import("node:child_process").then(({ spawnSync }) => {',
48
+ 'const result = spawnSync("npm", ["run", "validate:source", "--silent", "--", ...process.argv.slice(2)], {',
49
+ 'cwd: new URL("..", import.meta.url),',
50
+ 'encoding: "utf8",',
51
+ 'stdio: "inherit",',
52
+ '});',
53
+ 'process.exit(result.status ?? 1);',
54
+ '});',
55
+ ] }],
56
+ ]);
57
+ const hookFilePolicies = new Map([
58
+ ["scripts/hooks/claude-hook-adapter.js", { category: "runtime adapter", requiredNeedles: ["claude", "run-hook.js"] }],
59
+ ["scripts/hooks/codex-hook-adapter.js", { category: "runtime adapter", requiredNeedles: ["codex", "run-hook.js"] }],
60
+ ["scripts/hooks/claude-telemetry-hook.js", { category: "telemetry shim", requiredNeedles: ["claude", "telemetry"] }],
61
+ ["scripts/hooks/codex-telemetry-hook.js", { category: "telemetry shim", requiredNeedles: ["codex", "telemetry"] }],
62
+ ["scripts/hooks/run-hook.js", { category: "hook runner", requiredNeedles: ["isHookEnabled", "Path traversal rejected"] }],
63
+ ["scripts/hooks/config-protection.js", { category: "policy hook", requiredNeedles: ["Config Protection Hook"] }],
64
+ ["scripts/hooks/governance-audit.sh", { category: "policy hook", requiredNeedles: ["governance-audit.sh", "audit_emit"] }],
65
+ ["scripts/hooks/post-edit-accumulator.js", { category: "policy hook", requiredNeedles: ["Post-Edit"] }],
66
+ ["scripts/hooks/pre-commit-quality.js", { category: "repo guardrail hook", requiredNeedles: ["staged"] }],
67
+ ["scripts/hooks/quality-gate.js", { category: "policy hook", requiredNeedles: ["Quality"] }],
68
+ ["scripts/hooks/report-only-guard.js", { category: "policy hook", requiredNeedles: ["Report-Only Guard Hook"] }],
69
+ ["scripts/hooks/stop-format-typecheck.js", { category: "policy hook", requiredNeedles: ["Stop Hook", "typecheck"] }],
70
+ ["scripts/hooks/stop-goal-fit.js", { category: "policy hook", requiredNeedles: ["Stop Hook", "Goal Fit"] }],
71
+ ["scripts/hooks/workflow-steering.js", { category: "policy hook", requiredNeedles: ["Workflow Steering Hook"] }],
72
+ ["scripts/hooks/desktop-notify.sh", { category: "local notification helper", requiredNeedles: ["desktop-notify.sh", "osascript"] }],
73
+ ["scripts/hooks/lib/audit-transport.sh", { category: "shared hook library", requiredNeedles: ["audit_emit"] }],
74
+ ["scripts/hooks/lib/hook-flags.js", { category: "shared hook library", requiredNeedles: ["isHookEnabled"] }],
75
+ ["scripts/hooks/lib/patterns.sh", { category: "shared hook library", requiredNeedles: ["_detect_secrets"] }],
76
+ ["scripts/hooks/lib/resolve-formatter.js", { category: "shared hook library", requiredNeedles: ["resolveFormatter"] }],
77
+ ]);
78
+ const fixtureOwnerPolicies = new Map([
79
+ ["evals/fixtures/backlog-provider-settings", { owners: ["evals/integration/test_effective_backlog_settings.sh"], classification: "settings precedence fixtures" }],
80
+ ["evals/fixtures/builder-kit-workflow-state", { owners: ["evals/static/test_workflow_skills.sh"], classification: "Builder Kit workflow-state fixtures" }],
81
+ ["evals/fixtures/console-learning-projection", { owners: ["evals/integration/test_console_learning_projection.sh"], classification: "console learning projection fixtures" }],
82
+ ["evals/fixtures/flow-kit-repository", { owners: ["evals/integration/test_flow_kit_repository.sh", "evals/integration/test_local_flow_kit_install.sh", "evals/integration/test_runtime_adapter_activation.sh", "evals/static/test_workflow_skills.sh"], classification: "Flow Kit repository contract fixtures" }],
83
+ ["evals/fixtures/hook-influence", { owners: ["evals/integration/test_hook_influence_cases.sh", "evals/static/test_workflow_skills.sh", "scripts/validate-hook-influence-cases.js"], classification: "hook influence behavioral cases" }],
84
+ ["evals/fixtures/pull-work-provider", { owners: ["evals/integration/test_pull_work_provider.sh"], classification: "work item provider normalization fixtures" }],
85
+ ["evals/fixtures/pull-work-wip-shepherding", { owners: ["evals/static/test_workflow_skills.sh"], classification: "WIP shepherding state fixtures" }],
86
+ ["evals/fixtures/surface-trust", { owners: ["evals/integration/test_workflow_sidecar_writer.sh"], classification: "Surface trust evidence fixtures" }],
87
+ ["evals/fixtures/usage-feedback", { owners: ["evals/integration/test_usage_feedback_import.sh", "evals/integration/test_usage_feedback_outcomes.sh", "evals/integration/test_usage_feedback_report.sh"], classification: "usage feedback import/outcome fixtures" }],
88
+ ["evals/fixtures/veritas-governance-adapter", { owners: ["evals/integration/test_veritas_governance_adapter.sh"], classification: "Veritas governance adapter fixtures" }],
89
+ ]);
90
+ const requiredUsageFeedbackFiles = [
91
+ "package.json", "tsconfig.json", "scripts/usage-feedback.js", "src/cli/usage-feedback.ts", "docs/agent-usage-feedback-loop.md",
92
+ "scripts/hooks/stop-goal-fit.js", "scripts/promote-workflow-artifact.js", "evals/integration/test_goal_fit_hook.sh",
93
+ ];
94
+ const fixtureOwnershipSelfAuditRefs = new Set([
95
+ "evals/integration/test_fixture_retirement_audit.sh",
96
+ ]);
97
+ const pythonInventoryExcludes = new Set([".git", ".flow-agents", "node_modules", ".venv", "dist", "__pycache__", ".pytest_cache", ".cache", "build"]);
98
+ const pythonCommandScanRoots = ["README.md", "docs", "context", "skills", "prompts", "agents", "evals", "scripts", "packaging", "package.json"];
99
+ const allowedPythonCommandFiles = [
100
+ /^agents\/tool-explore-deps\.json$/,
101
+ /^evals\/results\//,
102
+ /^evals\/lib\/python\.sh$/,
103
+ /(^|\/)telemetry\/lib\/enrich\.sh$/,
104
+ ];
105
+ function tryLoadJson(file, reporter) {
106
+ try {
107
+ return loadJson(file);
108
+ }
109
+ catch (error) {
110
+ reporter.fail(`${rel(file)}: invalid JSON: ${error instanceof Error ? error.message : String(error)}`);
111
+ return undefined;
112
+ }
113
+ }
114
+ function sourcePath(pathText, manifest) {
115
+ let normalized = pathText;
116
+ for (const alias of manifest.source_root_aliases ?? [])
117
+ normalized = normalized.split(alias).join(root);
118
+ normalized = normalized.replace(/^~/, process.env.HOME ?? "");
119
+ return path.isAbsolute(normalized) ? normalized : path.join(root, normalized);
120
+ }
121
+ function validateAgents(reporter) {
122
+ const names = new Set();
123
+ const files = fs.readdirSync(path.join(root, "agents")).filter((name) => name.endsWith(".json")).sort();
124
+ const allStems = new Set(files.map((name) => path.basename(name, ".json")));
125
+ for (const name of files) {
126
+ const file = path.join(root, "agents", name);
127
+ const data = tryLoadJson(file, reporter);
128
+ if (!data || typeof data !== "object")
129
+ continue;
130
+ const agentName = data.name;
131
+ reporter.check(typeof agentName === "string" && !!agentName, `${rel(file)}: missing .name`);
132
+ if (typeof agentName !== "string" || !agentName)
133
+ continue;
134
+ names.add(agentName);
135
+ reporter.check(path.basename(file, ".json") === agentName, `${rel(file)}: filename must match agent name '${agentName}'`);
136
+ reporter.check(/^[a-z][a-z0-9-]*$/.test(agentName), `${rel(file)}: invalid agent name '${agentName}'`);
137
+ for (const key of ["description", "prompt", "model"])
138
+ reporter.check(typeof data[key] === "string" && !!data[key], `${rel(file)}: missing .${key}`);
139
+ if ("allowedTools" in data)
140
+ reporter.check(Array.isArray(data.allowedTools), `${rel(file)}: .allowedTools must be a list`);
141
+ for (const pattern of data.toolsSettings?.subagent?.availableAgents ?? []) {
142
+ if (typeof pattern !== "string") {
143
+ reporter.fail(`${rel(file)}: subagent pattern is not a string`);
144
+ continue;
145
+ }
146
+ const regex = new RegExp(`^${pattern.replace(/[.*+?^${}()|[\]\\]/g, "\\$&").replace(/\\\*/g, ".*")}$`);
147
+ reporter.check([...new Set([...names, ...allStems])].some((candidate) => regex.test(candidate)), `${rel(file)}: subagent pattern '${pattern}' matches no canonical agents`);
148
+ }
149
+ }
150
+ return names;
151
+ }
152
+ function validateAgentCards(reporter, agentNames) {
153
+ for (const file of walkFiles(path.join(root, "agent-cards")).filter((item) => item.endsWith(".json")).sort()) {
154
+ const data = tryLoadJson(file, reporter);
155
+ if (!data || typeof data !== "object")
156
+ continue;
157
+ reporter.check(typeof data.name === "string" && !!data.name, `${rel(file)}: missing .name`);
158
+ reporter.check(typeof data.agent === "string" && !!data.agent, `${rel(file)}: missing .agent`);
159
+ if (typeof data.agent === "string")
160
+ reporter.check(agentNames.has(data.agent), `${rel(file)}: agent '${data.agent}' has no canonical agents/${data.agent}.json`);
161
+ }
162
+ }
163
+ function validatePowers(reporter) {
164
+ for (const file of walkFiles(path.join(root, "powers")).filter((item) => item.endsWith("/mcp.json")).sort()) {
165
+ const data = tryLoadJson(file, reporter);
166
+ reporter.check(fs.existsSync(path.join(path.dirname(file), "POWER.md")), `${rel(path.dirname(file))}: missing POWER.md`);
167
+ const servers = data?.mcpServers;
168
+ reporter.check(Boolean(servers && typeof servers === "object" && Object.keys(servers).length > 0), `${rel(file)}: missing .mcpServers`);
169
+ for (const [serverName, server] of Object.entries(servers ?? {})) {
170
+ reporter.check(Boolean(server && typeof server === "object"), `${rel(file)}: mcpServers.${serverName} must be an object`);
171
+ reporter.check(typeof server?.command === "string" && !!server.command, `${rel(file)}: mcpServers.${serverName} missing .command`);
172
+ }
173
+ }
174
+ }
175
+ function validateManifest(reporter, manifest, agentNames) {
176
+ for (const key of ["canonical_copy_dirs", "source_root_aliases", "target_substitutions"])
177
+ reporter.check(key in manifest, `${rel(manifestPath)}: missing .${key}`);
178
+ for (const dir of manifest.canonical_copy_dirs ?? [])
179
+ reporter.check(fs.existsSync(path.join(root, dir)), `${rel(manifestPath)}: canonical_copy_dirs entry missing: ${dir}`);
180
+ for (const file of manifest.root_copy_files ?? []) {
181
+ reporter.check(typeof file === "string" && !path.isAbsolute(file) && !file.split(/[\\/]/).includes(".."), `${rel(manifestPath)}: root_copy_files entry must be a safe relative path: ${file}`);
182
+ if (typeof file === "string" && !path.isAbsolute(file) && !file.split(/[\\/]/).includes("..")) {
183
+ reporter.check(fs.existsSync(path.join(root, file)), `${rel(manifestPath)}: root_copy_files entry missing: ${file}`);
184
+ }
185
+ }
186
+ for (const dir of manifest.optional_copy_dirs ?? [])
187
+ if (!fs.existsSync(path.join(root, dir)))
188
+ console.log(`warning: ${rel(manifestPath)} optional_copy_dirs entry absent: ${dir}`);
189
+ for (const agent of manifest.codex?.excluded_agents ?? [])
190
+ reporter.check(agentNames.has(agent), `${rel(manifestPath)}: codex excluded agent '${agent}' does not exist`);
191
+ }
192
+ function validatePacksManifest(reporter, agentNames) {
193
+ const data = tryLoadJson(packsPath, reporter);
194
+ if (!data || typeof data !== "object")
195
+ return;
196
+ reporter.check(data.schema_version === "1.0", `${rel(packsPath)}: schema_version must be 1.0`);
197
+ reporter.check(Array.isArray(data.packs) && data.packs.length > 0, `${rel(packsPath)}: packs must be a non-empty list`);
198
+ const skillNames = new Set(fs.readdirSync(path.join(root, "skills")).filter((name) => fs.existsSync(path.join(root, "skills", name, "SKILL.md"))));
199
+ const powerNames = new Set(fs.readdirSync(path.join(root, "powers")).filter((name) => fs.existsSync(path.join(root, "powers", name, "POWER.md"))));
200
+ const names = new Set();
201
+ const defaults = new Set();
202
+ const assigned = { skills: new Set(), agents: new Set(), powers: new Set() };
203
+ (Array.isArray(data.packs) ? data.packs : []).forEach((pack, index) => {
204
+ const name = pack?.name;
205
+ if (typeof name !== "string" || !/^[a-z][a-z0-9-]*$/.test(name)) {
206
+ reporter.fail(`${rel(packsPath)}: packs[${index}].name must be a kebab-case string`);
207
+ return;
208
+ }
209
+ if (names.has(name))
210
+ reporter.fail(`${rel(packsPath)}: duplicate pack name '${name}'`);
211
+ names.add(name);
212
+ if (pack.default === true)
213
+ defaults.add(name);
214
+ reporter.check(typeof pack.description === "string" && !!pack.description, `${rel(packsPath)}: pack '${name}' missing description`);
215
+ for (const [field, available] of [["skills", skillNames], ["agents", agentNames], ["powers", powerNames]]) {
216
+ const values = pack[field] ?? [];
217
+ reporter.check(Array.isArray(values), `${rel(packsPath)}: pack '${name}' .${field} must be a list`);
218
+ const seen = new Set();
219
+ for (const value of Array.isArray(values) ? values : []) {
220
+ if (typeof value !== "string") {
221
+ reporter.fail(`${rel(packsPath)}: pack '${name}' .${field} entry is not a string`);
222
+ continue;
223
+ }
224
+ if (seen.has(value))
225
+ reporter.fail(`${rel(packsPath)}: pack '${name}' has duplicate ${field} entry '${value}'`);
226
+ seen.add(value);
227
+ assigned[field].add(value);
228
+ reporter.check(available.has(value), `${rel(packsPath)}: pack '${name}' references missing ${field.slice(0, -1)} '${value}'`);
229
+ }
230
+ }
231
+ });
232
+ reporter.check(defaults.has("core"), `${rel(packsPath)}: core pack must be default`);
233
+ const missingSkills = [...skillNames].filter((name) => !assigned.skills.has(name)).sort();
234
+ reporter.check(missingSkills.length === 0, `${rel(packsPath)}: skills missing from all packs: ${missingSkills.join(", ")}`);
235
+ }
236
+ function safeLocalPath(baseDir, pathText, label, reporter) {
237
+ if (typeof pathText !== "string" || !pathText) {
238
+ reporter.fail(`${label} must be a non-empty relative path`);
239
+ return undefined;
240
+ }
241
+ if (path.isAbsolute(pathText)) {
242
+ reporter.fail(`${label} must be relative; absolute paths are not allowed`);
243
+ return undefined;
244
+ }
245
+ if (pathText.split(/[\\/]/).includes("..")) {
246
+ reporter.fail(`${label} must stay inside the kit directory; '..' path traversal is not allowed`);
247
+ return undefined;
248
+ }
249
+ return path.join(baseDir, pathText);
250
+ }
251
+ function validateFlowDefinitionShape(file, data, reporter) {
252
+ const localCli = flowCliPath;
253
+ if (fs.existsSync(localCli)) {
254
+ const result = spawnSync("node", [localCli, "validate-definition", file, "--json"], { encoding: "utf8" });
255
+ if (result.status !== 0)
256
+ reporter.fail(`${rel(file)}: Flow validation failed: ${(result.stderr || result.stdout).trim()}`);
257
+ return;
258
+ }
259
+ if (!data || typeof data !== "object") {
260
+ reporter.fail(`${rel(file)}: Flow Definition must be an object`);
261
+ return;
262
+ }
263
+ for (const key of ["id", "version", "steps", "gates"])
264
+ reporter.check(key in data, `${rel(file)}: missing .${key}`);
265
+ }
266
+ function validateKitRepository(kitDir, reporter) {
267
+ if (!fs.existsSync(kitDir) || !fs.statSync(kitDir).isDirectory()) {
268
+ reporter.fail(`${rel(kitDir)}: kit directory does not exist`);
269
+ return;
270
+ }
271
+ const kitJson = path.join(kitDir, "kit.json");
272
+ reporter.check(fs.existsSync(kitJson), `${rel(kitDir)}: missing kit.json at repository root`);
273
+ if (!fs.existsSync(kitJson))
274
+ return;
275
+ const data = tryLoadJson(kitJson, reporter);
276
+ if (!data || typeof data !== "object")
277
+ return;
278
+ const unknownKeys = Object.keys(data).filter((key) => !kitTopLevelKeys.has(key)).sort();
279
+ if (unknownKeys.length)
280
+ reporter.fail(`${rel(kitJson)}: unsupported fields ${unknownKeys.join(", ")}; remove them or add them to the Flow Kit Repository contract`);
281
+ reporter.check(data.schema_version === "1.0", `${rel(kitJson)}: .schema_version must be "1.0"`);
282
+ reporter.check(typeof data.id === "string" && kitIdRe.test(data.id), `${rel(kitJson)}: .id must be a stable kebab-case string`);
283
+ reporter.check(typeof data.name === "string" && !!data.name.trim(), `${rel(kitJson)}: .name must be a non-empty string`);
284
+ for (const section of [...kitAssetSections].sort())
285
+ if (section in data) {
286
+ if (!Array.isArray(data[section])) {
287
+ reporter.fail(`${rel(kitJson)}: .${section} must be a list of relative asset paths or objects with path`);
288
+ continue;
289
+ }
290
+ const seenPaths = new Set();
291
+ const seenIds = new Set();
292
+ data[section].forEach((entry, index) => {
293
+ const pathValue = typeof entry === "string" ? entry : entry?.path;
294
+ const assetId = typeof entry === "object" ? entry.id : undefined;
295
+ if (typeof entry === "object") {
296
+ const unknown = Object.keys(entry).filter((key) => !["id", "path", "description"].includes(key)).sort();
297
+ if (unknown.length)
298
+ reporter.fail(`${rel(kitJson)}: ${section}[${index}] has unsupported fields ${unknown.join(", ")}; use id, path, or description`);
299
+ }
300
+ if (assetId !== undefined && (typeof assetId !== "string" || !kitIdRe.test(assetId)))
301
+ reporter.fail(`${rel(kitJson)}: ${section}[${index}].id must be a stable dot/kebab-case string`);
302
+ const assetPath = safeLocalPath(kitDir, pathValue, `${rel(kitJson)}: ${section}[${index}].path`, reporter);
303
+ if (!assetPath)
304
+ return;
305
+ if (seenPaths.has(String(pathValue)))
306
+ reporter.fail(`${rel(kitJson)}: ${section}[${index}].path duplicates '${pathValue}'; declare each asset once`);
307
+ seenPaths.add(String(pathValue));
308
+ if (typeof assetId === "string") {
309
+ if (seenIds.has(assetId))
310
+ reporter.fail(`${rel(kitJson)}: ${section}[${index}].id duplicates '${assetId}'; use a unique asset id`);
311
+ seenIds.add(assetId);
312
+ }
313
+ reporter.check(fs.existsSync(assetPath), `${rel(kitJson)}: ${section}[${index}].path points at missing asset: ${pathValue}; add the file or remove the entry`);
314
+ });
315
+ }
316
+ if (!Array.isArray(data.flows) || !data.flows.length) {
317
+ reporter.fail(`${rel(kitJson)}: .flows must be a non-empty list; add at least one Flow Definition entry`);
318
+ return;
319
+ }
320
+ const seenIds = new Set();
321
+ const seenPaths = new Set();
322
+ data.flows.forEach((flow, index) => {
323
+ if (!flow || typeof flow !== "object") {
324
+ reporter.fail(`${rel(kitJson)}: flows[${index}] must be an object with id and path`);
325
+ return;
326
+ }
327
+ if (typeof flow.id !== "string" || !kitIdRe.test(flow.id))
328
+ reporter.fail(`${rel(kitJson)}: flows[${index}].id must be a stable dot/kebab-case string`);
329
+ else if (seenIds.has(flow.id))
330
+ reporter.fail(`${rel(kitJson)}: flows[${index}].id duplicates '${flow.id}'; use a unique Flow id`);
331
+ else
332
+ seenIds.add(flow.id);
333
+ const flowPath = safeLocalPath(kitDir, flow.path, `${rel(kitJson)}: flows[${index}].path`, reporter);
334
+ if (!flowPath)
335
+ return;
336
+ if (seenPaths.has(String(flow.path))) {
337
+ reporter.fail(`${rel(kitJson)}: flows[${index}].path duplicates '${flow.path}'; declare each Flow Definition once`);
338
+ return;
339
+ }
340
+ seenPaths.add(String(flow.path));
341
+ reporter.check(fs.existsSync(flowPath), `${rel(kitJson)}: flows[${index}].path points at missing Flow Definition: ${flow.path}; add the file or fix the path`);
342
+ if (fs.existsSync(flowPath))
343
+ validateFlowDefinitionShape(flowPath, tryLoadJson(flowPath, reporter), reporter);
344
+ });
345
+ }
346
+ function validateKits(reporter) {
347
+ reporter.check(fs.existsSync(path.join(root, "kits")), "kits directory missing");
348
+ const catalog = tryLoadJson(kitsCatalogPath, reporter);
349
+ const kits = catalog?.kits;
350
+ reporter.check(Array.isArray(kits) && kits.length > 0, `${rel(kitsCatalogPath)}: .kits must be a non-empty list`);
351
+ if (!Array.isArray(kits))
352
+ return;
353
+ const localCli = flowCliPath;
354
+ if (flowSchemaPath && fs.existsSync(flowSchemaPath))
355
+ console.log(fs.existsSync(localCli) ? `info: validating kit Flow Definitions with Flow CLI at ${localCli}` : `warning: Flow validator unavailable; source-tree check only verifies Flow Definition top-level shape`);
356
+ else
357
+ console.log("warning: Flow schema not configured; source-tree check only verifies Flow Definition top-level shape. Set FLOW_CLI_ROOT to enable Flow CLI validation.");
358
+ kits.forEach((entry, index) => {
359
+ const kitText = typeof entry === "string" ? entry : ["path", "directory", "dir", "id", "name"].map((key) => entry?.[key]).find((value) => typeof value === "string" && value);
360
+ if (!kitText) {
361
+ reporter.fail(`${rel(kitsCatalogPath)}: kits[${index}] missing path, directory, dir, id, or name`);
362
+ return;
363
+ }
364
+ const kitRef = String(kitText).startsWith("kits/") ? path.join(root, kitText) : path.join(root, "kits", kitText);
365
+ const kitDir = path.basename(kitRef) === "kit.json" ? path.dirname(kitRef) : kitRef;
366
+ reporter.check(fs.existsSync(kitDir) && fs.statSync(kitDir).isDirectory(), `${rel(kitsCatalogPath)}: kits[${index}] points at missing kit folder: ${kitText}`);
367
+ validateKitRepository(kitDir, reporter);
368
+ });
369
+ }
370
+ function validateAgentPaths(reporter, manifest) {
371
+ for (const file of walkFiles(path.join(root, "agents")).filter((item) => item.endsWith(".json"))) {
372
+ const data = tryLoadJson(file, reporter);
373
+ for (const [idx, resource] of (data?.resources ?? []).entries()) {
374
+ const source = typeof resource === "object" ? resource.source : resource;
375
+ if (typeof source !== "string" || !source.startsWith("file://") || source.includes("*"))
376
+ continue;
377
+ reporter.check(fs.existsSync(sourcePath(source.slice("file://".length), manifest)), `${rel(file)}: resources[${idx}] points at missing path: ${source.slice("file://".length)}`);
378
+ }
379
+ for (const entry of data?.toolsSettings?.write?.allowedPaths ?? []) {
380
+ if (typeof entry !== "string" || entry.startsWith("."))
381
+ continue;
382
+ reporter.check(fs.existsSync(sourcePath(entry.replace("**/*", ""), manifest)), `${rel(file)}: allowedPaths entry points at missing path: ${entry}`);
383
+ }
384
+ }
385
+ }
386
+ function validateLegacyRefs(reporter) {
387
+ for (const file of walkFiles(path.join(root, "evals")).sort()) {
388
+ if (!textRefExtensions.has(path.extname(file)))
389
+ continue;
390
+ const parts = path.relative(path.join(root, "evals"), file).split(path.sep);
391
+ if (parts.includes("results") || parts.some((part) => ignoredRefDirs.has(part)))
392
+ continue;
393
+ const text = readText(file);
394
+ for (const match of text.matchAll(legacyRefRe)) {
395
+ const ref = match[0].replace(/[.,)'"\]]+$/, "");
396
+ if (/[{}$]/.test(ref))
397
+ continue;
398
+ if (ref.split(/[\\/]/).includes("node_modules"))
399
+ continue;
400
+ const candidates = [path.join(root, ref), ...(ref.startsWith("evals/") ? [] : [path.join(root, "evals", ref)])];
401
+ if (!candidates.some(fs.existsSync))
402
+ reporter.fail(`${rel(file)}: references missing source path: ${ref}`);
403
+ }
404
+ }
405
+ }
406
+ function validateMirrors(reporter) {
407
+ for (const [rootRel, policy] of mirroredFiles) {
408
+ const rootFile = path.join(root, rootRel);
409
+ const mirror = path.join(root, policy.mirror);
410
+ reporter.check(fs.existsSync(rootFile), `${rootRel}: mirrored root file missing`);
411
+ reporter.check(fs.existsSync(mirror), `${policy.mirror}: mirrored context file missing`);
412
+ if (!fs.existsSync(rootFile) || !fs.existsSync(mirror))
413
+ continue;
414
+ let left = readText(rootFile);
415
+ let right = readText(mirror);
416
+ for (const [rootLine, mirrorLine] of policy.allowedDifferences) {
417
+ left = left.replace(rootLine, "__ALLOWED_ROOT_DIR_LINE__");
418
+ right = right.replace(mirrorLine, "__ALLOWED_ROOT_DIR_LINE__");
419
+ }
420
+ reporter.check(left === right, `${rootRel} and ${policy.mirror} differ outside allowed mirror policy`);
421
+ }
422
+ }
423
+ function validateUsageFeedbackFiles(reporter) {
424
+ for (const file of requiredUsageFeedbackFiles)
425
+ reporter.check(fs.existsSync(path.join(root, file)), `required usage feedback artifact missing: ${file}`);
426
+ }
427
+ function significantScriptLines(text) {
428
+ return text.split(/\r?\n/)
429
+ .map((line) => line.trim())
430
+ .filter((line) => line && !line.startsWith("#!"));
431
+ }
432
+ function validatePublicScriptWrappers(reporter) {
433
+ const readme = readText(path.join(root, "scripts/README.md"));
434
+ for (const [file, policy] of publicScriptWrappers) {
435
+ const abs = path.join(root, file);
436
+ reporter.check(fs.existsSync(abs), `${file}: public script wrapper is missing`);
437
+ reporter.check(readme.includes(path.basename(file)), `scripts/README.md: public wrapper table is missing ${path.basename(file)}`);
438
+ if (!fs.existsSync(abs))
439
+ continue;
440
+ const text = readText(abs);
441
+ const significantLines = significantScriptLines(text);
442
+ reporter.check(JSON.stringify(significantLines) === JSON.stringify(policy.significantLines), `${file}: public wrapper must match the exact thin launcher body for ${policy.target}`);
443
+ }
444
+ }
445
+ function validateHookInventory(reporter) {
446
+ const readme = readText(path.join(root, "scripts/README.md"));
447
+ const hookFiles = walkFiles(path.join(root, "scripts/hooks"))
448
+ .filter((file) => [".js", ".sh"].includes(path.extname(file)))
449
+ .map((file) => rel(file))
450
+ .sort();
451
+ const expected = [...hookFilePolicies.keys()].sort();
452
+ reporter.check(JSON.stringify(hookFiles) === JSON.stringify(expected), `scripts/hooks: hook file inventory changed; update validate-source-tree hookFilePolicies and scripts/README.md`);
453
+ for (const [file, policy] of hookFilePolicies) {
454
+ const abs = path.join(root, file);
455
+ reporter.check(fs.existsSync(abs), `${file}: ${policy.category} missing`);
456
+ reporter.check(readme.includes(path.basename(file)), `scripts/README.md: hook inventory is missing ${path.basename(file)}`);
457
+ reporter.check(readme.includes(policy.category), `scripts/README.md: hook inventory is missing category '${policy.category}'`);
458
+ if (!fs.existsSync(abs))
459
+ continue;
460
+ const text = readText(abs);
461
+ for (const needle of policy.requiredNeedles)
462
+ reporter.check(text.toLowerCase().includes(needle.toLowerCase()), `${file}: expected ${policy.category} marker '${needle}'`);
463
+ }
464
+ }
465
+ function validateFixtureOwnership(reporter) {
466
+ const doc = readText(path.join(root, "docs/fixture-ownership.md"));
467
+ const ownerScanFiles = ["evals/static", "evals/integration", "scripts"]
468
+ .flatMap((entry) => walkFiles(path.join(root, entry)))
469
+ .filter((file) => textRefExtensions.has(path.extname(file)))
470
+ .sort();
471
+ const fixtureDirs = fs.readdirSync(path.join(root, "evals/fixtures"))
472
+ .filter((name) => fs.statSync(path.join(root, "evals/fixtures", name)).isDirectory())
473
+ .map((name) => `evals/fixtures/${name}`)
474
+ .sort();
475
+ const expected = [...fixtureOwnerPolicies.keys()].sort();
476
+ reporter.check(JSON.stringify(fixtureDirs) === JSON.stringify(expected), `evals/fixtures: fixture directory inventory changed; update fixtureOwnerPolicies and docs/fixture-ownership.md`);
477
+ for (const [dir, policy] of fixtureOwnerPolicies) {
478
+ reporter.check(fs.existsSync(path.join(root, dir)), `${dir}: fixture directory missing`);
479
+ reporter.check(doc.includes(dir), `docs/fixture-ownership.md: missing fixture directory ${dir}`);
480
+ reporter.check(doc.includes(policy.classification), `docs/fixture-ownership.md: missing fixture classification '${policy.classification}'`);
481
+ for (const owner of policy.owners) {
482
+ reporter.check(fs.existsSync(path.join(root, owner)), `${dir}: fixture owner missing: ${owner}`);
483
+ reporter.check(doc.includes(owner), `docs/fixture-ownership.md: ${dir} missing owner ${owner}`);
484
+ }
485
+ const directRefs = ownerScanFiles
486
+ .filter((file) => readText(file).includes(dir))
487
+ .map((file) => rel(file))
488
+ .filter((file) => !file.startsWith("evals/fixtures/"))
489
+ .filter((file) => !fixtureOwnershipSelfAuditRefs.has(file))
490
+ .sort();
491
+ const missingOwners = directRefs.filter((file) => !policy.owners.includes(file));
492
+ reporter.check(missingOwners.length === 0, `${dir}: direct fixture references missing from owner inventory: ${missingOwners.join(", ")}`);
493
+ }
494
+ }
495
+ function validatePackageCommandSurface(reporter) {
496
+ const pkg = tryLoadJson(path.join(root, "package.json"), reporter);
497
+ if (!pkg || typeof pkg !== "object")
498
+ return;
499
+ const cli = readText(path.join(root, "src/cli.ts"));
500
+ reporter.check(!cli.includes("pendingCommands"), "src/cli.ts: pending command migration scaffolding must not return; add real commands or remove stale registrations");
501
+ const availableBlock = /const availableCommands = new Map[\s\S]*?\n\]\);/.exec(cli)?.[0] ?? "";
502
+ const aliasesBlock = /const aliases = new Map[\s\S]*?\n\]\);/.exec(cli)?.[0] ?? "";
503
+ reporter.check(Boolean(availableBlock), "src/cli.ts: availableCommands map not found");
504
+ reporter.check(Boolean(aliasesBlock), "src/cli.ts: aliases map not found");
505
+ const availableCommands = new Set([...availableBlock.matchAll(/\["([^"]+)",/g)].map((match) => match[1]));
506
+ const aliases = new Map([...aliasesBlock.matchAll(/\["(flow-agents-[^"]+)",\s*"([^"]+)"\]/g)].map((match) => [match[1], match[2]]));
507
+ const scripts = pkg.scripts && typeof pkg.scripts === "object" ? pkg.scripts : {};
508
+ for (const [name, value] of Object.entries(scripts)) {
509
+ if (typeof value !== "string")
510
+ continue;
511
+ const command = /node build\/src\/cli\.js ([a-z0-9:-]+)/.exec(value)?.[1];
512
+ if (command)
513
+ reporter.check(availableCommands.has(command), `package.json scripts.${name}: command '${command}' is not registered in src/cli.ts`);
514
+ }
515
+ const bins = pkg.bin && typeof pkg.bin === "object" ? pkg.bin : {};
516
+ for (const [name, value] of Object.entries(bins)) {
517
+ if (typeof value !== "string")
518
+ continue;
519
+ if (value === "build/src/cli.js" && name !== "flow-agents") {
520
+ reporter.check(aliases.has(name), `package.json bin '${name}' points at build/src/cli.js but has no src/cli.ts alias`);
521
+ const target = aliases.get(name);
522
+ if (target)
523
+ reporter.check(availableCommands.has(target), `package.json bin '${name}' aliases missing command '${target}'`);
524
+ continue;
525
+ }
526
+ if (value.startsWith("build/src/cli/")) {
527
+ const source = value.replace(/^build\//, "").replace(/\.js$/, ".ts");
528
+ reporter.check(fs.existsSync(path.join(root, source)), `package.json bin '${name}' points at missing TypeScript source ${source}`);
529
+ }
530
+ }
531
+ }
532
+ function isExcludedPythonPath(file) {
533
+ return path.relative(root, file).split(path.sep).some((part) => pythonInventoryExcludes.has(part));
534
+ }
535
+ function validateNoFirstPartyPythonFiles(reporter) {
536
+ for (const file of walkFiles(root).filter((item) => item.endsWith(".py")).sort()) {
537
+ if (!isExcludedPythonPath(file))
538
+ reporter.fail(`${rel(file)}: first-party Python source is not allowed; use TypeScript tooling`);
539
+ }
540
+ }
541
+ function validateNoFirstPartyPythonCommands(reporter) {
542
+ const commandRe = /(?<![A-Za-z0-9_./-])(?:python3?|uv run python)\b/;
543
+ const firstPartyPythonCommandRe = /(?<![A-Za-z0-9_./-])(?:python3?|uv run python)\b[^\n]*(?:^|[\s"'])(?:\.\/)?(?:scripts|context\/scripts|evals|skills)\/[A-Za-z0-9_./-]+\.py\b/m;
544
+ const files = [];
545
+ for (const entry of pythonCommandScanRoots) {
546
+ const abs = path.join(root, entry);
547
+ if (!fs.existsSync(abs))
548
+ continue;
549
+ if (fs.statSync(abs).isDirectory())
550
+ files.push(...walkFiles(abs));
551
+ else
552
+ files.push(abs);
553
+ }
554
+ for (const file of files.sort()) {
555
+ const relative = rel(file);
556
+ if (allowedPythonCommandFiles.some((pattern) => pattern.test(relative)))
557
+ continue;
558
+ if (!textRefExtensions.has(path.extname(file)) && relative !== "package.json")
559
+ continue;
560
+ if (isExcludedPythonPath(file))
561
+ continue;
562
+ const text = readText(file);
563
+ if (!commandRe.test(text))
564
+ continue;
565
+ if (relative.startsWith("evals/") && !firstPartyPythonCommandRe.test(text))
566
+ continue;
567
+ reporter.fail(`${relative}: direct first-party Python command reference is not allowed; use npm/flow-agents TypeScript commands`);
568
+ }
569
+ }
570
+ export function main(argv = process.argv.slice(2)) {
571
+ const kitIndex = argv.indexOf("--kit");
572
+ if (kitIndex >= 0) {
573
+ const kitDir = argv[kitIndex + 1];
574
+ if (!kitDir) {
575
+ console.error("usage: validate-source-tree --kit DIR");
576
+ return 2;
577
+ }
578
+ const reporter = new Reporter();
579
+ const localCli = flowCliPath;
580
+ if (flowSchemaPath && fs.existsSync(flowSchemaPath) && fs.existsSync(localCli))
581
+ console.log(`info: validating kit Flow Definitions with Flow CLI at ${localCli}`);
582
+ else
583
+ console.log("warning: Flow validation surface unavailable; local kit check uses the minimal Flow Definition fallback");
584
+ validateKitRepository(path.resolve(kitDir), reporter);
585
+ if (reporter.errors.length) {
586
+ console.log("Flow Kit repository validation failed:");
587
+ for (const error of reporter.errors)
588
+ console.log(` - ${error}`);
589
+ return 1;
590
+ }
591
+ console.log("Flow Kit repository validation passed.");
592
+ return 0;
593
+ }
594
+ const reporter = new Reporter();
595
+ const manifest = tryLoadJson(manifestPath, reporter) ?? {};
596
+ const agentNames = validateAgents(reporter);
597
+ validateAgentCards(reporter, agentNames);
598
+ validatePowers(reporter);
599
+ validateManifest(reporter, manifest, agentNames);
600
+ validatePacksManifest(reporter, agentNames);
601
+ validateKits(reporter);
602
+ validateAgentPaths(reporter, manifest);
603
+ validateLegacyRefs(reporter);
604
+ validateMirrors(reporter);
605
+ validateUsageFeedbackFiles(reporter);
606
+ validatePublicScriptWrappers(reporter);
607
+ validateHookInventory(reporter);
608
+ validateFixtureOwnership(reporter);
609
+ validatePackageCommandSurface(reporter);
610
+ validateNoFirstPartyPythonFiles(reporter);
611
+ validateNoFirstPartyPythonCommands(reporter);
612
+ if (reporter.errors.length) {
613
+ console.log("Source tree validation failed:");
614
+ for (const error of reporter.errors)
615
+ console.log(` - ${error}`);
616
+ return 1;
617
+ }
618
+ console.log("Source tree validation passed.");
619
+ return 0;
620
+ }
621
+ if (import.meta.url === `file://${process.argv[1]}`)
622
+ process.exit(main());