@warpgogol/forge 5.2.4 → 5.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (590) hide show
  1. package/README.md +1 -1
  2. package/README.uk.md +8 -8
  3. package/bin/cli.ts +25 -33
  4. package/dist/bin/cli.js +21 -32
  5. package/dist/bin/cli.js.map +1 -1
  6. package/dist/os/adr/adr.module.d.ts.map +1 -1
  7. package/dist/os/adr/adr.module.js +8 -3
  8. package/dist/os/adr/adr.module.js.map +1 -1
  9. package/dist/os/adr/frontmatter-io.js +5 -5
  10. package/dist/os/adr/frontmatter-io.js.map +1 -1
  11. package/dist/os/adr/handlers/archive.d.ts.map +1 -1
  12. package/dist/os/adr/handlers/archive.js +10 -4
  13. package/dist/os/adr/handlers/archive.js.map +1 -1
  14. package/dist/os/adr/handlers/implement-stamp.d.ts.map +1 -1
  15. package/dist/os/adr/handlers/implement-stamp.js +10 -10
  16. package/dist/os/adr/handlers/implement-stamp.js.map +1 -1
  17. package/dist/os/adr/handlers/list-create.js +5 -5
  18. package/dist/os/adr/handlers/list-create.js.map +1 -1
  19. package/dist/os/adr/handlers/validate.js +1 -1
  20. package/dist/os/adr/handlers/validate.js.map +1 -1
  21. package/dist/os/audit/audit.module.js +3 -3
  22. package/dist/os/audit/audit.module.js.map +1 -1
  23. package/dist/os/audit/frontmatter-io.js +5 -5
  24. package/dist/os/audit/frontmatter-io.js.map +1 -1
  25. package/dist/os/audit/handlers/archive.d.ts.map +1 -1
  26. package/dist/os/audit/handlers/archive.js +10 -4
  27. package/dist/os/audit/handlers/archive.js.map +1 -1
  28. package/dist/os/compass/compass.module.d.ts.map +1 -1
  29. package/dist/os/compass/compass.module.js +17 -1
  30. package/dist/os/compass/compass.module.js.map +1 -1
  31. package/dist/os/compass/handlers/compass-audit-handler.d.ts.map +1 -1
  32. package/dist/os/compass/handlers/compass-audit-handler.js +12 -5
  33. package/dist/os/compass/handlers/compass-audit-handler.js.map +1 -1
  34. package/dist/os/compass/handlers/compass-change-summary-handler.d.ts.map +1 -1
  35. package/dist/os/compass/handlers/compass-change-summary-handler.js +3 -2
  36. package/dist/os/compass/handlers/compass-change-summary-handler.js.map +1 -1
  37. package/dist/os/compass/handlers/compass-inventory-handler.d.ts.map +1 -1
  38. package/dist/os/compass/handlers/compass-inventory-handler.js +6 -4
  39. package/dist/os/compass/handlers/compass-inventory-handler.js.map +1 -1
  40. package/dist/os/compass/handlers/compass-inventory.d.ts.map +1 -1
  41. package/dist/os/compass/handlers/compass-inventory.js +10 -8
  42. package/dist/os/compass/handlers/compass-inventory.js.map +1 -1
  43. package/dist/os/compass/handlers/compass-migrate-handler.js +1 -1
  44. package/dist/os/compass/handlers/compass-migrate-handler.js.map +1 -1
  45. package/dist/os/compass/handlers/compass-migrate.d.ts.map +1 -1
  46. package/dist/os/compass/handlers/compass-migrate.js +6 -3
  47. package/dist/os/compass/handlers/compass-migrate.js.map +1 -1
  48. package/dist/os/compass/handlers/git-revision.d.ts.map +1 -1
  49. package/dist/os/compass/handlers/git-revision.js +9 -6
  50. package/dist/os/compass/handlers/git-revision.js.map +1 -1
  51. package/dist/os/compass/handlers/resolve-scan-root.js +1 -1
  52. package/dist/os/compass/handlers/resolve-scan-root.js.map +1 -1
  53. package/dist/os/compass/handlers/summary-record.d.ts.map +1 -1
  54. package/dist/os/compass/handlers/summary-record.js +8 -4
  55. package/dist/os/compass/handlers/summary-record.js.map +1 -1
  56. package/dist/os/core/core.module.d.ts.map +1 -1
  57. package/dist/os/core/core.module.js +43 -4
  58. package/dist/os/core/core.module.js.map +1 -1
  59. package/dist/os/core/handlers/assets-helpers.d.ts.map +1 -1
  60. package/dist/os/core/handlers/assets-helpers.js +6 -3
  61. package/dist/os/core/handlers/assets-helpers.js.map +1 -1
  62. package/dist/os/core/handlers/build.js +1 -1
  63. package/dist/os/core/handlers/build.js.map +1 -1
  64. package/dist/os/core/handlers/determinism-check.d.ts.map +1 -1
  65. package/dist/os/core/handlers/determinism-check.js +10 -6
  66. package/dist/os/core/handlers/determinism-check.js.map +1 -1
  67. package/dist/os/core/handlers/dev.js +1 -1
  68. package/dist/os/core/handlers/dev.js.map +1 -1
  69. package/dist/os/core/handlers/file-size-lint.d.ts.map +1 -1
  70. package/dist/os/core/handlers/file-size-lint.js +11 -7
  71. package/dist/os/core/handlers/file-size-lint.js.map +1 -1
  72. package/dist/os/core/handlers/forge-autonomy-validate.d.ts.map +1 -1
  73. package/dist/os/core/handlers/forge-autonomy-validate.js +9 -5
  74. package/dist/os/core/handlers/forge-autonomy-validate.js.map +1 -1
  75. package/dist/os/core/handlers/knowledge-compact.js +1 -1
  76. package/dist/os/core/handlers/knowledge-compact.js.map +1 -1
  77. package/dist/os/core/handlers/package-health.js +1 -1
  78. package/dist/os/core/handlers/package-health.js.map +1 -1
  79. package/dist/os/core/handlers/pinned-check.js +2 -2
  80. package/dist/os/core/handlers/pinned-check.js.map +1 -1
  81. package/dist/os/core/handlers/pinned-init.d.ts.map +1 -1
  82. package/dist/os/core/handlers/pinned-init.js +9 -8
  83. package/dist/os/core/handlers/pinned-init.js.map +1 -1
  84. package/dist/os/core/handlers/pinned-validate.js +3 -3
  85. package/dist/os/core/handlers/pinned-validate.js.map +1 -1
  86. package/dist/os/core/handlers/profile-resolve.js +1 -1
  87. package/dist/os/core/handlers/profile-resolve.js.map +1 -1
  88. package/dist/os/core/handlers/public-surface.js +1 -1
  89. package/dist/os/core/handlers/public-surface.js.map +1 -1
  90. package/dist/os/core/handlers/release-prepare.d.ts.map +1 -1
  91. package/dist/os/core/handlers/release-prepare.js +10 -6
  92. package/dist/os/core/handlers/release-prepare.js.map +1 -1
  93. package/dist/os/core/handlers/release-publish.js +6 -6
  94. package/dist/os/core/handlers/release-publish.js.map +1 -1
  95. package/dist/os/core/handlers/validate.js +1 -1
  96. package/dist/os/core/handlers/validate.js.map +1 -1
  97. package/dist/os/exploration/exploration.module.d.ts.map +1 -1
  98. package/dist/os/exploration/exploration.module.js +1 -0
  99. package/dist/os/exploration/exploration.module.js.map +1 -1
  100. package/dist/os/exploration/frontmatter-io.js +4 -4
  101. package/dist/os/exploration/frontmatter-io.js.map +1 -1
  102. package/dist/os/exploration/handlers/archive.js +3 -3
  103. package/dist/os/exploration/handlers/archive.js.map +1 -1
  104. package/dist/os/mission/handlers/archive.d.ts.map +1 -1
  105. package/dist/os/mission/handlers/archive.js +44 -23
  106. package/dist/os/mission/handlers/archive.js.map +1 -1
  107. package/dist/os/mission/mission.module.js +3 -3
  108. package/dist/os/mission/mission.module.js.map +1 -1
  109. package/dist/os/naming/naming-convention.d.ts.map +1 -1
  110. package/dist/os/naming/naming-convention.js +7 -3
  111. package/dist/os/naming/naming-convention.js.map +1 -1
  112. package/dist/os/naming/naming.module.d.ts.map +1 -1
  113. package/dist/os/naming/naming.module.js +4 -3
  114. package/dist/os/naming/naming.module.js.map +1 -1
  115. package/dist/os/notes/notes.module.d.ts.map +1 -1
  116. package/dist/os/notes/notes.module.js +6 -3
  117. package/dist/os/notes/notes.module.js.map +1 -1
  118. package/dist/os/plan/frontmatter-io.js +5 -5
  119. package/dist/os/plan/frontmatter-io.js.map +1 -1
  120. package/dist/os/plan/handlers/archive.d.ts.map +1 -1
  121. package/dist/os/plan/handlers/archive.js +10 -4
  122. package/dist/os/plan/handlers/archive.js.map +1 -1
  123. package/dist/os/plan/plan.module.js +3 -3
  124. package/dist/os/plan/plan.module.js.map +1 -1
  125. package/dist/os/plugin/plugin.module.d.ts.map +1 -1
  126. package/dist/os/plugin/plugin.module.js +6 -3
  127. package/dist/os/plugin/plugin.module.js.map +1 -1
  128. package/dist/os/program/discovery.js +2 -2
  129. package/dist/os/program/discovery.js.map +1 -1
  130. package/dist/os/program/handlers/complete.js +1 -1
  131. package/dist/os/program/handlers/complete.js.map +1 -1
  132. package/dist/os/program/handlers/lease.js +1 -1
  133. package/dist/os/program/handlers/lease.js.map +1 -1
  134. package/dist/os/program/handlers/seal.js +1 -1
  135. package/dist/os/program/handlers/seal.js.map +1 -1
  136. package/dist/os/program/lease.js +1 -1
  137. package/dist/os/program/lease.js.map +1 -1
  138. package/dist/os/program/program.module.d.ts.map +1 -1
  139. package/dist/os/program/program.module.js +9 -3
  140. package/dist/os/program/program.module.js.map +1 -1
  141. package/dist/os/queue/index.js +1 -1
  142. package/dist/os/queue/manifest.d.ts.map +1 -1
  143. package/dist/os/queue/manifest.js +4 -8
  144. package/dist/os/queue/manifest.js.map +1 -1
  145. package/dist/os/rfc/acceptance.d.ts.map +1 -1
  146. package/dist/os/rfc/acceptance.js +10 -6
  147. package/dist/os/rfc/acceptance.js.map +1 -1
  148. package/dist/os/rfc/decision-log.d.ts.map +1 -1
  149. package/dist/os/rfc/decision-log.js +4 -3
  150. package/dist/os/rfc/decision-log.js.map +1 -1
  151. package/dist/os/rfc/dna-trace.d.ts.map +1 -1
  152. package/dist/os/rfc/dna-trace.js +7 -2
  153. package/dist/os/rfc/dna-trace.js.map +1 -1
  154. package/dist/os/rfc/frontmatter-io.js +5 -5
  155. package/dist/os/rfc/frontmatter-io.js.map +1 -1
  156. package/dist/os/rfc/handlers/archive.d.ts.map +1 -1
  157. package/dist/os/rfc/handlers/archive.js +11 -5
  158. package/dist/os/rfc/handlers/archive.js.map +1 -1
  159. package/dist/os/rfc/handlers/check.d.ts.map +1 -1
  160. package/dist/os/rfc/handlers/check.js +6 -3
  161. package/dist/os/rfc/handlers/check.js.map +1 -1
  162. package/dist/os/rfc/handlers/implement-stamp.d.ts.map +1 -1
  163. package/dist/os/rfc/handlers/implement-stamp.js +11 -10
  164. package/dist/os/rfc/handlers/implement-stamp.js.map +1 -1
  165. package/dist/os/rfc/handlers/index-graph.js +3 -3
  166. package/dist/os/rfc/handlers/index-graph.js.map +1 -1
  167. package/dist/os/rfc/handlers/lifecycle.d.ts.map +1 -1
  168. package/dist/os/rfc/handlers/lifecycle.js +6 -2
  169. package/dist/os/rfc/handlers/lifecycle.js.map +1 -1
  170. package/dist/os/rfc/handlers/list-create.d.ts.map +1 -1
  171. package/dist/os/rfc/handlers/list-create.js +7 -6
  172. package/dist/os/rfc/handlers/list-create.js.map +1 -1
  173. package/dist/os/rfc/handlers/shared.js +3 -3
  174. package/dist/os/rfc/handlers/shared.js.map +1 -1
  175. package/dist/os/rfc/handlers/supersede-propose.d.ts.map +1 -1
  176. package/dist/os/rfc/handlers/supersede-propose.js +7 -3
  177. package/dist/os/rfc/handlers/supersede-propose.js.map +1 -1
  178. package/dist/os/rfc/handlers/validate-rules.d.ts.map +1 -1
  179. package/dist/os/rfc/handlers/validate-rules.js +8 -5
  180. package/dist/os/rfc/handlers/validate-rules.js.map +1 -1
  181. package/dist/os/rfc/handlers/validate.d.ts.map +1 -1
  182. package/dist/os/rfc/handlers/validate.js +6 -1
  183. package/dist/os/rfc/handlers/validate.js.map +1 -1
  184. package/dist/os/rfc/rfc.module.d.ts.map +1 -1
  185. package/dist/os/rfc/rfc.module.js +44 -4
  186. package/dist/os/rfc/rfc.module.js.map +1 -1
  187. package/dist/os/rfc/verification-evidence.d.ts.map +1 -1
  188. package/dist/os/rfc/verification-evidence.js +9 -5
  189. package/dist/os/rfc/verification-evidence.js.map +1 -1
  190. package/dist/os/rfc/verification-refresh.d.ts.map +1 -1
  191. package/dist/os/rfc/verification-refresh.js +4 -3
  192. package/dist/os/rfc/verification-refresh.js.map +1 -1
  193. package/dist/os/session/frontmatter-io.js +12 -12
  194. package/dist/os/session/frontmatter-io.js.map +1 -1
  195. package/dist/os/session/handlers/archive.d.ts.map +1 -1
  196. package/dist/os/session/handlers/archive.js +10 -4
  197. package/dist/os/session/handlers/archive.js.map +1 -1
  198. package/dist/os/session/handlers/metrics-aggregate.d.ts.map +1 -1
  199. package/dist/os/session/handlers/metrics-aggregate.js +9 -5
  200. package/dist/os/session/handlers/metrics-aggregate.js.map +1 -1
  201. package/dist/os/session/handlers/metrics-rfc.d.ts.map +1 -1
  202. package/dist/os/session/handlers/metrics-rfc.js +11 -8
  203. package/dist/os/session/handlers/metrics-rfc.js.map +1 -1
  204. package/dist/os/session/handlers/metrics-session.d.ts.map +1 -1
  205. package/dist/os/session/handlers/metrics-session.js +6 -3
  206. package/dist/os/session/handlers/metrics-session.js.map +1 -1
  207. package/dist/os/session/handlers/save.d.ts.map +1 -1
  208. package/dist/os/session/handlers/save.js +14 -8
  209. package/dist/os/session/handlers/save.js.map +1 -1
  210. package/dist/os/session/session.module.d.ts.map +1 -1
  211. package/dist/os/session/session.module.js +6 -0
  212. package/dist/os/session/session.module.js.map +1 -1
  213. package/dist/os/spec/live-spec-list.js +4 -4
  214. package/dist/os/spec/live-spec-list.js.map +1 -1
  215. package/dist/os/spec/live-spec-merge.js +5 -5
  216. package/dist/os/spec/live-spec-merge.js.map +1 -1
  217. package/dist/os/spec/live-spec-show.js +3 -3
  218. package/dist/os/spec/live-spec-show.js.map +1 -1
  219. package/dist/os/spec/live-spec-validate.js +4 -4
  220. package/dist/os/spec/live-spec-validate.js.map +1 -1
  221. package/dist/os/spec/spec-materialize.js +9 -9
  222. package/dist/os/spec/spec-materialize.js.map +1 -1
  223. package/dist/os/spec/spec-status.js +9 -9
  224. package/dist/os/spec/spec-status.js.map +1 -1
  225. package/dist/os/spec/spec-validate.js +7 -7
  226. package/dist/os/spec/spec-validate.js.map +1 -1
  227. package/dist/os/spec/spec.module.d.ts.map +1 -1
  228. package/dist/os/spec/spec.module.js +8 -0
  229. package/dist/os/spec/spec.module.js.map +1 -1
  230. package/dist/os/werkstatt/handlers/lock.js +16 -16
  231. package/dist/os/werkstatt/handlers/lock.js.map +1 -1
  232. package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js +3 -3
  233. package/dist/os/werkstatt/handlers/werkstatt-lock-recover.js.map +1 -1
  234. package/dist/os/werkstatt/handlers/werkstatt-operation-validate.d.ts.map +1 -1
  235. package/dist/os/werkstatt/handlers/werkstatt-operation-validate.js +3 -2
  236. package/dist/os/werkstatt/handlers/werkstatt-operation-validate.js.map +1 -1
  237. package/dist/os/werkstatt/handlers/workspace-deps.d.ts.map +1 -1
  238. package/dist/os/werkstatt/handlers/workspace-deps.js +0 -1
  239. package/dist/os/werkstatt/handlers/workspace-deps.js.map +1 -1
  240. package/dist/os/werkstatt/werkstatt.module.d.ts.map +1 -1
  241. package/dist/os/werkstatt/werkstatt.module.js +6 -2
  242. package/dist/os/werkstatt/werkstatt.module.js.map +1 -1
  243. package/dist/os/workflow/handlers.d.ts.map +1 -1
  244. package/dist/os/workflow/handlers.js +8 -5
  245. package/dist/os/workflow/handlers.js.map +1 -1
  246. package/dist/os/workflow/workflow.module.d.ts.map +1 -1
  247. package/dist/os/workflow/workflow.module.js +6 -3
  248. package/dist/os/workflow/workflow.module.js.map +1 -1
  249. package/dist/src/cli-flags.d.ts +28 -0
  250. package/dist/src/cli-flags.d.ts.map +1 -0
  251. package/dist/src/cli-flags.js +232 -0
  252. package/dist/src/cli-flags.js.map +1 -0
  253. package/dist/src/compass/contract-registry.js +1 -1
  254. package/dist/src/compass/contract-registry.js.map +1 -1
  255. package/dist/src/config/forge-config.d.ts +1 -1
  256. package/dist/src/config/forge-config.js +1 -1
  257. package/dist/src/config/forge-config.js.map +1 -1
  258. package/dist/src/knowledge/budgets.js +1 -1
  259. package/dist/src/knowledge/budgets.js.map +1 -1
  260. package/dist/src/knowledge/compact.js +1 -1
  261. package/dist/src/knowledge/compact.js.map +1 -1
  262. package/dist/src/knowledge/parse.js +1 -1
  263. package/dist/src/knowledge/parse.js.map +1 -1
  264. package/dist/src/knowledge/sync.d.ts.map +1 -1
  265. package/dist/src/knowledge/sync.js +3 -5
  266. package/dist/src/knowledge/sync.js.map +1 -1
  267. package/dist/src/migration-adapters/git-utils.js +2 -2
  268. package/dist/src/migration-adapters/git-utils.js.map +1 -1
  269. package/dist/src/migration-adapters/ignored-files.js +2 -2
  270. package/dist/src/migration-adapters/ignored-files.js.map +1 -1
  271. package/dist/src/migration-adapters/node-typescript-pnpm/index.js +1 -1
  272. package/dist/src/migration-adapters/node-typescript-pnpm/index.js.map +1 -1
  273. package/dist/src/migration-adapters/phaser-pnpm/index.js +1 -1
  274. package/dist/src/migration-adapters/phaser-pnpm/index.js.map +1 -1
  275. package/dist/src/onboarding/agents-generate.d.ts.map +1 -1
  276. package/dist/src/onboarding/agents-generate.js +76 -34
  277. package/dist/src/onboarding/agents-generate.js.map +1 -1
  278. package/dist/src/onboarding/create.d.ts.map +1 -1
  279. package/dist/src/onboarding/create.js +10 -10
  280. package/dist/src/onboarding/create.js.map +1 -1
  281. package/dist/src/onboarding/doctor.d.ts.map +1 -1
  282. package/dist/src/onboarding/doctor.js +68 -59
  283. package/dist/src/onboarding/doctor.js.map +1 -1
  284. package/dist/src/onboarding/init.d.ts.map +1 -1
  285. package/dist/src/onboarding/init.js +30 -1
  286. package/dist/src/onboarding/init.js.map +1 -1
  287. package/dist/src/onboarding/invariant-engine.js +1 -1
  288. package/dist/src/onboarding/invariant-engine.js.map +1 -1
  289. package/dist/src/onboarding/memory-compact.d.ts.map +1 -1
  290. package/dist/src/onboarding/memory-compact.js +5 -4
  291. package/dist/src/onboarding/memory-compact.js.map +1 -1
  292. package/dist/src/onboarding/memory-scaffold.js +1 -1
  293. package/dist/src/onboarding/memory-scaffold.js.map +1 -1
  294. package/dist/src/onboarding/nested-agents-generate.d.ts +4 -1
  295. package/dist/src/onboarding/nested-agents-generate.d.ts.map +1 -1
  296. package/dist/src/onboarding/nested-agents-generate.js +34 -5
  297. package/dist/src/onboarding/nested-agents-generate.js.map +1 -1
  298. package/dist/src/onboarding/nested-agents-templates.d.ts.map +1 -1
  299. package/dist/src/onboarding/nested-agents-templates.js +8 -12
  300. package/dist/src/onboarding/nested-agents-templates.js.map +1 -1
  301. package/dist/src/onboarding/npm-token-check.js +2 -2
  302. package/dist/src/onboarding/npm-token-check.js.map +1 -1
  303. package/dist/src/onboarding/prettierignore.d.ts +29 -0
  304. package/dist/src/onboarding/prettierignore.d.ts.map +1 -0
  305. package/dist/src/onboarding/prettierignore.js +231 -0
  306. package/dist/src/onboarding/prettierignore.js.map +1 -0
  307. package/dist/src/onboarding/profile-validate.d.ts.map +1 -1
  308. package/dist/src/onboarding/profile-validate.js +5 -4
  309. package/dist/src/onboarding/profile-validate.js.map +1 -1
  310. package/dist/src/onboarding/scaffold-project.d.ts.map +1 -1
  311. package/dist/src/onboarding/scaffold-project.js +35 -27
  312. package/dist/src/onboarding/scaffold-project.js.map +1 -1
  313. package/dist/src/onboarding/scaffold.js +1 -1
  314. package/dist/src/onboarding/scaffold.js.map +1 -1
  315. package/dist/src/onboarding/skill-markers.d.ts +27 -0
  316. package/dist/src/onboarding/skill-markers.d.ts.map +1 -0
  317. package/dist/src/onboarding/skill-markers.js +131 -0
  318. package/dist/src/onboarding/skill-markers.js.map +1 -0
  319. package/dist/src/onboarding/upgrade.d.ts +10 -0
  320. package/dist/src/onboarding/upgrade.d.ts.map +1 -1
  321. package/dist/src/onboarding/upgrade.js +215 -74
  322. package/dist/src/onboarding/upgrade.js.map +1 -1
  323. package/dist/src/onboarding/workspace-discovery.js +1 -1
  324. package/dist/src/onboarding/workspace-discovery.js.map +1 -1
  325. package/dist/src/pipeline-status.d.ts +6 -5
  326. package/dist/src/pipeline-status.d.ts.map +1 -1
  327. package/dist/src/pipeline-status.js +24 -19
  328. package/dist/src/pipeline-status.js.map +1 -1
  329. package/dist/src/profiles/stack-profile.js +1 -1
  330. package/dist/src/profiles/stack-profile.js.map +1 -1
  331. package/dist/src/registry.d.ts +1 -1
  332. package/dist/src/registry.d.ts.map +1 -1
  333. package/dist/src/registry.js +3 -3
  334. package/dist/src/registry.js.map +1 -1
  335. package/dist/src/skill-schema.d.ts +1 -1
  336. package/dist/src/skill-schema.d.ts.map +1 -1
  337. package/dist/src/skill-schema.js +4 -1
  338. package/dist/src/skill-schema.js.map +1 -1
  339. package/dist/src/types.d.ts +116 -0
  340. package/dist/src/types.d.ts.map +1 -1
  341. package/dist/src/utils/editable-region.d.ts +46 -0
  342. package/dist/src/utils/editable-region.d.ts.map +1 -0
  343. package/dist/src/utils/editable-region.js +125 -0
  344. package/dist/src/utils/editable-region.js.map +1 -0
  345. package/dist/src/utils/fs-idempotent.d.ts +2 -1
  346. package/dist/src/utils/fs-idempotent.d.ts.map +1 -1
  347. package/dist/src/utils/fs-idempotent.js +11 -7
  348. package/dist/src/utils/fs-idempotent.js.map +1 -1
  349. package/dist/src/utils/generated-marker.d.ts +17 -0
  350. package/dist/src/utils/generated-marker.d.ts.map +1 -1
  351. package/dist/src/utils/generated-marker.js +29 -1
  352. package/dist/src/utils/generated-marker.js.map +1 -1
  353. package/dist/src/utils/index.d.ts +3 -1
  354. package/dist/src/utils/index.d.ts.map +1 -1
  355. package/dist/src/utils/index.js +3 -1
  356. package/dist/src/utils/index.js.map +1 -1
  357. package/dist/src/utils/io.d.ts +9 -0
  358. package/dist/src/utils/io.d.ts.map +1 -0
  359. package/dist/src/utils/io.js +130 -0
  360. package/dist/src/utils/io.js.map +1 -0
  361. package/dist/src/utils/markdown-table.d.ts +21 -0
  362. package/dist/src/utils/markdown-table.d.ts.map +1 -0
  363. package/dist/src/utils/markdown-table.js +116 -0
  364. package/dist/src/utils/markdown-table.js.map +1 -0
  365. package/dist/src/utils/sync-fs.d.ts +5 -0
  366. package/dist/src/utils/sync-fs.d.ts.map +1 -0
  367. package/dist/src/utils/sync-fs.js +26 -0
  368. package/dist/src/utils/sync-fs.js.map +1 -0
  369. package/dist/src/validators/note-frontmatter-validate.js +1 -1
  370. package/dist/src/validators/note-frontmatter-validate.js.map +1 -1
  371. package/dist/src/validators/note-link-validate.js +1 -1
  372. package/dist/src/validators/note-link-validate.js.map +1 -1
  373. package/dist/src/validators/note-orphan-detect.js +1 -1
  374. package/dist/src/validators/note-orphan-detect.js.map +1 -1
  375. package/dist/src/validators/port-validate.js +1 -1
  376. package/dist/src/validators/port-validate.js.map +1 -1
  377. package/dist/src/validators/skill-validate.d.ts.map +1 -1
  378. package/dist/src/validators/skill-validate.js +34 -13
  379. package/dist/src/validators/skill-validate.js.map +1 -1
  380. package/os/adr/adr.module.ts +136 -132
  381. package/os/adr/frontmatter-io.ts +5 -5
  382. package/os/adr/handlers/archive.ts +8 -4
  383. package/os/adr/handlers/implement-stamp.ts +14 -10
  384. package/os/adr/handlers/list-create.ts +5 -5
  385. package/os/adr/handlers/validate.ts +1 -1
  386. package/os/audit/audit.module.ts +33 -34
  387. package/os/audit/frontmatter-io.ts +5 -5
  388. package/os/audit/handlers/archive.ts +8 -4
  389. package/os/compass/compass.module.ts +17 -1
  390. package/os/compass/handlers/compass-audit-handler.ts +13 -5
  391. package/os/compass/handlers/compass-change-summary-handler.ts +4 -2
  392. package/os/compass/handlers/compass-inventory-handler.ts +7 -4
  393. package/os/compass/handlers/compass-inventory.ts +11 -8
  394. package/os/compass/handlers/compass-migrate-handler.ts +1 -1
  395. package/os/compass/handlers/compass-migrate.ts +7 -3
  396. package/os/compass/handlers/git-revision.ts +11 -6
  397. package/os/compass/handlers/resolve-scan-root.ts +1 -1
  398. package/os/compass/handlers/summary-record.ts +10 -4
  399. package/os/compass/handlers/tests/compass-audit-plan.test.ts +2 -1
  400. package/os/core/core.module.ts +43 -4
  401. package/os/core/handlers/assets-helpers.ts +7 -3
  402. package/os/core/handlers/build.ts +1 -1
  403. package/os/core/handlers/determinism-check.ts +12 -6
  404. package/os/core/handlers/dev.ts +1 -1
  405. package/os/core/handlers/file-size-lint.ts +16 -9
  406. package/os/core/handlers/forge-autonomy-validate.ts +10 -5
  407. package/os/core/handlers/knowledge-compact.ts +1 -1
  408. package/os/core/handlers/package-health.ts +1 -1
  409. package/os/core/handlers/pinned-check.ts +2 -2
  410. package/os/core/handlers/pinned-init.ts +9 -8
  411. package/os/core/handlers/pinned-validate.ts +3 -3
  412. package/os/core/handlers/profile-resolve.ts +1 -1
  413. package/os/core/handlers/public-surface.ts +1 -1
  414. package/os/core/handlers/release-prepare.ts +11 -6
  415. package/os/core/handlers/release-publish.ts +6 -6
  416. package/os/core/handlers/validate.ts +1 -1
  417. package/os/exploration/exploration.module.ts +1 -0
  418. package/os/exploration/frontmatter-io.ts +4 -4
  419. package/os/exploration/handlers/archive.ts +3 -3
  420. package/os/mission/handlers/archive.test.ts +38 -1
  421. package/os/mission/handlers/archive.ts +54 -23
  422. package/os/mission/mission.module.ts +38 -39
  423. package/os/naming/naming-convention.ts +9 -3
  424. package/os/naming/naming.module.ts +25 -25
  425. package/os/notes/notes.module.ts +87 -85
  426. package/os/plan/frontmatter-io.ts +5 -5
  427. package/os/plan/handlers/archive.ts +8 -4
  428. package/os/plan/plan.module.ts +33 -34
  429. package/os/plugin/plugin.module.ts +8 -6
  430. package/os/program/discovery.ts +2 -2
  431. package/os/program/handlers/complete.ts +1 -1
  432. package/os/program/handlers/lease.ts +1 -1
  433. package/os/program/handlers/seal.ts +1 -1
  434. package/os/program/lease.ts +1 -1
  435. package/os/program/program.module.ts +198 -193
  436. package/os/queue/index.ts +1 -1
  437. package/os/queue/manifest.ts +10 -9
  438. package/os/queue/queue-validate.test.ts +6 -15
  439. package/os/rfc/acceptance.ts +12 -6
  440. package/os/rfc/decision-log.ts +4 -3
  441. package/os/rfc/dna-trace.ts +8 -2
  442. package/os/rfc/frontmatter-io.ts +5 -5
  443. package/os/rfc/handlers/archive.ts +9 -5
  444. package/os/rfc/handlers/check.ts +5 -3
  445. package/os/rfc/handlers/implement-stamp.ts +15 -10
  446. package/os/rfc/handlers/index-graph.ts +3 -3
  447. package/os/rfc/handlers/lifecycle.ts +7 -2
  448. package/os/rfc/handlers/list-create.ts +7 -6
  449. package/os/rfc/handlers/shared.ts +3 -3
  450. package/os/rfc/handlers/supersede-propose.ts +8 -3
  451. package/os/rfc/handlers/validate-rules-rfc1006.test.ts +18 -6
  452. package/os/rfc/handlers/validate-rules.ts +10 -5
  453. package/os/rfc/handlers/validate.test.ts +71 -0
  454. package/os/rfc/handlers/validate.ts +6 -1
  455. package/os/rfc/rfc-create-concurrent.test.ts +11 -3
  456. package/os/rfc/rfc.module.ts +413 -374
  457. package/os/rfc/verification-evidence.ts +11 -5
  458. package/os/rfc/verification-refresh.ts +4 -3
  459. package/os/session/frontmatter-io.ts +14 -14
  460. package/os/session/handlers/archive.ts +8 -4
  461. package/os/session/handlers/metrics-aggregate.ts +34 -13
  462. package/os/session/handlers/metrics-rfc.ts +13 -8
  463. package/os/session/handlers/metrics-session.ts +7 -3
  464. package/os/session/handlers/save.ts +12 -8
  465. package/os/session/session.module.ts +6 -0
  466. package/os/spec/live-spec-list.ts +4 -4
  467. package/os/spec/live-spec-merge.ts +5 -5
  468. package/os/spec/live-spec-show.ts +3 -3
  469. package/os/spec/live-spec-validate.ts +4 -4
  470. package/os/spec/spec-materialize.ts +9 -9
  471. package/os/spec/spec-status.ts +9 -9
  472. package/os/spec/spec-validate.ts +7 -7
  473. package/os/spec/spec.module.ts +8 -0
  474. package/os/werkstatt/handlers/lock.ts +16 -16
  475. package/os/werkstatt/handlers/werkstatt-lock-recover.ts +3 -3
  476. package/os/werkstatt/handlers/werkstatt-operation-validate.ts +4 -2
  477. package/os/werkstatt/handlers/workspace-deps.ts +0 -1
  478. package/os/werkstatt/werkstatt.module.ts +8 -5
  479. package/os/workflow/handlers.ts +7 -5
  480. package/os/workflow/workflow.module.ts +45 -44
  481. package/package.json +1 -1
  482. package/profiles/forge-shell.yaml +1 -1
  483. package/profiles/godot-game.yaml +1 -1
  484. package/profiles/knowledge.yaml +1 -1
  485. package/profiles/phaser-game.yaml +1 -1
  486. package/profiles/site-workshop.yaml +1 -1
  487. package/profiles/typescript.yaml +1 -1
  488. package/skills/fo/fo-add-tests/SKILL.md +2 -2
  489. package/skills/fo/fo-architecture/SKILL.md +2 -2
  490. package/skills/fo/fo-compass-annotate/SKILL.md +2 -2
  491. package/skills/fo/fo-design-summit/SKILL.md +2 -2
  492. package/skills/fo/fo-doc-audit/SKILL.md +2 -2
  493. package/skills/fo/fo-explore/SKILL.md +2 -2
  494. package/skills/fo/fo-extract-dna/SKILL.md +2 -2
  495. package/skills/fo/fo-fix/SKILL.md +2 -2
  496. package/skills/fo/fo-handoff/SKILL.md +2 -2
  497. package/skills/fo/fo-harvest/SKILL.md +2 -2
  498. package/skills/fo/fo-idea/SKILL.md +2 -2
  499. package/skills/fo/fo-idea-audit/SKILL.md +2 -2
  500. package/skills/fo/fo-idea-create-adr/SKILL.md +3 -3
  501. package/skills/fo/fo-idea-create-rfc/SKILL.md +3 -3
  502. package/skills/fo/fo-idea-enhance/SKILL.md +2 -2
  503. package/skills/fo/fo-idea-i-just-want-to-see-the-plan/SKILL.md +2 -2
  504. package/skills/fo/fo-idea-i-just-want-to-see-the-result/SKILL.md +1 -1
  505. package/skills/fo/fo-idea-implement/SKILL.md +2 -2
  506. package/skills/fo/fo-idea-plan/SKILL.md +2 -2
  507. package/skills/fo/fo-idea-status/SKILL.md +2 -2
  508. package/skills/fo/fo-knowledge-distill/SKILL.md +2 -2
  509. package/skills/fo/fo-memory-sync/SKILL.md +2 -2
  510. package/skills/fo/fo-qa/SKILL.md +2 -2
  511. package/skills/fo/fo-review/SKILL.md +2 -2
  512. package/skills/fo/fo-session-retro/SKILL.md +2 -2
  513. package/skills/fo/fo-session-save/SKILL.md +2 -2
  514. package/skills/fo/fo-spec-ingest/SKILL.md +2 -2
  515. package/skills/fo/fo-step-commit/SKILL.md +1 -1
  516. package/skills/fo/fo-triage/SKILL.md +2 -2
  517. package/skills/meta/forge-bootstrap/SKILL.md +1 -1
  518. package/skills/meta/port-to-forge/SKILL.md +1 -1
  519. package/skills/meta/skill-create/SKILL.md +1 -1
  520. package/skills/shared/grilling/SKILL.md +1 -1
  521. package/skills/shared/grilling/learned-principles.archive.md +17 -0
  522. package/skills/shared/grilling/learned-principles.md +0 -1
  523. package/skills/shared/knowledge/learned-principles.md +6 -0
  524. package/skills/shared/my-preferences/SKILL.md +1 -1
  525. package/skills/shared/windows-ai-tooling/SKILL.md +1 -1
  526. package/skills/shared/writing-great-skills/SKILL.md +1 -1
  527. package/src/cli-flags.ts +285 -0
  528. package/src/compass/contract-registry.ts +1 -1
  529. package/src/config/forge-config.ts +1 -1
  530. package/src/knowledge/__tests__/sync.test.ts +2 -7
  531. package/src/knowledge/budgets.ts +1 -1
  532. package/src/knowledge/compact.ts +1 -1
  533. package/src/knowledge/parse.ts +1 -1
  534. package/src/knowledge/sync.ts +3 -5
  535. package/src/migration-adapters/git-utils.ts +2 -2
  536. package/src/migration-adapters/ignored-files.ts +2 -2
  537. package/src/migration-adapters/node-typescript-pnpm/index.ts +1 -1
  538. package/src/migration-adapters/phaser-pnpm/index.ts +1 -1
  539. package/src/onboarding/agents-generate.ts +92 -36
  540. package/src/onboarding/create.ts +10 -10
  541. package/src/onboarding/doctor.ts +70 -55
  542. package/src/onboarding/init.ts +38 -1
  543. package/src/onboarding/invariant-engine.ts +1 -1
  544. package/src/onboarding/memory-compact.ts +5 -4
  545. package/src/onboarding/memory-scaffold.ts +1 -1
  546. package/src/onboarding/nested-agents-generate.ts +45 -4
  547. package/src/onboarding/nested-agents-templates.ts +19 -13
  548. package/src/onboarding/npm-token-check.ts +2 -2
  549. package/src/onboarding/prettierignore.ts +263 -0
  550. package/src/onboarding/profile-validate.ts +5 -4
  551. package/src/onboarding/scaffold-project.ts +38 -27
  552. package/src/onboarding/scaffold.ts +1 -1
  553. package/src/onboarding/skill-markers.ts +162 -0
  554. package/src/onboarding/templates/behavioral-layer-core.md +2 -4
  555. package/src/onboarding/upgrade.ts +285 -89
  556. package/src/onboarding/workspace-discovery.ts +1 -1
  557. package/src/pipeline-status.test.ts +1 -4
  558. package/src/pipeline-status.ts +30 -13
  559. package/src/profiles/stack-profile.ts +1 -1
  560. package/src/registry.ts +4 -4
  561. package/src/skill-schema.ts +4 -1
  562. package/src/tests/acceptance-probe-kinds.test.ts +5 -15
  563. package/src/tests/agents-generate.test.ts +28 -5
  564. package/src/tests/cli-flags.test.ts +169 -0
  565. package/src/tests/contract-registry.test.ts +12 -6
  566. package/src/tests/doctor-fix.test.ts +1 -4
  567. package/src/tests/doctor-nested-agents.test.ts +53 -1
  568. package/src/tests/editable-generated-region.test.ts +231 -0
  569. package/src/tests/file-size-lint.test.ts +8 -12
  570. package/src/tests/fixtures/agents-generate-business-before.txt +85 -56
  571. package/src/tests/markdown-table.test.ts +107 -0
  572. package/src/tests/prettierignore-manage.test.ts +134 -0
  573. package/src/tests/skill-agents-sync.test.ts +168 -0
  574. package/src/tests/skill-sync-marker.test.ts +134 -0
  575. package/src/tests/skill-validate.test.ts +12 -5
  576. package/src/tests/upgrade.test.ts +87 -41
  577. package/src/types.ts +123 -0
  578. package/src/utils/editable-region.ts +156 -0
  579. package/src/utils/fs-idempotent.ts +12 -6
  580. package/src/utils/generated-marker.ts +36 -2
  581. package/src/utils/index.ts +17 -0
  582. package/src/utils/io.ts +143 -0
  583. package/src/utils/markdown-table.ts +121 -0
  584. package/src/utils/sync-fs.ts +43 -0
  585. package/src/validators/note-frontmatter-validate.ts +1 -1
  586. package/src/validators/note-link-validate.ts +1 -1
  587. package/src/validators/note-orphan-detect.ts +1 -1
  588. package/src/validators/port-validate.ts +1 -1
  589. package/src/validators/skill-validate.ts +38 -13
  590. package/AGENTS.md +0 -502
