@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,1178 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Plans and runs user-level Gofer install and update actions for supported hosts.
5
+ * This script is intentionally independent of a repository scaffold.
6
+ */
7
+
8
+ import { execFile } from 'node:child_process';
9
+ import { randomBytes } from 'node:crypto';
10
+ import { constants as fsConstants, promises as fs } from 'node:fs';
11
+ import os from 'node:os';
12
+ import { fileURLToPath } from 'node:url';
13
+ import path from 'node:path';
14
+ import { promisify } from 'node:util';
15
+ import { cleanupLocalSettings } from './gofer-local-settings-cleanup.mjs';
16
+
17
+ const execFileAsync = promisify(execFile);
18
+ const REPOSITORY_URL = 'https://github.com/eai-support/eai-gofer';
19
+ const VS_CODE_EXTENSION_ID = 'EnterpriseAI.gofer';
20
+ export const SUPPORTED_HOSTS = Object.freeze([
21
+ 'claude',
22
+ 'codex',
23
+ 'copilot',
24
+ 'antigravity',
25
+ 'grok',
26
+ 'vscode',
27
+ ]);
28
+ export const INVOCATION_PREFIXES = Object.freeze({
29
+ claude: '/',
30
+ codex: '$',
31
+ copilot: '/',
32
+ antigravity: '/',
33
+ grok: '/',
34
+ vscode: '/',
35
+ });
36
+ const LEGACY_HOST_ALIASES = Object.freeze({
37
+ gemini: 'antigravity',
38
+ });
39
+ const scriptPath = fileURLToPath(import.meta.url);
40
+ const DEFAULT_PLUGIN_ROOT = path.resolve(path.dirname(scriptPath), '..', '..', '..');
41
+ const ALWAYS_ON_EAI_START = '<!-- gofer:always-on-eai:start -->';
42
+ const ALWAYS_ON_EAI_END = '<!-- gofer:always-on-eai:end -->';
43
+ const ALWAYS_ON_EAI_SECTION = `## Always-On EAI Contract
44
+ ${ALWAYS_ON_EAI_START}
45
+
46
+ Apply Gofer to every request. The user does not need to type \`/eai\` or \`$eai\`.
47
+
48
+ 1. Preserve the user's request. Do not add a visible command prefix.
49
+ 2. Use Gofer's internal routing. Do not make the user choose a pipeline stage.
50
+ 3. Use concise, business-first ASD-STE100 style.
51
+ 4. Check workspace health before meaningful repo work, tool use, or a pipeline stage. Do not repeat setup on every message.
52
+ 5. Use Gofer maintenance only when the user explicitly asks to install or update Gofer.
53
+ ${ALWAYS_ON_EAI_END}`;
54
+ const ALWAYS_ON_EAI_MARKER = /## Always-On EAI Contract\r?\n<!-- gofer:always-on-eai:start -->[\s\S]*?<!-- gofer:always-on-eai:end -->/;
55
+ const MAX_DIAGNOSTIC_CHARACTERS = 4096;
56
+ const VS_CODE_INSTRUCTIONS_FRONTMATTER = `---
57
+ name: EAI Gofer
58
+ description: Apply the EAI Gofer delivery contract to every workspace request.
59
+ applyTo: '**'
60
+ ---`;
61
+ const LEGACY_VS_CODE_INSTRUCTIONS_KEY = 'github.copilot.chat.codeGeneration.instructions';
62
+
63
+ function escapeRegExp(value) {
64
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
65
+ }
66
+
67
+ export function sanitizeDiagnostic(value, { home = os.homedir() } = {}) {
68
+ if (value === undefined || value === null) return '';
69
+ let output = String(value)
70
+ .replace(/\u001b\[[0-?]*[ -\/]*[@-~]/g, '')
71
+ .replace(/(https?:\/\/)[^\s/@:]+:[^\s/@]+@/gi, '$1<redacted>@')
72
+ .replace(/\b(Bearer\s+)[A-Za-z0-9._~+/=-]+/gi, '$1<redacted>')
73
+ .replace(
74
+ /\b((?:api[-_ ]?key|access[-_ ]?token|refresh[-_ ]?token|token|secret|password|credential)\s*[:=]\s*)[^\s,;]+/gi,
75
+ '$1<redacted>'
76
+ );
77
+
78
+ if (home && path.isAbsolute(home)) {
79
+ output = output.replace(new RegExp(escapeRegExp(path.resolve(home)), 'gi'), '<home>');
80
+ }
81
+ output = output
82
+ .replace(/\b[A-Za-z]:[\\/]Users[\\/][^\\/\s"']+/gi, '<home>')
83
+ .replace(/\/(?:Users|home)\/[^/\s"']+/g, '<home>')
84
+ .replace(/\/(?:private\/)?tmp\/[^\s"']+/g, '<local-path>')
85
+ .replace(/\/var\/folders\/[^\s"']+/g, '<local-path>')
86
+ .replace(/(^|[\s"'[(=])[A-Za-z]:[\\/][^\s"'\]),;]+/gm, '$1<local-path>')
87
+ .replace(/(^|[\s"'[(=])\/(?!\/)[^\s"'\]),;]+/gm, '$1<local-path>')
88
+ .replace(/[\u0000-\u0008\u000B\u000C\u000E-\u001F\u007F]/g, '');
89
+
90
+ if (output.length > MAX_DIAGNOSTIC_CHARACTERS) {
91
+ return `${output.slice(0, MAX_DIAGNOSTIC_CHARACTERS)}…<truncated>`;
92
+ }
93
+ return output;
94
+ }
95
+
96
+ function sanitizeLocalPath(value) {
97
+ if (!value) return undefined;
98
+ return `<local-path>/${path.basename(String(value))}`;
99
+ }
100
+
101
+ export function sanitizeSurfaceUpdateOutput(value, options = {}, key = '') {
102
+ if (value === undefined || value === null) return value;
103
+ if (typeof value === 'string') {
104
+ if (/(?:path|root|cwd|directory)$/i.test(key) && isLocalMarketplacePath(value)) {
105
+ return sanitizeLocalPath(value);
106
+ }
107
+ return sanitizeDiagnostic(value, options);
108
+ }
109
+ if (Array.isArray(value)) {
110
+ return value.slice(0, 100).map((entry) => sanitizeSurfaceUpdateOutput(entry, options));
111
+ }
112
+ if (typeof value === 'object') {
113
+ return Object.fromEntries(
114
+ Object.entries(value)
115
+ .slice(0, 100)
116
+ .map(([entryKey, entryValue]) => [
117
+ entryKey,
118
+ /(?:password|secret|token|credential|api[-_]?key)/i.test(entryKey)
119
+ ? '<redacted>'
120
+ : sanitizeSurfaceUpdateOutput(entryValue, options, entryKey),
121
+ ])
122
+ );
123
+ }
124
+ return value;
125
+ }
126
+
127
+ function command(command, args, label) {
128
+ return { command, args, label };
129
+ }
130
+
131
+ export function getAlwaysOnInstructionPath(host, {
132
+ home = os.homedir(),
133
+ platform = process.platform,
134
+ env = process.env,
135
+ } = {}) {
136
+ if (host === 'claude') return path.join(home, '.claude', 'CLAUDE.md');
137
+ if (host === 'codex') return path.join(home, '.codex', 'AGENTS.md');
138
+ if (host === 'copilot') return path.join(home, '.copilot', 'copilot-instructions.md');
139
+ if (host === 'antigravity') return path.join(home, '.gemini', 'GEMINI.md');
140
+ if (host === 'vscode') return path.join(home, '.copilot', 'instructions', 'eai-gofer.instructions.md');
141
+ throw new Error(`Unsupported host: ${host}`);
142
+ }
143
+
144
+ function getLegacyVsCodeSettingsPath({ home, platform, env }) {
145
+ if (platform === 'darwin') {
146
+ return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'settings.json');
147
+ }
148
+ if (platform === 'win32') {
149
+ return path.join(
150
+ env.APPDATA || path.join(home, 'AppData', 'Roaming'),
151
+ 'Code',
152
+ 'User',
153
+ 'settings.json'
154
+ );
155
+ }
156
+ return path.join(env.XDG_CONFIG_HOME || path.join(home, '.config'), 'Code', 'User', 'settings.json');
157
+ }
158
+
159
+ export function getBundledAlwaysOnSkillPath(host, {
160
+ pluginRoot = DEFAULT_PLUGIN_ROOT,
161
+ } = {}) {
162
+ if (host !== 'grok') throw new Error(`Unsupported bundled-skill host: ${host}`);
163
+ return path.join(pluginRoot, 'skills', 'eai', 'SKILL.md');
164
+ }
165
+
166
+ function verifyAlwaysOnSkill(content, host) {
167
+ if (
168
+ !content.includes(ALWAYS_ON_EAI_START) ||
169
+ !content.includes(ALWAYS_ON_EAI_END) ||
170
+ !content.includes('Apply this contract to every request')
171
+ ) {
172
+ throw new Error(`The bundled ${host} EAI skill does not contain the always-on contract.`);
173
+ }
174
+ }
175
+
176
+ export function upsertAlwaysOnEaiSection(content) {
177
+ if (ALWAYS_ON_EAI_MARKER.test(content)) {
178
+ return content.replace(ALWAYS_ON_EAI_MARKER, ALWAYS_ON_EAI_SECTION);
179
+ }
180
+ const separator = content.length === 0 ? '' : content.endsWith('\n') ? '\n' : '\n\n';
181
+ return `${content}${separator}${ALWAYS_ON_EAI_SECTION}\n`;
182
+ }
183
+
184
+ function upsertVsCodeInstructionFile(content) {
185
+ const withContract = upsertAlwaysOnEaiSection(content);
186
+ const frontmatter = withContract.match(/^---\r?\n([\s\S]*?)\r?\n---/);
187
+ if (!frontmatter) {
188
+ return `${VS_CODE_INSTRUCTIONS_FRONTMATTER}\n\n${withContract}`;
189
+ }
190
+ const body = frontmatter[1];
191
+ const updatedBody = /^applyTo\s*:/m.test(body)
192
+ ? body.replace(/^applyTo\s*:.*$/m, "applyTo: '**'")
193
+ : `${body}\napplyTo: '**'`;
194
+ return `${withContract.slice(0, frontmatter.index)}---\n${updatedBody}\n---${withContract.slice((frontmatter.index ?? 0) + frontmatter[0].length)}`;
195
+ }
196
+
197
+ function skipJsoncWhitespaceAndComments(content, start) {
198
+ let index = start;
199
+ while (index < content.length) {
200
+ if (/\s/.test(content[index])) {
201
+ index += 1;
202
+ continue;
203
+ }
204
+ if (content[index] === '/' && content[index + 1] === '/') {
205
+ index += 2;
206
+ while (index < content.length && !['\n', '\r'].includes(content[index])) index += 1;
207
+ continue;
208
+ }
209
+ if (content[index] === '/' && content[index + 1] === '*') {
210
+ index += 2;
211
+ while (index < content.length && !(content[index] === '*' && content[index + 1] === '/')) {
212
+ index += 1;
213
+ }
214
+ index += 2;
215
+ continue;
216
+ }
217
+ break;
218
+ }
219
+ return index;
220
+ }
221
+
222
+ function parseJsoncObject(content) {
223
+ const output = [];
224
+ let index = 0;
225
+ let quote = '';
226
+ let escaped = false;
227
+ while (index < content.length) {
228
+ const character = content[index];
229
+ const next = content[index + 1];
230
+ if (quote) {
231
+ output.push(character);
232
+ if (escaped) escaped = false;
233
+ else if (character === '\\') escaped = true;
234
+ else if (character === quote) quote = '';
235
+ index += 1;
236
+ continue;
237
+ }
238
+ if (character === '"') {
239
+ quote = character;
240
+ output.push(character);
241
+ index += 1;
242
+ continue;
243
+ }
244
+ if (character === '/' && next === '/') {
245
+ index += 2;
246
+ while (index < content.length && !['\n', '\r'].includes(content[index])) index += 1;
247
+ continue;
248
+ }
249
+ if (character === '/' && next === '*') {
250
+ index += 2;
251
+ while (index < content.length && !(content[index] === '*' && content[index + 1] === '/')) {
252
+ if (['\n', '\r'].includes(content[index])) output.push(content[index]);
253
+ index += 1;
254
+ }
255
+ index += 2;
256
+ continue;
257
+ }
258
+ if (character === ',') {
259
+ const lookahead = skipJsoncWhitespaceAndComments(content, index + 1);
260
+ if (content[lookahead] === '}' || content[lookahead] === ']') {
261
+ index += 1;
262
+ continue;
263
+ }
264
+ }
265
+ output.push(character);
266
+ index += 1;
267
+ }
268
+ const parsed = JSON.parse(output.join(''));
269
+ if (!parsed || Array.isArray(parsed) || typeof parsed !== 'object') {
270
+ throw new Error('VS Code settings.json must contain a JSON object.');
271
+ }
272
+ return parsed;
273
+ }
274
+
275
+ function findJsonStringEnd(content, start) {
276
+ let escaped = false;
277
+ for (let index = start + 1; index < content.length; index += 1) {
278
+ if (escaped) escaped = false;
279
+ else if (content[index] === '\\') escaped = true;
280
+ else if (content[index] === '"') return index + 1;
281
+ }
282
+ throw new Error('VS Code settings.json contains an unterminated string.');
283
+ }
284
+
285
+ function findJsoncValueEnd(content, start) {
286
+ const opening = content[start];
287
+ if (opening === '"') return findJsonStringEnd(content, start);
288
+ if (opening !== '[' && opening !== '{') {
289
+ let index = start;
290
+ while (index < content.length && !',}]'.includes(content[index])) index += 1;
291
+ return index;
292
+ }
293
+ const closing = opening === '[' ? ']' : '}';
294
+ let depth = 0;
295
+ let index = start;
296
+ while (index < content.length) {
297
+ if (content[index] === '"') {
298
+ index = findJsonStringEnd(content, index);
299
+ continue;
300
+ }
301
+ if (content[index] === '/' && ['/', '*'].includes(content[index + 1])) {
302
+ index = skipJsoncWhitespaceAndComments(content, index);
303
+ continue;
304
+ }
305
+ if (content[index] === opening) depth += 1;
306
+ if (content[index] === closing) {
307
+ depth -= 1;
308
+ if (depth === 0) return index + 1;
309
+ }
310
+ index += 1;
311
+ }
312
+ throw new Error('VS Code settings.json contains an unterminated value.');
313
+ }
314
+
315
+ function findJsoncPropertyRange(content, key) {
316
+ let depth = 0;
317
+ for (let index = 0; index < content.length;) {
318
+ if (content[index] === '"') {
319
+ const stringStart = index;
320
+ const stringEnd = findJsonStringEnd(content, index);
321
+ const next = skipJsoncWhitespaceAndComments(content, stringEnd);
322
+ if (depth === 1 && content[next] === ':' && JSON.parse(content.slice(stringStart, stringEnd)) === key) {
323
+ const valueStart = skipJsoncWhitespaceAndComments(content, next + 1);
324
+ const valueEnd = findJsoncValueEnd(content, valueStart);
325
+ const afterValue = skipJsoncWhitespaceAndComments(content, valueEnd);
326
+ if (content[afterValue] === ',') {
327
+ return { removeStart: stringStart, removeEnd: afterValue + 1, valueStart, valueEnd };
328
+ }
329
+ let beforeKey = stringStart - 1;
330
+ while (beforeKey >= 0 && /\s/.test(content[beforeKey])) beforeKey -= 1;
331
+ if (content[beforeKey] === ',') {
332
+ return { removeStart: beforeKey, removeEnd: valueEnd, valueStart, valueEnd };
333
+ }
334
+ return { removeStart: stringStart, removeEnd: valueEnd, valueStart, valueEnd };
335
+ }
336
+ index = stringEnd;
337
+ continue;
338
+ }
339
+ if (content[index] === '/' && ['/', '*'].includes(content[index + 1])) {
340
+ index = skipJsoncWhitespaceAndComments(content, index);
341
+ continue;
342
+ }
343
+ if (content[index] === '{' || content[index] === '[') depth += 1;
344
+ if (content[index] === '}' || content[index] === ']') depth -= 1;
345
+ index += 1;
346
+ }
347
+ return undefined;
348
+ }
349
+
350
+ function removeLegacyVsCodeGoferInstruction(content) {
351
+ const settings = parseJsoncObject(content);
352
+ const instructions = settings[LEGACY_VS_CODE_INSTRUCTIONS_KEY];
353
+ if (!Array.isArray(instructions)) return content;
354
+ const retained = instructions.filter(
355
+ (entry) => typeof entry?.text !== 'string' || !entry.text.includes(ALWAYS_ON_EAI_START)
356
+ );
357
+ if (retained.length === instructions.length) return content;
358
+ const range = findJsoncPropertyRange(content, LEGACY_VS_CODE_INSTRUCTIONS_KEY);
359
+ if (!range) throw new Error('Could not safely locate the legacy VS Code Gofer setting.');
360
+ if (retained.length === 0) {
361
+ return `${content.slice(0, range.removeStart)}${content.slice(range.removeEnd)}`;
362
+ }
363
+ return `${content.slice(0, range.valueStart)}${JSON.stringify(retained, null, 2)}${content.slice(range.valueEnd)}`;
364
+ }
365
+
366
+ function getInstructionSecurityRoot(host, { home, platform, env }) {
367
+ return home;
368
+ }
369
+
370
+ async function assertNoSymlinkComponents(targetPath, securityRoot, fileSystem) {
371
+ if (!path.isAbsolute(targetPath) || !path.isAbsolute(securityRoot)) {
372
+ throw new Error('Refusing to update a non-absolute user instruction target.');
373
+ }
374
+
375
+ const relativeTarget = path.relative(securityRoot, targetPath);
376
+ if (
377
+ relativeTarget === '..' ||
378
+ relativeTarget.startsWith(`..${path.sep}`) ||
379
+ path.isAbsolute(relativeTarget)
380
+ ) {
381
+ throw new Error('Refusing to update a user instruction target outside its configuration root.');
382
+ }
383
+
384
+ const components = relativeTarget.split(path.sep).filter(Boolean);
385
+ let currentPath = securityRoot;
386
+ try {
387
+ const rootStatus = await fileSystem.lstat(currentPath);
388
+ if (rootStatus.isSymbolicLink()) {
389
+ throw new Error('Refusing to update a user instruction target through a symbolic link.');
390
+ }
391
+ } catch (error) {
392
+ if (error?.code === 'ENOENT') return;
393
+ throw error;
394
+ }
395
+ for (const component of components) {
396
+ currentPath = path.join(currentPath, component);
397
+ try {
398
+ const status = await fileSystem.lstat(currentPath);
399
+ if (status.isSymbolicLink()) {
400
+ throw new Error('Refusing to update a user instruction target through a symbolic link.');
401
+ }
402
+ } catch (error) {
403
+ if (error?.code === 'ENOENT') return;
404
+ throw error;
405
+ }
406
+ }
407
+ }
408
+
409
+ async function readText(targetPath, securityRoot, fileSystem) {
410
+ await assertNoSymlinkComponents(targetPath, securityRoot, fileSystem);
411
+ let handle;
412
+ try {
413
+ const status = await fileSystem.lstat(targetPath);
414
+ if (!status.isFile()) {
415
+ throw new Error('Refusing to update a user instruction target that is not a regular file.');
416
+ }
417
+ handle = await fileSystem.open(
418
+ targetPath,
419
+ fsConstants.O_RDONLY | (fsConstants.O_NOFOLLOW ?? 0)
420
+ );
421
+ const openedStatus = await handle.stat();
422
+ if (!openedStatus.isFile() || !sameFileIdentity(
423
+ { exists: true, dev: status.dev, ino: status.ino },
424
+ { exists: true, dev: openedStatus.dev, ino: openedStatus.ino }
425
+ )) {
426
+ throw new Error('Refusing to read a user instruction target that changed during the update.');
427
+ }
428
+ return {
429
+ content: await handle.readFile('utf8'),
430
+ exists: true,
431
+ mode: openedStatus.mode & 0o777,
432
+ dev: openedStatus.dev,
433
+ ino: openedStatus.ino,
434
+ };
435
+ } catch (error) {
436
+ if (error?.code === 'ENOENT') {
437
+ return { content: '', exists: false, mode: 0o600, dev: undefined, ino: undefined };
438
+ }
439
+ throw error;
440
+ } finally {
441
+ await handle?.close();
442
+ }
443
+ }
444
+
445
+ function sameFileIdentity(left, right) {
446
+ if (!left.exists || !right.exists) return left.exists === right.exists;
447
+ if (left.dev === undefined || left.ino === undefined || right.dev === undefined || right.ino === undefined) {
448
+ return true;
449
+ }
450
+ return left.dev === right.dev && left.ino === right.ino;
451
+ }
452
+
453
+ async function writeOpenFileContent(handle, content) {
454
+ const bytes = Buffer.from(content, 'utf8');
455
+ let offset = 0;
456
+ while (offset < bytes.length) {
457
+ const { bytesWritten } = await handle.write(bytes, offset, bytes.length - offset, offset);
458
+ if (bytesWritten === 0) throw new Error('Unable to make progress while writing instructions.');
459
+ offset += bytesWritten;
460
+ }
461
+ }
462
+
463
+ async function readPathIdentity(targetPath, fileSystem) {
464
+ try {
465
+ const status = await fileSystem.lstat(targetPath);
466
+ return {
467
+ exists: true,
468
+ file: status.isFile(),
469
+ symlink: status.isSymbolicLink(),
470
+ dev: status.dev,
471
+ ino: status.ino,
472
+ };
473
+ } catch (error) {
474
+ if (error?.code === 'ENOENT') {
475
+ return { exists: false, file: false, symlink: false, dev: undefined, ino: undefined };
476
+ }
477
+ throw error;
478
+ }
479
+ }
480
+
481
+ async function unlinkOnlyIfIdentityMatches(candidatePath, expected, fileSystem) {
482
+ try {
483
+ const current = await readPathIdentity(candidatePath, fileSystem);
484
+ if (current.exists && sameFileIdentity(expected, current)) {
485
+ await fileSystem.unlink(candidatePath);
486
+ }
487
+ } catch {
488
+ // Preserve the primary fail-closed error. Never unlink an unverified path.
489
+ }
490
+ }
491
+
492
+ async function writeText(targetPath, content, securityRoot, fileSystem, original, platform) {
493
+ const parentPath = path.dirname(targetPath);
494
+ await fileSystem.mkdir(parentPath, { recursive: true });
495
+ await assertNoSymlinkComponents(targetPath, securityRoot, fileSystem);
496
+ const parentBeforeOpen = await fileSystem.lstat(parentPath);
497
+ if (parentBeforeOpen.isSymbolicLink() || !parentBeforeOpen.isDirectory()) {
498
+ throw new Error('Refusing to update instructions through an unsafe parent directory.');
499
+ }
500
+ const parentHandle = await fileSystem.open(
501
+ parentPath,
502
+ fsConstants.O_RDONLY | (fsConstants.O_DIRECTORY ?? 0)
503
+ );
504
+ const parentStatus = await parentHandle.stat();
505
+ if (!parentStatus.isDirectory()) {
506
+ await parentHandle.close();
507
+ throw new Error('Refusing to update instructions through a non-directory parent.');
508
+ }
509
+ if (!sameFileIdentity(
510
+ { exists: true, dev: parentBeforeOpen.dev, ino: parentBeforeOpen.ino },
511
+ { exists: true, dev: parentStatus.dev, ino: parentStatus.ino }
512
+ )) {
513
+ await parentHandle.close();
514
+ throw new Error('Refusing to update instructions through a parent that changed while opening.');
515
+ }
516
+ let temporaryHandle;
517
+ let temporaryPath;
518
+ let temporaryIdentity;
519
+ let renamed = false;
520
+ try {
521
+ const beforeCreateParent = await fileSystem.lstat(parentPath);
522
+ if (!sameFileIdentity(
523
+ { exists: true, dev: parentStatus.dev, ino: parentStatus.ino },
524
+ { exists: true, dev: beforeCreateParent.dev, ino: beforeCreateParent.ino }
525
+ )) {
526
+ throw new Error('Refusing to replace instructions through a parent that changed during the update.');
527
+ }
528
+
529
+ const temporaryName = `.${path.basename(targetPath)}.gofer-${randomBytes(16).toString('hex')}.tmp`;
530
+ temporaryPath = path.join(parentPath, temporaryName);
531
+ temporaryHandle = await fileSystem.open(
532
+ temporaryPath,
533
+ fsConstants.O_WRONLY |
534
+ fsConstants.O_CREAT |
535
+ fsConstants.O_EXCL |
536
+ (fsConstants.O_NOFOLLOW ?? 0),
537
+ 0o600
538
+ );
539
+ const temporaryStatus = await temporaryHandle.stat();
540
+ if (!temporaryStatus.isFile()) {
541
+ throw new Error('Refusing to create a non-file instruction replacement.');
542
+ }
543
+ temporaryIdentity = {
544
+ exists: true,
545
+ dev: temporaryStatus.dev,
546
+ ino: temporaryStatus.ino,
547
+ };
548
+
549
+ const afterCreateParent = await fileSystem.lstat(parentPath);
550
+ if (!sameFileIdentity(
551
+ { exists: true, dev: parentStatus.dev, ino: parentStatus.ino },
552
+ { exists: true, dev: afterCreateParent.dev, ino: afterCreateParent.ino }
553
+ )) {
554
+ throw new Error('Refusing to replace instructions through a parent that changed during the update.');
555
+ }
556
+
557
+ await writeOpenFileContent(temporaryHandle, content);
558
+ await temporaryHandle.chmod(original.mode);
559
+ await temporaryHandle.sync();
560
+ await temporaryHandle.close();
561
+ temporaryHandle = undefined;
562
+
563
+ const currentTarget = await readPathIdentity(targetPath, fileSystem);
564
+ if (
565
+ currentTarget.symlink ||
566
+ (currentTarget.exists && !currentTarget.file) ||
567
+ !sameFileIdentity(original, currentTarget)
568
+ ) {
569
+ throw new Error('Refusing to replace a user instruction target that changed during the update.');
570
+ }
571
+ const beforeRenameParent = await fileSystem.lstat(parentPath);
572
+ if (!sameFileIdentity(
573
+ { exists: true, dev: parentStatus.dev, ino: parentStatus.ino },
574
+ { exists: true, dev: beforeRenameParent.dev, ino: beforeRenameParent.ino }
575
+ )) {
576
+ throw new Error('Refusing to replace instructions through a parent that changed during the update.');
577
+ }
578
+
579
+ await fileSystem.rename(temporaryPath, targetPath);
580
+ renamed = true;
581
+
582
+ const [afterRenameParent, installedTarget] = await Promise.all([
583
+ fileSystem.lstat(parentPath),
584
+ readPathIdentity(targetPath, fileSystem),
585
+ ]);
586
+ if (
587
+ !sameFileIdentity(
588
+ { exists: true, dev: parentStatus.dev, ino: parentStatus.ino },
589
+ { exists: true, dev: afterRenameParent.dev, ino: afterRenameParent.ino }
590
+ ) ||
591
+ !sameFileIdentity(temporaryIdentity, installedTarget)
592
+ ) {
593
+ await unlinkOnlyIfIdentityMatches(targetPath, temporaryIdentity, fileSystem);
594
+ throw new Error('Refusing to keep instructions after the parent changed during the update.');
595
+ }
596
+
597
+ if (platform !== 'win32') {
598
+ await parentHandle.sync();
599
+ }
600
+ } catch (error) {
601
+ if (!renamed && temporaryPath && temporaryIdentity) {
602
+ await unlinkOnlyIfIdentityMatches(temporaryPath, temporaryIdentity, fileSystem);
603
+ }
604
+ throw error;
605
+ } finally {
606
+ await temporaryHandle?.close();
607
+ await parentHandle.close();
608
+ }
609
+ }
610
+
611
+ export async function configureAlwaysOnInstructions(hosts, {
612
+ home = os.homedir(),
613
+ platform = process.platform,
614
+ env = process.env,
615
+ fileSystem = fs,
616
+ pluginRoot = DEFAULT_PLUGIN_ROOT,
617
+ } = {}) {
618
+ const results = [];
619
+ for (const host of [...new Set(hosts)]) {
620
+ let targetPath;
621
+ try {
622
+ if (host === 'grok') {
623
+ targetPath = getBundledAlwaysOnSkillPath(host, { pluginRoot });
624
+ const content = await fileSystem.readFile(targetPath, 'utf8');
625
+ verifyAlwaysOnSkill(content, host);
626
+ results.push({
627
+ host,
628
+ targetPath: sanitizeLocalPath(targetPath),
629
+ ok: true,
630
+ managedByPlugin: true,
631
+ });
632
+ continue;
633
+ }
634
+
635
+ targetPath = getAlwaysOnInstructionPath(host, { home, platform, env });
636
+ const securityRoot = getInstructionSecurityRoot(host, { home, platform, env });
637
+ const original = await readText(targetPath, securityRoot, fileSystem);
638
+ const existing = original.content;
639
+ if (host === 'vscode') {
640
+ const updated = upsertVsCodeInstructionFile(existing);
641
+ if (updated !== existing) {
642
+ await writeText(targetPath, updated, securityRoot, fileSystem, original, platform);
643
+ }
644
+ const legacySettingsPath = getLegacyVsCodeSettingsPath({ home, platform, env });
645
+ const legacyOriginal = await readText(legacySettingsPath, securityRoot, fileSystem);
646
+ if (legacyOriginal.exists) {
647
+ const migratedSettings = removeLegacyVsCodeGoferInstruction(legacyOriginal.content);
648
+ if (migratedSettings !== legacyOriginal.content) {
649
+ await writeText(
650
+ legacySettingsPath,
651
+ migratedSettings,
652
+ securityRoot,
653
+ fileSystem,
654
+ legacyOriginal,
655
+ platform
656
+ );
657
+ }
658
+ }
659
+ } else {
660
+ const updated = upsertAlwaysOnEaiSection(existing);
661
+ if (updated !== existing) {
662
+ await writeText(targetPath, updated, securityRoot, fileSystem, original, platform);
663
+ }
664
+ }
665
+ results.push({ host, targetPath: sanitizeLocalPath(targetPath), ok: true });
666
+ } catch (error) {
667
+ results.push({
668
+ host,
669
+ targetPath: sanitizeLocalPath(targetPath),
670
+ ok: false,
671
+ managedByPlugin: host === 'grok',
672
+ error: sanitizeDiagnostic(error instanceof Error ? error.message : String(error), { home }),
673
+ });
674
+ }
675
+ }
676
+ return results;
677
+ }
678
+
679
+ const SURFACE_ACTIONS = {
680
+ claude: {
681
+ install: [
682
+ command('claude', ['plugin', 'marketplace', 'add', REPOSITORY_URL, '--scope', 'user', '--sparse', '.claude-plugin', '--sparse', 'plugins/eai-gofer'], 'Add the EAI Gofer marketplace'),
683
+ command('claude', ['plugin', 'install', 'eai-gofer@eai-gofer', '--scope', 'user'], 'Install EAI Gofer'),
684
+ ],
685
+ update: [
686
+ command('claude', ['plugin', 'marketplace', 'update', 'eai-gofer'], 'Refresh the EAI Gofer marketplace'),
687
+ command('claude', ['plugin', 'update', 'eai-gofer@eai-gofer', '--scope', 'user'], 'Update EAI Gofer'),
688
+ ],
689
+ refresh: 'Run /reload-plugins, then start a new Claude Code conversation.',
690
+ },
691
+ codex: {
692
+ install: [
693
+ command('codex', ['plugin', 'marketplace', 'add', REPOSITORY_URL, '--sparse', '.agents/plugins', '--sparse', 'plugins/eai-gofer'], 'Add the EAI Gofer marketplace'),
694
+ command('codex', ['plugin', 'add', 'eai-gofer@eai-gofer'], 'Install EAI Gofer'),
695
+ ],
696
+ update: [
697
+ command('codex', ['plugin', 'marketplace', 'upgrade', 'eai-gofer'], 'Refresh the EAI Gofer marketplace'),
698
+ command('codex', ['plugin', 'add', 'eai-gofer@eai-gofer'], 'Apply the refreshed EAI Gofer plugin'),
699
+ ],
700
+ refresh: 'Start a new Codex task or restart Codex so it loads the refreshed plugin.',
701
+ },
702
+ copilot: {
703
+ install: [
704
+ command('copilot', ['plugin', 'marketplace', 'add', REPOSITORY_URL], 'Add the EAI Gofer marketplace'),
705
+ command('copilot', ['plugin', 'install', 'eai-gofer@eai-gofer'], 'Install EAI Gofer'),
706
+ ],
707
+ update: [
708
+ command('copilot', ['plugin', 'marketplace', 'update', 'eai-gofer'], 'Refresh the EAI Gofer marketplace'),
709
+ command('copilot', ['plugin', 'update', 'eai-gofer@eai-gofer'], 'Update EAI Gofer'),
710
+ ],
711
+ refresh: 'Run /restart in Copilot CLI or start a new Copilot app chat.',
712
+ },
713
+ antigravity: {
714
+ install: [
715
+ command('agy', ['plugin', 'install', REPOSITORY_URL], 'Install EAI Gofer'),
716
+ ],
717
+ update: [
718
+ command('agy', ['plugin', 'install', REPOSITORY_URL], 'Reinstall the current EAI Gofer plugin'),
719
+ ],
720
+ refresh: 'Start a new Antigravity session so it loads the installed plugin.',
721
+ },
722
+ grok: {
723
+ install: [
724
+ command('grok', ['plugin', 'install', '--trust', REPOSITORY_URL], 'Install EAI Gofer'),
725
+ ],
726
+ update: [
727
+ command('grok', ['plugin', 'update', 'eai-gofer'], 'Update EAI Gofer'),
728
+ ],
729
+ refresh: 'Start a new Grok Build session so it loads the updated plugin.',
730
+ },
731
+ vscode: {
732
+ install: [
733
+ command('code', ['--install-extension', VS_CODE_EXTENSION_ID, '--force'], 'Install or update the EAI Gofer VS Code extension'),
734
+ ],
735
+ update: [
736
+ command('code', ['--install-extension', VS_CODE_EXTENSION_ID, '--force'], 'Install or update the EAI Gofer VS Code extension'),
737
+ ],
738
+ refresh: 'Run Developer: Reload Window in VS Code.',
739
+ },
740
+ };
741
+
742
+ export function parseArgs(argv) {
743
+ const result = { action: 'inspect', host: 'auto', execute: false, json: false, help: false };
744
+ if (argv.includes('--help') || argv.includes('-h')) return { ...result, help: true };
745
+ for (let index = 0; index < argv.length; index += 1) {
746
+ const value = argv[index];
747
+ const nextValue = argv[index + 1];
748
+ if (value === '--action') {
749
+ if (!nextValue || nextValue.startsWith('-')) throw new Error('Missing value for --action.');
750
+ result.action = argv[++index];
751
+ } else if (value === '--host') {
752
+ if (!nextValue || nextValue.startsWith('-')) throw new Error('Missing value for --host.');
753
+ result.host = argv[++index];
754
+ }
755
+ else if (value === '--execute') result.execute = true;
756
+ else if (value === '--json') result.json = true;
757
+ else if (value === '--help' || value === '-h') result.help = true;
758
+ else throw new Error('Unsupported option. Use --action, --host, --execute, --json, or --help.');
759
+ }
760
+ if (!['inspect', 'install', 'update'].includes(result.action)) {
761
+ throw new Error(`Unsupported action: ${result.action}. Use inspect, install, or update.`);
762
+ }
763
+ result.host = LEGACY_HOST_ALIASES[result.host] || result.host;
764
+ if (!['auto', 'all', ...SUPPORTED_HOSTS].includes(result.host)) {
765
+ throw new Error(`Unsupported host: ${result.host}. Use auto, all, ${SUPPORTED_HOSTS.join(', ')}`);
766
+ }
767
+ return result;
768
+ }
769
+
770
+ export function resolveHosts(host, currentHost = process.env.GOFER_HOST) {
771
+ const normalizedHost = LEGACY_HOST_ALIASES[host] || host;
772
+ const normalizedCurrentHost = LEGACY_HOST_ALIASES[currentHost] || currentHost;
773
+ if (normalizedHost === 'all') return [...SUPPORTED_HOSTS];
774
+ if (normalizedHost !== 'auto') {
775
+ if (!SUPPORTED_HOSTS.includes(normalizedHost)) {
776
+ throw new Error(`Unsupported host: ${normalizedHost}. Use auto, all, ${SUPPORTED_HOSTS.join(', ')}`);
777
+ }
778
+ return [normalizedHost];
779
+ }
780
+ if (SUPPORTED_HOSTS.includes(normalizedCurrentHost)) return [normalizedCurrentHost];
781
+ return [];
782
+ }
783
+
784
+ export function buildSurfacePlan({ action, host, currentHost }) {
785
+ if (!['inspect', 'install', 'update'].includes(action)) {
786
+ throw new Error(`Unsupported action: ${action}. Use inspect, install, or update.`);
787
+ }
788
+ const selectedHosts = resolveHosts(host, currentHost);
789
+ if (selectedHosts.length === 0) {
790
+ throw new Error(`Use --host with ${SUPPORTED_HOSTS.join(', ')}, or all.`);
791
+ }
792
+ return selectedHosts.map((surface) => ({
793
+ host: surface,
794
+ action,
795
+ commands: action === 'inspect' ? [] : SURFACE_ACTIONS[surface][action],
796
+ refresh: SURFACE_ACTIONS[surface].refresh,
797
+ }));
798
+ }
799
+
800
+ export async function inspectHost(host, execute = execFileAsync) {
801
+ const normalizedHost = LEGACY_HOST_ALIASES[host] || host;
802
+ if (!SUPPORTED_HOSTS.includes(normalizedHost)) {
803
+ throw new Error(`Unsupported host: ${normalizedHost}. Use ${SUPPORTED_HOSTS.join(', ')}`);
804
+ }
805
+ const executable = executableForHost(normalizedHost);
806
+ const listArgs = {
807
+ claude: ['plugin', 'list'],
808
+ codex: ['plugin', 'list', '--json'],
809
+ copilot: ['plugin', 'list'],
810
+ antigravity: ['plugin', 'list'],
811
+ grok: ['plugin', 'list'],
812
+ vscode: ['--list-extensions', '--show-versions'],
813
+ }[normalizedHost];
814
+ try {
815
+ const version = await execute(executable, ['--version'], { windowsHide: true });
816
+ const listing = await execute(executable, listArgs, { windowsHide: true });
817
+ return {
818
+ host: normalizedHost,
819
+ available: true,
820
+ version: sanitizeDiagnostic(version.stdout.trim().split('\n')[0]),
821
+ installed: pluginListingHasGofer(normalizedHost, listing.stdout),
822
+ };
823
+ } catch (error) {
824
+ return {
825
+ host: normalizedHost,
826
+ available: false,
827
+ installed: false,
828
+ error: sanitizeDiagnostic(error?.code ?? error?.message ?? String(error)),
829
+ };
830
+ }
831
+ }
832
+
833
+ function isExactGoferIdentifier(host, value) {
834
+ const candidate = String(value).trim().replace(/[,:;]$/, '');
835
+ const version = String.raw`v?\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?`;
836
+ if (host === 'vscode') {
837
+ return new RegExp(`^EnterpriseAI\\.gofer(?:@${version})?$`, 'i').test(candidate);
838
+ }
839
+ return new RegExp(`^eai-gofer(?:@(?:eai-gofer|${version}))?$`, 'i').test(candidate);
840
+ }
841
+
842
+ function structuredListingContainsGofer(host, value) {
843
+ if (typeof value === 'string') return isExactGoferIdentifier(host, value);
844
+ if (Array.isArray(value)) {
845
+ return value.some((entry) => structuredListingContainsGofer(host, entry));
846
+ }
847
+ if (value && typeof value === 'object') {
848
+ return Object.values(value).some((entry) => structuredListingContainsGofer(host, entry));
849
+ }
850
+ return false;
851
+ }
852
+
853
+ export function pluginListingHasGofer(host, output) {
854
+ const normalizedHost = LEGACY_HOST_ALIASES[host] || host;
855
+ if (!SUPPORTED_HOSTS.includes(normalizedHost)) return false;
856
+
857
+ if (normalizedHost === 'codex') {
858
+ try {
859
+ return structuredListingContainsGofer(normalizedHost, JSON.parse(String(output)));
860
+ } catch {
861
+ return false;
862
+ }
863
+ }
864
+
865
+ return String(output)
866
+ .split(/\r?\n/)
867
+ .map((line) => line.trim().replace(/^[*+\-✓✔•]+\s*/, ''))
868
+ .filter(Boolean)
869
+ .some((line) => {
870
+ const candidate = normalizedHost === 'vscode' ? line : line.split(/\s+/, 1)[0];
871
+ return isExactGoferIdentifier(normalizedHost, candidate);
872
+ });
873
+ }
874
+
875
+ export async function inspectHosts(hosts, inspect = inspectHost) {
876
+ return Promise.all(hosts.map((host) => inspect(host)));
877
+ }
878
+
879
+ function executableForHost(host) {
880
+ if (host === 'vscode') return 'code';
881
+ if (host === 'antigravity') return 'agy';
882
+ return host;
883
+ }
884
+
885
+ function isLocalMarketplacePath(value) {
886
+ return /^(?:[A-Za-z]:[\\/]|[/~])/.test(value.trim());
887
+ }
888
+
889
+ function isOfficialRepositoryUrl(value) {
890
+ const normalized = value.trim().replace(/\.git$/, '').replace(/\/$/, '');
891
+ return [
892
+ 'https://github.com/eai-support/eai-gofer',
893
+ 'git@github.com:eai-support/eai-gofer',
894
+ 'ssh://git@github.com/eai-support/eai-gofer',
895
+ ].includes(normalized);
896
+ }
897
+
898
+ export async function inspectLocalCodexMarketplace(root, execute = execFileAsync) {
899
+ try {
900
+ const [status, remote, branch] = await Promise.all([
901
+ execute('git', ['-C', root, 'status', '--porcelain'], { windowsHide: true }),
902
+ execute('git', ['-C', root, 'remote', 'get-url', 'origin'], { windowsHide: true }),
903
+ execute('git', ['-C', root, 'branch', '--show-current'], { windowsHide: true }),
904
+ ]);
905
+ return {
906
+ root,
907
+ clean: status.stdout.trim().length === 0,
908
+ official: isOfficialRepositoryUrl(remote.stdout),
909
+ branch: sanitizeDiagnostic(branch.stdout.trim()),
910
+ };
911
+ } catch (error) {
912
+ return {
913
+ root,
914
+ clean: false,
915
+ official: false,
916
+ branch: '',
917
+ error: sanitizeDiagnostic(error?.stderr?.trim() ?? error?.message ?? String(error)),
918
+ };
919
+ }
920
+ }
921
+
922
+ export async function inspectCodexMarketplace(execute = execFileAsync) {
923
+ try {
924
+ const result = await execute('codex', ['plugin', 'marketplace', 'list'], {
925
+ windowsHide: true,
926
+ });
927
+ const marketplace = result.stdout
928
+ .split(/\r?\n/)
929
+ .map((line) => line.trim())
930
+ .find((line) => line.startsWith('eai-gofer'));
931
+ const root = marketplace?.replace(/^eai-gofer\s+/, '').trim();
932
+
933
+ if (!root) {
934
+ return { type: 'unknown' };
935
+ }
936
+
937
+ return {
938
+ type: isLocalMarketplacePath(root) ? 'local' : 'git',
939
+ root,
940
+ };
941
+ } catch {
942
+ return { type: 'unknown' };
943
+ }
944
+ }
945
+
946
+ export async function runPlan(
947
+ plan,
948
+ {
949
+ inspect = inspectHost,
950
+ inspectMarketplace = inspectCodexMarketplace,
951
+ inspectLocalMarketplace = inspectLocalCodexMarketplace,
952
+ execute = execFileAsync,
953
+ cleanup = cleanupLocalSettings,
954
+ configureInstructions = configureAlwaysOnInstructions,
955
+ } = {}
956
+ ) {
957
+ if (!Array.isArray(plan)) {
958
+ throw new Error('Surface plan must be an array.');
959
+ }
960
+ for (const surface of plan) {
961
+ if (!surface || !SUPPORTED_HOSTS.includes(surface.host)) {
962
+ throw new Error(`Unsupported host: ${surface?.host}. Use ${SUPPORTED_HOSTS.join(', ')}`);
963
+ }
964
+ if (!['install', 'update'].includes(surface.action)) {
965
+ throw new Error(`Unsupported executable action: ${surface.action}. Use install or update.`);
966
+ }
967
+ }
968
+
969
+ const executablePlan = plan.map((surface) => {
970
+ const canonicalCommands = SURFACE_ACTIONS[surface.host][surface.action];
971
+ if (JSON.stringify(surface.commands) !== JSON.stringify(canonicalCommands)) {
972
+ throw new Error(
973
+ `Surface plan for ${surface.host} must exactly match the packaged ${surface.action} action.`
974
+ );
975
+ }
976
+ return {
977
+ ...surface,
978
+ commands: canonicalCommands.map((step) => ({ ...step, args: [...step.args] })),
979
+ };
980
+ });
981
+
982
+ const results = [];
983
+ let completedUpdate = false;
984
+ let planFailed = false;
985
+ const configuredHosts = [];
986
+
987
+ for (const surface of executablePlan) {
988
+ const availability = await inspect(surface.host);
989
+ if (!availability.available) {
990
+ planFailed = true;
991
+ results.push({
992
+ host: surface.host,
993
+ skipped: true,
994
+ ok: false,
995
+ label: 'Inspect host availability',
996
+ reason: `${executableForHost(surface.host)} is not installed or is not on PATH.`,
997
+ });
998
+ continue;
999
+ }
1000
+ if (surface.host === 'codex' && surface.action === 'update') {
1001
+ const marketplace = await inspectMarketplace(execute);
1002
+ if (marketplace.type === 'local') {
1003
+ const local = await inspectLocalMarketplace(marketplace.root, execute);
1004
+ if (!local.clean || !local.official || local.branch !== 'main') {
1005
+ const reasons = [
1006
+ !local.clean ? 'it has uncommitted changes' : '',
1007
+ !local.official ? 'its origin is not the official EAI Gofer repository' : '',
1008
+ local.branch !== 'main' ? `it is on ${local.branch || 'a detached branch'}, not main` : '',
1009
+ ].filter(Boolean).join('; ');
1010
+ results.push({
1011
+ host: surface.host,
1012
+ label: 'Update local EAI Gofer marketplace',
1013
+ ok: false,
1014
+ error:
1015
+ `EAI Gofer uses the local marketplace at ${sanitizeLocalPath(marketplace.root)}, but it was not updated because ${sanitizeDiagnostic(reasons)}. ` +
1016
+ 'The always-on EAI instruction was refreshed. Commit, stash, or switch the local checkout to a clean official main branch, or replace it with the public Git marketplace, then restart Codex.',
1017
+ });
1018
+ planFailed = true;
1019
+ configuredHosts.push(surface.host);
1020
+ continue;
1021
+ }
1022
+ surface.commands = [
1023
+ command('git', ['-C', marketplace.root, 'fetch', 'origin', 'main'], 'Fetch the local EAI Gofer marketplace'),
1024
+ command('git', ['-C', marketplace.root, 'merge', '--ff-only', 'origin/main'], 'Fast-forward the local EAI Gofer marketplace'),
1025
+ command('codex', ['plugin', 'add', 'eai-gofer@eai-gofer'], 'Apply the refreshed EAI Gofer plugin'),
1026
+ ];
1027
+ }
1028
+ if (marketplace.type !== 'git' && marketplace.type !== 'local') {
1029
+ results.push({
1030
+ host: surface.host,
1031
+ label: 'Inspect Codex EAI Gofer marketplace',
1032
+ ok: false,
1033
+ error:
1034
+ 'Could not confirm the Codex marketplace source. Update stopped to protect local Gofer work and settings.',
1035
+ });
1036
+ planFailed = true;
1037
+ continue;
1038
+ }
1039
+ }
1040
+ let completedSurface = true;
1041
+ for (const step of surface.commands) {
1042
+ try {
1043
+ const result = await execute(step.command, step.args, { windowsHide: true });
1044
+ results.push({
1045
+ host: surface.host,
1046
+ label: step.label,
1047
+ ok: true,
1048
+ stdout: sanitizeDiagnostic(result.stdout.trim()),
1049
+ });
1050
+ } catch (error) {
1051
+ results.push({
1052
+ host: surface.host,
1053
+ label: step.label,
1054
+ ok: false,
1055
+ error: sanitizeDiagnostic(error?.stderr?.trim() ?? error?.message ?? String(error)),
1056
+ });
1057
+ completedSurface = false;
1058
+ planFailed = true;
1059
+ break;
1060
+ }
1061
+ }
1062
+ completedUpdate ||= completedSurface && surface.commands.length > 0;
1063
+ if (completedSurface && surface.commands.length > 0) {
1064
+ configuredHosts.push(surface.host);
1065
+ }
1066
+ }
1067
+
1068
+ if (configuredHosts.length > 0) {
1069
+ const instructionResults = await configureInstructions(configuredHosts);
1070
+ for (const entry of instructionResults) {
1071
+ if (!entry.ok) planFailed = true;
1072
+ results.push({
1073
+ host: entry.host,
1074
+ label: entry.managedByPlugin
1075
+ ? 'Verify bundled always-on Gofer skill'
1076
+ : 'Enable always-on Gofer instructions',
1077
+ ok: entry.ok,
1078
+ targetPath: sanitizeLocalPath(entry.targetPath),
1079
+ error: sanitizeDiagnostic(entry.error),
1080
+ });
1081
+ }
1082
+ }
1083
+
1084
+ if (completedUpdate && !planFailed) {
1085
+ try {
1086
+ const report = await cleanup({ apply: true });
1087
+ results.push({
1088
+ host: 'local',
1089
+ label: 'Archive stale Gofer surface entries',
1090
+ ok: true,
1091
+ archived: report.removed.length,
1092
+ archiveRoot: sanitizeLocalPath(report.archiveRoot),
1093
+ });
1094
+ } catch (error) {
1095
+ results.push({
1096
+ host: 'local',
1097
+ label: 'Archive stale Gofer surface entries',
1098
+ ok: false,
1099
+ error: sanitizeDiagnostic(error instanceof Error ? error.message : String(error)),
1100
+ });
1101
+ }
1102
+ }
1103
+
1104
+ return results;
1105
+ }
1106
+
1107
+ export function formatSurfaceUpdateReport(result) {
1108
+ result = sanitizeSurfaceUpdateOutput(result);
1109
+ const lines = [
1110
+ `Action: ${result.action}`,
1111
+ `Mode: ${result.execute ? 'execute' : 'plan only'}`,
1112
+ ];
1113
+
1114
+ if (result.action === 'inspect') {
1115
+ for (const host of result.hosts) {
1116
+ const status = host.available
1117
+ ? `available${host.installed ? ', Gofer installed' : ', Gofer not installed'}`
1118
+ : `not available${host.error ? `: ${host.error}` : ''}`;
1119
+ lines.push(`${host.host}: ${status}`);
1120
+ }
1121
+ return lines.join('\n');
1122
+ }
1123
+
1124
+ for (const surface of result.plan) {
1125
+ lines.push(`${surface.host}: ${surface.commands.map((step) => step.label).join('; ')}`);
1126
+ lines.push(`${surface.host}: reload - ${surface.refresh}`);
1127
+ }
1128
+ for (const entry of result.results) {
1129
+ if (entry.skipped) lines.push(`${entry.host}: skipped - ${entry.reason}`);
1130
+ else if (entry.ok) lines.push(`${entry.host}: ${entry.label} completed${entry.note ? ` - ${entry.note}` : ''}`);
1131
+ else lines.push(`${entry.host}: ${entry.label} failed - ${entry.error}`);
1132
+ }
1133
+
1134
+ return lines.join('\n');
1135
+ }
1136
+
1137
+ function printUsage() {
1138
+ process.stdout.write(`Usage: node gofer-surface-update.mjs --action <inspect|install|update> --host <${SUPPORTED_HOSTS.join('|')}|all> [--execute] [--json]\n`);
1139
+ }
1140
+
1141
+ async function main() {
1142
+ const args = parseArgs(process.argv.slice(2));
1143
+ if (args.help) {
1144
+ printUsage();
1145
+ return;
1146
+ }
1147
+ const selectedHosts = resolveHosts(args.host);
1148
+ if (selectedHosts.length === 0) {
1149
+ throw new Error('Use --host when running this helper outside a Gofer surface.');
1150
+ }
1151
+ if (args.action === 'inspect') {
1152
+ const report = await inspectHosts(selectedHosts);
1153
+ const result = { action: 'inspect', execute: false, hosts: report };
1154
+ process.stdout.write(
1155
+ `${args.json ? JSON.stringify(sanitizeSurfaceUpdateOutput(result), null, 2) : formatSurfaceUpdateReport(result)}\n`
1156
+ );
1157
+ return;
1158
+ }
1159
+ const plan = buildSurfacePlan(args);
1160
+ const result = {
1161
+ action: args.action,
1162
+ execute: args.execute,
1163
+ plan,
1164
+ results: args.execute ? await runPlan(plan) : [],
1165
+ };
1166
+ process.stdout.write(
1167
+ `${args.json ? JSON.stringify(sanitizeSurfaceUpdateOutput(result), null, 2) : formatSurfaceUpdateReport(result)}\n`
1168
+ );
1169
+ if (args.execute && result.results.some((entry) => entry.ok === false)) process.exitCode = 1;
1170
+ }
1171
+
1172
+ const invokedPath = process.argv[1] ? path.resolve(process.argv[1]) : '';
1173
+ if (fileURLToPath(import.meta.url) === invokedPath) {
1174
+ main().catch((error) => {
1175
+ process.stderr.write(`${sanitizeDiagnostic(error instanceof Error ? error.message : String(error))}\n`);
1176
+ process.exit(1);
1177
+ });
1178
+ }