@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
@@ -1,16 +1,22 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { promises as fs } from 'fs';
4
+ import { randomUUID } from 'crypto';
4
5
  import path from 'path';
5
6
  import { fileURLToPath } from 'url';
7
+ import { SUPPORTED_HOSTS } from './gofer-surface-update.mjs';
6
8
  import { parseStageCommand } from './parse-stage-command.mjs';
7
9
 
8
10
  export const GOFER_VERSION_FILE = path.join('.specify', '.gofer-version');
9
11
  export const CORE_SENTINELS = [
10
12
  GOFER_VERSION_FILE,
11
13
  path.join('.specify', 'commands', '0_gofer_start.md'),
14
+ path.join('.specify', 'config', 'object-type-routing.json'),
15
+ path.join('.specify', 'config', 'typesafe-semantic-review.json'),
12
16
  path.join('.specify', 'templates', 'spec-template.md'),
17
+ path.join('.specify', 'templates', 'build-map-template.md'),
13
18
  path.join('.specify', 'templates', 'loop-contract-template.json'),
19
+ path.join('.specify', 'templates', 'business-scenarios-template.json'),
14
20
  path.join('.specify', 'templates', 'working-backwards-prfaq-template.md'),
15
21
  path.join('.specify', 'templates', 'business-owner-summary-template.md'),
16
22
  path.join('.specify', 'templates', 'cto-architecture-summary-template.md'),
@@ -21,7 +27,11 @@ export const CORE_SENTINELS = [
21
27
  path.join('.specify', 'templates', 'brand', 'marp-theme-template.css'),
22
28
  path.join('.specify', 'scripts', 'bash', 'create-new-feature.sh'),
23
29
  path.join('.specify', 'scripts', 'node', 'parse-stage-command.mjs'),
30
+ path.join('.specify', 'scripts', 'node', 'gofer-local-settings-cleanup.mjs'),
24
31
  path.join('.specify', 'scripts', 'node', 'gofer-loop-audit.mjs'),
32
+ path.join('.specify', 'scripts', 'node', 'gofer-typesafe-credentials.mjs'),
33
+ path.join('.specify', 'scripts', 'node', 'gofer-semantic-drift.mjs'),
34
+ path.join('.specify', 'scripts', 'node', 'gofer-ui-preview.mjs'),
25
35
  path.join('.specify', 'scripts', 'hooks', 'post-tool-use.mjs'),
26
36
  path.join('.specify', 'scripts', 'powershell', 'install-optional-tools.ps1'),
27
37
  path.join('.specify', 'references', 'platform', 'README.md'),
@@ -42,8 +52,11 @@ const LEGACY_MANAGED_PATHS = [
42
52
  path.join('.gemini', 'commands', 'gofer', '0_business_scenario.md'),
43
53
  path.join('.gemini', 'commands', 'gofer', '0_business_scenario.toml'),
44
54
  ];
55
+ const RETIRED_PUBLIC_ENTRYPOINT_STEMS = ['gofer'];
45
56
  const LEGACY_MANAGED_ARCHIVE_ROOT = path.join('.specify', 'logs', 'legacy-command-backups');
46
57
 
58
+ export const WORKSPACE_HOSTS = SUPPORTED_HOSTS;
59
+
47
60
  export const HOST_POLICIES = {
48
61
  auto: { required: [] },
49
62
  claude: {
@@ -55,11 +68,21 @@ export const HOST_POLICIES = {
55
68
  copilot: {
56
69
  required: [path.join('.github', 'copilot-instructions.md')],
57
70
  },
58
- gemini: {
59
- required: [],
71
+ antigravity: {
72
+ required: ['AGENTS.md', 'GEMINI.md'],
73
+ },
74
+ grok: {
75
+ required: ['AGENTS.md'],
76
+ },
77
+ vscode: {
78
+ required: [path.join('.github', 'copilot-instructions.md')],
60
79
  },
61
80
  };
62
81
 
82
+ const LEGACY_HOST_ALIASES = {
83
+ gemini: 'antigravity',
84
+ };
85
+
63
86
  const WORKSPACE_MARKERS = [
64
87
  '.git',
65
88
  'package.json',
@@ -77,7 +100,9 @@ const EAI_RUNTIME_CONTRACT_MARKER = 'eai.runtime.json';
77
100
 
78
101
  const EXTENSION_RESOURCE_PATHS = new Map([
79
102
  [path.join('.specify', 'commands'), path.join('resources', 'specify-commands')],
103
+ [path.join('.specify', 'config'), path.join('resources', 'specify-config')],
80
104
  [path.join('.specify', 'references'), path.join('resources', 'references')],
105
+ [path.join('.specify', 'schemas'), path.join('resources', 'schemas')],
81
106
  [path.join('.specify', 'templates'), path.join('resources', 'templates')],
82
107
  [path.join('.specify', 'scripts', 'bash'), path.join('resources', 'bash-scripts')],
83
108
  [path.join('.specify', 'scripts', 'node'), path.join('resources', 'node-scripts')],
@@ -94,6 +119,7 @@ const EXTENSION_RESOURCE_PATHS = new Map([
94
119
  [path.join('.github', 'instructions'), path.join('resources', 'copilot-instructions')],
95
120
  [path.join('.github', 'skills'), path.join('resources', 'github-skills')],
96
121
  [path.join('.gemini'), path.join('resources', 'gemini')],
122
+ [path.join('.grok', 'skills'), path.join('resources', 'grok-skills')],
97
123
  [path.join('.agents', 'skills'), 'skills'],
98
124
  [path.join('.system', 'skills'), 'skills'],
99
125
  ]);
@@ -108,6 +134,7 @@ const GOFER_GITIGNORE_ENTRIES = [
108
134
  '.specify/memory/context-health-state.json',
109
135
  '.specify/memory/observation-cache/',
110
136
  '.specify/specs/*/research-index.json',
137
+ '.specify/secrets/',
111
138
  ];
112
139
 
113
140
  const CLAUDE_HOOKS_CONFIG = {
@@ -160,8 +187,19 @@ function createEmptyProjectInfo(workspaceRoot) {
160
187
  }
161
188
 
162
189
  export function normalizeHost(host = 'auto') {
163
- const normalized = String(host || 'auto').trim().toLowerCase();
164
- return Object.prototype.hasOwnProperty.call(HOST_POLICIES, normalized) ? normalized : 'auto';
190
+ const normalized = host == null ? 'auto' : String(host).trim().toLowerCase();
191
+ if (normalized === '' || normalized === 'auto') {
192
+ return 'auto';
193
+ }
194
+ if (Object.prototype.hasOwnProperty.call(LEGACY_HOST_ALIASES, normalized)) {
195
+ return LEGACY_HOST_ALIASES[normalized];
196
+ }
197
+ if (Object.prototype.hasOwnProperty.call(HOST_POLICIES, normalized)) {
198
+ return normalized;
199
+ }
200
+ throw new Error(
201
+ `Unsupported Gofer host. Expected auto or one of: ${WORKSPACE_HOSTS.join(', ')}.`
202
+ );
165
203
  }
166
204
 
167
205
  export function scriptRootFromUrl(scriptUrl) {
@@ -185,46 +223,392 @@ export async function pathExists(targetPath) {
185
223
  }
186
224
  }
187
225
 
188
- function buildArchiveStamp() {
189
- return `${new Date().toISOString().replace(/[:.]/g, '-')}-${process.pid}`;
226
+ async function lstatIfExists(targetPath) {
227
+ try {
228
+ return await fs.lstat(targetPath);
229
+ } catch (error) {
230
+ if (error?.code === 'ENOENT') {
231
+ return null;
232
+ }
233
+ throw error;
234
+ }
235
+ }
236
+
237
+ function resolveConfinedWorkspacePath(workspaceRoot, relativePath) {
238
+ const resolvedRoot = path.resolve(workspaceRoot);
239
+ if (path.isAbsolute(relativePath)) {
240
+ throw new Error('Unsafe Gofer workspace path: absolute managed paths are not allowed.');
241
+ }
242
+
243
+ const targetPath = path.resolve(resolvedRoot, relativePath);
244
+ const relativeTarget = path.relative(resolvedRoot, targetPath);
245
+ if (
246
+ relativeTarget === '..' ||
247
+ relativeTarget.startsWith(`..${path.sep}`) ||
248
+ path.isAbsolute(relativeTarget)
249
+ ) {
250
+ throw new Error('Unsafe Gofer workspace path: managed paths must stay inside the workspace.');
251
+ }
252
+
253
+ return { resolvedRoot, targetPath, relativeTarget };
254
+ }
255
+
256
+ async function assertSafeWorkspaceRoot(workspaceRoot) {
257
+ const resolvedRoot = path.resolve(workspaceRoot);
258
+ const rootStat = await lstatIfExists(resolvedRoot);
259
+ if (!rootStat) {
260
+ throw new Error('Unsafe Gofer workspace path: the workspace root does not exist.');
261
+ }
262
+ if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
263
+ throw new Error(
264
+ 'Unsafe Gofer workspace path: the workspace root must be a real directory, not a symbolic link.'
265
+ );
266
+ }
267
+ return resolvedRoot;
268
+ }
269
+
270
+ async function inspectWorkspacePath(
271
+ workspaceRoot,
272
+ relativePath,
273
+ { leafType = 'any', allowMissing = true } = {}
274
+ ) {
275
+ const { resolvedRoot, targetPath, relativeTarget } = resolveConfinedWorkspacePath(
276
+ workspaceRoot,
277
+ relativePath
278
+ );
279
+ await assertSafeWorkspaceRoot(resolvedRoot);
280
+
281
+ if (relativeTarget === '') {
282
+ return {
283
+ exists: true,
284
+ path: resolvedRoot,
285
+ stat: await fs.lstat(resolvedRoot),
286
+ relativeTarget,
287
+ };
288
+ }
289
+
290
+ const components = relativeTarget.split(path.sep).filter(Boolean);
291
+ let currentPath = resolvedRoot;
292
+ let leafStat = null;
293
+ for (let index = 0; index < components.length; index += 1) {
294
+ currentPath = path.join(currentPath, components[index]);
295
+ const currentStat = await lstatIfExists(currentPath);
296
+ if (!currentStat) {
297
+ if (!allowMissing) {
298
+ throw new Error('Unsafe Gofer workspace path: a required managed path is missing.');
299
+ }
300
+ return { exists: false, path: targetPath, stat: null, relativeTarget };
301
+ }
302
+ if (currentStat.isSymbolicLink()) {
303
+ throw new Error(
304
+ 'Unsafe Gofer workspace path: symbolic links are not allowed in managed paths.'
305
+ );
306
+ }
307
+
308
+ const isLeaf = index === components.length - 1;
309
+ if (isLeaf) {
310
+ leafStat = currentStat;
311
+ }
312
+ if (!isLeaf && !currentStat.isDirectory()) {
313
+ throw new Error(
314
+ 'Unsafe Gofer workspace path: every managed parent component must be a directory.'
315
+ );
316
+ }
317
+ if (isLeaf && leafType === 'directory' && !currentStat.isDirectory()) {
318
+ throw new Error('Unsafe Gofer workspace path: the managed path must be a directory.');
319
+ }
320
+ if (isLeaf && leafType === 'file' && !currentStat.isFile()) {
321
+ throw new Error('Unsafe Gofer workspace path: the managed path must be a regular file.');
322
+ }
323
+ if (
324
+ isLeaf &&
325
+ leafType === 'any' &&
326
+ !currentStat.isDirectory() &&
327
+ !currentStat.isFile()
328
+ ) {
329
+ throw new Error(
330
+ 'Unsafe Gofer workspace path: managed paths must be regular files or directories.'
331
+ );
332
+ }
333
+ }
334
+
335
+ return {
336
+ exists: true,
337
+ path: targetPath,
338
+ stat: leafStat,
339
+ relativeTarget,
340
+ };
341
+ }
342
+
343
+ async function ensureSafeWorkspaceDirectory(workspaceRoot, relativePath, dryRun) {
344
+ const { resolvedRoot, targetPath, relativeTarget } = resolveConfinedWorkspacePath(
345
+ workspaceRoot,
346
+ relativePath
347
+ );
348
+ await assertSafeWorkspaceRoot(resolvedRoot);
349
+ if (relativeTarget === '') {
350
+ return targetPath;
351
+ }
352
+
353
+ const components = relativeTarget.split(path.sep).filter(Boolean);
354
+ let currentPath = resolvedRoot;
355
+ for (const component of components) {
356
+ currentPath = path.join(currentPath, component);
357
+ let currentStat = await lstatIfExists(currentPath);
358
+ if (!currentStat) {
359
+ if (dryRun) {
360
+ return targetPath;
361
+ }
362
+ try {
363
+ await fs.mkdir(currentPath);
364
+ } catch (error) {
365
+ if (error?.code !== 'EEXIST') {
366
+ throw error;
367
+ }
368
+ }
369
+ currentStat = await lstatIfExists(currentPath);
370
+ }
371
+ if (!currentStat || currentStat.isSymbolicLink() || !currentStat.isDirectory()) {
372
+ throw new Error(
373
+ 'Unsafe Gofer workspace path: every managed directory component must be a real directory; symbolic links are not allowed.'
374
+ );
375
+ }
376
+ }
377
+
378
+ return targetPath;
379
+ }
380
+
381
+ function directoryIdentityMatches(left, right) {
382
+ return (
383
+ left.dev === right.dev &&
384
+ left.ino === right.ino &&
385
+ left.isDirectory() &&
386
+ right.isDirectory()
387
+ );
190
388
  }
191
389
 
192
- async function movePathPreservingAcrossDevices(sourcePath, targetPath) {
193
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
390
+ async function openValidatedDirectory(directoryPath) {
391
+ const initialStat = await fs.lstat(directoryPath);
392
+ let handle = null;
194
393
  try {
195
- await fs.rename(sourcePath, targetPath);
394
+ handle = await fs.open(directoryPath, 'r');
196
395
  } catch (error) {
197
- if (error?.code !== 'EXDEV') {
396
+ if (
397
+ process.platform !== 'win32' ||
398
+ !['EACCES', 'EISDIR', 'EPERM'].includes(String(error?.code))
399
+ ) {
198
400
  throw error;
199
401
  }
402
+ }
403
+ const validation = { handle, initialStat };
404
+ await assertDirectoryMatchesPath(validation, directoryPath);
405
+ return validation;
406
+ }
200
407
 
201
- await fs.cp(sourcePath, targetPath, {
202
- recursive: true,
203
- force: true,
204
- dereference: false,
408
+ async function assertDirectoryMatchesPath(validation, directoryPath) {
409
+ const [openedStat, pathStat] = await Promise.all([
410
+ validation.handle ? validation.handle.stat() : validation.initialStat,
411
+ fs.lstat(directoryPath),
412
+ ]);
413
+ if (pathStat.isSymbolicLink() || !directoryIdentityMatches(openedStat, pathStat)) {
414
+ throw new Error(
415
+ 'Unsafe Gofer workspace path: a managed parent directory changed during validation.'
416
+ );
417
+ }
418
+ }
419
+
420
+ async function writeWorkspaceFile(workspaceRoot, relativePath, content, dryRun, mode = null) {
421
+ const target = await inspectWorkspacePath(workspaceRoot, relativePath, {
422
+ leafType: 'file',
423
+ allowMissing: true,
424
+ });
425
+ if (dryRun) {
426
+ return;
427
+ }
428
+
429
+ const parentRelativePath = path.dirname(target.relativeTarget);
430
+ const normalizedParent = parentRelativePath === '.' ? '' : parentRelativePath;
431
+ const parentPath = await ensureSafeWorkspaceDirectory(
432
+ workspaceRoot,
433
+ normalizedParent,
434
+ false
435
+ );
436
+ const existingMode = target.exists ? target.stat.mode & 0o777 : 0o666;
437
+ const temporaryName = `.gofer-write-${process.pid}-${randomUUID()}.tmp`;
438
+ const temporaryRelativePath = path.join(normalizedParent, temporaryName);
439
+ const temporaryPath = path.join(parentPath, temporaryName);
440
+ let temporaryHandle;
441
+ let parentValidation;
442
+
443
+ try {
444
+ parentValidation = await openValidatedDirectory(parentPath);
445
+ await inspectWorkspacePath(workspaceRoot, temporaryRelativePath, {
446
+ leafType: 'file',
447
+ allowMissing: true,
205
448
  });
206
- await fs.rm(sourcePath, { recursive: true, force: true });
449
+ temporaryHandle = await fs.open(temporaryPath, 'wx', mode ?? existingMode);
450
+ await temporaryHandle.writeFile(content);
451
+ await temporaryHandle.sync();
452
+ await temporaryHandle.close();
453
+ temporaryHandle = null;
454
+
455
+ await inspectWorkspacePath(workspaceRoot, normalizedParent, {
456
+ leafType: 'directory',
457
+ allowMissing: false,
458
+ });
459
+ await assertDirectoryMatchesPath(parentValidation, parentPath);
460
+ await inspectWorkspacePath(workspaceRoot, relativePath, {
461
+ leafType: 'file',
462
+ allowMissing: true,
463
+ });
464
+ await fs.rename(temporaryPath, target.path);
465
+ await assertDirectoryMatchesPath(parentValidation, parentPath);
466
+ await inspectWorkspacePath(workspaceRoot, relativePath, {
467
+ leafType: 'file',
468
+ allowMissing: false,
469
+ });
470
+ } finally {
471
+ if (temporaryHandle) {
472
+ await temporaryHandle.close().catch(() => {});
473
+ }
474
+ if (parentValidation) {
475
+ try {
476
+ await assertDirectoryMatchesPath(parentValidation, parentPath);
477
+ await fs.rm(temporaryPath, { force: true });
478
+ } catch {
479
+ // If the parent identity changed, do not risk deleting through its replacement.
480
+ }
481
+ }
482
+ if (parentValidation?.handle) {
483
+ await parentValidation.handle.close().catch(() => {});
484
+ }
207
485
  }
208
486
  }
209
487
 
488
+ function buildArchiveStamp() {
489
+ return `${new Date().toISOString().replace(/[:.]/g, '-')}-${process.pid}`;
490
+ }
491
+
210
492
  async function archiveLegacyManagedPath(workspaceRoot, relativePath, archiveStamp, dryRun) {
211
493
  const archiveRelativePath = path.join(LEGACY_MANAGED_ARCHIVE_ROOT, archiveStamp, relativePath);
494
+ const source = await inspectWorkspacePath(workspaceRoot, relativePath, {
495
+ leafType: 'any',
496
+ allowMissing: false,
497
+ });
498
+ const archiveTarget = await inspectWorkspacePath(workspaceRoot, archiveRelativePath, {
499
+ leafType: 'any',
500
+ allowMissing: true,
501
+ });
502
+ if (archiveTarget.exists) {
503
+ throw new Error('Unsafe Gofer archive path: the managed archive target already exists.');
504
+ }
505
+
212
506
  if (!dryRun) {
213
- await movePathPreservingAcrossDevices(
214
- path.join(workspaceRoot, relativePath),
215
- path.join(workspaceRoot, archiveRelativePath)
507
+ const archiveParent = path.dirname(archiveRelativePath);
508
+ const archiveParentPath = await ensureSafeWorkspaceDirectory(
509
+ workspaceRoot,
510
+ archiveParent,
511
+ false
216
512
  );
513
+ const sourceParentPath = resolveConfinedWorkspacePath(
514
+ workspaceRoot,
515
+ path.dirname(relativePath)
516
+ ).targetPath;
517
+ const sourceParentValidation = await openValidatedDirectory(sourceParentPath);
518
+ const archiveParentValidation = await openValidatedDirectory(archiveParentPath);
519
+ try {
520
+ await assertDirectoryMatchesPath(sourceParentValidation, sourceParentPath);
521
+ await assertDirectoryMatchesPath(archiveParentValidation, archiveParentPath);
522
+ const currentSource = await inspectWorkspacePath(workspaceRoot, relativePath, {
523
+ leafType: 'any',
524
+ allowMissing: false,
525
+ });
526
+ if (
527
+ currentSource.stat.dev !== source.stat.dev ||
528
+ currentSource.stat.ino !== source.stat.ino ||
529
+ currentSource.stat.isDirectory() !== source.stat.isDirectory() ||
530
+ currentSource.stat.isFile() !== source.stat.isFile()
531
+ ) {
532
+ throw new Error(
533
+ 'Unsafe Gofer archive path: the legacy path changed during validation.'
534
+ );
535
+ }
536
+ const currentArchiveTarget = await inspectWorkspacePath(
537
+ workspaceRoot,
538
+ archiveRelativePath,
539
+ { leafType: 'any', allowMissing: true }
540
+ );
541
+ if (currentArchiveTarget.exists) {
542
+ throw new Error('Unsafe Gofer archive path: the managed archive target already exists.');
543
+ }
544
+ await fs.rename(source.path, archiveTarget.path);
545
+ await assertDirectoryMatchesPath(sourceParentValidation, sourceParentPath);
546
+ await assertDirectoryMatchesPath(archiveParentValidation, archiveParentPath);
547
+ } catch (error) {
548
+ if (error?.code === 'EXDEV') {
549
+ throw new Error(
550
+ 'Unsafe Gofer archive path: cross-device legacy archives are not supported.'
551
+ );
552
+ }
553
+ throw error;
554
+ } finally {
555
+ if (sourceParentValidation.handle) {
556
+ await sourceParentValidation.handle.close().catch(() => {});
557
+ }
558
+ if (archiveParentValidation.handle) {
559
+ await archiveParentValidation.handle.close().catch(() => {});
560
+ }
561
+ }
562
+ const archived = await inspectWorkspacePath(workspaceRoot, archiveRelativePath, {
563
+ leafType: 'any',
564
+ allowMissing: false,
565
+ });
566
+ if (
567
+ archived.stat.dev !== source.stat.dev ||
568
+ archived.stat.ino !== source.stat.ino ||
569
+ archived.stat.isDirectory() !== source.stat.isDirectory() ||
570
+ archived.stat.isFile() !== source.stat.isFile()
571
+ ) {
572
+ throw new Error('Unsafe Gofer archive path: the archived path failed identity validation.');
573
+ }
217
574
  }
218
575
 
219
576
  return archiveRelativePath;
220
577
  }
221
578
 
222
- async function removeLegacyManagedPaths(workspaceRoot, dryRun) {
579
+ function buildVisibleSurfacePathsForStem(stem) {
580
+ return [
581
+ path.join('.claude', 'commands', `${stem}.md`),
582
+ path.join('.github', 'prompts', `${stem}.prompt.md`),
583
+ path.join('.agents', 'skills', stem),
584
+ path.join('.system', 'skills', stem),
585
+ path.join('.gemini', 'commands', 'gofer', `${stem}.md`),
586
+ path.join('.gemini', 'commands', 'gofer', `${stem}.toml`),
587
+ ];
588
+ }
589
+
590
+ function buildStaleVisibleManagedPaths(stages) {
591
+ const staleStems = new Set(RETIRED_PUBLIC_ENTRYPOINT_STEMS);
592
+ for (const stage of stages) {
593
+ staleStems.add(String(stage.stem));
594
+ }
595
+
596
+ return Array.from(staleStems).flatMap((stem) => buildVisibleSurfacePathsForStem(stem));
597
+ }
598
+
599
+ async function removeLegacyManagedPaths(workspaceRoot, dryRun, stages = []) {
223
600
  const archived = [];
224
601
  const archiveStamp = buildArchiveStamp();
225
- for (const relativePath of LEGACY_MANAGED_PATHS) {
226
- const targetPath = path.join(workspaceRoot, relativePath);
227
- if (!(await pathExists(targetPath))) {
602
+ const legacyPaths = Array.from(
603
+ new Set([...LEGACY_MANAGED_PATHS, ...buildStaleVisibleManagedPaths(stages)])
604
+ );
605
+
606
+ for (const relativePath of legacyPaths) {
607
+ const target = await inspectWorkspacePath(workspaceRoot, relativePath, {
608
+ leafType: 'any',
609
+ allowMissing: true,
610
+ });
611
+ if (!target.exists) {
228
612
  continue;
229
613
  }
230
614
 
@@ -244,9 +628,13 @@ async function resolveSourcePath(sourceRoot, relativePath) {
244
628
 
245
629
  const resourceRelativePath = EXTENSION_RESOURCE_PATHS.get(path.normalize(relativePath));
246
630
  if (resourceRelativePath) {
247
- const resourcePath = path.join(sourceRoot, resourceRelativePath);
248
- if (await pathExists(resourcePath)) {
249
- return resourcePath;
631
+ for (const resourcePath of [
632
+ path.join(sourceRoot, resourceRelativePath),
633
+ path.join(sourceRoot, 'extension', resourceRelativePath),
634
+ ]) {
635
+ if (await pathExists(resourcePath)) {
636
+ return resourcePath;
637
+ }
250
638
  }
251
639
  }
252
640
 
@@ -546,25 +934,56 @@ function buildEaiRepoContractSection(projectInfo) {
546
934
  if (!projectInfo.eaiInitialized) {
547
935
  return `## EAI Platform Readiness
548
936
 
549
- - This repo is not confirmed as EAI-initialized yet. Before any Gofer pipeline work, run \`eai whoami\`.
550
- - If \`eai\` is missing, login fails, the token is expired, or no active workspace is visible, run \`/gofer:eai-first-run\` before building.
551
- - Build on EAI Platform first and Azure second. Treat non-EAI runtimes as explicit exceptions only.
937
+ - Classify the request before EAI readiness. If it is EAI app delivery or ambiguous, continue directly to EAI readiness. If it is clearly non-app work, confirm once before skipping EAI workspace/app setup.
938
+ - This repo is not confirmed as EAI-initialized yet. Run \`eai whoami\` only for EAI app delivery work or explicit EAI CLI recovery.
939
+ - If \`eai\` is missing, login fails, the token is expired, or no active workspace is visible during app delivery, use the public \`eai\` entrypoint and the internal \`.specify/commands/gofer_eai_first_run.md\` setup contract before building.
940
+ - Do not invent, guess, or complete EAI CLI commands from memory. Verify exact \`eai ...\` syntax and flags with \`eai --describe\` and command-specific \`--help\` before suggesting or running them.
941
+ - Build on EAI Platform first and Azure second for app delivery. Treat non-EAI runtimes as explicit exceptions only.
552
942
  - Do not write tokens, secrets, private workspace IDs, Entra/CIAM authority tenant IDs, or local \`.env\` values into Gofer artifacts.`;
553
943
  }
554
944
 
555
945
  return `## EAI Repo Contract
556
946
 
557
947
  - This repo appears to be initialized from the EAI app template. Before app-delivery work, read \`.specify/references/platform/eai-repo-contract.md\` and \`.specify/references/platform/eai-error-catalog.yaml\`.
558
- - Before any Gofer pipeline work, run \`eai whoami\`. If \`eai\` is missing, login fails, the token is expired, or no active workspace is visible, run \`/gofer:eai-first-run\` before building.
559
- - If CLI, login, workspace, template, or Gofer readiness is missing or stale, run \`/gofer:eai-first-run\` before building.
948
+ - Classify the request before EAI readiness. If it is EAI app delivery or ambiguous, continue directly to EAI readiness. If it is clearly non-app work, confirm once before skipping EAI workspace/app setup.
949
+ - Run \`eai whoami\` only for EAI app delivery work or explicit EAI CLI recovery. If \`eai\` is missing, login fails, the token is expired, or no active workspace is visible during app delivery, use the public \`eai\` entrypoint and the internal \`.specify/commands/gofer_eai_first_run.md\` setup contract before building.
950
+ - If CLI, login, workspace, template, or Gofer readiness is missing or stale during app delivery, use the public \`eai\` entrypoint and the internal first-run setup contract before building.
560
951
  - Use \`eai update --check\`, \`eai --describe\`, \`eai agent guide --format json\`, \`eai template check --format json\`, \`eai gofer refresh --check --format json\`, and \`eai workflow readiness --format json\` when the CLI advertises them before assuming the repo is current.
952
+ - Do not invent, guess, or complete EAI CLI commands from memory. Verify exact \`eai ...\` syntax and flags with \`eai --describe\` and command-specific \`--help\` before suggesting or running them. If the installed CLI does not list a command, do not run it.
561
953
  - After any \`eai\` command error, use \`eai errors explain <code-or-reason> --format json\` before guessing remediation.
562
954
  - If \`eai errors explain\` is unavailable, match \`.specify/references/platform/eai-error-catalog.yaml\`, run read-only diagnostics before mutating fixes, and stop at the retry or escalation condition.
563
- - For \`eai user invite\` 5xx or \`EXTERNAL_SERVICE_ERROR\`, check existing members with \`eai user list --workspace <workspace-id> --search <email> --format json\`; use \`eai user role set --workspace <workspace-id> --member-id <member-id> --role tenant-admin --format json\` only after verification and user approval, then tell the app user to sign out and sign back in.
955
+ - For \`eai user invite\` 5xx or \`EXTERNAL_SERVICE_ERROR\`, check existing members with \`eai user list --workspace <workspace-id> --search <email> --format json\`; use \`eai user role set --workspace <workspace-id> --member-id <member-id> --role tenant-admin --format json\` only after verification and user approval. The platform role ID \`tenant-admin\` means workspace admin access. Then tell the app user to sign out and sign back in.
956
+ - For \`MISSING_TENANT\`, \`app_token_tenant_context_required\`, or "Tenant context required for app tokens" on platform user lookup or membership prerequisites, run \`eai errors explain app_token_tenant_context_required --format json\`, confirm workspace context, and retry \`/v4/platform/tenants/<tenant-id>/...\` routes before changing workspace members, Entra, role definitions, databases, or cloud portals.
564
957
  - Build on EAI Platform first and Azure second. Treat non-EAI runtimes as explicit exceptions only.
565
958
  - Keep provisioning, types seed, schema/storage health, workflow readiness, and preview as separate gates.`;
566
959
  }
567
960
 
961
+ function buildUserFacingResponseGateSection() {
962
+ return `## User-Facing Response Gate
963
+
964
+ Before each user-facing reply, check the draft against these rules:
965
+
966
+ 1. Lead with the business outcome, effect, risk, or decision.
967
+ 2. Use concise, simple language.
968
+ 3. Include technical detail only when it supports a decision or the user asks for it.
969
+ 4. If any check fails, rewrite the reply before sending it.`;
970
+ }
971
+
972
+ function buildAlwaysOnEaiSection() {
973
+ return `## Always-On EAI Contract
974
+ <!-- gofer:always-on-eai:start -->
975
+
976
+ Apply this contract to every request after Gofer is installed for this repo or AI coding app. The user does not need to type \`/eai\` or \`$eai\`.
977
+
978
+ 1. Preserve the user's request. Do not rewrite it or add a visible command prefix.
979
+ 2. Treat an explicit \`/eai\` in Claude, Copilot, Antigravity, Grok, or VS Code, and \`$eai\` in Codex, as an idempotent request for the same contract.
980
+ 3. Apply Gofer's Controlled English and business-first response rules.
981
+ 4. Select the internal pipeline stage. Do not make the user select a stage.
982
+ 5. Check workspace health before meaningful repo work, tool use, or a pipeline stage. Do not repeat setup on every message.
983
+ 6. When the user explicitly asks to update Gofer, use its maintenance contract only.
984
+ <!-- gofer:always-on-eai:end -->`;
985
+ }
986
+
568
987
  export function buildAgentsMd(projectInfo, stages) {
569
988
  const corePipelineOrder = [
570
989
  '0_gofer_start',
@@ -612,6 +1031,10 @@ export function buildAgentsMd(projectInfo, stages) {
612
1031
 
613
1032
  **Project**: ${projectInfo.name} | **Language**: ${formatLanguage(projectInfo.language)}${frameworkLine} | **Package Manager**: ${projectInfo.packageManager || 'Not detected'}
614
1033
 
1034
+ ${buildUserFacingResponseGateSection()}
1035
+
1036
+ ${buildAlwaysOnEaiSection()}
1037
+
615
1038
  ## Core Pipeline Stages
616
1039
 
617
1040
  ${pipelineSections}
@@ -642,7 +1065,7 @@ ${buildCodeStyleSection(projectInfo)}
642
1065
 
643
1066
  ## Gofer Pipeline
644
1067
 
645
- This project uses Gofer for spec-driven development. Run \`/0_gofer_start\` to start the core pipeline (Gofer Start -> research -> specify -> plan -> tasks -> implement -> validate). \`/6_gofer_validate\` is the terminal quality gate and includes the final engineering review loop. Artifacts in \`.specify/specs/{feature}/\`.
1068
+ This project uses Gofer for spec-driven development. Run \`/eai\` in Claude, Copilot, Antigravity, Grok, or VS Code, and \`$eai\` in Codex, to start or continue the core pipeline (Gofer Start -> research -> specify -> plan -> tasks -> implement -> validate). Gofer routes internally through \`.specify/commands/*.md\` contracts; validation is the terminal quality gate and includes the final engineering review loop. Before EAI readiness, classify the request: app delivery continues directly, while clear non-app work asks once before skipping EAI workspace/app setup. Artifacts in \`.specify/specs/{feature}/\`.
646
1069
 
647
1070
  Each feature should carry a bounded loop contract:
648
1071
 
@@ -673,6 +1096,8 @@ export function buildClaudeMd(projectInfo) {
673
1096
 
674
1097
  See @AGENTS.md for project conventions, commands, and code style.
675
1098
 
1099
+ ${buildAlwaysOnEaiSection()}
1100
+
676
1101
  ## Workflow Orchestration
677
1102
 
678
1103
  ### 1. Plan Node Default
@@ -704,7 +1129,7 @@ See @AGENTS.md for project conventions, commands, and code style.
704
1129
 
705
1130
  ## Gofer Pipeline
706
1131
 
707
- Run \`/0_gofer_start\` to start the core pipeline: Gofer Start -> research -> specify -> plan -> tasks -> implement -> validate. \`/6_gofer_validate\` is the terminal quality gate and includes the final engineering review loop. Use \`/7_gofer_save\` for checkpoints and \`/8_gofer_branding\` to apply company, client, or consulting-firm presentation style. Artifacts go to \`.specify/specs/{feature}/\`.
1132
+ Run \`/eai\` in Claude, Copilot, Antigravity, Grok, or VS Code, and \`$eai\` in Codex, to start or continue the core pipeline: Gofer Start -> research -> specify -> plan -> tasks -> implement -> validate. Gofer routes internally through \`.specify/commands/*.md\` contracts; validation is the terminal quality gate and includes the final engineering review loop. Before EAI readiness, classify the request: app delivery continues directly, while clear non-app work asks once before skipping EAI workspace/app setup. Artifacts go to \`.specify/specs/{feature}/\`.
708
1133
 
709
1134
  For each active feature, keep \`loop-contract.json\`, \`loop-ledger.jsonl\`, and
710
1135
  \`loop-audit-report.md\` in the feature directory. The loop contract bounds
@@ -725,11 +1150,13 @@ export function buildCopilotInstructions(projectInfo) {
725
1150
 
726
1151
  **${projectInfo.name}** is a ${formatLanguage(projectInfo.language)} project${frameworkBit}.
727
1152
 
1153
+ ${buildAlwaysOnEaiSection()}
1154
+
728
1155
  ## Gofer Pipeline
729
1156
 
730
- This project uses Gofer for spec-driven development. Run \`/0_gofer_start\` to start the core pipeline: Gofer Start -> research -> specify -> plan -> tasks -> implement -> validate.
1157
+ This project uses Gofer for spec-driven development. Run \`/eai\` to start or continue the core pipeline: Gofer Start -> research -> specify -> plan -> tasks -> implement -> validate.
731
1158
 
732
- Key commands: \`/1_gofer_research\`, \`/2_gofer_specify\`, \`/3_gofer_plan\`, \`/4_gofer_tasks\`, \`/5_gofer_implement\`, \`/6_gofer_validate\`. \`/6_gofer_validate\` is the terminal quality gate and includes the final engineering review loop. Use \`/7_gofer_save\` for checkpoints and \`/8_gofer_branding\` for branded document/deck templates. Artifacts in \`.specify/specs/{feature}/\`.
1159
+ Gofer routes internally through \`.specify/commands/*.md\` contracts; validation is the terminal quality gate and includes the final engineering review loop. Before EAI readiness, classify the request: app delivery continues directly, while clear non-app work asks once before skipping EAI workspace/app setup. Artifacts in \`.specify/specs/{feature}/\`.
733
1160
 
734
1161
  ${eaiSection}
735
1162
 
@@ -753,21 +1180,25 @@ ${buildCodeStyleSection(projectInfo)}
753
1180
  }
754
1181
 
755
1182
  export async function readManagedVersion(workspaceRoot) {
756
- try {
757
- const content = await fs.readFile(path.join(workspaceRoot, GOFER_VERSION_FILE), 'utf8');
758
- return content.split('\n')[0]?.trim() || null;
759
- } catch (error) {
760
- if (error?.code === 'ENOENT') {
761
- return null;
762
- }
763
- throw error;
1183
+ const target = await inspectWorkspacePath(workspaceRoot, GOFER_VERSION_FILE, {
1184
+ leafType: 'file',
1185
+ allowMissing: true,
1186
+ });
1187
+ if (!target.exists) {
1188
+ return null;
764
1189
  }
1190
+ const content = await fs.readFile(target.path, 'utf8');
1191
+ return content.split('\n')[0]?.trim() || null;
765
1192
  }
766
1193
 
767
1194
  async function collectMissingPaths(workspaceRoot, relativePaths) {
768
1195
  const missing = [];
769
1196
  for (const relativePath of relativePaths) {
770
- if (!(await pathExists(path.join(workspaceRoot, relativePath)))) {
1197
+ const target = await inspectWorkspacePath(workspaceRoot, relativePath, {
1198
+ leafType: 'any',
1199
+ allowMissing: true,
1200
+ });
1201
+ if (!target.exists) {
771
1202
  missing.push(relativePath);
772
1203
  }
773
1204
  }
@@ -780,6 +1211,7 @@ export async function checkWorkspaceState({
780
1211
  sourceRoot,
781
1212
  }) {
782
1213
  const normalizedHost = normalizeHost(host);
1214
+ await assertSafeWorkspaceRoot(workspaceRoot);
783
1215
  const expectedVersion = await detectGoferVersion(sourceRoot);
784
1216
  const actualVersion = await readManagedVersion(workspaceRoot);
785
1217
  const missingCore = await collectMissingPaths(workspaceRoot, CORE_SENTINELS);
@@ -822,44 +1254,139 @@ export async function checkWorkspaceState({
822
1254
  };
823
1255
  }
824
1256
 
825
- async function ensureDir(targetPath, dryRun) {
826
- if (!dryRun) {
827
- await fs.mkdir(targetPath, { recursive: true });
1257
+ async function ensureDir(workspaceRoot, relativePath, dryRun) {
1258
+ await ensureSafeWorkspaceDirectory(workspaceRoot, relativePath, dryRun);
1259
+ }
1260
+
1261
+ function sourceIdentityMatches(left, right) {
1262
+ return (
1263
+ left.dev === right.dev &&
1264
+ left.ino === right.ino &&
1265
+ left.size === right.size &&
1266
+ left.mtimeMs === right.mtimeMs &&
1267
+ left.isFile() === right.isFile()
1268
+ );
1269
+ }
1270
+
1271
+ async function readStableSourceFile(sourcePath) {
1272
+ const before = await fs.lstat(sourcePath);
1273
+ if (before.isSymbolicLink() || !before.isFile()) {
1274
+ throw new Error('Unsafe Gofer source path: copied entries must be regular files.');
1275
+ }
1276
+
1277
+ const handle = await fs.open(sourcePath, 'r');
1278
+ try {
1279
+ const opened = await handle.stat();
1280
+ if (!sourceIdentityMatches(before, opened)) {
1281
+ throw new Error('Unsafe Gofer source path: a copied file changed during validation.');
1282
+ }
1283
+ const content = await handle.readFile();
1284
+ const after = await fs.lstat(sourcePath);
1285
+ if (!sourceIdentityMatches(opened, after)) {
1286
+ throw new Error('Unsafe Gofer source path: a copied file changed during validation.');
1287
+ }
1288
+ return { content, mode: opened.mode & 0o777 };
1289
+ } finally {
1290
+ await handle.close();
828
1291
  }
829
1292
  }
830
1293
 
831
- async function copyDirectory(sourcePath, targetPath, dryRun) {
832
- if (!(await pathExists(sourcePath))) {
1294
+ async function copyDirectory(sourcePath, workspaceRoot, targetRelativePath, dryRun) {
1295
+ const sourceStat = await lstatIfExists(sourcePath);
1296
+ if (!sourceStat) {
833
1297
  return false;
834
1298
  }
835
- if (dryRun) {
836
- return true;
1299
+ if (sourceStat.isSymbolicLink() || !sourceStat.isDirectory()) {
1300
+ throw new Error('Unsafe Gofer source path: copied roots must be real directories.');
1301
+ }
1302
+
1303
+ await ensureSafeWorkspaceDirectory(workspaceRoot, targetRelativePath, dryRun);
1304
+ const entries = await fs.readdir(sourcePath, { withFileTypes: true });
1305
+ for (const entry of entries) {
1306
+ const sourceEntryPath = path.join(sourcePath, entry.name);
1307
+ const targetEntryRelativePath = path.join(targetRelativePath, entry.name);
1308
+ const entryStat = await fs.lstat(sourceEntryPath);
1309
+ if (entryStat.isSymbolicLink()) {
1310
+ throw new Error('Unsafe Gofer source path: symbolic links are not copied.');
1311
+ }
1312
+ if (entryStat.isDirectory()) {
1313
+ await copyDirectory(sourceEntryPath, workspaceRoot, targetEntryRelativePath, dryRun);
1314
+ } else if (entryStat.isFile()) {
1315
+ const sourceFile = await readStableSourceFile(sourceEntryPath);
1316
+ await writeWorkspaceFile(
1317
+ workspaceRoot,
1318
+ targetEntryRelativePath,
1319
+ sourceFile.content,
1320
+ dryRun,
1321
+ sourceFile.mode
1322
+ );
1323
+ } else {
1324
+ throw new Error(
1325
+ 'Unsafe Gofer source path: only regular files and directories may be copied.'
1326
+ );
1327
+ }
837
1328
  }
838
- await fs.mkdir(path.dirname(targetPath), { recursive: true });
839
- await fs.cp(sourcePath, targetPath, { recursive: true, force: true, dereference: false });
840
1329
  return true;
841
1330
  }
842
1331
 
843
- async function writeFileIfMissing(filePath, content, dryRun) {
844
- if (await pathExists(filePath)) {
1332
+ async function writeFileIfMissing(workspaceRoot, relativePath, content, dryRun) {
1333
+ const target = await inspectWorkspacePath(workspaceRoot, relativePath, {
1334
+ leafType: 'file',
1335
+ allowMissing: true,
1336
+ });
1337
+ if (target.exists) {
845
1338
  return false;
846
1339
  }
847
- if (!dryRun) {
848
- await fs.mkdir(path.dirname(filePath), { recursive: true });
849
- await fs.writeFile(filePath, content, 'utf8');
850
- }
1340
+ await writeWorkspaceFile(workspaceRoot, relativePath, content, dryRun);
851
1341
  return true;
852
1342
  }
853
1343
 
854
- async function writeTextFile(filePath, content, dryRun) {
855
- if (!dryRun) {
856
- await fs.mkdir(path.dirname(filePath), { recursive: true });
857
- await fs.writeFile(filePath, content, 'utf8');
1344
+ async function ensureAlwaysOnEaiSection(workspaceRoot, relativePath, fallbackContent, dryRun) {
1345
+ let existing = '';
1346
+ const target = await inspectWorkspacePath(workspaceRoot, relativePath, {
1347
+ leafType: 'file',
1348
+ allowMissing: true,
1349
+ });
1350
+ if (target.exists) {
1351
+ existing = await fs.readFile(target.path, 'utf8');
858
1352
  }
1353
+
1354
+ const section = buildAlwaysOnEaiSection();
1355
+ const markerPattern = /## Always-On EAI Contract\r?\n<!-- gofer:always-on-eai:start -->[\s\S]*?<!-- gofer:always-on-eai:end -->/;
1356
+ const legacySectionPattern = /## Always-On EAI Contract\r?\n[\s\S]*?(?=^##\s|(?![\s\S]))/m;
1357
+ const updated = existing
1358
+ ? markerPattern.test(existing)
1359
+ ? existing.replace(markerPattern, section)
1360
+ : legacySectionPattern.test(existing)
1361
+ ? existing.replace(legacySectionPattern, `${section}\n`)
1362
+ : `${existing.trimEnd()}\n\n${section}\n`
1363
+ : fallbackContent;
1364
+
1365
+ if (updated === existing) {
1366
+ return false;
1367
+ }
1368
+
1369
+ await writeTextFile(workspaceRoot, relativePath, updated, dryRun);
1370
+ return true;
1371
+ }
1372
+
1373
+ function buildGeminiMd(projectInfo) {
1374
+ return `# GEMINI.md
1375
+
1376
+ See @AGENTS.md for project conventions, commands, and code style.
1377
+
1378
+ ${buildAlwaysOnEaiSection()}
1379
+
1380
+ Use the internal Gofer pipeline to select the next stage. Keep the user-facing
1381
+ conversation in business language.\n`;
1382
+ }
1383
+
1384
+ async function writeTextFile(workspaceRoot, relativePath, content, dryRun) {
1385
+ await writeWorkspaceFile(workspaceRoot, relativePath, content, dryRun);
859
1386
  }
860
1387
 
861
1388
  async function writeModelPolicyIfMissing(workspaceRoot, sourceRoot, dryRun) {
862
- const targetPath = path.join(workspaceRoot, '.specify', 'memory', 'gofer-model-policy.yaml');
1389
+ const targetRelativePath = path.join('.specify', 'memory', 'gofer-model-policy.yaml');
863
1390
 
864
1391
  let template = '';
865
1392
  try {
@@ -874,18 +1401,18 @@ async function writeModelPolicyIfMissing(workspaceRoot, sourceRoot, dryRun) {
874
1401
  throw error;
875
1402
  }
876
1403
 
877
- return writeFileIfMissing(targetPath, template, dryRun);
1404
+ return writeFileIfMissing(workspaceRoot, targetRelativePath, template, dryRun);
878
1405
  }
879
1406
 
880
1407
  async function mergeGitignore(workspaceRoot, dryRun) {
881
- const gitignorePath = path.join(workspaceRoot, '.gitignore');
1408
+ const gitignoreRelativePath = '.gitignore';
882
1409
  let existing = '';
883
- try {
884
- existing = await fs.readFile(gitignorePath, 'utf8');
885
- } catch (error) {
886
- if (error?.code !== 'ENOENT') {
887
- throw error;
888
- }
1410
+ const gitignoreTarget = await inspectWorkspacePath(workspaceRoot, gitignoreRelativePath, {
1411
+ leafType: 'file',
1412
+ allowMissing: true,
1413
+ });
1414
+ if (gitignoreTarget.exists) {
1415
+ existing = await fs.readFile(gitignoreTarget.path, 'utf8');
889
1416
  }
890
1417
 
891
1418
  const missingEntries = GOFER_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
@@ -904,7 +1431,7 @@ async function mergeGitignore(workspaceRoot, dryRun) {
904
1431
  updated += `${entry}\n`;
905
1432
  }
906
1433
 
907
- await writeTextFile(gitignorePath, updated, dryRun);
1434
+ await writeTextFile(workspaceRoot, gitignoreRelativePath, updated, dryRun);
908
1435
  return true;
909
1436
  }
910
1437
 
@@ -924,12 +1451,14 @@ This folder contains all project specifications for AI-driven feature developmen
924
1451
 
925
1452
  ## Quick Start
926
1453
 
927
- Run the unified Gofer pipeline with:
1454
+ Run the unified Gofer pipeline with one public command:
928
1455
 
929
1456
  \`\`\`
930
- /0_gofer_start Add user authentication with OAuth2 and JWT
1457
+ /eai Add user authentication with OAuth2 and JWT
931
1458
  \`\`\`
932
1459
 
1460
+ Use \`/eai\` in Claude, Copilot, Antigravity, Grok, or VS Code, and \`$eai\` in Codex. Gofer routes internally through \`.specify/commands/*.md\` contracts.
1461
+
933
1462
  Artifacts are stored in \`.specify/specs/{feature}/\`.
934
1463
 
935
1464
  Each feature should include a bounded loop contract:
@@ -957,8 +1486,9 @@ Each feature should also keep a running stakeholder review pack:
957
1486
 
958
1487
  ## Model Policy
959
1488
 
960
- Edit \`.specify/memory/gofer-model-policy.yaml\` to tune simple, medium, hard,
961
- and arbiter model routes for Claude, Codex/OpenAI, Gemini, and Copilot. The
1489
+ Edit \`.specify/memory/gofer-model-policy.yaml\` to set advisory simple, medium, hard,
1490
+ and arbiter capability constraints for Claude, Codex/OpenAI, Google Antigravity,
1491
+ Copilot, Grok, and VS Code. Fresh signed capability receipts select models. The
962
1492
  file is copied from \`.specify/templates/gofer-model-policy.yaml\` when missing
963
1493
  and is not overwritten by bootstrap.
964
1494
  `;
@@ -989,19 +1519,26 @@ function getMirrorCopyCandidates() {
989
1519
  target: path.join('.github', 'skills'),
990
1520
  },
991
1521
  { sourceRelativePath: '.gemini', target: '.gemini' },
1522
+ { sourceRelativePath: path.join('.grok', 'skills'), target: path.join('.grok', 'skills') },
992
1523
  { sourceRelativePath: path.join('.agents', 'skills'), target: path.join('.agents', 'skills') },
993
1524
  { sourceRelativePath: path.join('.system', 'skills'), target: path.join('.system', 'skills') },
994
1525
  ];
995
1526
  }
996
1527
 
997
1528
  async function installClaudeHooksSettings(workspaceRoot, dryRun) {
998
- const settingsPath = path.join(workspaceRoot, '.claude', 'settings.json');
1529
+ const settingsRelativePath = path.join('.claude', 'settings.json');
999
1530
  let settings = {};
1000
- try {
1001
- settings = JSON.parse(await fs.readFile(settingsPath, 'utf8'));
1002
- } catch (error) {
1003
- if (error?.code !== 'ENOENT' && !(error instanceof SyntaxError)) {
1004
- throw error;
1531
+ const settingsTarget = await inspectWorkspacePath(workspaceRoot, settingsRelativePath, {
1532
+ leafType: 'file',
1533
+ allowMissing: true,
1534
+ });
1535
+ if (settingsTarget.exists) {
1536
+ try {
1537
+ settings = JSON.parse(await fs.readFile(settingsTarget.path, 'utf8'));
1538
+ } catch (error) {
1539
+ if (!(error instanceof SyntaxError)) {
1540
+ throw error;
1541
+ }
1005
1542
  }
1006
1543
  }
1007
1544
 
@@ -1010,7 +1547,12 @@ async function installClaudeHooksSettings(workspaceRoot, dryRun) {
1010
1547
  ...CLAUDE_HOOKS_CONFIG,
1011
1548
  };
1012
1549
 
1013
- await writeTextFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, dryRun);
1550
+ await writeTextFile(
1551
+ workspaceRoot,
1552
+ settingsRelativePath,
1553
+ `${JSON.stringify(settings, null, 2)}\n`,
1554
+ dryRun
1555
+ );
1014
1556
  }
1015
1557
 
1016
1558
  export async function bootstrapWorkspace({
@@ -1021,6 +1563,7 @@ export async function bootstrapWorkspace({
1021
1563
  includeMirrors = false,
1022
1564
  }) {
1023
1565
  const normalizedHost = normalizeHost(host);
1566
+ await assertSafeWorkspaceRoot(workspaceRoot);
1024
1567
  const projectInfo = await detectProjectInfo(workspaceRoot);
1025
1568
  const stages = await loadStageMetadata(sourceRoot);
1026
1569
  const version = await detectGoferVersion(sourceRoot);
@@ -1032,13 +1575,15 @@ export async function bootstrapWorkspace({
1032
1575
  path.join('.specify', 'logs'),
1033
1576
  ];
1034
1577
  for (const relativeDir of coreDirs) {
1035
- await ensureDir(path.join(workspaceRoot, relativeDir), dryRun);
1578
+ await ensureDir(workspaceRoot, relativeDir, dryRun);
1036
1579
  changed.push(relativeDir);
1037
1580
  }
1038
1581
 
1039
1582
  const coreCopies = [
1040
1583
  path.join('.specify', 'commands'),
1584
+ path.join('.specify', 'config'),
1041
1585
  path.join('.specify', 'references'),
1586
+ path.join('.specify', 'schemas'),
1042
1587
  path.join('.specify', 'templates'),
1043
1588
  path.join('.specify', 'scripts', 'bash'),
1044
1589
  path.join('.specify', 'scripts', 'node'),
@@ -1048,7 +1593,8 @@ export async function bootstrapWorkspace({
1048
1593
  for (const relativePath of coreCopies) {
1049
1594
  const copied = await copyDirectory(
1050
1595
  await resolveSourcePath(sourceRoot, relativePath),
1051
- path.join(workspaceRoot, relativePath),
1596
+ workspaceRoot,
1597
+ relativePath,
1052
1598
  dryRun
1053
1599
  );
1054
1600
  if (copied) {
@@ -1060,52 +1606,75 @@ export async function bootstrapWorkspace({
1060
1606
  changed.push(path.join('.specify', 'memory', 'gofer-model-policy.yaml'));
1061
1607
  }
1062
1608
 
1063
- await writeTextFile(
1064
- path.join(workspaceRoot, GOFER_VERSION_FILE),
1065
- `${version}\n`,
1066
- dryRun
1067
- );
1609
+ await writeTextFile(workspaceRoot, GOFER_VERSION_FILE, `${version}\n`, dryRun);
1068
1610
  changed.push(GOFER_VERSION_FILE);
1069
1611
 
1070
- if (await writeFileIfMissing(path.join(workspaceRoot, '.specify', 'README.md'), buildSpecifyReadme(), dryRun)) {
1612
+ if (
1613
+ await writeFileIfMissing(
1614
+ workspaceRoot,
1615
+ path.join('.specify', 'README.md'),
1616
+ buildSpecifyReadme(),
1617
+ dryRun
1618
+ )
1619
+ ) {
1071
1620
  changed.push(path.join('.specify', 'README.md'));
1072
1621
  }
1073
1622
 
1074
- if (await writeFileIfMissing(path.join(workspaceRoot, 'AGENTS.md'), buildAgentsMd(projectInfo, stages), dryRun)) {
1623
+ if (
1624
+ await ensureAlwaysOnEaiSection(
1625
+ workspaceRoot,
1626
+ 'AGENTS.md',
1627
+ buildAgentsMd(projectInfo, stages),
1628
+ dryRun
1629
+ )
1630
+ ) {
1075
1631
  changed.push('AGENTS.md');
1076
1632
  }
1077
1633
 
1634
+ if (
1635
+ await ensureAlwaysOnEaiSection(
1636
+ workspaceRoot,
1637
+ 'CLAUDE.md',
1638
+ buildClaudeMd(projectInfo),
1639
+ dryRun
1640
+ )
1641
+ ) {
1642
+ changed.push('CLAUDE.md');
1643
+ }
1644
+
1645
+ if (
1646
+ await ensureAlwaysOnEaiSection(
1647
+ workspaceRoot,
1648
+ path.join('.github', 'copilot-instructions.md'),
1649
+ buildCopilotInstructions(projectInfo),
1650
+ dryRun
1651
+ )
1652
+ ) {
1653
+ changed.push(path.join('.github', 'copilot-instructions.md'));
1654
+ }
1655
+
1656
+ if (
1657
+ await ensureAlwaysOnEaiSection(
1658
+ workspaceRoot,
1659
+ 'GEMINI.md',
1660
+ buildGeminiMd(projectInfo),
1661
+ dryRun
1662
+ )
1663
+ ) {
1664
+ changed.push('GEMINI.md');
1665
+ }
1666
+
1078
1667
  if (normalizedHost === 'claude') {
1079
- if (
1080
- await writeFileIfMissing(
1081
- path.join(workspaceRoot, 'CLAUDE.md'),
1082
- buildClaudeMd(projectInfo),
1083
- dryRun
1084
- )
1085
- ) {
1086
- changed.push('CLAUDE.md');
1087
- }
1088
1668
  await installClaudeHooksSettings(workspaceRoot, dryRun);
1089
1669
  changed.push(path.join('.claude', 'settings.json'));
1090
1670
  }
1091
1671
 
1092
- if (normalizedHost === 'copilot') {
1093
- if (
1094
- await writeFileIfMissing(
1095
- path.join(workspaceRoot, '.github', 'copilot-instructions.md'),
1096
- buildCopilotInstructions(projectInfo),
1097
- dryRun
1098
- )
1099
- ) {
1100
- changed.push(path.join('.github', 'copilot-instructions.md'));
1101
- }
1102
- }
1103
-
1104
1672
  if (includeMirrors) {
1105
1673
  for (const candidate of getMirrorCopyCandidates(sourceRoot)) {
1106
1674
  const copied = await copyDirectory(
1107
1675
  await resolveSourcePath(sourceRoot, candidate.sourceRelativePath),
1108
- path.join(workspaceRoot, candidate.target),
1676
+ workspaceRoot,
1677
+ candidate.target,
1109
1678
  dryRun
1110
1679
  );
1111
1680
  if (copied) {
@@ -1114,7 +1683,7 @@ export async function bootstrapWorkspace({
1114
1683
  }
1115
1684
  }
1116
1685
 
1117
- const archivedLegacyPaths = await removeLegacyManagedPaths(workspaceRoot, dryRun);
1686
+ const archivedLegacyPaths = await removeLegacyManagedPaths(workspaceRoot, dryRun, stages);
1118
1687
  changed.push(
1119
1688
  ...archivedLegacyPaths.map(
1120
1689
  ({ relativePath, archivePath }) => `${relativePath} (archived legacy to ${archivePath})`