@@ -21,8 +21,10 @@ Sweep batch 2: real KEY_DECISIONS on 10 files, expanded purposes (CONTRACT-02/PU
21
21
  </CHANGE_SUMMARY>
22
22
  */
23
23
 
24
- import { readFile, readdir, stat } from "node:fs/promises";
25
- import { readFileSync, existsSync } from "node:fs";
24
+ import type { WorkspaceIO } from "../types.ts";
25
+ import { resolveIo } from "../utils/io.ts";
26
+ import { hasEditableGeneratedMarker, mergeEditableGenerated } from "../utils/index.ts";
27
+ import { readFileSync, existsSync } from "../utils/sync-fs.ts";
26
28
  import { join, relative, dirname, resolve } from "node:path";
27
29
  import { parse as parseYaml } from "yaml";
28
30
  import { forgePluginManifestSchema } from "../plugin/forge-plugin-manifest.ts";
@@ -52,8 +54,8 @@ import { compactMemoryMd } from "./memory-compact.ts";
52
54
  import { checkInvariants } from "./invariant-engine.ts";
53
55
  import type { InvariantViolation } from "./invariant-engine.ts";
54
56
  import { checkNpmToken, workshopNeedsWarpgogolToken } from "./npm-token-check.ts";
55
- import { execSync } from "node:child_process";
56
- import fs from "node:fs";
57
+ import { execSync } from "../utils/sync-fs.ts";
58
+ import * as fs from "../utils/sync-fs.ts";
57
59
  import type { ProfilePrerequisite } from "../profiles/profile-schema.ts";
58
60
 
59
61
  interface DoctorFixResult {
@@ -87,13 +89,8 @@ interface BindingNotice {
87
89
  suggestion: string;
88
90
  }
89
91
 
90
- async function pathExists(target: string): Promise<boolean> {
91
- try {
92
- await stat(target);
93
- return true;
94
- } catch {
95
- return false;
96
- }
92
+ async function pathExists(target: string, io: WorkspaceIO): Promise<boolean> {
93
+ return io.exists(target);
97
94
  }
98
95
 
99
96
  const FORBIDDEN_IMPORT_PATTERN =
@@ -102,19 +99,20 @@ const FORBIDDEN_IMPORT_PATTERN =
102
99
  async function scanForForbiddenImports(
103
100
  dir: string,
104
101
  workspaceRoot: string,
102
+ io: WorkspaceIO,
105
103
  ): Promise<ForbiddenImport[]> {
106
104
  const violations: ForbiddenImport[] = [];
107
- const entries = await readdir(dir, { withFileTypes: true }).catch(() => []);
105
+ const entries = await io.readdir(dir).catch(() => []);
108
106
 
109
107
  for (const entry of entries) {
110
108
  const fullPath = join(dir, entry.name);
111
109
 
112
- if (entry.isDirectory()) {
110
+ if (entry.isDirectory) {
113
111
  if (entry.name === "node_modules" || entry.name === "tests") continue;
114
- const sub = await scanForForbiddenImports(fullPath, workspaceRoot);
112
+ const sub = await scanForForbiddenImports(fullPath, workspaceRoot, io);
115
113
  violations.push(...sub);
116
114
  } else if (entry.name.endsWith(".ts")) {
117
- const content = await readFile(fullPath, "utf8").catch(() => "");
115
+ const content = await io.readFile(fullPath).catch(() => "");
118
116
  let match: RegExpExecArray | null;
119
117
  const pattern = new RegExp(FORBIDDEN_IMPORT_PATTERN.source, "g");
120
118
  while ((match = pattern.exec(content)) !== null) {
@@ -151,7 +149,7 @@ const BINDING_COMMAND_KEYS = [
151
149
  "commands.sessionSave",
152
150
  ];
153
151
 
154
- async function validateBindings(workspaceRoot: string): Promise<BindingValidation> {
152
+ async function validateBindings(workspaceRoot: string, io: WorkspaceIO): Promise<BindingValidation> {
155
153
  const result: BindingValidation = { resolved: [], absent: [], invalid: [], notices: [] };
156
154
 
157
155
  let config;
@@ -170,7 +168,7 @@ async function validateBindings(workspaceRoot: string): Promise<BindingValidatio
170
168
  if (value === null) {
171
169
  result.absent.push(key);
172
170
  } else if (typeof value === "string") {
173
- const exists = await pathExists(join(workspaceRoot, value));
171
+ const exists = await pathExists(join(workspaceRoot, value), io);
174
172
  if (exists) {
175
173
  result.resolved.push(key);
176
174
  } else {
@@ -182,7 +180,7 @@ async function validateBindings(workspaceRoot: string): Promise<BindingValidatio
182
180
  const compassDocs = resolveBinding(config, "paths.compassDocs");
183
181
  if (Array.isArray(compassDocs) && compassDocs.length > 0) {
184
182
  for (const docPath of compassDocs) {
185
- const exists = await pathExists(join(workspaceRoot, docPath));
183
+ const exists = await pathExists(join(workspaceRoot, docPath), io);
186
184
  if (exists) {
187
185
  result.resolved.push(`paths.compassDocs[${docPath}]`);
188
186
  } else {
@@ -226,6 +224,7 @@ async function validateBindings(workspaceRoot: string): Promise<BindingValidatio
226
224
  async function checkStaleKnowledgeFiles(
227
225
  workspaceRoot: string,
228
226
  forgeRoot: string,
227
+ io: WorkspaceIO,
229
228
  ): Promise<DoctorCheck> {
230
229
  const stale: string[] = [];
231
230
 
@@ -247,8 +246,8 @@ async function checkStaleKnowledgeFiles(
247
246
  const srcPath = join(skillSrcDir, kf);
248
247
  const destPath = join(agentsSkillsDir, skill.name, kf);
249
248
 
250
- const srcExists = await pathExists(srcPath);
251
- const destExists = await pathExists(destPath);
249
+ const srcExists = await pathExists(srcPath, io);
250
+ const destExists = await pathExists(destPath, io);
252
251
 
253
252
  if (srcExists && destExists) {
254
253
  // Append-only contract: local copies legitimately accumulate entries.
@@ -279,8 +278,8 @@ async function checkStaleKnowledgeFiles(
279
278
  const srcPath = join(skillSrcDir, kf);
280
279
  const destPath = join(agentsSkillsDir, skill.name, kf);
281
280
 
282
- const srcExists = await pathExists(srcPath);
283
- const destExists = await pathExists(destPath);
281
+ const srcExists = await pathExists(srcPath, io);
282
+ const destExists = await pathExists(destPath, io);
284
283
 
285
284
  if (srcExists && destExists) {
286
285
  const plan = planKnowledgeSync(srcPath, destPath);
@@ -642,7 +641,7 @@ function checkSharedKnowledgeFile(
642
641
  // Independent version packages diagnostics (RFC-0704)
643
642
  // ---------------------------------------------------------------------------
644
643
 
645
- async function checkIndependentVersionPackages(workspaceRoot: string): Promise<DoctorCheck> {
644
+ async function checkIndependentVersionPackages(workspaceRoot: string, io: WorkspaceIO): Promise<DoctorCheck> {
646
645
  let config;
647
646
  try {
648
647
  config = loadForgeConfig(workspaceRoot);
@@ -658,8 +657,8 @@ async function checkIndependentVersionPackages(workspaceRoot: string): Promise<D
658
657
  let validCount = 0;
659
658
 
660
659
  for (const pkgPath of config.independentVersionPackages) {
661
- const dirExists = await pathExists(join(workspaceRoot, pkgPath));
662
- const pkgJsonExists = await pathExists(join(workspaceRoot, pkgPath, "package.json"));
660
+ const dirExists = await pathExists(join(workspaceRoot, pkgPath), io);
661
+ const pkgJsonExists = await pathExists(join(workspaceRoot, pkgPath, "package.json"), io);
663
662
  if (!dirExists) {
664
663
  issues.push(`path '${pkgPath}' does not exist`);
665
664
  } else if (!pkgJsonExists) {
@@ -688,7 +687,7 @@ async function checkIndependentVersionPackages(workspaceRoot: string): Promise<D
688
687
  // Pack skill diagnostics (RFC-0539)
689
688
  // ---------------------------------------------------------------------------
690
689
 
691
- async function checkPackSkills(workspaceRoot: string): Promise<DoctorCheck> {
690
+ async function checkPackSkills(workspaceRoot: string, io: WorkspaceIO): Promise<DoctorCheck> {
692
691
  let config;
693
692
  try {
694
693
  config = loadForgeConfig(workspaceRoot);
@@ -720,7 +719,7 @@ async function checkPackSkills(workspaceRoot: string): Promise<DoctorCheck> {
720
719
  }
721
720
  dirs.add(pack.dir);
722
721
 
723
- const packDirExists = await pathExists(join(workspaceRoot, pack.dir));
722
+ const packDirExists = await pathExists(join(workspaceRoot, pack.dir), io);
724
723
  if (!packDirExists) {
725
724
  issues.push(`pack dir '${pack.dir}' does not exist`);
726
725
  }
@@ -739,12 +738,12 @@ async function checkPackSkills(workspaceRoot: string): Promise<DoctorCheck> {
739
738
  const srcPath = join(workspaceRoot, skill.dir, skill.path);
740
739
  const destPath = join(agentsSkillsDir, skill.name, "SKILL.md");
741
740
 
742
- const srcExists = await pathExists(srcPath);
743
- const destExists = await pathExists(destPath);
741
+ const srcExists = await pathExists(srcPath, io);
742
+ const destExists = await pathExists(destPath, io);
744
743
 
745
744
  if (srcExists && destExists) {
746
- const srcContent = await readFile(srcPath, "utf8").catch(() => "");
747
- const destContent = await readFile(destPath, "utf8").catch(() => "");
745
+ const srcContent = await io.readFile(srcPath).catch(() => "");
746
+ const destContent = await io.readFile(destPath).catch(() => "");
748
747
  if (srcContent !== destContent) {
749
748
  issues.push(`stale copy of '${skill.name}'`);
750
749
  }
@@ -767,7 +766,7 @@ async function checkPackSkills(workspaceRoot: string): Promise<DoctorCheck> {
767
766
  // Pack manifest diagnostics (RFC-0941)
768
767
  // ---------------------------------------------------------------------------
769
768
 
770
- async function checkPackManifests(workspaceRoot: string): Promise<DoctorCheck> {
769
+ async function checkPackManifests(workspaceRoot: string, _io: WorkspaceIO): Promise<DoctorCheck> {
771
770
  let config;
772
771
  try {
773
772
  config = loadForgeConfig(workspaceRoot);
@@ -820,7 +819,8 @@ async function checkPackManifests(workspaceRoot: string): Promise<DoctorCheck> {
820
819
 
821
820
  async function checkNestedAgentsMd(
822
821
  workspaceRoot: string,
823
- workspaceTypes?: ProfileWorkspaceType[],
822
+ workspaceTypes: ProfileWorkspaceType[] | undefined,
823
+ io: WorkspaceIO,
824
824
  ): Promise<DoctorCheck> {
825
825
  let config;
826
826
  try {
@@ -851,6 +851,7 @@ async function checkNestedAgentsMd(
851
851
  let missing = 0;
852
852
  let stale = 0;
853
853
  let handwritten = 0;
854
+ let unmapped = 0;
854
855
 
855
856
  for (const ws of workspaces) {
856
857
  if (!ws.hasAgentsMd) {
@@ -863,7 +864,7 @@ async function checkNestedAgentsMd(
863
864
  handwritten++;
864
865
  const suggestions: string[] = [];
865
866
  try {
866
- const content = await readFile(join(workspaceRoot, ws.path, "AGENTS.md"), "utf8");
867
+ const content = await io.readFile(join(workspaceRoot, ws.path, "AGENTS.md"));
867
868
  if (!content.includes("AGENTS.md")) {
868
869
  suggestions.push("reference root AGENTS.md");
869
870
  }
@@ -885,8 +886,19 @@ async function checkNestedAgentsMd(
885
886
  const terminology = resolveAllTerminology(config, profile);
886
887
  const wsType = workspaceTypes?.find((wt) => wt.id === ws.type);
887
888
  const expected = selectNestedTemplate(wsType, profile, terminology, fallback);
888
- const actual = await readFile(join(workspaceRoot, ws.path, "AGENTS.md"), "utf8");
889
- if (expected !== actual) {
889
+ const actual = await io.readFile(join(workspaceRoot, ws.path, "AGENTS.md"));
890
+ // RFC-1153: boundary-aware staleness — merge(expected, actual) compares
891
+ // only the generated head; content below the forge:custom boundary (or
892
+ // the canonical footer fallback) is preserved, not drift. Files bearing
893
+ // the restrictive GENERATED marker are generator-owned (RFC-0081) — any
894
+ // divergence is plain staleness, never unmapped-customization.
895
+ const merged = mergeEditableGenerated(expected, actual, `${ws.path}/AGENTS.md`);
896
+ if (merged === null && hasEditableGeneratedMarker(actual)) {
897
+ unmapped++;
898
+ issues.push(
899
+ `${ws.path}/AGENTS.md has custom content but no resolvable boundary — add <!-- forge:custom --> manually`,
900
+ );
901
+ } else if (merged === null || merged !== actual.replace(/\r\n/g, "\n")) {
890
902
  stale++;
891
903
  issues.push(`${ws.path}/AGENTS.md stale — run 'forge agents generate'`);
892
904
  }
@@ -906,11 +918,12 @@ async function checkNestedAgentsMd(
906
918
  const parts: string[] = [];
907
919
  if (missing > 0) parts.push(`${missing} missing`);
908
920
  if (stale > 0) parts.push(`${stale} stale`);
921
+ if (unmapped > 0) parts.push(`${unmapped} unmapped-customization`);
909
922
  if (handwritten > 0) parts.push(`${handwritten} hand-written`);
910
923
 
911
924
  return {
912
925
  name: "nested-AGENTS.md",
913
- status: stale > 0 ? "warn" : "pass",
926
+ status: stale > 0 || unmapped > 0 ? "warn" : "pass",
914
927
  message: `${parts.join(", ")} — ${issues.slice(0, 3).join("; ")}${issues.length > 3 ? ` (+${issues.length - 3} more)` : ""}`,
915
928
  };
916
929
  }
@@ -1222,12 +1235,13 @@ export async function runDoctor(
1222
1235
  context: ForgeRuntimeContext,
1223
1236
  ): Promise<ForgeCommandResult<{ command: string; checks: DoctorCheck[]; allPass: boolean; forbiddenImports: ForbiddenImport[]; bindings: BindingValidation; domain?: DomainReport; fixes?: DoctorFixResult[] }>> {
1224
1237
  const { workspaceRoot, logger, outputFormat, dryRun } = context;
1238
+ const fio = resolveIo(context.io);
1225
1239
  const strict = input.flags["strict"] === true;
1226
1240
  const fix = input.flags["fix"] === true;
1227
1241
  const checks: DoctorCheck[] = [];
1228
1242
 
1229
1243
  // Check forge.yaml
1230
- const forgeYamlExists = await pathExists(join(workspaceRoot, "forge.yaml"));
1244
+ const forgeYamlExists = await pathExists(join(workspaceRoot, "forge.yaml"), fio);
1231
1245
  checks.push({
1232
1246
  name: "forge.yaml",
1233
1247
  status: forgeYamlExists ? "pass" : "warn",
@@ -1237,7 +1251,7 @@ export async function runDoctor(
1237
1251
  });
1238
1252
 
1239
1253
  // Check AGENTS.md
1240
- const agentsMdExists = await pathExists(join(workspaceRoot, "AGENTS.md"));
1254
+ const agentsMdExists = await pathExists(join(workspaceRoot, "AGENTS.md"), fio);
1241
1255
  checks.push({
1242
1256
  name: "AGENTS.md",
1243
1257
  status: agentsMdExists ? "pass" : "warn",
@@ -1247,7 +1261,7 @@ export async function runDoctor(
1247
1261
  });
1248
1262
 
1249
1263
  // Check PREFERENCES.md
1250
- const preferencesExists = await pathExists(join(workspaceRoot, "PREFERENCES.md"));
1264
+ const preferencesExists = await pathExists(join(workspaceRoot, "PREFERENCES.md"), fio);
1251
1265
  checks.push({
1252
1266
  name: "PREFERENCES.md",
1253
1267
  status: preferencesExists ? "pass" : "warn",
@@ -1257,7 +1271,7 @@ export async function runDoctor(
1257
1271
  });
1258
1272
 
1259
1273
  // Check .agents/skills/
1260
- const skillsDirExists = await pathExists(join(workspaceRoot, ".agents", "skills"));
1274
+ const skillsDirExists = await pathExists(join(workspaceRoot, ".agents", "skills"), fio);
1261
1275
  checks.push({
1262
1276
  name: ".agents/skills/",
1263
1277
  status: skillsDirExists ? "pass" : "fail",
@@ -1267,7 +1281,7 @@ export async function runDoctor(
1267
1281
  });
1268
1282
 
1269
1283
  // Check docs/rfcs/
1270
- const rfcsDirExists = await pathExists(join(workspaceRoot, "docs", "rfcs"));
1284
+ const rfcsDirExists = await pathExists(join(workspaceRoot, "docs", "rfcs"), fio);
1271
1285
  checks.push({
1272
1286
  name: "docs/rfcs/",
1273
1287
  status: rfcsDirExists ? "pass" : "warn",
@@ -1277,7 +1291,7 @@ export async function runDoctor(
1277
1291
  });
1278
1292
 
1279
1293
  // Check docs/adrs/
1280
- const adrsDirExists = await pathExists(join(workspaceRoot, "docs", "adrs"));
1294
+ const adrsDirExists = await pathExists(join(workspaceRoot, "docs", "adrs"), fio);
1281
1295
  checks.push({
1282
1296
  name: "docs/adrs/",
1283
1297
  status: adrsDirExists ? "pass" : "warn",
@@ -1386,7 +1400,7 @@ export async function runDoctor(
1386
1400
  : `${invalidProfiles.length} profile(s) invalid (advisory): ${invalidProfiles.map((p) => p.id).join(", ")}`,
1387
1401
  });
1388
1402
 
1389
- const forbiddenImports = await scanForForbiddenImports(forgeRoot, workspaceRoot);
1403
+ const forbiddenImports = await scanForForbiddenImports(forgeRoot, workspaceRoot, fio);
1390
1404
  checks.push({
1391
1405
  name: "autonomy-guard",
1392
1406
  status: forbiddenImports.length === 0 ? "pass" : "fail",
@@ -1397,7 +1411,7 @@ export async function runDoctor(
1397
1411
  });
1398
1412
 
1399
1413
  // Check bindings contract (RFC-0393)
1400
- const bindingsResult = await validateBindings(workspaceRoot);
1414
+ const bindingsResult = await validateBindings(workspaceRoot, fio);
1401
1415
  checks.push({
1402
1416
  name: "bindings",
1403
1417
  status: bindingsResult.invalid.length === 0 ? "pass" : "fail",
@@ -1408,7 +1422,7 @@ export async function runDoctor(
1408
1422
  });
1409
1423
 
1410
1424
  // Check stale knowledge files (RFC-0524, RFC-0539)
1411
- const staleCheck = await checkStaleKnowledgeFiles(workspaceRoot, forgeRoot);
1425
+ const staleCheck = await checkStaleKnowledgeFiles(workspaceRoot, forgeRoot, fio);
1412
1426
  checks.push(staleCheck);
1413
1427
 
1414
1428
  // RFC-0660: Check legacy sections in knowledge files
@@ -1434,15 +1448,15 @@ export async function runDoctor(
1434
1448
  checks.push(checkDistFreshness(forgeRoot));
1435
1449
 
1436
1450
  // RFC-0539: Check pack skills — stale/missing copies and config validation
1437
- const packCheck = await checkPackSkills(workspaceRoot);
1451
+ const packCheck = await checkPackSkills(workspaceRoot, fio);
1438
1452
  checks.push(packCheck);
1439
1453
 
1440
1454
  // RFC-0941: Check pack manifests — forge.plugin.yaml existence and validity
1441
- const packManifestCheck = await checkPackManifests(workspaceRoot);
1455
+ const packManifestCheck = await checkPackManifests(workspaceRoot, fio);
1442
1456
  checks.push(packManifestCheck);
1443
1457
 
1444
1458
  // RFC-0704: Check independent version packages — paths must exist and contain package.json
1445
- const independentCheck = await checkIndependentVersionPackages(workspaceRoot);
1459
+ const independentCheck = await checkIndependentVersionPackages(workspaceRoot, fio);
1446
1460
  checks.push(independentCheck);
1447
1461
 
1448
1462
  // RFC-0611: Check nested AGENTS.md — missing, stale, hand-written
@@ -1470,7 +1484,7 @@ export async function runDoctor(
1470
1484
  } catch {
1471
1485
  // profiles not loadable
1472
1486
  }
1473
- const nestedCheck = await checkNestedAgentsMd(workspaceRoot, wsTypes);
1487
+ const nestedCheck = await checkNestedAgentsMd(workspaceRoot, wsTypes, fio);
1474
1488
  checks.push(nestedCheck);
1475
1489
  }
1476
1490
 
@@ -1492,7 +1506,7 @@ export async function runDoctor(
1492
1506
  if (fix) {
1493
1507
  for (const check of finalChecks) {
1494
1508
  if (check.status === "pass") continue;
1495
- fixes.push(await applyDoctorRemediation(check, workspaceRoot, wsTypes, dryRun === true));
1509
+ fixes.push(await applyDoctorRemediation(check, workspaceRoot, wsTypes, dryRun === true, fio));
1496
1510
  }
1497
1511
  }
1498
1512
 
@@ -1571,12 +1585,13 @@ async function applyDoctorRemediation(
1571
1585
  workspaceRoot: string,
1572
1586
  workspaceTypes: ProfileWorkspaceType[] | undefined,
1573
1587
  dryRun: boolean,
1588
+ io: WorkspaceIO,
1574
1589
  ): Promise<DoctorFixResult> {
1575
1590
  switch (check.name) {
1576
1591
  case "nested-AGENTS.md": {
1577
1592
  try {
1578
1593
  const config = loadForgeConfig(workspaceRoot);
1579
- const res = await generateNestedAgentsMd(workspaceRoot, config, dryRun, workspaceTypes);
1594
+ const res = await generateNestedAgentsMd(workspaceRoot, config, dryRun, workspaceTypes, io);
1580
1595
  return {
1581
1596
  check: check.name,
1582
1597
  action: res.generated.length > 0 ? "fixed" : "skipped",
@@ -1589,10 +1604,10 @@ async function applyDoctorRemediation(
1589
1604
  case "memory-layer": {
1590
1605
  try {
1591
1606
  const memoryPath = join(workspaceRoot, ".agents", "memory", "MEMORY.md");
1592
- if (!fs.existsSync(memoryPath)) {
1607
+ if (!(await io.exists(memoryPath))) {
1593
1608
  return { check: check.name, action: "skipped", detail: "MEMORY.md absent" };
1594
1609
  }
1595
- const content = fs.readFileSync(memoryPath, "utf8");
1610
+ const content = await io.readFile(memoryPath);
1596
1611
  const budget = resolveMemoryBudget(workspaceRoot);
1597
1612
  const { lines, removedLines, fitsBudget } = compactMemoryMd(content, budget);
1598
1613
  if (!fitsBudget) {
@@ -1606,7 +1621,7 @@ async function applyDoctorRemediation(
1606
1621
  return { check: check.name, action: "skipped", detail: "within budget" };
1607
1622
  }
1608
1623
  if (!dryRun) {
1609
- fs.writeFileSync(memoryPath, lines.join("\n"), "utf8");
1624
+ await io.writeFile(memoryPath, lines.join("\n"));
1610
1625
  }
1611
1626
  return {
1612
1627
  check: check.name,
@@ -20,11 +20,13 @@ Sweep batch 2: real KEY_DECISIONS on 10 files, expanded purposes (CONTRACT-02/PU
20
20
  </CHANGE_SUMMARY>
21
21
  */
22
22
 
23
- import fs from "node:fs";
23
+ import * as fs from "../utils/sync-fs.ts";
24
24
  import path from "node:path";
25
25
  import { stringify as stringifyYaml, parse as parseYaml } from "yaml";
26
26
  import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
27
27
  import { syncKnowledgeFile } from "../knowledge/index.ts";
28
+ import { writeSkillMarkerSync } from "./skill-markers.ts";
29
+ import { planPrettierignoreSync, applyPrettierignoreSync } from "./prettierignore.ts";
28
30
  import { defaultForgeConfig, loadForgeConfig, resolveForgeRoot, type ForgeConfig } from "../config/forge-config.ts";
29
31
  import { listStackProfiles, detectStack } from "../profiles/stack-profile.ts";
30
32
 
@@ -280,6 +282,11 @@ export function runInit(
280
282
  fs.writeFileSync(destPath, content, "utf8");
281
283
  created.push(`${config.paths.skillsDir}/${skillName}/SKILL.md`);
282
284
 
285
+ // RFC-1154: marker manifest lists SKILL.md + knowledge files whose content
286
+ // is purely forge's (copied/unchanged); merged/skipped files carry consumer
287
+ // entries and stay out of the manifest so prune never deletes them.
288
+ const markerFiles = ["SKILL.md"];
289
+
283
290
  // RFC-0524: sync declared knowledge files
284
291
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
285
292
  if (fmMatch) {
@@ -298,6 +305,9 @@ export function runInit(
298
305
  if (syncResult.action === "copied" || syncResult.action === "merged") {
299
306
  created.push(`${config.paths.skillsDir}/${skillName}/${kf}`);
300
307
  }
308
+ if (syncResult.action === "copied" || syncResult.action === "unchanged") {
309
+ markerFiles.push(kf);
310
+ }
301
311
  } else {
302
312
  errors.push(`Knowledge file '${kf}' declared in ${skillName} but not found in source`);
303
313
  }
@@ -307,6 +317,7 @@ export function runInit(
307
317
  // Frontmatter parse error — SKILL-01 will catch this in validation
308
318
  }
309
319
  }
320
+ writeSkillMarkerSync(destDir, markerFiles);
310
321
  }
311
322
 
312
323
  // RFC-0539: Copy declared pack skills to .agents/skills/<name>/ for IDE discovery
@@ -352,6 +363,9 @@ export function runInit(
352
363
  fs.writeFileSync(destPath, content, "utf8");
353
364
  created.push(`${config.paths.skillsDir}/${skillName}/SKILL.md`);
354
365
 
366
+ // RFC-1154: same marker rule as forge skills.
367
+ const markerFiles = ["SKILL.md"];
368
+
355
369
  // RFC-0524: sync declared knowledge files for pack skills
356
370
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
357
371
  if (fmMatch) {
@@ -370,6 +384,9 @@ export function runInit(
370
384
  if (syncResult.action === "copied" || syncResult.action === "merged") {
371
385
  created.push(`${config.paths.skillsDir}/${skillName}/${kf}`);
372
386
  }
387
+ if (syncResult.action === "copied" || syncResult.action === "unchanged") {
388
+ markerFiles.push(kf);
389
+ }
373
390
  } else {
374
391
  errors.push(`Knowledge file '${kf}' declared in ${skillName} but not found in source`);
375
392
  }
@@ -379,6 +396,7 @@ export function runInit(
379
396
  // Frontmatter parse error — SKILL-01 will catch this in validation
380
397
  }
381
398
  }
399
+ writeSkillMarkerSync(destDir, markerFiles);
382
400
  }
383
401
 
384
402
  // RFC-0663: Sync shared knowledge layer to .agents/skills/shared-knowledge/
@@ -391,6 +409,25 @@ export function runInit(
391
409
  if (syncResult.action === "copied" || syncResult.action === "merged") {
392
410
  created.push(`${config.paths.skillsDir}/shared-knowledge/learned-principles.md`);
393
411
  }
412
+ // RFC-1154: marker lists the file only when its content is purely forge's.
413
+ writeSkillMarkerSync(
414
+ sharedKnowledgeDestDir,
415
+ syncResult.action === "copied" || syncResult.action === "unchanged"
416
+ ? ["learned-principles.md"]
417
+ : [],
418
+ );
419
+ }
420
+
421
+ // RFC-1154: reconcile the forge-managed .prettierignore block (prettier
422
+ // consumers only; additive + idempotent, no-op otherwise). Non-fatal —
423
+ // a prettierignore write failure must not fail init.
424
+ try {
425
+ applyPrettierignoreSync(
426
+ workspaceRoot,
427
+ planPrettierignoreSync(workspaceRoot, config.paths.skillsDir),
428
+ );
429
+ } catch (err) {
430
+ errors.push(`.prettierignore reconcile failed: ${(err as Error).message}`);
394
431
  }
395
432
 
396
433
  // 4. Create docs directories from config paths
@@ -20,7 +20,7 @@ Sweep batch 4: 73 Compass headers on headerless engine files (certification, com
20
20
  </CHANGE_SUMMARY>
21
21
  */
22
22
 
23
- import fs from "node:fs";
23
+ import * as fs from "../utils/sync-fs.ts";
24
24
  import path from "node:path";
25
25
  import { parse as parseYaml } from "yaml";
26
26
  import type { StackProfile } from "../profiles/stack-profile.ts";
@@ -14,7 +14,7 @@ Invoked standalone and by forge doctor --fix for the memory-layer check (RFC-115
14
14
  </CHANGE_SUMMARY>
15
15
  */
16
16
 
17
- import fs from "node:fs";
17
+ import { resolveIo } from "../utils/io.ts";
18
18
  import path from "node:path";
19
19
  import type { ForgeCommandInput, ForgeCommandResult, ForgeRuntimeContext } from "../types.ts";
20
20
  import { resolveMemoryBudget } from "./memory-scaffold.ts";
@@ -100,6 +100,7 @@ export async function runMemoryCompact(
100
100
  context: ForgeRuntimeContext,
101
101
  ): Promise<ForgeCommandResult<MemoryCompactResult>> {
102
102
  const { workspaceRoot, logger, outputFormat, dryRun } = context;
103
+ const fio = resolveIo(context.io);
103
104
  const memoryPath = path.join(workspaceRoot, MEMORY_MD_PATH);
104
105
  const budget = resolveMemoryBudget(workspaceRoot);
105
106
 
@@ -116,7 +117,7 @@ export async function runMemoryCompact(
116
117
  summary: `forge.memory.compact: failed — ${msg}`,
117
118
  });
118
119
 
119
- if (!fs.existsSync(memoryPath)) {
120
+ if (!(await fio.exists(memoryPath))) {
120
121
  return {
121
122
  data: {
122
123
  command: "forge.memory.compact",
@@ -131,7 +132,7 @@ export async function runMemoryCompact(
131
132
  };
132
133
  }
133
134
 
134
- const content = fs.readFileSync(memoryPath, "utf8");
135
+ const content = await fio.readFile(memoryPath);
135
136
  if (content.length <= budget) {
136
137
  return {
137
138
  data: {
@@ -158,7 +159,7 @@ export async function runMemoryCompact(
158
159
  }
159
160
 
160
161
  if (!dryRun) {
161
- fs.writeFileSync(memoryPath, lines.join("\n"), "utf8");
162
+ await fio.writeFile(memoryPath, lines.join("\n"));
162
163
  }
163
164
 
164
165
  if (outputFormat === "pretty") {
@@ -12,7 +12,7 @@
12
12
  </CHANGE_SUMMARY>
13
13
  */
14
14
 
15
- import fs from "node:fs";
15
+ import * as fs from "../utils/sync-fs.ts";
16
16
  import path from "node:path";
17
17
  import { loadForgeConfig } from "../config/forge-config.ts";
18
18
 
@@ -23,19 +23,29 @@ Sweep batch 4: 73 Compass headers on headerless engine files (certification, com
23
23
  </CHANGE_SUMMARY>
24
24
  */
25
25
 
26
- import fs from "node:fs";
26
+ import * as fs from "../utils/sync-fs.ts";
27
27
  import path from "node:path";
28
- import { writeFileIfChanged } from "../utils/index.ts";
28
+ import {
29
+ canonicalFooterOf,
30
+ hasEditableGeneratedMarker,
31
+ mergeEditableGenerated,
32
+ splitEditableGenerated,
33
+ writeFileIfChanged,
34
+ } from "../utils/index.ts";
35
+ import { resolveIo } from "../utils/io.ts";
29
36
  import { discoverWorkspaces } from "./workspace-discovery.ts";
30
37
  import { buildNestedAgentsMd, selectNestedTemplate, type PackageInfo } from "./nested-agents-templates.ts";
31
38
  import type { ForgeConfig } from "../config/forge-config.ts";
32
39
  import type { ProfileWorkspaceType } from "../profiles/profile-schema.ts";
33
40
  import type { StackProfile } from "../profiles/stack-profile.ts";
41
+ import type { WorkspaceIO } from "../types.ts";
34
42
  import { resolveAllTerminology } from "../profiles/terminology-utils.ts";
35
43
 
36
44
  export interface NestedGenerateResult {
37
45
  generated: string[];
38
46
  skipped: string[];
47
+ /** RFC-1153: files merged with a carried custom tail below the boundary. */
48
+ preserved: string[];
39
49
  renderedFiles: { [relPath: string]: string };
40
50
  workspaceTypeMap?: { [relPath: string]: string };
41
51
  }
@@ -55,6 +65,7 @@ export async function generateNestedAgentsMd(
55
65
  config: ForgeConfig,
56
66
  dryRun: boolean,
57
67
  workspaceTypes?: ProfileWorkspaceType[],
68
+ io?: WorkspaceIO,
58
69
  ): Promise<NestedGenerateResult> {
59
70
  const workspaces = discoverWorkspaces(
60
71
  workspaceRoot,
@@ -63,7 +74,9 @@ export async function generateNestedAgentsMd(
63
74
  );
64
75
  const generated: string[] = [];
65
76
  const skipped: string[] = [];
77
+ const preserved: string[] = [];
66
78
  const renderedFiles: { [relPath: string]: string } = {};
79
+ const fio = resolveIo(io);
67
80
  const workspaceTypeMap: { [relPath: string]: string } = {};
68
81
 
69
82
  // RFC-0643: resolve terminology from config + profile for nested template substitution
@@ -90,12 +103,40 @@ export async function generateNestedAgentsMd(
90
103
  continue;
91
104
  }
92
105
 
93
- await writeFileIfChanged(agentsMdPath, content);
106
+ // RFC-1153: merge instead of overwrite — the `forge:custom` boundary marks
107
+ // the split between the generator-owned head and the preserved custom tail.
108
+ let existing: string | null = null;
109
+ if (ws.hasAgentsMd) {
110
+ try {
111
+ existing = await fio.readFile(agentsMdPath);
112
+ } catch {
113
+ // Unreadable — treat as absent; merge will emit a fresh file.
114
+ }
115
+ }
116
+ let merged = mergeEditableGenerated(content, existing, relPath);
117
+ if (merged === null && existing !== null && !hasEditableGeneratedMarker(existing)) {
118
+ // RFC-0081/RFC-1153: restrictive-marker file is fully generator-owned —
119
+ // upgrade it to the editable boundary contract (fresh render + marker).
120
+ merged = mergeEditableGenerated(content, null, relPath);
121
+ }
122
+ if (merged === null) {
123
+ skipped.push(`${relPath} (unmapped-customization)`);
124
+ continue;
125
+ }
126
+
127
+ await writeFileIfChanged(agentsMdPath, merged, fio);
94
128
  generated.push(relPath);
95
129
  workspaceTypeMap[relPath] = ws.type;
130
+ const footer = canonicalFooterOf(content);
131
+ if (
132
+ existing !== null &&
133
+ splitEditableGenerated(existing, relPath, footer ?? undefined).customTail !== null
134
+ ) {
135
+ preserved.push(relPath);
136
+ }
96
137
  }
97
138
 
98
- return { generated, skipped, renderedFiles, workspaceTypeMap };
139
+ return { generated, skipped, preserved, renderedFiles, workspaceTypeMap };
99
140
  }
100
141
 
101
142
  export { discoverWorkspaces, type WorkspaceDir, type WorkspaceType } from "./workspace-discovery.ts";