@enterpriseai/cli 3.19.0 → 3.19.2

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 (364) hide show
  1. package/package.json +3 -3
  2. package/resources/deploy/eai-app-template/producer-pin.json +5 -5
  3. package/resources/deploy/eai-app-template/tests/source-unknown-deployment-evidence.test.mjs +2141 -1
  4. package/resources/gofer/.gofer-version +4 -357
  5. package/resources/gofer/agents-skills/eai/SKILL.md +365 -0
  6. package/resources/gofer/agents-skills/eai-update/SKILL.md +56 -0
  7. package/resources/gofer/agents-skills/gofer-documentation/SKILL.md +93 -0
  8. package/resources/gofer/bash-scripts/create-new-feature.sh +3 -1
  9. package/resources/gofer/bash-scripts/sync-implementation-status.sh +4 -3
  10. package/resources/gofer/bash-scripts/update-agent-context.sh +7 -8
  11. package/resources/gofer/bash-scripts/validate-artifact.sh +10 -6
  12. package/resources/gofer/bash-scripts/verify-object-type-routing-workspace.sh +184 -0
  13. package/resources/gofer/claude-commands/eai-update.md +46 -0
  14. package/resources/gofer/claude-commands/eai.md +355 -0
  15. package/resources/gofer/claude-skills/eai/SKILL.md +365 -0
  16. package/resources/gofer/claude-skills/eai-update/SKILL.md +56 -0
  17. package/resources/gofer/claude-skills/gofer-documentation/SKILL.md +93 -0
  18. package/resources/gofer/commands/0_gofer_start.md +440 -99
  19. package/resources/gofer/commands/0a_problem_validation.md +187 -27
  20. package/resources/gofer/commands/10_gofer_cloud.md +183 -23
  21. package/resources/gofer/commands/1_gofer_research.md +239 -30
  22. package/resources/gofer/commands/2_gofer_specify.md +239 -35
  23. package/resources/gofer/commands/3_gofer_plan.md +259 -43
  24. package/resources/gofer/commands/4_gofer_tasks.md +401 -81
  25. package/resources/gofer/commands/5_gofer_implement.md +375 -84
  26. package/resources/gofer/commands/6_gofer_validate.md +284 -53
  27. package/resources/gofer/commands/7_gofer_save.md +187 -26
  28. package/resources/gofer/commands/7a_stakeholder_comms.md +183 -23
  29. package/resources/gofer/commands/8_gofer_branding.md +183 -23
  30. package/resources/gofer/commands/9_gofer_tests.md +212 -23
  31. package/resources/gofer/commands/gofer_bootstrap_workspace.md +121 -8
  32. package/resources/gofer/commands/gofer_check_workspace.md +123 -8
  33. package/resources/gofer/commands/gofer_constitution.md +183 -23
  34. package/resources/gofer/commands/gofer_diagnose.md +183 -23
  35. package/resources/gofer/commands/gofer_eai_first_run.md +254 -25
  36. package/resources/gofer/commands/gofer_hydrate.md +183 -23
  37. package/resources/gofer/commands/gofer_personality.md +117 -6
  38. package/resources/gofer/commands/gofer_plan.md +117 -6
  39. package/resources/gofer/commands/gofer_side.md +117 -6
  40. package/resources/gofer/commands/gofer_spec_summary.md +183 -23
  41. package/resources/gofer/commands/gofer_tdd.md +183 -23
  42. package/resources/gofer/commands/gofer_vocabulary.md +185 -25
  43. package/resources/gofer/commands/gofer_zoom_out.md +183 -23
  44. package/resources/gofer/config/typesafe-learning-review.json +16 -0
  45. package/resources/gofer/config/typesafe-semantic-review.json +9 -0
  46. package/resources/gofer/copilot-instructions/gofer-cost.instructions.md +15 -6
  47. package/resources/gofer/copilot-instructions/typescript.instructions.md +1 -1
  48. package/resources/gofer/copilot-prompts/eai-update.prompt.md +58 -0
  49. package/resources/gofer/copilot-prompts/eai.prompt.md +367 -0
  50. package/resources/gofer/gemini/commands/gofer/eai-update.md +22 -0
  51. package/resources/gofer/gemini/commands/gofer/eai-update.toml +2 -0
  52. package/resources/gofer/gemini/commands/gofer/eai.md +357 -0
  53. package/resources/gofer/gemini/commands/gofer/eai.toml +2 -0
  54. package/resources/gofer/gemini/commands/gofer/manifest.json +3 -27
  55. package/resources/gofer/gemini/extension.json +4 -4
  56. package/resources/gofer/github-agents/eai.agent.md +83 -0
  57. package/resources/gofer/github-agents/gofer-business.agent.md +73 -4
  58. package/resources/gofer/github-agents/gofer-implement.agent.md +71 -2
  59. package/resources/gofer/github-agents/gofer-plan.agent.md +72 -2
  60. package/resources/gofer/github-agents/gofer-research.agent.md +72 -2
  61. package/resources/gofer/github-agents/gofer-validate.agent.md +72 -2
  62. package/resources/gofer/github-skills/eai/SKILL.md +365 -0
  63. package/resources/gofer/github-skills/eai-update/SKILL.md +56 -0
  64. package/resources/gofer/github-skills/gofer-documentation/SKILL.md +93 -0
  65. package/resources/gofer/grok-skills/eai/SKILL.md +267 -14
  66. package/resources/gofer/grok-skills/eai-update/SKILL.md +56 -0
  67. package/resources/gofer/hook-scripts/user-prompt-submit.mjs +58 -13
  68. package/resources/gofer/instruction-templates/base/agents-base.md +8 -4
  69. package/resources/gofer/instruction-templates/gofer/gofer-claude.md +7 -5
  70. package/resources/gofer/instruction-templates/gofer/gofer-copilot.md +10 -8
  71. package/resources/gofer/memory/constitution.md +9 -7
  72. package/resources/gofer/memory/gofer-model-policy.yaml +23 -1
  73. package/resources/gofer/memory/lessons.md +0 -31
  74. package/resources/gofer/node-scripts/canonical-descriptions.mjs +1 -1
  75. package/resources/gofer/node-scripts/check-version-alignment.mjs +4 -7
  76. package/resources/gofer/node-scripts/delivery-lineage-corpus.mjs +156 -0
  77. package/resources/gofer/node-scripts/generate-commands.mjs +1073 -265
  78. package/resources/gofer/node-scripts/gofer-acceptance-check.mjs +143 -0
  79. package/resources/gofer/node-scripts/gofer-agent-catalog.mjs +271 -0
  80. package/resources/gofer/node-scripts/gofer-benchmark-executor.mjs +123 -0
  81. package/resources/gofer/node-scripts/gofer-benchmark-sign.mjs +62 -0
  82. package/resources/gofer/node-scripts/gofer-benchmark-signer.mjs +89 -0
  83. package/resources/gofer/node-scripts/gofer-benchmark.mjs +76 -0
  84. package/resources/gofer/node-scripts/gofer-blocker-control.mjs +274 -0
  85. package/resources/gofer/node-scripts/gofer-claude-adapter.mjs +154 -0
  86. package/resources/gofer/node-scripts/gofer-closed-loop-audit.mjs +19 -2
  87. package/resources/gofer/node-scripts/gofer-delivery-check.mjs +121 -0
  88. package/resources/gofer/node-scripts/gofer-encrypted-key.mjs +58 -0
  89. package/resources/gofer/node-scripts/gofer-execution-metrics.mjs +477 -0
  90. package/resources/gofer/node-scripts/gofer-execution-recovery.mjs +334 -0
  91. package/resources/gofer/node-scripts/gofer-grok-adapter.mjs +184 -0
  92. package/resources/gofer/node-scripts/gofer-heldout-corpus.mjs +73 -0
  93. package/resources/gofer/node-scripts/gofer-heldout-snapshot.mjs +286 -0
  94. package/resources/gofer/node-scripts/gofer-heldout-verifier.mjs +211 -0
  95. package/resources/gofer/node-scripts/gofer-host-capability.mjs +335 -0
  96. package/resources/gofer/node-scripts/gofer-independent-reviewer.mjs +130 -0
  97. package/resources/gofer/node-scripts/gofer-live-routing.mjs +109 -0
  98. package/resources/gofer/node-scripts/gofer-local-capability-issuer.mjs +71 -0
  99. package/resources/gofer/node-scripts/gofer-local-isolation.mjs +175 -0
  100. package/resources/gofer/node-scripts/gofer-local-settings-cleanup.mjs +388 -0
  101. package/resources/gofer/node-scripts/gofer-loop-audit.mjs +54 -3
  102. package/resources/gofer/node-scripts/gofer-native-adapter.mjs +567 -0
  103. package/resources/gofer/node-scripts/gofer-native-benchmark-dispatch.mjs +145 -0
  104. package/resources/gofer/node-scripts/gofer-native-runtime.mjs +169 -0
  105. package/resources/gofer/node-scripts/gofer-pre-edit-check.mjs +204 -0
  106. package/resources/gofer/node-scripts/gofer-priority-check.mjs +214 -0
  107. package/resources/gofer/node-scripts/gofer-response-check.mjs +56 -0
  108. package/resources/gofer/node-scripts/gofer-reviewed-learning.mjs +1232 -0
  109. package/resources/gofer/node-scripts/gofer-run-verified-task.mjs +330 -0
  110. package/resources/gofer/node-scripts/gofer-runtime-ledger.mjs +289 -0
  111. package/resources/gofer/node-scripts/gofer-semantic-drift.mjs +584 -0
  112. package/resources/gofer/node-scripts/gofer-surface-update.mjs +1178 -0
  113. package/resources/gofer/node-scripts/gofer-trust-bootstrap.mjs +77 -0
  114. package/resources/gofer/node-scripts/gofer-trusted-benchmark.mjs +102 -0
  115. package/resources/gofer/node-scripts/gofer-trusted-evaluator.mjs +274 -0
  116. package/resources/gofer/node-scripts/gofer-tty-prompt.mjs +34 -0
  117. package/resources/gofer/node-scripts/gofer-typesafe-credentials.mjs +435 -0
  118. package/resources/gofer/node-scripts/gofer-ui-preview.mjs +1212 -0
  119. package/resources/gofer/node-scripts/gofer-verified-execution.mjs +484 -0
  120. package/resources/gofer/node-scripts/gofer-verifier-key-ceremony.mjs +101 -0
  121. package/resources/gofer/node-scripts/gofer-worktree-layout.mjs +32 -0
  122. package/resources/gofer/node-scripts/object-type-routing-phase-bundle.mjs +360 -0
  123. package/resources/gofer/node-scripts/package-agent-plugin.mjs +574 -82
  124. package/resources/gofer/node-scripts/parse-stage-command.mjs +1 -1
  125. package/resources/gofer/node-scripts/render-delivery-lineage.mjs +178 -0
  126. package/resources/gofer/node-scripts/sync-extension-resources.mjs +78 -2
  127. package/resources/gofer/node-scripts/validate-object-type-identifiers.mjs +767 -0
  128. package/resources/gofer/node-scripts/validate-object-type-routing-workspace.mjs +1325 -0
  129. package/resources/gofer/node-scripts/validate-v4-resource-contract.d.mts +44 -0
  130. package/resources/gofer/node-scripts/validate-v4-resource-contract.mjs +957 -0
  131. package/resources/gofer/node-scripts/workspace-bootstrap-lib.mjs +685 -116
  132. package/resources/gofer/references/agent-catalog.json +126 -0
  133. package/resources/gofer/references/blocker-mediation.md +156 -0
  134. package/resources/gofer/references/business-updates-and-goal-checks.md +109 -0
  135. package/resources/gofer/references/delivery-lineage.md +48 -0
  136. package/resources/gofer/references/mvp-capability-validation.md +102 -0
  137. package/resources/gofer/references/platform/README.md +1 -1
  138. package/resources/gofer/references/platform/eai-app-template.md +15 -13
  139. package/resources/gofer/references/platform/eai-auth-access.md +104 -0
  140. package/resources/gofer/references/platform/eai-config-driven-ui.md +5 -5
  141. package/resources/gofer/references/platform/eai-error-catalog.yaml +77 -19
  142. package/resources/gofer/references/platform/eai-repo-contract.md +61 -23
  143. package/resources/gofer/references/platform/eai-service-patterns.md +102 -105
  144. package/resources/gofer/references/platform/eai.md +44 -8
  145. package/resources/gofer/references/platform/vertical-template.md +10 -9
  146. package/resources/gofer/references/priority-outcome-protection.md +152 -0
  147. package/resources/gofer/references/verified-agent-execution.md +179 -0
  148. package/resources/gofer/schemas/gofer-learning-trace-v1.json +70 -0
  149. package/resources/gofer/specify-commands/0_gofer_start.md +440 -99
  150. package/resources/gofer/specify-commands/0a_problem_validation.md +187 -27
  151. package/resources/gofer/specify-commands/10_gofer_cloud.md +183 -23
  152. package/resources/gofer/specify-commands/1_gofer_research.md +239 -30
  153. package/resources/gofer/specify-commands/2_gofer_specify.md +239 -35
  154. package/resources/gofer/specify-commands/3_gofer_plan.md +259 -43
  155. package/resources/gofer/specify-commands/4_gofer_tasks.md +401 -81
  156. package/resources/gofer/specify-commands/5_gofer_implement.md +375 -84
  157. package/resources/gofer/specify-commands/6_gofer_validate.md +284 -53
  158. package/resources/gofer/specify-commands/7_gofer_save.md +187 -26
  159. package/resources/gofer/specify-commands/7a_stakeholder_comms.md +183 -23
  160. package/resources/gofer/specify-commands/8_gofer_branding.md +183 -23
  161. package/resources/gofer/specify-commands/9_gofer_tests.md +212 -23
  162. package/resources/gofer/specify-commands/gofer_bootstrap_workspace.md +121 -8
  163. package/resources/gofer/specify-commands/gofer_check_workspace.md +123 -8
  164. package/resources/gofer/specify-commands/gofer_constitution.md +183 -23
  165. package/resources/gofer/specify-commands/gofer_diagnose.md +183 -23
  166. package/resources/gofer/specify-commands/gofer_eai_first_run.md +254 -25
  167. package/resources/gofer/specify-commands/gofer_hydrate.md +183 -23
  168. package/resources/gofer/specify-commands/gofer_personality.md +117 -6
  169. package/resources/gofer/specify-commands/gofer_plan.md +117 -6
  170. package/resources/gofer/specify-commands/gofer_side.md +117 -6
  171. package/resources/gofer/specify-commands/gofer_spec_summary.md +183 -23
  172. package/resources/gofer/specify-commands/gofer_tdd.md +183 -23
  173. package/resources/gofer/specify-commands/gofer_vocabulary.md +185 -25
  174. package/resources/gofer/specify-commands/gofer_zoom_out.md +183 -23
  175. package/resources/gofer/specify-config/object-type-routing.json +24 -0
  176. package/resources/gofer/specify-config/typesafe-learning-review.json +16 -0
  177. package/resources/gofer/specify-config/typesafe-semantic-review.json +9 -0
  178. package/resources/gofer/system-skills/eai/SKILL.md +365 -0
  179. package/resources/gofer/system-skills/eai-update/SKILL.md +56 -0
  180. package/resources/gofer/system-skills/gofer-documentation/SKILL.md +93 -0
  181. package/resources/gofer/templates/build-map-template.md +64 -0
  182. package/resources/gofer/templates/business-scenarios-template.json +14 -0
  183. package/resources/gofer/templates/ciso-security-summary-template.md +3 -3
  184. package/resources/gofer/templates/context-bundle-template.md +4 -3
  185. package/resources/gofer/templates/contract-pack-template.md +8 -8
  186. package/resources/gofer/templates/cto-architecture-summary-template.md +3 -3
  187. package/resources/gofer/templates/delivery-lineage-template.json +8 -0
  188. package/resources/gofer/templates/eai-preflight-template.md +109 -68
  189. package/resources/gofer/templates/goal-ledger-template.json +1 -1
  190. package/resources/gofer/templates/gofer-model-policy.yaml +28 -6
  191. package/resources/gofer/templates/loop-contract-template.json +3 -1
  192. package/resources/gofer/templates/plan-template.md +8 -7
  193. package/resources/gofer/templates/priority-plan-template.json +29 -0
  194. package/resources/gofer/templates/proposal-review-template.md +6 -3
  195. package/resources/gofer/templates/release-capability-ledger-template.md +24 -0
  196. package/resources/gofer/templates/research-template.md +7 -6
  197. package/resources/gofer/templates/spec-summary-template.md +1 -1
  198. package/resources/gofer/templates/spec-template.md +45 -6
  199. package/resources/gofer/templates/tasks-template.md +12 -3
  200. package/resources/gofer/templates/ui-preview-brief-template.md +55 -5
  201. package/resources/gofer/templates/ui-review-log-template.md +21 -3
  202. package/resources/gofer/templates/ui-show-and-tell-template.md +46 -0
  203. package/resources/gofer/templates/visuals/capability-heatmap-template.md +2 -2
  204. package/resources/gofer/templates/working-backwards-prfaq-template.md +9 -8
  205. package/resources/linked-sources.json +4 -4
  206. package/resources/gofer/agents-skills/0_gofer_start/SKILL.md +0 -1287
  207. package/resources/gofer/agents-skills/0a_problem_validation/SKILL.md +0 -429
  208. package/resources/gofer/agents-skills/10_gofer_cloud/SKILL.md +0 -635
  209. package/resources/gofer/agents-skills/1_gofer_research/SKILL.md +0 -1159
  210. package/resources/gofer/agents-skills/2_gofer_specify/SKILL.md +0 -860
  211. package/resources/gofer/agents-skills/3_gofer_plan/SKILL.md +0 -899
  212. package/resources/gofer/agents-skills/4_gofer_tasks/SKILL.md +0 -760
  213. package/resources/gofer/agents-skills/5_gofer_implement/SKILL.md +0 -944
  214. package/resources/gofer/agents-skills/6_gofer_validate/SKILL.md +0 -2302
  215. package/resources/gofer/agents-skills/7_gofer_save/SKILL.md +0 -423
  216. package/resources/gofer/agents-skills/7a_stakeholder_comms/SKILL.md +0 -508
  217. package/resources/gofer/agents-skills/8_gofer_branding/SKILL.md +0 -315
  218. package/resources/gofer/agents-skills/9_gofer_tests/SKILL.md +0 -558
  219. package/resources/gofer/agents-skills/gofer_bootstrap_workspace/SKILL.md +0 -148
  220. package/resources/gofer/agents-skills/gofer_check_workspace/SKILL.md +0 -119
  221. package/resources/gofer/agents-skills/gofer_constitution/SKILL.md +0 -612
  222. package/resources/gofer/agents-skills/gofer_diagnose/SKILL.md +0 -114
  223. package/resources/gofer/agents-skills/gofer_eai_first_run/SKILL.md +0 -397
  224. package/resources/gofer/agents-skills/gofer_hydrate/SKILL.md +0 -561
  225. package/resources/gofer/agents-skills/gofer_personality/SKILL.md +0 -44
  226. package/resources/gofer/agents-skills/gofer_plan/SKILL.md +0 -40
  227. package/resources/gofer/agents-skills/gofer_side/SKILL.md +0 -45
  228. package/resources/gofer/agents-skills/gofer_spec_summary/SKILL.md +0 -112
  229. package/resources/gofer/agents-skills/gofer_tdd/SKILL.md +0 -112
  230. package/resources/gofer/agents-skills/gofer_vocabulary/SKILL.md +0 -112
  231. package/resources/gofer/agents-skills/gofer_zoom_out/SKILL.md +0 -112
  232. package/resources/gofer/claude-commands/0_gofer_start.md +0 -1282
  233. package/resources/gofer/claude-commands/0a_problem_validation.md +0 -424
  234. package/resources/gofer/claude-commands/10_gofer_cloud.md +0 -630
  235. package/resources/gofer/claude-commands/1_gofer_research.md +0 -1154
  236. package/resources/gofer/claude-commands/2_gofer_specify.md +0 -855
  237. package/resources/gofer/claude-commands/3_gofer_plan.md +0 -894
  238. package/resources/gofer/claude-commands/4_gofer_tasks.md +0 -755
  239. package/resources/gofer/claude-commands/5_gofer_implement.md +0 -939
  240. package/resources/gofer/claude-commands/6_gofer_validate.md +0 -2297
  241. package/resources/gofer/claude-commands/7_gofer_save.md +0 -418
  242. package/resources/gofer/claude-commands/7a_stakeholder_comms.md +0 -503
  243. package/resources/gofer/claude-commands/8_gofer_branding.md +0 -310
  244. package/resources/gofer/claude-commands/9_gofer_tests.md +0 -553
  245. package/resources/gofer/claude-commands/gofer_bootstrap_workspace.md +0 -143
  246. package/resources/gofer/claude-commands/gofer_check_workspace.md +0 -114
  247. package/resources/gofer/claude-commands/gofer_constitution.md +0 -607
  248. package/resources/gofer/claude-commands/gofer_diagnose.md +0 -109
  249. package/resources/gofer/claude-commands/gofer_eai_first_run.md +0 -392
  250. package/resources/gofer/claude-commands/gofer_hydrate.md +0 -556
  251. package/resources/gofer/claude-commands/gofer_personality.md +0 -39
  252. package/resources/gofer/claude-commands/gofer_plan.md +0 -35
  253. package/resources/gofer/claude-commands/gofer_side.md +0 -40
  254. package/resources/gofer/claude-commands/gofer_spec_summary.md +0 -107
  255. package/resources/gofer/claude-commands/gofer_tdd.md +0 -107
  256. package/resources/gofer/claude-commands/gofer_vocabulary.md +0 -107
  257. package/resources/gofer/claude-commands/gofer_zoom_out.md +0 -107
  258. package/resources/gofer/claude-skills/eai-gofer/SKILL.md +0 -56
  259. package/resources/gofer/copilot-prompts/0_gofer_start.prompt.md +0 -1308
  260. package/resources/gofer/copilot-prompts/0a_problem_validation.prompt.md +0 -429
  261. package/resources/gofer/copilot-prompts/10_gofer_cloud.prompt.md +0 -644
  262. package/resources/gofer/copilot-prompts/1_gofer_research.prompt.md +0 -1177
  263. package/resources/gofer/copilot-prompts/2_gofer_specify.prompt.md +0 -878
  264. package/resources/gofer/copilot-prompts/3_gofer_plan.prompt.md +0 -917
  265. package/resources/gofer/copilot-prompts/4_gofer_tasks.prompt.md +0 -778
  266. package/resources/gofer/copilot-prompts/5_gofer_implement.prompt.md +0 -962
  267. package/resources/gofer/copilot-prompts/6_gofer_validate.prompt.md +0 -2310
  268. package/resources/gofer/copilot-prompts/7_gofer_save.prompt.md +0 -433
  269. package/resources/gofer/copilot-prompts/7a_stakeholder_comms.prompt.md +0 -516
  270. package/resources/gofer/copilot-prompts/8_gofer_branding.prompt.md +0 -323
  271. package/resources/gofer/copilot-prompts/9_gofer_tests.prompt.md +0 -566
  272. package/resources/gofer/copilot-prompts/gofer_bootstrap_workspace.prompt.md +0 -161
  273. package/resources/gofer/copilot-prompts/gofer_check_workspace.prompt.md +0 -132
  274. package/resources/gofer/copilot-prompts/gofer_constitution.prompt.md +0 -621
  275. package/resources/gofer/copilot-prompts/gofer_diagnose.prompt.md +0 -127
  276. package/resources/gofer/copilot-prompts/gofer_eai_first_run.prompt.md +0 -410
  277. package/resources/gofer/copilot-prompts/gofer_hydrate.prompt.md +0 -571
  278. package/resources/gofer/copilot-prompts/gofer_personality.prompt.md +0 -57
  279. package/resources/gofer/copilot-prompts/gofer_plan.prompt.md +0 -53
  280. package/resources/gofer/copilot-prompts/gofer_side.prompt.md +0 -58
  281. package/resources/gofer/copilot-prompts/gofer_spec_summary.prompt.md +0 -125
  282. package/resources/gofer/copilot-prompts/gofer_tdd.prompt.md +0 -125
  283. package/resources/gofer/copilot-prompts/gofer_vocabulary.prompt.md +0 -125
  284. package/resources/gofer/copilot-prompts/gofer_zoom_out.prompt.md +0 -125
  285. package/resources/gofer/gemini/commands/gofer/0_gofer_start.md +0 -1282
  286. package/resources/gofer/gemini/commands/gofer/0_gofer_start.toml +0 -2
  287. package/resources/gofer/gemini/commands/gofer/0a_problem_validation.md +0 -424
  288. package/resources/gofer/gemini/commands/gofer/0a_problem_validation.toml +0 -2
  289. package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.md +0 -630
  290. package/resources/gofer/gemini/commands/gofer/10_gofer_cloud.toml +0 -2
  291. package/resources/gofer/gemini/commands/gofer/1_gofer_research.md +0 -1154
  292. package/resources/gofer/gemini/commands/gofer/1_gofer_research.toml +0 -2
  293. package/resources/gofer/gemini/commands/gofer/2_gofer_specify.md +0 -855
  294. package/resources/gofer/gemini/commands/gofer/2_gofer_specify.toml +0 -2
  295. package/resources/gofer/gemini/commands/gofer/3_gofer_plan.md +0 -894
  296. package/resources/gofer/gemini/commands/gofer/3_gofer_plan.toml +0 -2
  297. package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.md +0 -755
  298. package/resources/gofer/gemini/commands/gofer/4_gofer_tasks.toml +0 -2
  299. package/resources/gofer/gemini/commands/gofer/5_gofer_implement.md +0 -939
  300. package/resources/gofer/gemini/commands/gofer/5_gofer_implement.toml +0 -2
  301. package/resources/gofer/gemini/commands/gofer/6_gofer_validate.md +0 -2297
  302. package/resources/gofer/gemini/commands/gofer/6_gofer_validate.toml +0 -2
  303. package/resources/gofer/gemini/commands/gofer/7_gofer_save.md +0 -418
  304. package/resources/gofer/gemini/commands/gofer/7_gofer_save.toml +0 -2
  305. package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.md +0 -503
  306. package/resources/gofer/gemini/commands/gofer/7a_stakeholder_comms.toml +0 -2
  307. package/resources/gofer/gemini/commands/gofer/8_gofer_branding.md +0 -310
  308. package/resources/gofer/gemini/commands/gofer/8_gofer_branding.toml +0 -2
  309. package/resources/gofer/gemini/commands/gofer/9_gofer_tests.md +0 -553
  310. package/resources/gofer/gemini/commands/gofer/9_gofer_tests.toml +0 -2
  311. package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.md +0 -143
  312. package/resources/gofer/gemini/commands/gofer/gofer_bootstrap_workspace.toml +0 -2
  313. package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.md +0 -114
  314. package/resources/gofer/gemini/commands/gofer/gofer_check_workspace.toml +0 -2
  315. package/resources/gofer/gemini/commands/gofer/gofer_constitution.md +0 -607
  316. package/resources/gofer/gemini/commands/gofer/gofer_constitution.toml +0 -2
  317. package/resources/gofer/gemini/commands/gofer/gofer_diagnose.md +0 -109
  318. package/resources/gofer/gemini/commands/gofer/gofer_diagnose.toml +0 -2
  319. package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.md +0 -392
  320. package/resources/gofer/gemini/commands/gofer/gofer_eai_first_run.toml +0 -2
  321. package/resources/gofer/gemini/commands/gofer/gofer_hydrate.md +0 -556
  322. package/resources/gofer/gemini/commands/gofer/gofer_hydrate.toml +0 -2
  323. package/resources/gofer/gemini/commands/gofer/gofer_personality.md +0 -39
  324. package/resources/gofer/gemini/commands/gofer/gofer_personality.toml +0 -2
  325. package/resources/gofer/gemini/commands/gofer/gofer_plan.md +0 -35
  326. package/resources/gofer/gemini/commands/gofer/gofer_plan.toml +0 -2
  327. package/resources/gofer/gemini/commands/gofer/gofer_side.md +0 -40
  328. package/resources/gofer/gemini/commands/gofer/gofer_side.toml +0 -2
  329. package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.md +0 -107
  330. package/resources/gofer/gemini/commands/gofer/gofer_spec_summary.toml +0 -2
  331. package/resources/gofer/gemini/commands/gofer/gofer_tdd.md +0 -107
  332. package/resources/gofer/gemini/commands/gofer/gofer_tdd.toml +0 -2
  333. package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.md +0 -107
  334. package/resources/gofer/gemini/commands/gofer/gofer_vocabulary.toml +0 -2
  335. package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.md +0 -107
  336. package/resources/gofer/gemini/commands/gofer/gofer_zoom_out.toml +0 -2
  337. package/resources/gofer/github-skills/eai-gofer/SKILL.md +0 -56
  338. package/resources/gofer/system-skills/0_gofer_start/SKILL.md +0 -1287
  339. package/resources/gofer/system-skills/0a_problem_validation/SKILL.md +0 -429
  340. package/resources/gofer/system-skills/10_gofer_cloud/SKILL.md +0 -635
  341. package/resources/gofer/system-skills/1_gofer_research/SKILL.md +0 -1159
  342. package/resources/gofer/system-skills/2_gofer_specify/SKILL.md +0 -860
  343. package/resources/gofer/system-skills/3_gofer_plan/SKILL.md +0 -899
  344. package/resources/gofer/system-skills/4_gofer_tasks/SKILL.md +0 -760
  345. package/resources/gofer/system-skills/5_gofer_implement/SKILL.md +0 -944
  346. package/resources/gofer/system-skills/6_gofer_validate/SKILL.md +0 -2302
  347. package/resources/gofer/system-skills/7_gofer_save/SKILL.md +0 -423
  348. package/resources/gofer/system-skills/7a_stakeholder_comms/SKILL.md +0 -508
  349. package/resources/gofer/system-skills/8_gofer_branding/SKILL.md +0 -315
  350. package/resources/gofer/system-skills/9_gofer_tests/SKILL.md +0 -558
  351. package/resources/gofer/system-skills/gofer_bootstrap_workspace/SKILL.md +0 -148
  352. package/resources/gofer/system-skills/gofer_check_workspace/SKILL.md +0 -119
  353. package/resources/gofer/system-skills/gofer_constitution/SKILL.md +0 -612
  354. package/resources/gofer/system-skills/gofer_diagnose/SKILL.md +0 -114
  355. package/resources/gofer/system-skills/gofer_eai_first_run/SKILL.md +0 -397
  356. package/resources/gofer/system-skills/gofer_hydrate/SKILL.md +0 -561
  357. package/resources/gofer/system-skills/gofer_personality/SKILL.md +0 -44
  358. package/resources/gofer/system-skills/gofer_plan/SKILL.md +0 -40
  359. package/resources/gofer/system-skills/gofer_side/SKILL.md +0 -45
  360. package/resources/gofer/system-skills/gofer_spec_summary/SKILL.md +0 -112
  361. package/resources/gofer/system-skills/gofer_tdd/SKILL.md +0 -112
  362. package/resources/gofer/system-skills/gofer_vocabulary/SKILL.md +0 -112
  363. package/resources/gofer/system-skills/gofer_zoom_out/SKILL.md +0 -112
  364. package/resources/gofer/templates/ui-approval-template.md +0 -32
