@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
@@ -0,0 +1,134 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Unit tests for the RFC-1154 managed .prettierignore block:
4
+ root-only prettier detection, delimited-block reconcile preserving operator
5
+ content, marker-driven skill entries, and the skipped contract for
6
+ prettier-free consumers.</purpose>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>RFC-1154: initial prettierignore plan/apply tests.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ import { test, expect, beforeEach, afterEach } from "vitest";
14
+ import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises";
15
+ import { existsSync } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import {
19
+ planPrettierignore,
20
+ applyPrettierignore,
21
+ reconcilePrettierignoreContent,
22
+ PRETTIERIGNORE_BLOCK_BEGIN,
23
+ PRETTIERIGNORE_BLOCK_END,
24
+ } from "../onboarding/prettierignore.ts";
25
+ import { SKILL_MARKER_FILE } from "../onboarding/skill-markers.ts";
26
+
27
+ let tmpDir: string;
28
+
29
+ beforeEach(async () => {
30
+ tmpDir = await mkdtemp(join(tmpdir(), "prettierignore-"));
31
+ });
32
+
33
+ afterEach(async () => {
34
+ await rm(tmpDir, { recursive: true, force: true });
35
+ });
36
+
37
+ async function withPrettier(root: string): Promise<void> {
38
+ await writeFile(join(root, ".prettierrc"), "{}\n");
39
+ }
40
+
41
+ test("detects prettier via root .prettierrc", async () => {
42
+ await withPrettier(tmpDir);
43
+ const plan = await planPrettierignore(tmpDir);
44
+ expect(plan.applies).toBe(true);
45
+ });
46
+
47
+ test("detects prettier via package.json prettier key", async () => {
48
+ await writeFile(join(tmpDir, "package.json"), JSON.stringify({ prettier: {} }));
49
+ const plan = await planPrettierignore(tmpDir);
50
+ expect(plan.applies).toBe(true);
51
+ });
52
+
53
+ test("detects prettier via prettier devDependency", async () => {
54
+ await writeFile(
55
+ join(tmpDir, "package.json"),
56
+ JSON.stringify({ devDependencies: { prettier: "^3" } }),
57
+ );
58
+ const plan = await planPrettierignore(tmpDir);
59
+ expect(plan.applies).toBe(true);
60
+ });
61
+
62
+ test("detects prettier via existing .prettierignore alone", async () => {
63
+ await writeFile(join(tmpDir, ".prettierignore"), "dist/\n");
64
+ const plan = await planPrettierignore(tmpDir);
65
+ expect(plan.applies).toBe(true);
66
+ });
67
+
68
+ test("AC-4: no prettier markers — plan does not apply, apply writes nothing", async () => {
69
+ const plan = await planPrettierignore(tmpDir);
70
+ expect(plan.applies).toBe(false);
71
+ const result = await applyPrettierignore(tmpDir, plan);
72
+ expect(result).toBe("skipped");
73
+ expect(existsSync(join(tmpDir, ".prettierignore"))).toBe(false);
74
+ });
75
+
76
+ test("AC-2: block contains generated.yaml glob, sessions, metrics, queues entries", async () => {
77
+ await withPrettier(tmpDir);
78
+ const plan = await planPrettierignore(tmpDir);
79
+ const result = await applyPrettierignore(tmpDir, plan);
80
+ expect(result).toBe("written");
81
+ const content = await readFile(join(tmpDir, ".prettierignore"), "utf8");
82
+ expect(content).toContain(PRETTIERIGNORE_BLOCK_BEGIN);
83
+ expect(content).toContain("**/*.generated.yaml");
84
+ expect(content).toContain("docs/sessions/");
85
+ expect(content).toContain("docs/metrics/");
86
+ expect(content).toContain("docs/queues/session-*.yaml");
87
+ expect(content).toContain(PRETTIERIGNORE_BLOCK_END);
88
+ });
89
+
90
+ test("skill entries enumerate only marker-carrying dirs (AC-9)", async () => {
91
+ await withPrettier(tmpDir);
92
+ const skillsDir = join(tmpDir, ".agents", "skills");
93
+ await mkdir(join(skillsDir, "fo-idea"), { recursive: true });
94
+ await writeFile(join(skillsDir, "fo-idea", SKILL_MARKER_FILE), '{"files":["SKILL.md"]}');
95
+ await mkdir(join(skillsDir, "my-own-skill"), { recursive: true }); // unmarked — consumer-owned
96
+
97
+ const plan = await planPrettierignore(tmpDir);
98
+ expect(plan.entries).toContain(".agents/skills/fo-idea/");
99
+ expect(plan.entries).not.toContain(".agents/skills/my-own-skill/");
100
+ });
101
+
102
+ test("reconcile preserves operator content outside the block", async () => {
103
+ const existing = `dist/\nnode_modules/\n${PRETTIERIGNORE_BLOCK_BEGIN}\nold-entry/\n${PRETTIERIGNORE_BLOCK_END}\ncustom/\n`;
104
+ const { next, changed } = reconcilePrettierignoreContent(existing, ["a/", "b/"]);
105
+ expect(changed).toBe("updated");
106
+ expect(next).toContain("dist/\nnode_modules/\n");
107
+ expect(next).toContain("custom/\n");
108
+ expect(next).not.toContain("old-entry/");
109
+ expect(next).toContain("a/\nb/");
110
+ });
111
+
112
+ test("unbalanced markers treat the file as operator-owned and append a fresh block", async () => {
113
+ const existing = `dist/\n${PRETTIERIGNORE_BLOCK_BEGIN}\nstray/\n`; // no END marker
114
+ const { next, changed } = reconcilePrettierignoreContent(existing, ["x/"]);
115
+ expect(changed).toBe("updated");
116
+ expect(next).toContain("dist/\n");
117
+ // Original content preserved verbatim — forge does not merge into a
118
+ // corrupted region; a fresh block is appended.
119
+ expect(next.indexOf(PRETTIERIGNORE_BLOCK_BEGIN)).toBeLessThan(
120
+ next.lastIndexOf(PRETTIERIGNORE_BLOCK_BEGIN),
121
+ );
122
+ });
123
+
124
+ test("repeated apply is idempotent", async () => {
125
+ await withPrettier(tmpDir);
126
+ await writeFile(join(tmpDir, ".prettierignore"), "dist/\n");
127
+ const plan = await planPrettierignore(tmpDir);
128
+ const first = await applyPrettierignore(tmpDir, plan);
129
+ expect(first).toBe("updated");
130
+ const second = await applyPrettierignore(tmpDir, await planPrettierignore(tmpDir));
131
+ expect(second, "second apply must be unchanged — check reconcile block boundaries").toBe(
132
+ "unchanged",
133
+ );
134
+ });
@@ -0,0 +1,168 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>CI gate: every forge/pack skill source must be fully propagated to
4
+ `.agents/skills/` (IDE discovery copy written by forge.init/forge.upgrade).
5
+ SKILL.md syncs verbatim; declared knowledge files follow the append-only
6
+ syncKnowledgeFile contract — in the canonical repo a resync must be a no-op,
7
+ otherwise source and copy have drifted apart.</purpose>
8
+ <non-goals>
9
+ <item>Do not validate skill content/schema — forge.skill.validate owns that.</item>
10
+ <item>Do not flag unmarked dirs — a missing .forge-managed marker means
11
+ consumer-owned (RFC-1154), e.g. `_shared/`.</item>
12
+ </non-goals>
13
+ <KEY_DECISIONS>
14
+ <item>planKnowledgeSync is reused so the gate encodes the exact merge
15
+ semantics of the syncer — only "unchanged" is a pass; "merged" (source has
16
+ entries the copy lacks), "copied" (copy missing), and "skipped"
17
+ (non-mergeable divergence = hand-edited copy) all fail.</item>
18
+ <item>Runs on the real repo — this is a drift gate, not a fixture test.</item>
19
+ </KEY_DECISIONS>
20
+ <CHANGE_SUMMARY>
21
+ <item>Initial version — covers FORGE_SKILLS, declared pack skills
22
+ (discoverPackSkills), and the shared-knowledge layer.</item>
23
+ </CHANGE_SUMMARY>
24
+ */
25
+
26
+ import fs from "node:fs";
27
+ import path from "node:path";
28
+ import { describe, expect, it } from "vitest";
29
+
30
+ import { loadForgeConfig } from "../config/forge-config.ts";
31
+ import { planKnowledgeSync } from "../knowledge/index.ts";
32
+ import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
33
+
34
+ const forgeRoot = path.resolve(import.meta.dirname, "../..");
35
+ const workspaceRoot = path.resolve(import.meta.dirname, "../../../..");
36
+
37
+ interface SkillSource {
38
+ name: string;
39
+ srcDir: string;
40
+ knowledge: string[];
41
+ }
42
+
43
+ function collectSkillSources(): SkillSource[] {
44
+ const sources: SkillSource[] = [];
45
+ for (const skill of FORGE_SKILLS) {
46
+ sources.push({
47
+ name: skill.name,
48
+ srcDir: path.dirname(path.join(forgeRoot, skill.path)),
49
+ knowledge: skill.knowledge ?? [],
50
+ });
51
+ }
52
+ const config = loadForgeConfig(workspaceRoot);
53
+ for (const skill of discoverPackSkills(workspaceRoot, config)) {
54
+ sources.push({
55
+ name: skill.name,
56
+ srcDir: path.dirname(path.join(workspaceRoot, skill.dir, skill.path)),
57
+ knowledge: skill.knowledge ?? [],
58
+ });
59
+ }
60
+ return sources;
61
+ }
62
+
63
+ // Skipped in the standalone package repo — the gate needs a forge-managed
64
+ // workspace root (forge.yaml + synced skillsDir), which only exists for the
65
+ // monorepo and consumer projects, not for the published forge source tree.
66
+ const hasForgeWorkspace = fs.existsSync(path.join(workspaceRoot, "forge.yaml"));
67
+ describe.runIf(hasForgeWorkspace)(
68
+ ".agents/skills sync gate (forge.init/forge.upgrade parity)",
69
+ () => {
70
+ // describe callbacks still run during collection even when runIf is false,
71
+ // so initializers must tolerate the absent forge.yaml themselves.
72
+ const config = hasForgeWorkspace ? loadForgeConfig(workspaceRoot) : null;
73
+ const skillsDir = config ? path.join(workspaceRoot, config.paths.skillsDir) : "";
74
+ const sources = config ? collectSkillSources() : [];
75
+
76
+ it("every forge + pack skill has a synced copy in .agents/skills/", () => {
77
+ const missing = sources
78
+ .filter((s) => !fs.existsSync(path.join(skillsDir, s.name, "SKILL.md")))
79
+ .map((s) => `${s.name} (src ${path.relative(workspaceRoot, s.srcDir)})`);
80
+ expect(
81
+ missing,
82
+ `skills never synced — run \`forge init\`/\`forge upgrade\` to populate ${path.relative(workspaceRoot, skillsDir)}`,
83
+ ).toEqual([]);
84
+ });
85
+
86
+ it("SKILL.md copies are byte-identical to their sources", () => {
87
+ const drifted: string[] = [];
88
+ for (const s of sources) {
89
+ const src = path.join(s.srcDir, "SKILL.md");
90
+ const dest = path.join(skillsDir, s.name, "SKILL.md");
91
+ if (!fs.existsSync(dest)) continue; // reported by the presence test
92
+ if (fs.readFileSync(src, "utf8") !== fs.readFileSync(dest, "utf8")) {
93
+ drifted.push(s.name);
94
+ }
95
+ }
96
+ expect(
97
+ drifted,
98
+ "SKILL.md drift — edit the pack source and re-sync, never edit .agents/skills/ directly",
99
+ ).toEqual([]);
100
+ });
101
+
102
+ it("declared knowledge files are in sync (append-only merge would be a no-op)", () => {
103
+ const stale: string[] = [];
104
+ for (const s of sources) {
105
+ for (const kf of s.knowledge) {
106
+ const src = path.join(s.srcDir, kf);
107
+ const dest = path.join(skillsDir, s.name, kf);
108
+ if (!fs.existsSync(src)) {
109
+ stale.push(`${s.name}/${kf} — declared but missing in source`);
110
+ continue;
111
+ }
112
+ if (!fs.existsSync(dest)) {
113
+ stale.push(`${s.name}/${kf} — missing in .agents/skills (run forge upgrade)`);
114
+ continue;
115
+ }
116
+ const plan = planKnowledgeSync(src, dest);
117
+ if (plan.action === "merged") {
118
+ stale.push(`${s.name}/${kf} — source has entries the copy lacks`);
119
+ } else if (plan.action === "skipped") {
120
+ stale.push(`${s.name}/${kf} — copy diverged non-mergeably (hand-edited .agents copy)`);
121
+ }
122
+ }
123
+ }
124
+ expect(stale).toEqual([]);
125
+ });
126
+
127
+ it("undeclared aux files in the copy still match their sources", () => {
128
+ // The syncer writes SKILL.md + declared knowledge files only. Aux files
129
+ // (references, templates, placeholder dirs) that exist in BOTH trees are
130
+ // hand-copied and silently diverge — a same-named pair must stay identical,
131
+ // and a copy-only orphan can never resync.
132
+ const flagged: string[] = [];
133
+ for (const s of sources) {
134
+ const destDir = path.join(skillsDir, s.name);
135
+ if (!fs.existsSync(destDir)) continue;
136
+ const declared = new Set(["SKILL.md", ".forge-managed", ...s.knowledge]);
137
+ for (const entry of fs.readdirSync(destDir)) {
138
+ if (declared.has(entry)) continue;
139
+ const srcEntry = path.join(s.srcDir, entry);
140
+ const destEntry = path.join(destDir, entry);
141
+ if (fs.statSync(destEntry).isDirectory()) {
142
+ if (!fs.existsSync(srcEntry))
143
+ flagged.push(`${s.name}/${entry}/ — dir absent in source`);
144
+ continue;
145
+ }
146
+ if (!fs.existsSync(srcEntry)) {
147
+ flagged.push(`${s.name}/${entry} — absent in source, copy can never resync`);
148
+ } else if (fs.readFileSync(srcEntry, "utf8") !== fs.readFileSync(destEntry, "utf8")) {
149
+ flagged.push(`${s.name}/${entry} — diverged from source`);
150
+ }
151
+ }
152
+ }
153
+ expect(
154
+ flagged,
155
+ "aux-file drift — declare aux files via `knowledge:` in SKILL.md or keep copies identical to source",
156
+ ).toEqual([]);
157
+ });
158
+
159
+ it("shared-knowledge layer is synced", () => {
160
+ const src = path.join(forgeRoot, "skills", "shared", "knowledge", "learned-principles.md");
161
+ const dest = path.join(skillsDir, "shared-knowledge", "learned-principles.md");
162
+ if (!fs.existsSync(src)) return; // layer absent — nothing to gate
163
+ expect(fs.existsSync(dest), "shared-knowledge/learned-principles.md not synced").toBe(true);
164
+ const plan = planKnowledgeSync(src, dest);
165
+ expect(plan.action, `shared-knowledge drift (sync action: ${plan.action})`).toBe("unchanged");
166
+ });
167
+ },
168
+ );
@@ -0,0 +1,134 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Unit tests for RFC-1154 skill markers: .forge-managed manifests
4
+ recorded on sync, and the stale-dir prune under the subset-of-manifest rule
5
+ (keeps dirs with consumer files or corrupt markers, never touches unmarked
6
+ dirs).</purpose>
7
+ </MODULE_CONTRACT>
8
+ <CHANGE_SUMMARY>
9
+ <item>RFC-1154: initial marker + prune tests.</item>
10
+ </CHANGE_SUMMARY>
11
+ */
12
+
13
+ import { test, expect, beforeEach, afterEach } from "vitest";
14
+ import { mkdtemp, rm, writeFile, mkdir, readFile } from "node:fs/promises";
15
+ import { existsSync } from "node:fs";
16
+ import { tmpdir } from "node:os";
17
+ import { join } from "node:path";
18
+ import {
19
+ SKILL_MARKER_FILE,
20
+ writeSkillMarker,
21
+ writeSkillMarkerSync,
22
+ pruneStaleSkillDirs,
23
+ } from "../onboarding/skill-markers.ts";
24
+
25
+ let tmpDir: string;
26
+ let skillsDir: string;
27
+
28
+ beforeEach(async () => {
29
+ tmpDir = await mkdtemp(join(tmpdir(), "skill-marker-"));
30
+ skillsDir = join(tmpDir, ".agents", "skills");
31
+ });
32
+
33
+ afterEach(async () => {
34
+ await rm(tmpDir, { recursive: true, force: true });
35
+ });
36
+
37
+ async function makeSkillDir(name: string, files: string[]): Promise<void> {
38
+ const dir = join(skillsDir, name);
39
+ await mkdir(dir, { recursive: true });
40
+ for (const f of files) {
41
+ const filePath = join(dir, f);
42
+ await mkdir(join(filePath, ".."), { recursive: true });
43
+ await writeFile(filePath, `content of ${f}\n`);
44
+ }
45
+ }
46
+
47
+ test("AC-7: writeSkillMarker records the exact file list", async () => {
48
+ const dir = join(skillsDir, "fo-idea");
49
+ await mkdir(dir, { recursive: true });
50
+ await writeSkillMarker(dir, ["SKILL.md", "qa-log.md"]);
51
+ const raw = await readFile(join(dir, SKILL_MARKER_FILE), "utf8");
52
+ expect(JSON.parse(raw)).toEqual({ files: ["SKILL.md", "qa-log.md"] });
53
+ });
54
+
55
+ test("marker write is idempotent (identical content on repeat)", async () => {
56
+ const dir = join(skillsDir, "fo-idea");
57
+ await mkdir(dir, { recursive: true });
58
+ writeSkillMarkerSync(dir, ["b.md", "a.md"]);
59
+ const first = await readFile(join(dir, SKILL_MARKER_FILE), "utf8");
60
+ writeSkillMarkerSync(dir, ["b.md", "a.md"]);
61
+ expect(await readFile(join(dir, SKILL_MARKER_FILE), "utf8")).toBe(first);
62
+ expect(JSON.parse(first).files).toEqual(["a.md", "b.md"]); // sorted, deterministic
63
+ });
64
+
65
+ test("AC-8: prunes a stale marked dir whose contents match the manifest", async () => {
66
+ const dir = join(skillsDir, "old-skill");
67
+ await makeSkillDir("old-skill", ["SKILL.md"]);
68
+ await writeSkillMarker(dir, ["SKILL.md"]);
69
+
70
+ const result = await pruneStaleSkillDirs(skillsDir, new Set(["other-skill"]));
71
+ expect(result.pruned).toEqual(["old-skill"]);
72
+ expect(result.keptWithConsumerFiles).toEqual([]);
73
+ expect(existsSync(dir)).toBe(false);
74
+ });
75
+
76
+ test("AC-8: keeps a stale marked dir containing consumer files", async () => {
77
+ const dir = join(skillsDir, "old-skill");
78
+ await makeSkillDir("old-skill", ["SKILL.md", "my-notes.md"]);
79
+ await writeSkillMarker(dir, ["SKILL.md"]);
80
+
81
+ const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
82
+ expect(result.pruned).toEqual([]);
83
+ expect(result.keptWithConsumerFiles).toHaveLength(1);
84
+ expect(result.keptWithConsumerFiles[0]).toContain("old-skill");
85
+ expect(result.keptWithConsumerFiles[0]).toContain("my-notes.md");
86
+ expect(existsSync(dir)).toBe(true);
87
+ });
88
+
89
+ test("AC-9: unmarked dirs are never pruned", async () => {
90
+ const dir = join(skillsDir, "consumer-skill");
91
+ await makeSkillDir("consumer-skill", ["SKILL.md"]);
92
+
93
+ const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
94
+ expect(result.pruned).toEqual([]);
95
+ expect(result.keptWithConsumerFiles).toEqual([]);
96
+ expect(existsSync(dir)).toBe(true);
97
+ });
98
+
99
+ test("dirs still in the sync set are kept regardless of marker", async () => {
100
+ const dir = join(skillsDir, "current-skill");
101
+ await makeSkillDir("current-skill", ["SKILL.md"]);
102
+ await writeSkillMarker(dir, ["SKILL.md"]);
103
+
104
+ const result = await pruneStaleSkillDirs(skillsDir, new Set(["current-skill"]));
105
+ expect(result.pruned).toEqual([]);
106
+ expect(existsSync(dir)).toBe(true);
107
+ });
108
+
109
+ test("corrupt marker disables prune for that dir (fail-safe keep)", async () => {
110
+ const dir = join(skillsDir, "broken-skill");
111
+ await makeSkillDir("broken-skill", ["SKILL.md"]);
112
+ await writeFile(join(dir, SKILL_MARKER_FILE), "not json {{{");
113
+
114
+ const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
115
+ expect(result.pruned).toEqual([]);
116
+ expect(result.keptWithConsumerFiles).toHaveLength(1);
117
+ expect(result.keptWithConsumerFiles[0]).toContain("corrupt marker");
118
+ expect(existsSync(dir)).toBe(true);
119
+ });
120
+
121
+ test("nested manifest files are matched recursively", async () => {
122
+ const dir = join(skillsDir, "nested-skill");
123
+ await makeSkillDir("nested-skill", ["SKILL.md", "knowledge/notes.md"]);
124
+ await writeSkillMarker(dir, ["SKILL.md", "knowledge/notes.md"]);
125
+
126
+ const result = await pruneStaleSkillDirs(skillsDir, new Set([]));
127
+ expect(result.pruned).toEqual(["nested-skill"]);
128
+ expect(existsSync(dir)).toBe(false);
129
+ });
130
+
131
+ test("missing skills dir returns empty result", async () => {
132
+ const result = await pruneStaleSkillDirs(join(tmpDir, "nonexistent"), new Set());
133
+ expect(result).toEqual({ pruned: [], keptWithConsumerFiles: [] });
134
+ });
@@ -78,14 +78,14 @@ describe("RFC-0539: Pack skill validation (SKILL-14, SKILL-15)", () => {
78
78
  });
