@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,481 @@
1
+ import * as fs from "node:fs";
2
+ import { parseArgs, flagList, flagString } from "../lib/args.js";
3
+
4
+ const FLOW_ARTIFACT_PATTERN = /(?<path>\.flow-agents\/[^\s`'")]+)/g;
5
+ const GITHUB_ISSUE_PATTERN = /(?:(?<owner>[A-Za-z0-9_.-]+)\/(?<repo>[A-Za-z0-9_.-]+))?#(?<number>\d+)/g;
6
+ const GITHUB_URL_PATTERN = /https:\/\/github\.com\/(?<owner>[^/\s]+)\/(?<repo>[^/\s]+)\/(?:issues|pull)\/(?<number>\d+)/g;
7
+ const BLOCKER_PATTERN = /\b(?:blocked by|depends on|waiting on|requires|should follow|coordinate with)\b[:\s-]*(?<refs>[^\n]+)/gi;
8
+ const PR_URL_PATTERN = /https:\/\/github\.com\/[^/\s]+\/[^/\s]+\/pull\/\d+/g;
9
+ const WORK_ITEM_METADATA_PATTERN = /<!--\s*flow-agents:work-item-metadata\s*(?<json>[\s\S]*?)-->/g;
10
+ const DAY_MS = 24 * 60 * 60 * 1000;
11
+
12
+ type FreshnessInputs = {
13
+ currentRef?: string;
14
+ currentSha?: string;
15
+ changedFiles: string[];
16
+ commitsSince: Record<string, number>;
17
+ now: Date;
18
+ };
19
+
20
+ type FreshnessContext = {
21
+ plannedBaseRef?: string;
22
+ plannedBaseSha?: string;
23
+ plannedAgeDays: number | null;
24
+ commitsSince: number | undefined;
25
+ scopeRefs: string[];
26
+ scopeIntersections: string[];
27
+ };
28
+
29
+ function loadJson(file: string): unknown {
30
+ return file === "-" ? JSON.parse(fs.readFileSync(0, "utf8")) : JSON.parse(fs.readFileSync(file, "utf8"));
31
+ }
32
+
33
+ function names(values: unknown): string[] {
34
+ return Array.isArray(values) ? values.flatMap((item) => typeof item === "object" && item !== null && typeof (item as Record<string, unknown>).name === "string" ? [(item as Record<string, string>).name] : []) : [];
35
+ }
36
+
37
+ function statusKey(value: unknown): string | null {
38
+ if (typeof value !== "string") return null;
39
+ const key = value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_|_$/g, "");
40
+ return ({ todo: "todo", to_do: "todo", ready: "ready", doing: "in_progress", in_progress: "in_progress", blocked: "blocked", review: "review", verification: "verification", done: "done", closed: "done" } as Record<string, string>)[key] ?? key;
41
+ }
42
+
43
+ function projectStatus(issue: Record<string, unknown>): string | null {
44
+ for (const item of (Array.isArray(issue.projectItems) ? issue.projectItems : [])) {
45
+ const status = (item as Record<string, unknown>).status;
46
+ if (typeof status === "object" && status !== null && typeof (status as Record<string, unknown>).name === "string") return (status as Record<string, string>).name;
47
+ }
48
+ return null;
49
+ }
50
+
51
+ function extractRefs(text: string, owner: string, repo: string): Record<string, unknown>[] {
52
+ const refs: Record<string, unknown>[] = [];
53
+ const seen = new Set<string>();
54
+ for (const match of text.matchAll(GITHUB_URL_PATTERN)) {
55
+ const key = `${match.groups?.owner}/${match.groups?.repo}#${Number(match.groups?.number)}`;
56
+ if (!seen.has(key)) { seen.add(key); refs.push({ kind: "github", owner: match.groups?.owner, repo: match.groups?.repo, number: Number(match.groups?.number), url: match[0] }); }
57
+ }
58
+ for (const match of text.matchAll(GITHUB_ISSUE_PATTERN)) {
59
+ const refOwner = match.groups?.owner ?? owner;
60
+ const refRepo = match.groups?.repo ?? repo;
61
+ const key = `${refOwner}/${refRepo}#${Number(match.groups?.number)}`;
62
+ if (!seen.has(key)) { seen.add(key); refs.push({ kind: "github", owner: refOwner, repo: refRepo, number: Number(match.groups?.number) }); }
63
+ }
64
+ return refs;
65
+ }
66
+
67
+ function stringValue(value: unknown): string | undefined {
68
+ return typeof value === "string" && value.trim() ? value : undefined;
69
+ }
70
+
71
+ function stringList(value: unknown): string[] | undefined {
72
+ if (!Array.isArray(value)) return undefined;
73
+ const out = value.flatMap((item) => typeof item === "string" && item.trim() ? [item] : []);
74
+ return out.length ? out : undefined;
75
+ }
76
+
77
+ function parseCommitsSince(values: string[]): Record<string, number> {
78
+ const out: Record<string, number> = {};
79
+ for (const value of values) {
80
+ const [sha, rawCount] = value.split("=", 2);
81
+ const count = Number(rawCount);
82
+ if (sha && Number.isFinite(count) && count >= 0) out[sha] = count;
83
+ }
84
+ return out;
85
+ }
86
+
87
+ function ageDays(plannedAt: unknown, now: Date): number | null {
88
+ const planned = stringValue(plannedAt);
89
+ if (!planned) return null;
90
+ const time = Date.parse(planned);
91
+ if (!Number.isFinite(time)) return null;
92
+ return Math.max(0, Math.floor((now.getTime() - time) / DAY_MS));
93
+ }
94
+
95
+ function pathIntersects(scopeRef: string, changedFile: string): boolean {
96
+ const scope = scopeRef.replace(/^\.\/+/, "").replace(/\/+$/, "");
97
+ const changed = changedFile.replace(/^\.\/+/, "").replace(/\/+$/, "");
98
+ return scope === changed || changed.startsWith(`${scope}/`) || scope.startsWith(`${changed}/`);
99
+ }
100
+
101
+ function planningScopeIntersections(scopeRefs: unknown, changedFiles: string[]): string[] {
102
+ const scopes = stringList(scopeRefs) ?? [];
103
+ const out = new Set<string>();
104
+ for (const scope of scopes) {
105
+ if (changedFiles.some((changed) => pathIntersects(scope, changed))) out.add(scope);
106
+ }
107
+ return Array.from(out);
108
+ }
109
+
110
+ function materialFreshnessFiles(changedFiles: string[]): string[] {
111
+ return changedFiles.filter((file) => {
112
+ const normalized = file.replace(/^\.\/+/, "");
113
+ return normalized.startsWith("context/contracts/")
114
+ || normalized === "package.json"
115
+ || normalized === "package-lock.json"
116
+ || normalized === "pnpm-lock.yaml"
117
+ || normalized === "yarn.lock";
118
+ });
119
+ }
120
+
121
+ function freshnessBase(ctx: FreshnessContext, inputs: FreshnessInputs): Record<string, unknown> {
122
+ return {
123
+ planned_base_ref: ctx.plannedBaseRef,
124
+ planned_base_sha: ctx.plannedBaseSha ?? null,
125
+ current_ref: inputs.currentRef,
126
+ current_sha: inputs.currentSha ?? null,
127
+ planned_age_days: ctx.plannedAgeDays,
128
+ changed_files: inputs.changedFiles,
129
+ planning_scope_refs: ctx.scopeRefs,
130
+ planning_scope_intersections: ctx.scopeIntersections,
131
+ commits_since_planned_base: ctx.commitsSince ?? null,
132
+ };
133
+ }
134
+
135
+ function freshnessContext(item: Record<string, unknown>, inputs: FreshnessInputs): FreshnessContext {
136
+ const plannedBaseRef = stringValue(item.planned_base_ref);
137
+ const plannedBaseSha = stringValue(item.planned_base_sha);
138
+ return {
139
+ plannedBaseRef,
140
+ plannedBaseSha,
141
+ plannedAgeDays: ageDays(item.planned_at, inputs.now),
142
+ commitsSince: plannedBaseSha ? inputs.commitsSince[plannedBaseSha] : undefined,
143
+ scopeRefs: stringList(item.planning_scope_refs) ?? [],
144
+ scopeIntersections: planningScopeIntersections(item.planning_scope_refs, inputs.changedFiles),
145
+ };
146
+ }
147
+
148
+ function classifyRevisionFreshness(ctx: FreshnessContext, inputs: FreshnessInputs): { classification: string; reasons: Record<string, unknown>[]; route_recommendation?: Record<string, unknown> } {
149
+ const reasons: Record<string, unknown>[] = [];
150
+ const materialFiles = materialFreshnessFiles(inputs.changedFiles);
151
+
152
+ if (!ctx.plannedBaseSha) {
153
+ reasons.push({ code: "missing_planned_base_sha", message: "Work item has no planned_base_sha; freshness cannot be verified without inventing certainty." });
154
+ return { classification: "not_verified", reasons };
155
+ }
156
+
157
+ if (!inputs.currentSha) {
158
+ reasons.push({ code: "missing_current_sha", message: "Current target SHA was not provided; pass --current-sha from the caller's refreshed provider/repo context." });
159
+ return { classification: "not_verified", reasons };
160
+ }
161
+
162
+ if (ctx.plannedBaseSha === inputs.currentSha && ctx.scopeIntersections.length === 0) {
163
+ reasons.push({ code: "base_matches_current", message: "Planned base SHA matches current target SHA and no planning scope changes were reported." });
164
+ return { classification: "fresh", reasons };
165
+ }
166
+
167
+ if (ctx.plannedBaseSha !== inputs.currentSha) reasons.push({ code: "base_sha_changed", message: "Current target SHA differs from the planned base SHA." });
168
+ if (ctx.commitsSince === undefined && inputs.changedFiles.length === 0) {
169
+ reasons.push({ code: "missing_drift_evidence", message: "Current target moved, but commits-since and changed-file evidence were not provided." });
170
+ return { classification: "not_verified", reasons };
171
+ }
172
+ if ((ctx.commitsSince ?? 0) > 0) reasons.push({ code: "commits_since_planned_base", message: `${ctx.commitsSince} commits were reported since planned_base_sha.` });
173
+ if (ctx.scopeIntersections.length) reasons.push({ code: "planning_scope_changed", message: "Reported changed files intersect planning_scope_refs.", intersections: ctx.scopeIntersections });
174
+ if (materialFiles.length) reasons.push({ code: "material_freshness_files_changed", message: "Reported changed files include workflow contracts or dependency manifests.", changed_files: materialFiles });
175
+ if (ctx.plannedAgeDays !== null && ctx.plannedAgeDays >= 30) reasons.push({ code: "planned_base_age", message: `Planning baseline is ${ctx.plannedAgeDays} days old.` });
176
+
177
+ const stale = materialFiles.length > 0 || ctx.scopeIntersections.length > 0 || (ctx.commitsSince ?? 0) >= 10 || (ctx.plannedAgeDays !== null && ctx.plannedAgeDays >= 30);
178
+ return {
179
+ classification: stale ? "stale" : "drifted",
180
+ route_recommendation: stale ? { target: "idea-to-backlog", reason: "Stale revision freshness should be reshaped before implementation planning." } : undefined,
181
+ reasons,
182
+ };
183
+ }
184
+
185
+ function revisionFreshness(item: Record<string, unknown>, inputs: FreshnessInputs): Record<string, unknown> {
186
+ const ctx = freshnessContext(item, inputs);
187
+ const result = classifyRevisionFreshness(ctx, inputs);
188
+ return {
189
+ ...freshnessBase(ctx, inputs),
190
+ classification: result.classification,
191
+ route_recommendation: result.route_recommendation,
192
+ reasons: result.reasons,
193
+ };
194
+ }
195
+
196
+ function applyFreshnessToReadiness(readiness: Record<string, unknown>, freshness: Record<string, unknown>): Record<string, unknown> {
197
+ const reasons = Array.isArray(freshness.reasons) ? freshness.reasons as Record<string, unknown>[] : [];
198
+ const reasonCodes = new Set(reasons.map((reason) => reason.code));
199
+ if (readiness.classification === "ready" && freshness.classification === "stale") {
200
+ return {
201
+ classification: "stale",
202
+ reasons: [
203
+ ...(Array.isArray(readiness.reasons) ? readiness.reasons : []),
204
+ { code: "revision_freshness_stale", message: "Revision freshness is stale; route this item back to idea-to-backlog before planning.", freshness },
205
+ ],
206
+ };
207
+ }
208
+ if (
209
+ readiness.classification === "ready"
210
+ && freshness.classification === "not_verified"
211
+ && (reasonCodes.has("missing_drift_evidence") || reasonCodes.has("missing_current_sha"))
212
+ ) {
213
+ return {
214
+ classification: "blocked",
215
+ reasons: [
216
+ ...(Array.isArray(readiness.reasons) ? readiness.reasons : []),
217
+ { code: "revision_freshness_not_verified", message: "Revision freshness is not verified; planning needs an explicit accepted gap.", freshness },
218
+ ],
219
+ };
220
+ }
221
+ return readiness;
222
+ }
223
+
224
+ function parseWorkItemMetadata(body: string): Record<string, unknown> | null {
225
+ for (const match of body.matchAll(WORK_ITEM_METADATA_PATTERN)) {
226
+ const raw = match.groups?.json;
227
+ if (!raw) continue;
228
+ try {
229
+ const parsed = JSON.parse(raw.trim()) as unknown;
230
+ if (typeof parsed === "object" && parsed !== null && !Array.isArray(parsed)) return parsed as Record<string, unknown>;
231
+ } catch {
232
+ return null;
233
+ }
234
+ }
235
+ return null;
236
+ }
237
+
238
+ function normalizeSourceRevision(value: unknown): Record<string, unknown> | null {
239
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return null;
240
+ const source = value as Record<string, unknown>;
241
+ const out: Record<string, unknown> = {};
242
+ for (const key of ["repo", "planned_base_ref", "planned_base_sha", "planned_at", "planning_artifact_ref"]) {
243
+ const normalized = stringValue(source[key]);
244
+ if (normalized) out[key] = normalized;
245
+ }
246
+ const scopeRefs = stringList(source.planning_scope_refs);
247
+ if (scopeRefs) out.planning_scope_refs = scopeRefs;
248
+ return Object.keys(out).length ? out : null;
249
+ }
250
+
251
+ function sourceRevisionFields(metadata: Record<string, unknown> | null, owner: string, repo: string): Record<string, unknown> {
252
+ if (!metadata) return {};
253
+ const revisions = Array.isArray(metadata.source_revisions)
254
+ ? metadata.source_revisions.flatMap((item) => {
255
+ const revision = normalizeSourceRevision(item);
256
+ return revision ? [revision] : [];
257
+ })
258
+ : [];
259
+ const topLevelRevision = normalizeSourceRevision(metadata);
260
+ const singleRepoRevision = topLevelRevision ?? (
261
+ revisions.length === 1
262
+ ? revisions[0]
263
+ : revisions.find((revision) => revision.repo === `${owner}/${repo}`)
264
+ );
265
+ const out: Record<string, unknown> = {};
266
+ if (singleRepoRevision) {
267
+ for (const key of ["planned_base_ref", "planned_base_sha", "planned_at", "planning_artifact_ref", "planning_scope_refs"]) {
268
+ if (singleRepoRevision[key] !== undefined) out[key] = singleRepoRevision[key];
269
+ }
270
+ }
271
+ if (revisions.length) out.source_revisions = revisions;
272
+ return out;
273
+ }
274
+
275
+ function blockers(body: string, owner: string, repo: string): Record<string, unknown>[] {
276
+ const out: Record<string, unknown>[] = [];
277
+ const proseBody = body.replace(WORK_ITEM_METADATA_PATTERN, "");
278
+ for (const match of proseBody.matchAll(BLOCKER_PATTERN)) {
279
+ const refs = extractRefs(match.groups?.refs ?? "", owner, repo);
280
+ if (refs.length) out.push(...refs.map((ref) => ({ type: "provider_ref", ref, evidence: match[0].trim() })));
281
+ else out.push({ type: "text", evidence: match[0].trim() });
282
+ }
283
+ return out;
284
+ }
285
+
286
+ function providerRefKey(ref: Record<string, unknown> | undefined): string | null {
287
+ return ref && typeof ref.owner === "string" && typeof ref.repo === "string" && typeof ref.number === "number"
288
+ ? `${ref.owner}/${ref.repo}#${ref.number}`
289
+ : null;
290
+ }
291
+
292
+ function blockerResolvedStatus(ref: Record<string, unknown> | undefined, resolved: Record<string, string>): string | null {
293
+ const key = providerRefKey(ref);
294
+ return key ? resolved[key] ?? null : null;
295
+ }
296
+
297
+ function blockerImpactState(knownStatus: string | null): string {
298
+ if (["closed", "done", "merged", "satisfied"].includes(knownStatus ?? "")) return "resolved";
299
+ return knownStatus ? "blocking" : "unknown";
300
+ }
301
+
302
+ function isBlockerResolved(ref: Record<string, unknown> | undefined, resolved: Record<string, string>): boolean {
303
+ return blockerImpactState(blockerResolvedStatus(ref, resolved)) === "resolved";
304
+ }
305
+
306
+ function dependencyImpacts(item: Record<string, unknown>, resolved: Record<string, string>): Record<string, unknown>[] {
307
+ const current = item.source_provider as Record<string, unknown> | undefined;
308
+ const currentOwner = typeof current?.owner === "string" ? current.owner : undefined;
309
+ const currentRepo = typeof current?.repo === "string" ? current.repo : undefined;
310
+ return (item.blockers as Record<string, unknown>[] ?? []).flatMap((blocker) => {
311
+ const ref = blocker.ref as Record<string, unknown> | undefined;
312
+ if (!providerRefKey(ref)) return [];
313
+ const knownStatus = blockerResolvedStatus(ref, resolved);
314
+ return [{
315
+ ref,
316
+ source: {
317
+ type: blocker.type,
318
+ evidence: blocker.evidence,
319
+ },
320
+ cross_repo: Boolean(currentOwner && currentRepo && (ref?.owner !== currentOwner || ref?.repo !== currentRepo)),
321
+ known_status: knownStatus ?? "unknown",
322
+ impact_state: blockerImpactState(knownStatus),
323
+ }];
324
+ });
325
+ }
326
+
327
+ function normalizeStructuredBlockers(metadata: Record<string, unknown> | null, owner: string, repo: string): Record<string, unknown>[] {
328
+ if (!metadata || !Array.isArray(metadata.blockers)) return [];
329
+ const out: Record<string, unknown>[] = [];
330
+ for (const blocker of metadata.blockers) {
331
+ if (typeof blocker !== "object" || blocker === null || Array.isArray(blocker)) continue;
332
+ const record = blocker as Record<string, unknown>;
333
+ const evidence = stringValue(record.evidence) ?? stringValue(record.summary) ?? stringValue(record.ref) ?? stringValue(record.type);
334
+ const rawRef = record.ref;
335
+ const refs = typeof rawRef === "string" ? extractRefs(rawRef, owner, repo) : [];
336
+ if (refs.length) {
337
+ out.push(...refs.map((ref) => ({ type: "provider_ref", ref, evidence: evidence ?? rawRef })));
338
+ continue;
339
+ }
340
+ if (typeof rawRef === "object" && rawRef !== null && !Array.isArray(rawRef)) {
341
+ const refRecord = rawRef as Record<string, unknown>;
342
+ const normalizedRef = typeof refRecord.owner === "string" && typeof refRecord.repo === "string" && Number.isFinite(Number(refRecord.number))
343
+ ? { ...refRecord, number: Number(refRecord.number) }
344
+ : refRecord;
345
+ out.push({ type: stringValue(record.type) ?? "provider_ref", ref: normalizedRef, evidence: evidence ?? JSON.stringify(rawRef) });
346
+ continue;
347
+ }
348
+ if (evidence) out.push({ type: stringValue(record.type) ?? "text", evidence });
349
+ }
350
+ return out;
351
+ }
352
+
353
+ function mergeBlockers(structured: Record<string, unknown>[], prose: Record<string, unknown>[]): Record<string, unknown>[] {
354
+ const out: Record<string, unknown>[] = [];
355
+ const seenRefs = new Set<string>();
356
+ const seenText = new Set<string>();
357
+ for (const blocker of structured.concat(prose)) {
358
+ const refKey = providerRefKey(blocker.ref as Record<string, unknown> | undefined);
359
+ if (refKey) {
360
+ if (seenRefs.has(refKey)) continue;
361
+ seenRefs.add(refKey);
362
+ } else {
363
+ const evidence = String(blocker.evidence ?? "").trim().toLowerCase();
364
+ if (evidence && seenText.has(evidence)) continue;
365
+ if (evidence) seenText.add(evidence);
366
+ }
367
+ out.push(blocker);
368
+ }
369
+ return out;
370
+ }
371
+
372
+ function firstLabel(issue: Record<string, unknown>, prefixes: string[]): string | null {
373
+ for (const label of names(issue.labels)) {
374
+ const lower = label.toLowerCase();
375
+ if (prefixes.includes(lower) || prefixes.some((prefix) => lower.startsWith(`${prefix}:`))) return label;
376
+ }
377
+ return null;
378
+ }
379
+
380
+ function normalize(issue: Record<string, unknown>, settings: Record<string, unknown>): Record<string, unknown> {
381
+ const workProvider = settings.work_item_provider as Record<string, unknown>;
382
+ const boardProvider = (settings.board_provider ?? {}) as Record<string, unknown>;
383
+ const repo = workProvider.repo as Record<string, string>;
384
+ const owner = repo.owner;
385
+ const name = repo.name;
386
+ const number = Number(issue.number);
387
+ const body = String(issue.body ?? "");
388
+ const metadata = parseWorkItemMetadata(body);
389
+ const projectItems = Array.isArray(issue.projectItems) ? issue.projectItems as Record<string, unknown>[] : [];
390
+ const prLinks = names(issue.closedByPullRequestsReferences).concat(Array.from(body.matchAll(PR_URL_PATTERN), (m) => m[0]));
391
+ return {
392
+ id: `github:${owner}/${name}#${number}`,
393
+ title: issue.title ?? `#${number}`,
394
+ body,
395
+ status: statusKey(projectStatus(issue)) ?? (issue.state === "CLOSED" ? "done" : "todo"),
396
+ labels: names(issue.labels),
397
+ assignees: names(issue.assignees),
398
+ milestone: issue.milestone,
399
+ priority: firstLabel(issue, ["p0", "p1", "p2", "priority"]),
400
+ size: firstLabel(issue, ["xs", "small", "medium", "large", "xl", "size"]),
401
+ risk: firstLabel(issue, ["risk"]),
402
+ ...sourceRevisionFields(metadata, owner, name),
403
+ blockers: mergeBlockers(normalizeStructuredBlockers(metadata, owner, name), blockers(body, owner, name)),
404
+ related_links: extractRefs(body, owner, name),
405
+ source_provider: { role: "WorkItemProvider", kind: workProvider.kind, owner, repo: name, number, url: issue.url, state: issue.state, node_id: issue.id, capabilities: workProvider.capabilities ?? [] },
406
+ board_membership: { role: "BoardProvider", kind: boardProvider.kind, board: boardProvider.board, items: projectItems.map((item) => ({ title: item.title, status: typeof item.status === "object" && item.status !== null ? (item.status as Record<string, unknown>).name : null, provider: "github_project" })), capabilities: boardProvider.capabilities ?? [] },
407
+ pr_links: prLinks,
408
+ artifact_refs: Array.from(body.matchAll(FLOW_ARTIFACT_PATTERN), (m) => (m.groups?.path ?? "").replace(/[.,]+$/, "")),
409
+ updated_at: issue.updatedAt,
410
+ created_at: issue.createdAt,
411
+ };
412
+ }
413
+
414
+ function classify(item: Record<string, unknown>, settings: Record<string, unknown>, resolved: Record<string, string>): Record<string, unknown> {
415
+ const selection = (settings.selection ?? {}) as Record<string, unknown>;
416
+ const filters = (selection.filters ?? {}) as Record<string, unknown>;
417
+ const wip = (selection.wip_policy ?? {}) as Record<string, unknown>;
418
+ const status = String(item.status ?? "");
419
+ const labels = new Set((item.labels as string[] ?? []).map((label) => label.toLowerCase()));
420
+ if ((wip.active_statuses as string[] ?? []).includes(status) || ["review", "verification"].includes(status)) return { classification: "in_progress", reasons: [{ code: "active_status", message: `Item status is active: ${status}` }] };
421
+ const open = (item.blockers as Record<string, unknown>[] ?? []).filter((blocker) => {
422
+ const ref = blocker.ref as Record<string, unknown> | undefined;
423
+ return !isBlockerResolved(ref, resolved);
424
+ });
425
+ if (status === "blocked" || open.length || labels.has("blocked")) {
426
+ return {
427
+ classification: "blocked",
428
+ reasons: [{
429
+ code: "blocking_evidence",
430
+ message: "Item has unresolved blocker evidence.",
431
+ blockers: open.map((blocker) => {
432
+ const ref = blocker.ref as Record<string, unknown> | undefined;
433
+ return { blocker, known_status: blockerResolvedStatus(ref, resolved) ?? "unknown" };
434
+ }),
435
+ }],
436
+ };
437
+ }
438
+ const title = String(item.title ?? "").toLowerCase();
439
+ if (labels.has("research") || labels.has("spike") || status === "research" || title.includes("research")) return { classification: "related-only", reasons: [{ code: "parallel_research", message: "Research/spike item is related but not the implementation pickup." }] };
440
+ const readyStatuses = (filters.ready_statuses as string[] | undefined) ?? ["ready"];
441
+ const body = String(item.body ?? "").toLowerCase();
442
+ if (readyStatuses.includes(status) || (status === "todo" && body.includes("## scope") && body.includes("## acceptance criteria"))) return { classification: "ready", reasons: [{ code: "pickup_contract", message: "Item has scope, acceptance criteria, and no unresolved blockers." }] };
443
+ return { classification: "stale", reasons: [{ code: "insufficient_readiness", message: `Status ${JSON.stringify(status)} is not ready and no pickup contract was found.` }] };
444
+ }
445
+
446
+ function resolveSettings(doc: Record<string, unknown>): Record<string, unknown> {
447
+ if (typeof doc.settings === "object" && doc.settings !== null) return doc.settings as Record<string, unknown>;
448
+ if (doc.work_item_provider) return doc;
449
+ if (Array.isArray(doc.projects) && doc.projects.length) return doc.projects[0] as Record<string, unknown>;
450
+ if (typeof doc.defaults === "object" && doc.defaults !== null) return doc.defaults as Record<string, unknown>;
451
+ throw new Error("settings JSON must be an effective settings object or backlog-provider-settings document");
452
+ }
453
+
454
+ export function main(argv = process.argv.slice(2)): number {
455
+ const args = parseArgs(argv);
456
+ const settingsJson = flagString(args.flags, "settings-json");
457
+ const issuesJson = flagString(args.flags, "issues-json");
458
+ if (!settingsJson || !issuesJson) throw new Error("--settings-json and --issues-json are required");
459
+ const now = new Date(flagString(args.flags, "now") ?? Date.now());
460
+ if (Number.isNaN(now.getTime())) throw new Error("--now must be a valid date/time when provided");
461
+ const freshnessInputs: FreshnessInputs = {
462
+ currentRef: flagString(args.flags, "current-ref"),
463
+ currentSha: flagString(args.flags, "current-sha"),
464
+ changedFiles: flagList(args.flags, "changed-file"),
465
+ commitsSince: parseCommitsSince(flagList(args.flags, "commits-since")),
466
+ now,
467
+ };
468
+ const settings = resolveSettings(loadJson(settingsJson) as Record<string, unknown>);
469
+ const issuesDoc = loadJson(issuesJson);
470
+ const issues = Array.isArray(issuesDoc) ? issuesDoc : ((issuesDoc as Record<string, unknown>).items as unknown[] ?? [issuesDoc]);
471
+ const resolved = Object.fromEntries(flagList(args.flags, "resolved-ref").map((item) => item.split("=", 2) as [string, string]));
472
+ const items = (issues as Record<string, unknown>[]).map((issue) => {
473
+ const item = normalize(issue, settings);
474
+ const freshness = revisionFreshness(item, freshnessInputs);
475
+ return { ...item, dependency_impacts: dependencyImpacts(item, resolved), revision_freshness: freshness, readiness: applyFreshnessToReadiness(classify(item, settings, resolved), freshness) };
476
+ });
477
+ console.log(JSON.stringify({ items }, null, 2));
478
+ return 0;
479
+ }
480
+
481
+ if (import.meta.url === `file://${process.argv[1]}`) process.exit(main());
@@ -0,0 +1,24 @@
1
+ import * as path from "node:path";
2
+ import { parseArgs, flagString } from "../lib/args.js";
3
+ import { activateCodexLocal } from "../runtime-adapters.js";
4
+
5
+ export function main(argv = process.argv.slice(2)): number {
6
+ const [command, ...rest] = argv;
7
+ if (command !== "activate") {
8
+ console.error("usage: runtime-adapter activate [--dest DIR] [--source-root DIR] [--adapter codex-local]");
9
+ return 2;
10
+ }
11
+ const args = parseArgs(rest);
12
+ const dest = path.resolve(flagString(args.flags, "dest", ".") ?? ".");
13
+ const sourceRoot = path.resolve(flagString(args.flags, "source-root", path.resolve(path.dirname(process.argv[1]), "..")) ?? ".");
14
+ const adapter = flagString(args.flags, "adapter");
15
+ if (adapter && adapter !== "codex-local") {
16
+ console.log(JSON.stringify({ selected_adapter: null, available_adapters: ["codex-local"], supported_asset_classes: [], generated_runtime_files: [], skipped_assets: [], warnings: [], errors: [`unknown runtime adapter '${adapter}'; available adapters: codex-local`] }, null, 2));
17
+ return 2;
18
+ }
19
+ const result = activateCodexLocal(sourceRoot, dest);
20
+ console.log(JSON.stringify(result, null, 2));
21
+ return Array.isArray(result.errors) && result.errors.length ? 1 : 0;
22
+ }
23
+
24
+ if (import.meta.url === `file://${process.argv[1]}`) process.exit(main());