@@ -0,0 +1,584 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { createHash } from 'crypto';
4
+ import { constants } from 'fs';
5
+ import { promises as fs } from 'fs';
6
+ import path from 'path';
7
+ import process from 'process';
8
+ import { credentialStatus, resolveApiKey } from './gofer-typesafe-credentials.mjs';
9
+
10
+ const POLICY_RELATIVE_PATH = path.join('.specify', 'config', 'typesafe-semantic-review.json');
11
+ const TYPE_SAFE_TOTAL_INPUT_LIMIT = 64 * 1024;
12
+ const TYPE_SAFE_STATE_QUESTION_LIMIT = 32 * 1024;
13
+ const TYPE_SAFE_SAFETY_RESERVE_BYTES = 4 * 1024;
14
+ const MAX_PROVIDER_ERROR_BYTES = 4 * 1024;
15
+ const MAX_PROVIDER_RESPONSE_BYTES = 64 * 1024;
16
+ const REQUESTED_MODEL = 'jev-latest';
17
+ const CHAT_SECRET_PATTERNS = [
18
+ /\bBearer\s+[A-Za-z0-9._~+\/-]+=*/gi,
19
+ /\b(?:sk|ghp|github_pat|xox[baprs])[-_][A-Za-z0-9_-]{12,}\b/g,
20
+ /\bTYPESAFE_API_KEY\s*=\s*[^\s]+/gi,
21
+ /\b(?:api[-_]?key|access[-_]?token|password|secret|token)\s*=\s*[^\s&]+/gi,
22
+ ];
23
+ // A local artifact far larger than any real spec/plan/tasks file is treated
24
+ // as a hard error rather than hashed in full: this bounds worst-case memory
25
+ // use independent of the provider payload cap.
26
+ const MAX_ARTIFACT_READ_BYTES = 8 * 1024 * 1024;
27
+ const DELIVERY_ARTIFACTS = [
28
+ 'goal-ledger.json',
29
+ 'spec.md',
30
+ 'plan.md',
31
+ 'tasks.md',
32
+ 'decisions.md',
33
+ 'traceability.md',
34
+ 'test-spec.md',
35
+ 'change-manifest.json',
36
+ 'blast-radius-report.md',
37
+ ];
38
+
39
+ function digest(value) { return createHash('sha256').update(value).digest('hex'); }
40
+ // O_NOFOLLOW is unavailable on Windows; the bitwise OR silently contributes
41
+ // nothing there rather than erroring, which would otherwise look like
42
+ // protection that isn't actually applied. lstat detects a symlink or
43
+ // junction cross-platform (including Windows) and is the actual protection;
44
+ // O_NOFOLLOW only closes the small remaining gap between that check and the
45
+ // open, on platforms that support it.
46
+ const noFollowFlag = process.platform === 'win32' ? 0 : constants.O_NOFOLLOW;
47
+ async function assertNotSymlink(target) {
48
+ const info = await fs.lstat(target).catch((error) => {
49
+ if (error?.code === 'ENOENT') return null;
50
+ throw error;
51
+ });
52
+ if (info?.isSymbolicLink()) throw new Error('Gofer path must not be a symbolic link.');
53
+ return info;
54
+ }
55
+ async function openExistingNoFollow(target, flags) {
56
+ await assertNotSymlink(target);
57
+ return fs.open(target, flags | noFollowFlag);
58
+ }
59
+ // assertNoSymlinkComponents only walks descendants of root; it never checks
60
+ // root itself. A caller-supplied workspace that is a symlink (or missing, or
61
+ // not a directory) would otherwise sail through every confinement check
62
+ // below it. Matches workspace-bootstrap-lib.mjs's assertSafeWorkspaceRoot.
63
+ async function assertSafeWorkspaceRoot(workspaceRoot) {
64
+ const resolvedRoot = path.resolve(workspaceRoot);
65
+ const rootStat = await fs.lstat(resolvedRoot).catch((error) => {
66
+ if (error?.code === 'ENOENT') return null;
67
+ throw error;
68
+ });
69
+ if (!rootStat) throw new Error('Gofer workspace root does not exist.');
70
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
71
+ throw new Error('Gofer workspace root must be a real directory, not a symbolic link.');
72
+ }
73
+ return resolvedRoot;
74
+ }
75
+ // The event name is used verbatim to build the receipt file path. It is
76
+ // checked against policy.events, but that list is itself external config
77
+ // (or attacker-influenced if the config is compromised) and is never
78
+ // restricted to a safe charset, so a value like "../../evil" would satisfy
79
+ // the membership check while still escaping receiptDir on join. Restrict to
80
+ // a safe charset before it ever reaches a path.
81
+ function assertSafeEventName(event) {
82
+ if (!/^[A-Za-z0-9_-]+$/.test(event)) {
83
+ throw new Error('Gofer TypeSafe event name contains unsupported characters.');
84
+ }
85
+ }
86
+ // A lexical check alone does not stop a symlinked intermediate directory (or
87
+ // the feature directory itself) from redirecting reads/writes outside the
88
+ // workspace. Walk every component from the workspace root and reject any
89
+ // that is a symlink, matching this repository's other protected readers.
90
+ async function assertNoSymlinkComponents(root, relativeTarget) {
91
+ const components = relativeTarget.split(path.sep).filter(Boolean);
92
+ let currentPath = root;
93
+ for (const component of components) {
94
+ currentPath = path.join(currentPath, component);
95
+ try {
96
+ const status = await fs.lstat(currentPath);
97
+ if (status.isSymbolicLink()) throw new Error('Gofer feature directory must not pass through a symbolic link.');
98
+ } catch (error) {
99
+ if (error?.code === 'ENOENT') return;
100
+ throw error;
101
+ }
102
+ }
103
+ }
104
+ // Reject any feature directory outside the workspace instead of trusting the
105
+ // caller: an escaping path would read arbitrary files, send their contents to
106
+ // the external provider, and write the receipt outside the workspace.
107
+ async function confined(workspace, value) {
108
+ const target = path.resolve(workspace, value);
109
+ const relative = path.relative(workspace, target);
110
+ if (relative === '..' || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
111
+ throw new Error('Gofer feature directory must remain inside the workspace.');
112
+ }
113
+ await assertNoSymlinkComponents(workspace, relative);
114
+ // A typo'd or nonexistent feature directory must fail closed here, not
115
+ // silently produce an all-empty state that could earn a false "aligned"
116
+ // verdict and then have the directory created out from under it by mkdir
117
+ // when the receipt is written.
118
+ const info = await fs.lstat(target).catch(() => null);
119
+ if (!info || !info.isDirectory()) throw new Error('Gofer feature directory does not exist.');
120
+ return target;
121
+ }
122
+ async function parseArgs(argv) {
123
+ const args = { workspace: process.cwd(), featureDir: '', event: '', json: false };
124
+ for (let index = 0; index < argv.length; index += 1) {
125
+ const arg = argv[index];
126
+ if (arg === '--workspace') { args.workspace = argv[++index] || args.workspace; }
127
+ else if (arg === '--feature-dir') { args.featureDir = argv[++index] || ''; }
128
+ else if (arg === '--event') { args.event = argv[++index] || ''; }
129
+ else if (arg === '--json') { args.json = true; }
130
+ else throw new Error(`Unknown argument: ${arg}`);
131
+ }
132
+ if (!args.featureDir || !args.event) throw new Error('--feature-dir and --event are required');
133
+ assertSafeEventName(args.event);
134
+ args.workspace = await assertSafeWorkspaceRoot(args.workspace); args.featureDir = await confined(args.workspace, args.featureDir); return args;
135
+ }
136
+ // The policy path is fixed, but a symlinked .specify/config directory or the
137
+ // policy file itself could still redirect this read outside the workspace,
138
+ // bypassing the same confinement invariant enforced for the feature
139
+ // directory and its artifacts. Apply the same no-follow protection.
140
+ async function readConfinedJson(workspace, relativePath) {
141
+ await assertNoSymlinkComponents(workspace, relativePath);
142
+ const target = path.join(workspace, relativePath);
143
+ const handle = await openExistingNoFollow(target, constants.O_RDONLY);
144
+ try { return JSON.parse(await handle.readFile('utf8')); } finally { await handle.close(); }
145
+ }
146
+ // Hash and retain the complete bounded text so the provider never judges an
147
+ // artifact prefix. The local read cap bounds memory. Open with O_NOFOLLOW so a
148
+ // same-account symlink swap cannot redirect the read. Missing artifacts stay
149
+ // distinct from empty files and fail closed before any provider request.
150
+ async function readArtifact(target) {
151
+ let handle;
152
+ try { handle = await openExistingNoFollow(target, constants.O_RDONLY); }
153
+ catch (error) {
154
+ if (error?.code === 'ENOENT') return { present: false, sha256: digest(''), sent: '' };
155
+ throw error;
156
+ }
157
+ try {
158
+ const info = await handle.stat();
159
+ if (!info.isFile()) throw new Error('Gofer feature artifact must be a regular file.');
160
+ if (info.size > MAX_ARTIFACT_READ_BYTES) throw new Error('Gofer feature artifact exceeds the maximum readable size.');
161
+ const hash = createHash('sha256');
162
+ const contentChunks = [];
163
+ let fullBytes = 0;
164
+ // autoClose defaults to true, which would close the handle at EOF and
165
+ // make the finally block's own close() below fail on an already-closed
166
+ // handle. The finally block owns the close; the stream must not race it.
167
+ for await (const chunk of handle.createReadStream({ autoClose: false })) {
168
+ fullBytes += chunk.length;
169
+ if (fullBytes > MAX_ARTIFACT_READ_BYTES) throw new Error('Gofer feature artifact exceeds the maximum readable size.');
170
+ hash.update(chunk);
171
+ contentChunks.push(chunk);
172
+ }
173
+ const content = new TextDecoder('utf-8', { fatal: true }).decode(Buffer.concat(contentChunks));
174
+ return { present: true, sha256: hash.digest('hex'), bytes: fullBytes, content, complete: true };
175
+ } finally { await handle.close(); }
176
+ }
177
+ function normalizeAnswer(answer) { return String(answer || '').trim().toLowerCase(); }
178
+ function redactChatContext(value) {
179
+ let redacted = String(value || '');
180
+ for (const pattern of CHAT_SECRET_PATTERNS) redacted = redacted.replace(pattern, '[REDACTED]');
181
+ return redacted;
182
+ }
183
+
184
+ const QUESTION_CHOICES = {
185
+ goal_alignment: ['aligned', 'partial', 'conflict'],
186
+ specification_currency: ['aligned', 'partial', 'conflict'],
187
+ test_coverage: ['aligned', 'partial', 'conflict'],
188
+ blast_radius: ['aligned', 'partial', 'conflict'],
189
+ chat_readiness: ['ready', 'reconcile', 'ask_user'],
190
+ required_action: ['continue', 'reconcile', 'ask_user'],
191
+ };
192
+
193
+ function buildQuestions(event) {
194
+ return {
195
+ goal_alignment: { type: 'choice', instructions: 'Does the current work remain aligned to the approved goal and specification?', criteria: { aligned: 'The work remains aligned.', partial: 'The work needs document reconciliation.', conflict: 'The work conflicts with approved direction.' } },
196
+ specification_currency: { type: 'choice', instructions: 'Compare the supplied specification, plan, tasks, decisions, traceability, and change manifest. Are the stated implementation paths, completed-task status, commit evidence, and material changes internally consistent and current? Select partial only when you can identify a concrete missing or stale record.', criteria: { aligned: 'All supplied delivery records agree on the current implementation and no concrete stale or missing record is present.', partial: 'A concrete delivery record is missing, stale, or inconsistent and needs reconciliation.', conflict: 'The recorded implementation conflicts with the approved specification or decision.' } },
197
+ test_coverage: { type: 'choice', instructions: 'Does the test specification map the changed behavior to executable owning-repository tests and any required eai-testing-dev release evidence?', criteria: { aligned: 'The changed behavior has complete executable coverage.', partial: 'The test evidence is incomplete or stale.', conflict: 'The test evidence contradicts the claimed behavior.' } },
198
+ blast_radius: { type: 'choice', instructions: 'Does the change manifest and blast-radius report cover all affected interfaces, packages, release surfaces, dependencies, rollback paths, and external test contracts?', criteria: { aligned: 'The blast radius is complete and contained.', partial: 'The blast-radius evidence is incomplete or stale.', conflict: 'The reported blast radius conflicts with the changed surface.' } },
199
+ ...(event === 'chat_readiness' ? { chat_readiness: { type: 'choice', instructions: 'Using the supplied chat context summary and the current goal, specification, plan, tasks, decisions, and approved edit scope, is this chat ready to deliver the requested outcome? Identify any concrete missing context or contradiction.', criteria: { ready: 'The request and constraints are clear, the approved feature records match the current request, and the next task and edit scope are defined.', reconcile: 'The chat context or feature records have a concrete gap or inconsistency that must be reconciled before code edits.', ask_user: 'A material business, security, cost, deployment, or destructive decision requires the user.' } } } : {}),
200
+ required_action: { type: 'choice', instructions: 'Choose the action required by the evidence judgments. Select continue only when all are aligned; select reconcile when any is partial or evidence is missing; select ask_user only for a genuine conflict requiring a new business decision.', criteria: { continue: 'All evidence judgments are aligned.', reconcile: 'At least one evidence judgment is partial or missing.', ask_user: 'A conflict requires a material user decision.' } },
201
+ };
202
+ }
203
+
204
+ // TypeSafe publishes token limits, not a tokenizer endpoint. Count the exact
205
+ // UTF-8 JSON representation as a conservative local upper bound and reserve
206
+ // 4 KiB for provider framing. This is not an exact token estimate.
207
+ export function measureTypeSafeInputBudget(state, questions) {
208
+ const stateBytes = Buffer.byteLength(JSON.stringify(state), 'utf8');
209
+ const questionEntries = Object.entries(questions || {});
210
+ const questionBytes = questionEntries.map(([id, question]) => Buffer.byteLength(JSON.stringify({ [id]: question }), 'utf8'));
211
+ const longestQuestionBytes = questionBytes.length ? Math.max(...questionBytes) : 0;
212
+ const allQuestionsBytes = Buffer.byteLength(JSON.stringify(questions || {}), 'utf8');
213
+ const statePlusLongestQuestionBytes = stateBytes + longestQuestionBytes;
214
+ const statePlusAllQuestionsBytes = stateBytes + allQuestionsBytes;
215
+ const safePairLimitBytes = TYPE_SAFE_STATE_QUESTION_LIMIT - TYPE_SAFE_SAFETY_RESERVE_BYTES;
216
+ const safeTotalLimitBytes = TYPE_SAFE_TOTAL_INPUT_LIMIT - TYPE_SAFE_SAFETY_RESERVE_BYTES;
217
+ return {
218
+ basis: 'utf8_serialized_bytes_upper_bound_not_tokenizer',
219
+ stateBytes,
220
+ longestQuestionBytes,
221
+ allQuestionsBytes,
222
+ statePlusLongestQuestionBytes,
223
+ statePlusAllQuestionsBytes,
224
+ safetyReserveBytes: TYPE_SAFE_SAFETY_RESERVE_BYTES,
225
+ publishedStateQuestionLimit: TYPE_SAFE_STATE_QUESTION_LIMIT,
226
+ publishedTotalInputLimit: TYPE_SAFE_TOTAL_INPUT_LIMIT,
227
+ safeStateQuestionLimitBytes: safePairLimitBytes,
228
+ safeTotalInputLimitBytes: safeTotalLimitBytes,
229
+ withinLimits: statePlusLongestQuestionBytes <= safePairLimitBytes && statePlusAllQuestionsBytes <= safeTotalLimitBytes,
230
+ };
231
+ }
232
+
233
+ async function readBoundedResponseText(response, limit = MAX_PROVIDER_ERROR_BYTES) {
234
+ if (!response.body) return { text: '', truncated: false };
235
+ const reader = response.body.getReader();
236
+ const chunks = [];
237
+ let bytes = 0;
238
+ let truncated = false;
239
+ try {
240
+ while (true) {
241
+ const { done, value } = await reader.read();
242
+ if (done) break;
243
+ const remaining = limit - bytes;
244
+ if (value.length > remaining) {
245
+ if (remaining > 0) chunks.push(value.subarray(0, remaining));
246
+ bytes += Math.max(remaining, 0);
247
+ truncated = true;
248
+ await reader.cancel().catch(() => {});
249
+ break;
250
+ }
251
+ chunks.push(value);
252
+ bytes += value.length;
253
+ }
254
+ } finally {
255
+ try { reader.releaseLock(); } catch {}
256
+ }
257
+ return { text: Buffer.concat(chunks).toString('utf8'), truncated };
258
+ }
259
+
260
+ function safeToken(value, maxLength = 80) {
261
+ return typeof value === 'string' && value.length <= maxLength && /^[A-Za-z0-9_.:-]+$/.test(value) ? value : null;
262
+ }
263
+
264
+ function safeLocation(value) {
265
+ if (!Array.isArray(value)) return null;
266
+ const segments = value.slice(0, 8).map((segment) => {
267
+ if (Number.isInteger(segment) && segment >= 0) return String(segment);
268
+ return safeToken(segment);
269
+ });
270
+ return segments.length && segments.every(Boolean) ? segments.join('.') : null;
271
+ }
272
+
273
+ function providerDiagnostics(response, bodyText, bodyTruncated) {
274
+ const headers = response.headers;
275
+ const requestId = safeToken(headers?.get?.('x-request-id') || headers?.get?.('request-id') || '', 128);
276
+ const diagnostic = { httpStatus: response.status };
277
+ if (requestId) diagnostic.requestId = requestId;
278
+ if (bodyTruncated) {
279
+ diagnostic.bodyTruncated = true;
280
+ return diagnostic;
281
+ }
282
+ let payload;
283
+ try { payload = JSON.parse(bodyText); } catch { return diagnostic; }
284
+ const code = safeToken(payload?.code || payload?.type);
285
+ if (code) diagnostic.code = code;
286
+ const details = Array.isArray(payload?.detail) ? payload.detail : Array.isArray(payload?.errors) ? payload.errors : [];
287
+ const validation = details.slice(0, 8).map((item) => ({
288
+ ...(safeLocation(item?.loc) ? { location: safeLocation(item.loc) } : {}),
289
+ ...(safeToken(item?.type) ? { type: safeToken(item.type) } : {}),
290
+ })).filter((item) => item.location || item.type);
291
+ if (validation.length) diagnostic.validation = validation;
292
+ return diagnostic;
293
+ }
294
+
295
+ function normalizeProbabilities(answer, questionId) {
296
+ if (!answer?.probabilities || typeof answer.probabilities !== 'object' || Array.isArray(answer.probabilities)) return null;
297
+ const allowed = new Set(QUESTION_CHOICES[questionId] || []);
298
+ const entries = Object.entries(answer.probabilities).filter(([choice, probability]) =>
299
+ allowed.has(choice) && typeof probability === 'number' && Number.isFinite(probability) && probability >= 0 && probability <= 1
300
+ );
301
+ const total = entries.reduce((sum, [, probability]) => sum + probability, 0);
302
+ return entries.length && Math.abs(total - 1) <= 0.02 ? Object.fromEntries(entries) : null;
303
+ }
304
+
305
+ function answerRecord(answer, questionId) {
306
+ const probabilities = normalizeProbabilities(answer, questionId);
307
+ const choice = typeof answer?.choice === 'string' && QUESTION_CHOICES[questionId]?.includes(answer.choice) ? answer.choice : null;
308
+ const closestAlternative = probabilities
309
+ ? Object.entries(probabilities).filter(([option]) => option !== choice).sort((left, right) => right[1] - left[1])[0]
310
+ : null;
311
+ return {
312
+ choice,
313
+ confidence: answer?.confidence ?? null,
314
+ probabilities,
315
+ closestAlternative: closestAlternative ? { choice: closestAlternative[0], probability: closestAlternative[1] } : null,
316
+ };
317
+ }
318
+
319
+ async function saveReceipt(featureDir, event, receipt) {
320
+ const receiptRelativeDir = path.join('evidence', 'semantic-review');
321
+ await assertNoSymlinkComponents(featureDir, receiptRelativeDir);
322
+ const receiptDir = path.join(featureDir, receiptRelativeDir);
323
+ await fs.mkdir(receiptDir, { recursive: true });
324
+ const receiptPath = path.join(receiptDir, `${event}.json`);
325
+ await assertNotSymlink(receiptPath);
326
+ let previous;
327
+ try {
328
+ const previousHandle = await openExistingNoFollow(receiptPath, constants.O_RDONLY);
329
+ try { previous = await previousHandle.readFile('utf8'); } finally { await previousHandle.close(); }
330
+ } catch (error) {
331
+ if (error?.code !== 'ENOENT') throw error;
332
+ }
333
+ if (previous) {
334
+ let previousVersion;
335
+ try { previousVersion = JSON.parse(previous).schemaVersion; } catch {}
336
+ if (previousVersion === 2) {
337
+ const archivePath = path.join(receiptDir, `${event}.v2.json`);
338
+ await assertNotSymlink(archivePath);
339
+ try {
340
+ const archiveHandle = await fs.open(archivePath, constants.O_WRONLY | constants.O_CREAT | constants.O_EXCL | noFollowFlag, 0o600);
341
+ try { await archiveHandle.writeFile(previous); } finally { await archiveHandle.close(); }
342
+ } catch (error) {
343
+ if (error?.code !== 'EEXIST') throw error;
344
+ const archiveHandle = await openExistingNoFollow(archivePath, constants.O_RDONLY);
345
+ try {
346
+ if ((await archiveHandle.readFile('utf8')) !== previous) {
347
+ throw new Error('A different version-2 receipt archive already exists. Preserve it before writing a new review.');
348
+ }
349
+ } finally { await archiveHandle.close(); }
350
+ }
351
+ }
352
+ }
353
+ const receiptHandle = await fs.open(receiptPath, constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC | noFollowFlag, 0o600);
354
+ try { await receiptHandle.writeFile(`${JSON.stringify(receipt, null, 2)}\n`); } finally { await receiptHandle.close(); }
355
+ return receipt;
356
+ }
357
+ // A response outside this set is unexpected (a provider bug, a new API
358
+ // version, a malformed payload) and must not be read as a silent pass.
359
+ const KNOWN_ALIGNMENTS = new Set(['aligned', 'partial', 'conflict']);
360
+ const KNOWN_ACTIONS = new Set(['continue', 'reconcile', 'ask_user']);
361
+ const KNOWN_CHAT_READINESS = new Set(['ready', 'reconcile', 'ask_user']);
362
+
363
+ export async function runSemanticReview({ workspace = process.cwd(), featureDir, event, fetchImpl = globalThis.fetch, env = process.env, keychain, chatContext, onDemand = false } = {}) {
364
+ assertSafeEventName(event);
365
+ const resolvedWorkspace = await assertSafeWorkspaceRoot(workspace);
366
+ featureDir = await confined(resolvedWorkspace, featureDir);
367
+ const policy = await readConfinedJson(resolvedWorkspace, POLICY_RELATIVE_PATH);
368
+ const credentials = await credentialStatus({ workspace, env, keychain });
369
+ const globallyConnected = credentials.source === 'macos_keychain';
370
+ if (!policy.enabled && !globallyConnected && !onDemand) return { status: 'disabled', event };
371
+ // A syntactically valid but malformed enabled policy must not silently
372
+ // bypass its own gate: `confidence < undefined` is always false in JS, so
373
+ // a missing/non-numeric minimumConfidence would otherwise let any
374
+ // confidence "pass" the threshold check regardless of its actual value.
375
+ if (!Array.isArray(policy.events) || !policy.events.every((value) => typeof value === 'string') ||
376
+ typeof policy.minimumConfidence !== 'number' || !Number.isFinite(policy.minimumConfidence) ||
377
+ policy.minimumConfidence < 0 || policy.minimumConfidence > 1) {
378
+ throw new Error('Gofer TypeSafe policy is enabled but malformed (events or minimumConfidence).');
379
+ }
380
+ if (!policy.events.includes(event)) return { status: onDemand ? 'not_enabled_for_event' : 'disabled', event };
381
+ if (!credentials.configured) return { status: 'not_configured', event };
382
+ if (event === 'chat_readiness' && !String(chatContext || '').trim()) {
383
+ return { status: 'unavailable', event, reason: 'chat_context_missing' };
384
+ }
385
+ if (event !== 'chat_readiness' && chatContext !== undefined) {
386
+ return { status: 'unavailable', event, reason: 'chat_context_not_allowed_for_event' };
387
+ }
388
+ const artifacts = await Promise.all(DELIVERY_ARTIFACTS.map(async (name) => [name, await readArtifact(path.join(featureDir, name))]));
389
+ const missingArtifacts = artifacts.filter(([, info]) => !info.present).map(([name]) => name);
390
+ const artifactHashes = Object.fromEntries(artifacts.map(([name, info]) => [name, info.sha256]));
391
+ const artifactCoverage = Object.fromEntries(artifacts.map(([name, info]) => [name, {
392
+ present: info.present,
393
+ complete: Boolean(info.complete),
394
+ sourceBytes: info.bytes ?? 0,
395
+ sentBytes: 0,
396
+ }]));
397
+ const state = Object.fromEntries(artifacts.map(([name, info]) => [name, {
398
+ present: info.present,
399
+ complete: Boolean(info.complete),
400
+ sha256: info.sha256,
401
+ byteLength: info.bytes ?? 0,
402
+ content: info.content,
403
+ }]));
404
+ const redactedChatContext = event === 'chat_readiness' ? redactChatContext(chatContext) : '';
405
+ const chatContextSha256 = event === 'chat_readiness' ? digest(redactedChatContext) : null;
406
+ const chatContextBytes = event === 'chat_readiness' ? Buffer.byteLength(redactedChatContext, 'utf8') : null;
407
+ if (event === 'chat_readiness') {
408
+ state.chat_context = {
409
+ present: Boolean(redactedChatContext),
410
+ complete: true,
411
+ summaryOnly: true,
412
+ sha256: chatContextSha256,
413
+ byteLength: chatContextBytes,
414
+ content: redactedChatContext,
415
+ };
416
+ }
417
+ const questions = buildQuestions(event);
418
+ const inputBudget = measureTypeSafeInputBudget(state, questions);
419
+ const missingRequiredEvidence = missingArtifacts.length > 0;
420
+ const baseReceipt = {
421
+ schemaVersion: 3,
422
+ provider: 'typesafe',
423
+ event,
424
+ status: 'unavailable',
425
+ reason: null,
426
+ requestAttempted: false,
427
+ requestSent: false,
428
+ requestedModel: REQUESTED_MODEL,
429
+ model: null,
430
+ usage: null,
431
+ inputBudget,
432
+ missingArtifacts,
433
+ missingRequiredEvidence,
434
+ policySha256: digest(JSON.stringify(policy)),
435
+ artifacts: artifactHashes,
436
+ artifactCoverage,
437
+ ...(event === 'chat_readiness' ? { chatContextSha256, chatContextBytes } : {}),
438
+ confidence: null,
439
+ confidenceGate: {
440
+ minimum: policy.minimumConfidence,
441
+ observed: null,
442
+ belowMinimum: null,
443
+ weakestQuestion: null,
444
+ },
445
+ answers: {},
446
+ providerError: null,
447
+ };
448
+ if (missingRequiredEvidence) {
449
+ return saveReceipt(featureDir, event, {
450
+ ...baseReceipt,
451
+ status: 'reconcile',
452
+ reason: 'required_artifacts_missing',
453
+ });
454
+ }
455
+ if (!inputBudget.withinLimits) {
456
+ return saveReceipt(featureDir, event, {
457
+ ...baseReceipt,
458
+ status: 'unavailable',
459
+ reason: 'input_budget_exceeded',
460
+ });
461
+ }
462
+
463
+ const { apiKey } = await resolveApiKey({ workspace, env, keychain });
464
+ let response;
465
+ try {
466
+ baseReceipt.requestAttempted = true;
467
+ response = await fetchImpl('https://api.typesafe.ai/v1/systemone', {
468
+ method: 'POST', headers: { authorization: `Bearer ${apiKey}`, 'content-type': 'application/json' },
469
+ signal: AbortSignal.timeout(10_000),
470
+ body: JSON.stringify({ model: REQUESTED_MODEL, state, questions }),
471
+ });
472
+ } catch (error) {
473
+ return saveReceipt(featureDir, event, {
474
+ ...baseReceipt,
475
+ requestSent: null,
476
+ reason: error?.name === 'TimeoutError' || error?.name === 'AbortError' ? 'timeout' : 'network_error',
477
+ artifactCoverage: Object.fromEntries(artifacts.map(([name]) => [name, { ...artifactCoverage[name], sentBytes: null }])),
478
+ });
479
+ }
480
+ baseReceipt.requestSent = true;
481
+ baseReceipt.artifactCoverage = Object.fromEntries(artifacts.map(([name, info]) => [name, {
482
+ ...artifactCoverage[name],
483
+ sentBytes: info.bytes ?? 0,
484
+ }]));
485
+ if (!response.ok) {
486
+ const errorBody = await readBoundedResponseText(response);
487
+ const providerError = providerDiagnostics(response, errorBody.text, errorBody.truncated);
488
+ return saveReceipt(featureDir, event, {
489
+ ...baseReceipt,
490
+ requestSent: true,
491
+ reason: 'provider_http_error',
492
+ providerError,
493
+ });
494
+ }
495
+ const responseBody = await readBoundedResponseText(response, MAX_PROVIDER_RESPONSE_BYTES);
496
+ if (responseBody.truncated) {
497
+ return saveReceipt(featureDir, event, { ...baseReceipt, reason: 'response_too_large' });
498
+ }
499
+ let payload;
500
+ try { payload = JSON.parse(responseBody.text); } catch {
501
+ return saveReceipt(featureDir, event, { ...baseReceipt, reason: 'invalid_response_body' });
502
+ }
503
+ const answers = payload.answers && typeof payload.answers === 'object' ? payload.answers : {};
504
+ const alignmentAnswer = answers.goal_alignment || {};
505
+ const specificationAnswer = answers.specification_currency || {};
506
+ const testAnswer = answers.test_coverage || {};
507
+ const blastRadiusAnswer = answers.blast_radius || {};
508
+ const actionAnswer = answers.required_action || {};
509
+ const chatReadinessAnswer = answers.chat_readiness || {};
510
+ const alignment = normalizeAnswer(alignmentAnswer.choice);
511
+ const specification = normalizeAnswer(specificationAnswer.choice);
512
+ const testCoverage = normalizeAnswer(testAnswer.choice);
513
+ const blastRadius = normalizeAnswer(blastRadiusAnswer.choice);
514
+ const action = normalizeAnswer(actionAnswer.choice);
515
+ const chatReadiness = event === 'chat_readiness' ? normalizeAnswer(chatReadinessAnswer.choice) : '';
516
+ // An invalid or out-of-range confidence must count as zero, not be
517
+ // dropped: dropping it would let one bad value be outweighed by the
518
+ // other, and an out-of-range value (e.g. 2) would otherwise satisfy the
519
+ // minimum-confidence check on a malformed response.
520
+ const responseAnswers = [alignmentAnswer, specificationAnswer, testAnswer, blastRadiusAnswer, actionAnswer,
521
+ ...(event === 'chat_readiness' ? [chatReadinessAnswer] : [])];
522
+ const confidences = responseAnswers.map((answer) => {
523
+ const value = Number(answer.confidence);
524
+ return Number.isFinite(value) && value >= 0 && value <= 1 ? value : 0;
525
+ });
526
+ const confidence = Math.min(...confidences);
527
+ // An answer outside the known choice set fails toward reconcile, not
528
+ // toward a silent aligned pass: an unexpected label must not be read as
529
+ // "everything is fine".
530
+ const evidenceAlignments = [alignment, specification, testCoverage, blastRadius];
531
+ const recognized = evidenceAlignments.every((value) => KNOWN_ALIGNMENTS.has(value)) && KNOWN_ACTIONS.has(action) &&
532
+ (event !== 'chat_readiness' || KNOWN_CHAT_READINESS.has(chatReadiness));
533
+ const status = evidenceAlignments.includes('conflict') || action === 'ask_user' || chatReadiness === 'ask_user' ? 'conflict'
534
+ : !recognized || confidence < policy.minimumConfidence || evidenceAlignments.includes('partial') || action === 'reconcile' || chatReadiness === 'reconcile' ? 'reconcile'
535
+ : 'aligned';
536
+ const answerIds = ['goal_alignment', 'specification_currency', 'test_coverage', 'blast_radius', 'required_action',
537
+ ...(event === 'chat_readiness' ? ['chat_readiness'] : [])];
538
+ const answerRecords = Object.fromEntries(answerIds.map((id) => [id, answerRecord(answers[id] || {}, id)]));
539
+ const weakestIndex = confidences.indexOf(confidence);
540
+ const weakestQuestionId = answerIds[weakestIndex] || null;
541
+ const weakestQuestion = weakestQuestionId ? answerRecords[weakestQuestionId] : null;
542
+ const providerModel = safeToken(payload.model, 80);
543
+ const usage = payload.usage && Number.isSafeInteger(payload.usage.input_tokens) && payload.usage.input_tokens >= 0 &&
544
+ Number.isSafeInteger(payload.usage.output_tokens) && payload.usage.output_tokens >= 0
545
+ ? { inputTokens: payload.usage.input_tokens, outputTokens: payload.usage.output_tokens }
546
+ : null;
547
+ return saveReceipt(featureDir, event, {
548
+ ...baseReceipt,
549
+ status,
550
+ reason: status === 'reconcile' ? (confidence < policy.minimumConfidence ? 'confidence_below_minimum_or_evidence_needs_reconciliation' : 'evidence_needs_reconciliation') : null,
551
+ requestSent: true,
552
+ model: providerModel,
553
+ usage,
554
+ confidence,
555
+ confidenceGate: {
556
+ minimum: policy.minimumConfidence,
557
+ observed: confidence,
558
+ belowMinimum: confidence < policy.minimumConfidence,
559
+ weakestQuestion: weakestQuestionId ? {
560
+ id: weakestQuestionId,
561
+ choice: weakestQuestion.choice,
562
+ confidence: weakestQuestion.confidence,
563
+ probabilities: weakestQuestion.probabilities,
564
+ closestAlternative: weakestQuestion.closestAlternative,
565
+ } : null,
566
+ },
567
+ answers: answerRecords,
568
+ });
569
+ }
570
+ // A CI/automation caller must not read "exit 0" as a pass for anything other
571
+ // than a genuine alignment or an intentionally inactive review: reconcile,
572
+ // unavailable, and not_configured all mean the checkpoint was not verified.
573
+ export function exitCodeForStatus(status) {
574
+ if (status === 'conflict') return 2;
575
+ if (!['disabled', 'aligned'].includes(status)) return 3;
576
+ return 0;
577
+ }
578
+ async function main() {
579
+ const args = await parseArgs(process.argv.slice(2));
580
+ const result = await runSemanticReview(args);
581
+ process.stdout.write(`${JSON.stringify(result)}\n`);
582
+ process.exitCode = exitCodeForStatus(result.status);
583
+ }
584
+ if (import.meta.url === `file://${process.argv[1]}`) main().catch((error) => { process.stderr.write(`${error.message}\n`); process.exitCode = 1; });