79
79
  });
80
80
 
81
- describe("RFC-0548: SKILL-16 triggers field validation", () => {
82
- test("SKILL-16: real workspace fo-skills have valid triggers (no violations)", () => {
81
+ describe("RFC-0548: SKILL-16 triggerPhrases field validation", () => {
82
+ test("SKILL-16: real workspace fo-skills have valid triggerPhrases (no violations)", () => {
83
83
  const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
84
84
  const skill16Violations = result.violations.filter((v) => v.rule === "SKILL-16");
85
85
  expect(skill16Violations).toEqual([]);
86
86
  });
87
87
 
88
- test("SKILL-16: triggers are only on fo-category skills", () => {
88
+ test("SKILL-16: triggerPhrases are only on fo-category skills", () => {
89
89
  const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
90
90
  const skill16Violations = result.violations.filter(
91
91
  (v) => v.rule === "SKILL-16" && v.message.includes("only allowed on fo-category"),
@@ -93,13 +93,20 @@ describe("RFC-0548: SKILL-16 triggers field validation", () => {
93
93
  expect(skill16Violations).toEqual([]);
94
94
  });
95
95
 
96
- test("SKILL-16: no pack skills declare triggers", () => {
96
+ test("SKILL-16: no pack skills declare triggerPhrases", () => {
97
97
  const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
98
98
  const packTriggerViolations = result.violations.filter(
99
- (v) => v.rule === "SKILL-16" && v.message.includes("pack skills may not declare triggers"),
99
+ (v) =>
100
+ v.rule === "SKILL-16" && v.message.includes("pack skills may not declare triggerPhrases"),
100
101
  );
101
102
  expect(packTriggerViolations).toEqual([]);
102
103
  });
104
+
105
+ test("SKILL-22: no skill declares the reserved `triggers` key", () => {
106
+ const result = runSkillValidate({}, { workspaceRoot: process.cwd() });
107
+ const skill22Violations = result.violations.filter((v) => v.rule === "SKILL-22");
108
+ expect(skill22Violations).toEqual([]);
109
+ });
103
110
  });
104
111
 
105
112
  describe("RFC-0553: SKILL-17 platform reference prohibition", () => {
@@ -9,11 +9,15 @@
9
9
  */
10
10
 
11
11
  import { test, expect, beforeEach, afterEach, vi } from "vitest";
12
- import { mkdtemp, mkdir, writeFile, rm, readFile } from "node:fs/promises";
12
+ import { mkdtemp, mkdir, writeFile, rm, readFile, appendFile } from "node:fs/promises";
13
13
  import { existsSync } from "node:fs";
14
14
  import { tmpdir } from "node:os";
15
15
  import { join } from "node:path";
16
16
  import { runUpgrade } from "../onboarding/upgrade.ts";
17
+ import { generateNestedAgentsMd } from "../onboarding/nested-agents-generate.ts";
18
+ import { ambientIo } from "../utils/io.ts";
19
+ import { buildGeneratedHeader } from "../utils/index.ts";
20
+ import type { WorkspaceIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io";
17
21
  import { runInit } from "../onboarding/init.ts";
18
22
  import {
19
23
  loadForgeConfig,
@@ -163,7 +167,9 @@ test("forge.upgrade with syncedVersion matching installed version returns noop",
163
167
  expect(result.data?.status).toBe("noop");
164
168
  expect(result.data?.fromVersion).toBe("0.2.0");
165
169
  expect(result.data?.toVersion).toBe("0.2.0");
166
- expect(result.data?.skillsUpdated).toEqual([]);
170
+ // RFC-1154: the reconcile pass still runs on a same-version noop — skills it
171
+ // re-synced are reported in skillsUpdated.
172
+ expect(result.data?.skillsUpdated).toContain("fo-idea");
167
173
  expect(result.exitCode).toBe(0);
168
174
  });
169
175
 
@@ -358,66 +364,40 @@ test("forge.upgrade --update-npm attempts npm install for npm consumer", async (
358
364
  await setupNpmConsumerForge(tempDir, "0.2.0");
359
365
  await setupConsumerForgeYaml(tempDir, null);
360
366
 
361
- // Mock execSync to avoid real npm install
362
- const execSyncMock = vi.fn(() => "");
363
- vi.resetModules();
364
- vi.doMock("node:child_process", () => ({
365
- execSync: execSyncMock,
366
- exec: vi.fn(),
367
- execFile: vi.fn(),
368
- spawn: vi.fn(),
369
- fork: vi.fn(),
370
- }));
371
-
372
- // Re-import with mock
373
- const { runUpgrade: runUpgradeMocked } = await import("../onboarding/upgrade.ts");
374
-
375
- const result = await runUpgradeMocked(
367
+ // RFC-1152: inject a fake WorkspaceIO — exec is the port seam; real fs stays.
368
+ const execMock = vi.fn(async () => ({ exitCode: 0, stdout: "", stderr: "" }));
369
+ const io = { ...ambientIo, exec: execMock } as WorkspaceIO;
370
+
371
+ const result = await runUpgrade(
376
372
  { argv: [], flags: { "update-npm": true } },
377
- makeContext(tempDir, false),
373
+ { ...makeContext(tempDir, false), io },
378
374
  );
379
375
 
380
376
  expect(result.data?.status).toBe("pass");
381
- expect(execSyncMock).toHaveBeenCalled();
377
+ expect(execMock).toHaveBeenCalled();
382
378
  expect(result.data?.npmUpdated).toBe(true);
383
379
  expect(result.data?.npmUpdateSkipped).toBe(null);
384
-
385
- vi.doUnmock("node:child_process");
386
- vi.resetModules();
387
380
  });
388
381
 
389
382
  test("forge.upgrade --update-npm reports failure when npm install throws", async () => {
390
383
  await setupNpmConsumerForge(tempDir, "0.2.0");
391
384
  await setupConsumerForgeYaml(tempDir, null);
392
385
 
393
- // Mock execSync to throw (simulates network error / registry unavailable)
394
- const execSyncMock = vi.fn(() => {
395
- throw new Error("network error");
396
- });
397
- vi.resetModules();
398
- vi.doMock("node:child_process", () => ({
399
- execSync: execSyncMock,
400
- exec: vi.fn(),
401
- execFile: vi.fn(),
402
- spawn: vi.fn(),
403
- fork: vi.fn(),
404
- }));
405
-
406
- const { runUpgrade: runUpgradeMocked } = await import("../onboarding/upgrade.ts");
386
+ // RFC-1152: fake io.exec fails — simulates network error / registry unavailable
387
+ const execMock = vi.fn(async () => ({ exitCode: 1, stdout: "", stderr: "network error" }));
388
+ const io = { ...ambientIo, exec: execMock } as WorkspaceIO;
407
389
 
408
- const result = await runUpgradeMocked(
390
+ const result = await runUpgrade(
409
391
  { argv: [], flags: { "update-npm": true } },
410
- makeContext(tempDir, false),
392
+ { ...makeContext(tempDir, false), io },
411
393
  );
412
394
 
413
395
  expect(result.data?.status).toBe("pass");
414
- expect(execSyncMock).toHaveBeenCalled();
396
+ expect(execMock).toHaveBeenCalled();
415
397
  expect(result.data?.npmUpdated).toBe(false);
416
398
  expect(result.data?.npmUpdateSkipped).toBe(
417
399
  "install failed (network error or registry unavailable)",
418
400
  );
419
-
420
- vi.doUnmock("node:child_process");
421
401
  vi.resetModules();
422
402
  });
423
403
 
@@ -432,6 +412,72 @@ test("forge.upgrade without --update-npm does not attempt npm install", async ()
432
412
  expect(result.data?.npmUpdateSkipped).toBe(null);
433
413
  });
434
414
 
415
+ // RFC-1153: dry-run preview of the nested-AGENTS.md merge --------------------
416
+
417
+ test("AC-7: forge.upgrade --dry-run reports nestedAgentsPlanned and nestedAgentsSkipped", async () => {
418
+ await setupForgeSource(tempDir, "0.2.0");
419
+ await setupConsumerForgeYaml(tempDir, null);
420
+
421
+ // Workspace with no AGENTS.md — merged output would be written → planned
422
+ await mkdir(join(tempDir, "packages", "newpkg"), { recursive: true });
423
+ await writeFile(join(tempDir, "packages", "newpkg", "package.json"), '{"name":"newpkg"}', "utf8");
424
+
425
+ // Marker-bearing file that diverges with no resolvable boundary → skipped
426
+ await mkdir(join(tempDir, "packages", "unmapped"), { recursive: true });
427
+ await writeFile(
428
+ join(tempDir, "packages", "unmapped", "package.json"),
429
+ '{"name":"unmapped"}',
430
+ "utf8",
431
+ );
432
+ await writeFile(
433
+ join(tempDir, "packages", "unmapped", "AGENTS.md"),
434
+ `${buildGeneratedHeader({ filePath: "AGENTS.md", ownerCommand: "forge.agents.generate", editable: true })}\n# Hand-maintained\n\ndivergent body\n`,
435
+ "utf8",
436
+ );
437
+
438
+ const result = await runUpgrade(
439
+ { argv: [], flags: { "dry-run": true } },
440
+ makeContext(tempDir, true),
441
+ );
442
+
443
+ expect(result.data?.status).toBe("pass");
444
+ const planned = result.data?.nestedAgentsPlanned ?? [];
445
+ const skipped = result.data?.nestedAgentsSkipped ?? [];
446
+
447
+ expect(
448
+ planned,
449
+ "planned must contain exactly the guides whose merged output differs from disk",
450
+ ).toContain(join("packages", "newpkg", "AGENTS.md"));
451
+ expect(skipped.some((s) => s.includes("unmapped") && s.includes("unmapped-customization"))).toBe(
452
+ true,
453
+ );
454
+ // No files written under dry-run
455
+ expect(existsSync(join(tempDir, "packages", "newpkg", "AGENTS.md"))).toBe(false);
456
+ });
457
+
458
+ test("forge.upgrade executing path reports nestedAgentsPreserved for carried tails", async () => {
459
+ await setupForgeSource(tempDir, "0.2.0");
460
+ await setupConsumerForgeYaml(tempDir, null);
461
+
462
+ // First run materializes the nested guide with the boundary marker
463
+ await mkdir(join(tempDir, "packages", "pkg"), { recursive: true });
464
+ await writeFile(join(tempDir, "packages", "pkg", "package.json"), '{"name":"pkg"}', "utf8");
465
+ const config = loadForgeConfig(tempDir);
466
+ await generateNestedAgentsMd(tempDir, config, false);
467
+ await appendFile(
468
+ join(tempDir, "packages", "pkg", "AGENTS.md"),
469
+ "\n## Custom rules\n\n- keep me\n",
470
+ "utf8",
471
+ );
472
+
473
+ const result = await runUpgrade({ argv: [], flags: {} }, makeContext(tempDir, false));
474
+
475
+ expect(result.data?.status).toBe("pass");
476
+ expect(result.data?.nestedAgentsPreserved).toContain(join("packages", "pkg", "AGENTS.md"));
477
+ const merged = await readFile(join(tempDir, "packages", "pkg", "AGENTS.md"), "utf8");
478
+ expect(merged).toContain("- keep me");
479
+ });
480
+
435
481
  function qaLog(entries: { id: string; title: string; body: string }[]): string {
436
482
  const blocks = entries.map(
437
483
  (e) =>