@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
@@ -24,9 +24,11 @@ Sweep batch 4: 73 Compass headers on headerless engine files (certification, com
24
24
  </CHANGE_SUMMARY>
25
25
  */
26
26
 
27
- import fs from "node:fs";
27
+ import type { WorkspaceIO } from "../types.ts";
28
+ import { resolveIo } from "../utils/io.ts";
29
+ import { hasGeneratedMarker, mergeEditableGenerated } from "../utils/index.ts";
28
30
  import path from "node:path";
29
- import { execSync } from "node:child_process";
31
+
30
32
  import { stringify as stringifyYaml, parse as parseYaml } from "yaml";
31
33
  import {
32
34
  FORGE_CLI_BINDING_DEFAULTS,
@@ -40,6 +42,13 @@ import {
40
42
  } from "../config/forge-config.ts";
41
43
  import { FORGE_SKILLS, discoverPackSkills } from "../registry.ts";
42
44
  import { syncKnowledgeFile } from "../knowledge/index.ts";
45
+ import { writeFileIfChanged } from "../utils/fs-idempotent.ts";
46
+ import { writeSkillMarker, pruneStaleSkillDirs, type PruneResult } from "./skill-markers.ts";
47
+ import {
48
+ planPrettierignore,
49
+ applyPrettierignore,
50
+ type PrettierignoreResult,
51
+ } from "./prettierignore.ts";
43
52
  import { generateNestedAgentsMd } from "./nested-agents-generate.ts";
44
53
  import { scaffoldMemoryLayer } from "./memory-scaffold.ts";
45
54
  import type { SkippedSkill } from "./init.ts";
@@ -54,68 +63,80 @@ export interface UpgradeResult {
54
63
  bindingsAdded: { key: string; value: string }[];
55
64
  skippedSkills: SkippedSkill[];
56
65
  nestedAgentsGenerated: string[];
66
+ /** RFC-1153: dry-run only — nested guides whose merged output differs from disk. */
67
+ nestedAgentsPlanned: string[];
68
+ /** RFC-1153: files merged with a carried custom tail below the boundary. */
69
+ nestedAgentsPreserved: string[];
70
+ /** RFC-1153: hand-written or unmapped files skipped by the merge. */
71
+ nestedAgentsSkipped: string[];
57
72
  memoryScaffold: { created: string[]; gitignoreUpdated: boolean; skipped: string[] };
58
73
  npmUpdated: boolean;
59
74
  npmUpdateSkipped: string | null;
60
75
  latestNpmVersion: string | null;
61
76
  npmVersionWarning: string | null;
62
77
  doctorReport: unknown;
78
+ // RFC-1154: reconcile phase results (runs on every upgrade incl. noop).
79
+ skillsPruned: string[];
80
+ skillsKeptWithConsumerFiles: string[];
81
+ prettierignore: PrettierignoreResult;
63
82
  }
64
83
 
65
- function isMonorepoForge(workspaceRoot: string): boolean {
66
- return fs.existsSync(path.join(workspaceRoot, "packages", "forge", "package.json"));
84
+ async function isMonorepoForge(workspaceRoot: string, io: WorkspaceIO): Promise<boolean> {
85
+ return io.exists(path.join(workspaceRoot, "packages", "forge", "package.json"));
67
86
  }
68
87
 
69
- function updateNpmPackage(
88
+ async function updateNpmPackage(
70
89
  workspaceRoot: string,
71
90
  config: ForgeConfig,
72
91
  isDryRun: boolean,
73
- ): { updated: boolean; skipped: string | null } {
74
- if (isMonorepoForge(workspaceRoot)) {
92
+ io: WorkspaceIO,
93
+ ): Promise<{ updated: boolean; skipped: string | null }> {
94
+ if (await isMonorepoForge(workspaceRoot, io)) {
75
95
  return { updated: false, skipped: "monorepo (local package, not npm-installed)" };
76
96
  }
77
97
 
78
98
  const pm = config.project.packageManager;
79
99
  const installCmd = resolvePmInstall(pm);
80
- const fullCmd = `${installCmd} @warpgogol/forge@latest`;
100
+ const installArgs = installCmd.split(/\s+/);
81
101
 
82
102
  if (isDryRun) {
83
103
  return { updated: false, skipped: "dry-run" };
84
104
  }
85
105
 
86
- try {
87
- execSync(fullCmd, {
106
+ const result = await io
107
+ .exec(installArgs[0]!, [...installArgs.slice(1), "@warpgogol/forge@latest"], {
88
108
  cwd: workspaceRoot,
89
- stdio: "pipe",
90
- timeout: 120_000,
91
- });
109
+ timeoutMs: 120_000,
110
+ })
111
+ .catch(() => null);
112
+ if (result && result.exitCode === 0) {
92
113
  return { updated: true, skipped: null };
93
- } catch {
94
- return { updated: false, skipped: "install failed (network error or registry unavailable)" };
95
114
  }
115
+ return { updated: false, skipped: "install failed (network error or registry unavailable)" };
96
116
  }
97
117
 
98
- function readForgePackageVersion(forgeRoot: string): string {
118
+ async function readForgePackageVersion(forgeRoot: string, io: WorkspaceIO): Promise<string> {
99
119
  const pkgPath = path.join(forgeRoot, "package.json");
100
- const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")) as { version?: string };
120
+ const pkg = JSON.parse(await io.readFile(pkgPath)) as { version?: string };
101
121
  return pkg.version ?? "0.0.0-unknown";
102
122
  }
103
123
 
104
- function checkLatestNpmVersion(
124
+ async function checkLatestNpmVersion(
105
125
  workspaceRoot: string,
106
126
  installedVersion: string,
107
127
  isMonorepo: boolean,
108
- ): { latest: string | null; warning: string | null } {
128
+ io: WorkspaceIO,
129
+ ): Promise<{ latest: string | null; warning: string | null }> {
109
130
  if (isMonorepo) {
110
131
  return { latest: null, warning: null };
111
132
  }
112
133
  try {
113
- const latest = execSync("npm view @warpgogol/forge version", {
134
+ const result = await io.exec("npm", ["view", "@warpgogol/forge", "version"], {
114
135
  cwd: workspaceRoot,
115
- stdio: "pipe",
116
- timeout: 15_000,
117
- encoding: "utf8",
118
- }).trim();
136
+ timeoutMs: 15_000,
137
+ });
138
+ if (result.exitCode !== 0) throw new Error(result.stderr || "npm view failed");
139
+ const latest = result.stdout.trim();
119
140
  if (!latest || !/^\d+\.\d+\.\d+/.test(latest)) {
120
141
  return { latest: null, warning: null };
121
142
  }
@@ -139,28 +160,34 @@ function checkLatestNpmVersion(
139
160
  }
140
161
  }
141
162
 
142
- function syncForgeSkills(
163
+ async function syncForgeSkills(
143
164
  workspaceRoot: string,
144
165
  forgeRoot: string,
145
166
  skillsDir: string,
146
167
  dryRun: boolean,
147
- ): { updated: string[]; knowledgeConflicts: string[] } {
168
+ io: WorkspaceIO,
169
+ ): Promise<{ updated: string[]; knowledgeConflicts: string[] }> {
148
170
  const updated: string[] = [];
149
171
  const knowledgeConflicts: string[] = [];
150
172
  const agentsSkillsDir = path.join(workspaceRoot, skillsDir);
151
173
 
152
174
  for (const skill of FORGE_SKILLS) {
153
175
  const srcPath = path.join(forgeRoot, skill.path);
154
- if (!fs.existsSync(srcPath)) continue;
176
+ if (!(await io.exists(srcPath))) continue;
155
177
 
156
178
  const skillName = skill.name;
157
179
  const destDir = path.join(agentsSkillsDir, skillName);
158
180
  const destPath = path.join(destDir, "SKILL.md");
159
181
 
160
182
  if (!dryRun) {
161
- fs.mkdirSync(destDir, { recursive: true });
162
- const content = fs.readFileSync(srcPath, "utf8");
163
- fs.writeFileSync(destPath, content, "utf8");
183
+ await io.mkdir(destDir);
184
+ const content = await io.readFile(srcPath);
185
+ await writeFileIfChanged(destPath, content, io);
186
+
187
+ // RFC-1154: marker manifest lists SKILL.md + knowledge files that carry
188
+ // pure forge content. Merged/skipped knowledge files hold consumer
189
+ // entries — excluded so a stale dir is kept rather than deleted.
190
+ const markerFiles = ["SKILL.md"];
164
191
 
165
192
  // Sync knowledge files (append-only — never overwrite local entries)
166
193
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
@@ -174,11 +201,14 @@ function syncForgeSkills(
174
201
  if (typeof kf !== "string") continue;
175
202
  const kfSrcPath = path.join(skillSrcDir, kf);
176
203
  const kfDestPath = path.join(destDir, kf);
177
- if (fs.existsSync(kfSrcPath)) {
204
+ if (await io.exists(kfSrcPath)) {
178
205
  const syncResult = syncKnowledgeFile(kfSrcPath, kfDestPath);
179
206
  for (const id of syncResult.conflicts) {
180
207
  knowledgeConflicts.push(`${skillName}/${kf}#${id}`);
181
208
  }
209
+ if (syncResult.action === "copied" || syncResult.action === "unchanged") {
210
+ markerFiles.push(kf);
211
+ }
182
212
  }
183
213
  }
184
214
  }
@@ -186,6 +216,7 @@ function syncForgeSkills(
186
216
  // Frontmatter parse error — SKILL-01 will catch in validation
187
217
  }
188
218
  }
219
+ await writeSkillMarker(destDir, markerFiles, io);
189
220
  }
190
221
  updated.push(skillName);
191
222
  }
@@ -193,37 +224,45 @@ function syncForgeSkills(
193
224
  return { updated, knowledgeConflicts };
194
225
  }
195
226
 
196
- function syncSharedKnowledge(
227
+ async function syncSharedKnowledge(
197
228
  workspaceRoot: string,
198
229
  forgeRoot: string,
199
230
  skillsDir: string,
200
231
  dryRun: boolean,
201
- ): { updated: string[]; knowledgeConflicts: string[] } {
232
+ io: WorkspaceIO,
233
+ ): Promise<{ updated: string[]; knowledgeConflicts: string[] }> {
202
234
  const updated: string[] = [];
203
235
  const knowledgeConflicts: string[] = [];
204
236
  const srcPath = path.join(forgeRoot, "skills", "shared", "knowledge", "learned-principles.md");
205
- if (!fs.existsSync(srcPath)) return { updated, knowledgeConflicts };
237
+ if (!(await io.exists(srcPath))) return { updated, knowledgeConflicts };
206
238
 
207
239
  const destDir = path.join(workspaceRoot, skillsDir, "shared-knowledge");
208
240
  const destPath = path.join(destDir, "learned-principles.md");
209
241
 
210
242
  if (!dryRun) {
211
- fs.mkdirSync(destDir, { recursive: true });
243
+ await io.mkdir(destDir);
212
244
  const syncResult = syncKnowledgeFile(srcPath, destPath);
213
245
  for (const id of syncResult.conflicts) {
214
246
  knowledgeConflicts.push(`shared-knowledge/learned-principles.md#${id}`);
215
247
  }
248
+ // RFC-1154: marker lists the file only when its content is purely forge's.
249
+ const markerFiles =
250
+ syncResult.action === "copied" || syncResult.action === "unchanged"
251
+ ? ["learned-principles.md"]
252
+ : [];
253
+ await writeSkillMarker(destDir, markerFiles, io);
216
254
  }
217
255
  updated.push("shared-knowledge");
218
256
  return { updated, knowledgeConflicts };
219
257
  }
220
258
 
221
- function syncPackSkills(
259
+ async function syncPackSkills(
222
260
  workspaceRoot: string,
223
261
  config: ForgeConfig,
224
262
  skillsDir: string,
225
263
  dryRun: boolean,
226
- ): { updated: string[]; skipped: SkippedSkill[]; knowledgeConflicts: string[] } {
264
+ io: WorkspaceIO,
265
+ ): Promise<{ updated: string[]; skipped: SkippedSkill[]; knowledgeConflicts: string[] }> {
227
266
  const updated: string[] = [];
228
267
  const skipped: SkippedSkill[] = [];
229
268
  const knowledgeConflicts: string[] = [];
@@ -238,7 +277,7 @@ function syncPackSkills(
238
277
 
239
278
  for (const skill of packSkills) {
240
279
  const srcPath = path.join(workspaceRoot, skill.dir, skill.path);
241
- if (!fs.existsSync(srcPath)) continue;
280
+ if (!(await io.exists(srcPath))) continue;
242
281
 
243
282
  const skillName = skill.name;
244
283
 
@@ -251,9 +290,13 @@ function syncPackSkills(
251
290
  const destPath = path.join(destDir, "SKILL.md");
252
291
 
253
292
  if (!dryRun) {
254
- fs.mkdirSync(destDir, { recursive: true });
255
- const content = fs.readFileSync(srcPath, "utf8");
256
- fs.writeFileSync(destPath, content, "utf8");
293
+ await io.mkdir(destDir);
294
+ const content = await io.readFile(srcPath);
295
+ await writeFileIfChanged(destPath, content, io);
296
+
297
+ // RFC-1154: same marker rule as forge skills — merged/skipped knowledge
298
+ // files carry consumer entries and are excluded from the manifest.
299
+ const markerFiles = ["SKILL.md"];
257
300
 
258
301
  // Sync knowledge files (append-only — never overwrite local entries)
259
302
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
@@ -267,11 +310,14 @@ function syncPackSkills(
267
310
  if (typeof kf !== "string") continue;
268
311
  const kfSrcPath = path.join(skillSrcDir, kf);
269
312
  const kfDestPath = path.join(destDir, kf);
270
- if (fs.existsSync(kfSrcPath)) {
313
+ if (await io.exists(kfSrcPath)) {
271
314
  const syncResult = syncKnowledgeFile(kfSrcPath, kfDestPath);
272
315
  for (const id of syncResult.conflicts) {
273
316
  knowledgeConflicts.push(`${skillName}/${kf}#${id}`);
274
317
  }
318
+ if (syncResult.action === "copied" || syncResult.action === "unchanged") {
319
+ markerFiles.push(kf);
320
+ }
275
321
  }
276
322
  }
277
323
  }
@@ -279,6 +325,7 @@ function syncPackSkills(
279
325
  // Frontmatter parse error
280
326
  }
281
327
  }
328
+ await writeSkillMarker(destDir, markerFiles, io);
282
329
  }
283
330
  updated.push(skillName);
284
331
  }
@@ -286,6 +333,94 @@ function syncPackSkills(
286
333
  return { updated, skipped, knowledgeConflicts };
287
334
  }
288
335
 
336
+ /**
337
+ * RFC-1154 reconcile phase — runs on every forge upgrade, including the
338
+ * same-version noop path: markers for synced dirs, prune stale marked dirs,
339
+ * managed .prettierignore block. Idempotent; dry-run passes through.
340
+ */
341
+ async function reconcileGeneratedSurface(
342
+ workspaceRoot: string,
343
+ forgeRoot: string,
344
+ config: ForgeConfig,
345
+ dryRun: boolean,
346
+ io: WorkspaceIO,
347
+ ): Promise<{
348
+ updated: string[];
349
+ knowledgeConflicts: string[];
350
+ skipped: SkippedSkill[];
351
+ prune: PruneResult;
352
+ prettierignore: PrettierignoreResult;
353
+ /** Non-fatal reconcile failures — surface as warnings, never block upgrade. */
354
+ warnings: string[];
355
+ }> {
356
+ const forgeSkillsResult = await syncForgeSkills(
357
+ workspaceRoot,
358
+ forgeRoot,
359
+ config.paths.skillsDir,
360
+ dryRun,
361
+ io,
362
+ );
363
+ const packResult = await syncPackSkills(workspaceRoot, config, config.paths.skillsDir, dryRun, io);
364
+ const sharedKnowledgeResult = await syncSharedKnowledge(
365
+ workspaceRoot,
366
+ forgeRoot,
367
+ config.paths.skillsDir,
368
+ dryRun,
369
+ io,
370
+ );
371
+
372
+ const currentSyncSet = new Set([
373
+ ...forgeSkillsResult.updated,
374
+ ...packResult.updated,
375
+ ...sharedKnowledgeResult.updated,
376
+ ]);
377
+
378
+ const warnings: string[] = [];
379
+
380
+ let prune: PruneResult = { pruned: [], keptWithConsumerFiles: [] };
381
+ if (!dryRun) {
382
+ try {
383
+ prune = await pruneStaleSkillDirs(
384
+ path.join(workspaceRoot, config.paths.skillsDir),
385
+ currentSyncSet,
386
+ io,
387
+ );
388
+ } catch (err) {
389
+ warnings.push(`stale skill dir prune failed: ${(err as Error).message}`);
390
+ }
391
+ }
392
+
393
+ let prettierignore: PrettierignoreResult = "skipped";
394
+ if (!dryRun) {
395
+ try {
396
+ prettierignore = await applyPrettierignore(
397
+ workspaceRoot,
398
+ await planPrettierignore(workspaceRoot, config.paths.skillsDir, io),
399
+ io,
400
+ );
401
+ } catch (err) {
402
+ warnings.push(`.prettierignore reconcile failed: ${(err as Error).message}`);
403
+ }
404
+ }
405
+
406
+ return {
407
+ updated: [
408
+ ...forgeSkillsResult.updated,
409
+ ...packResult.updated,
410
+ ...sharedKnowledgeResult.updated,
411
+ ],
412
+ knowledgeConflicts: [
413
+ ...forgeSkillsResult.knowledgeConflicts,
414
+ ...packResult.knowledgeConflicts,
415
+ ...sharedKnowledgeResult.knowledgeConflicts,
416
+ ],
417
+ skipped: packResult.skipped,
418
+ prune,
419
+ prettierignore,
420
+ warnings,
421
+ };
422
+ }
423
+
289
424
  function addMissingBindingDefaults(
290
425
  config: ForgeConfig,
291
426
  dryRun: boolean,
@@ -313,12 +448,13 @@ function addMissingBindingDefaults(
313
448
  return added;
314
449
  }
315
450
 
316
- function updateSyncedVersion(
451
+ async function updateSyncedVersion(
317
452
  workspaceRoot: string,
318
453
  config: ForgeConfig,
319
454
  version: string,
320
455
  dryRun: boolean,
321
- ): void {
456
+ io: WorkspaceIO,
457
+ ): Promise<void> {
322
458
  if (dryRun) return;
323
459
 
324
460
  // Set the syncedVersion on the in-memory config (bindings already updated)
@@ -328,7 +464,7 @@ function updateSyncedVersion(
328
464
  // RFC-1118: serializeForgeConfig writes the declared profile id verbatim —
329
465
  // never the resolved StackProfile object, never drops an unresolvable id.
330
466
  const forgeYamlPath = path.join(workspaceRoot, "forge.yaml");
331
- fs.writeFileSync(forgeYamlPath, stringifyYaml(serializeForgeConfig(config)), "utf8");
467
+ await io.writeFile(forgeYamlPath, stringifyYaml(serializeForgeConfig(config)));
332
468
  }
333
469
 
334
470
  export async function runUpgrade(
@@ -336,12 +472,13 @@ export async function runUpgrade(
336
472
  context: ForgeRuntimeContext,
337
473
  ): Promise<ForgeCommandResult<UpgradeResult>> {
338
474
  const { workspaceRoot, dryRun } = context;
475
+ const fio = resolveIo(context.io);
339
476
  const isDryRun = dryRun || input.flags["dry-run"] === true;
340
477
  const isUpdateNpm = input.flags["update-npm"] === true;
341
478
 
342
479
  // Step 0: Check forge.yaml exists
343
480
  const forgeYamlPath = path.join(workspaceRoot, "forge.yaml");
344
- if (!fs.existsSync(forgeYamlPath)) {
481
+ if (!(await fio.exists(forgeYamlPath))) {
345
482
  return {
346
483
  data: {
347
484
  command: "forge.upgrade",
@@ -352,12 +489,18 @@ export async function runUpgrade(
352
489
  bindingsAdded: [],
353
490
  skippedSkills: [],
354
491
  nestedAgentsGenerated: [],
492
+ nestedAgentsPlanned: [],
493
+ nestedAgentsPreserved: [],
494
+ nestedAgentsSkipped: [],
355
495
  memoryScaffold: { created: [], gitignoreUpdated: false, skipped: [] },
356
496
  npmUpdated: false,
357
497
  npmUpdateSkipped: null,
358
498
  latestNpmVersion: null,
359
499
  npmVersionWarning: null,
360
500
  doctorReport: null,
501
+ skillsPruned: [],
502
+ skillsKeptWithConsumerFiles: [],
503
+ prettierignore: "skipped",
361
504
  },
362
505
  nextSteps: [{ action: "Run 'forge create' to create forge.yaml first", kind: "required" }],
363
506
  exitCode: 1,
@@ -375,7 +518,7 @@ export async function runUpgrade(
375
518
  } catch {
376
519
  configForNpm = { project: { packageManager: "npm" } } as unknown as ForgeConfig;
377
520
  }
378
- const npmResult = updateNpmPackage(workspaceRoot, configForNpm, isDryRun);
521
+ const npmResult = await updateNpmPackage(workspaceRoot, configForNpm, isDryRun, fio);
379
522
  npmUpdated = npmResult.updated;
380
523
  npmUpdateSkipped = npmResult.skipped;
381
524
  }
@@ -385,7 +528,7 @@ export async function runUpgrade(
385
528
  let toVersion: string;
386
529
  try {
387
530
  forgeRoot = context.forgeRoot ?? resolveForgeRoot(workspaceRoot);
388
- toVersion = readForgePackageVersion(forgeRoot);
531
+ toVersion = await readForgePackageVersion(forgeRoot, fio);
389
532
  } catch (err) {
390
533
  return {
391
534
  data: {
@@ -397,12 +540,18 @@ export async function runUpgrade(
397
540
  bindingsAdded: [],
398
541
  skippedSkills: [],
399
542
  nestedAgentsGenerated: [],
543
+ nestedAgentsPlanned: [],
544
+ nestedAgentsPreserved: [],
545
+ nestedAgentsSkipped: [],
400
546
  memoryScaffold: { created: [], gitignoreUpdated: false, skipped: [] },
401
547
  npmUpdated: false,
402
548
  npmUpdateSkipped: null,
403
549
  latestNpmVersion: null,
404
550
  npmVersionWarning: null,
405
551
  doctorReport: null,
552
+ skillsPruned: [],
553
+ skillsKeptWithConsumerFiles: [],
554
+ prettierignore: "skipped",
406
555
  },
407
556
  nextSteps: [
408
557
  { action: "Install @warpgogol/forge first: npm install @warpgogol/forge", kind: "required" },
@@ -413,7 +562,7 @@ export async function runUpgrade(
413
562
  }
414
563
 
415
564
  // Step 1.5: Check npm for newer version (non-fatal)
416
- const npmCheck = checkLatestNpmVersion(workspaceRoot, toVersion, isMonorepoForge(workspaceRoot));
565
+ const npmCheck = await checkLatestNpmVersion(workspaceRoot, toVersion, await isMonorepoForge(workspaceRoot, fio), fio);
417
566
  if (npmCheck.warning && context.outputFormat === "pretty") {
418
567
  context.logger.warn(`⚠ ${npmCheck.warning}`);
419
568
  }
@@ -433,12 +582,18 @@ export async function runUpgrade(
433
582
  bindingsAdded: [],
434
583
  skippedSkills: [],
435
584
  nestedAgentsGenerated: [],
585
+ nestedAgentsPlanned: [],
586
+ nestedAgentsPreserved: [],
587
+ nestedAgentsSkipped: [],
436
588
  memoryScaffold: { created: [], gitignoreUpdated: false, skipped: [] },
437
589
  npmUpdated: false,
438
590
  npmUpdateSkipped: null,
439
591
  latestNpmVersion: npmCheck.latest,
440
592
  npmVersionWarning: npmCheck.warning,
441
593
  doctorReport: null,
594
+ skillsPruned: [],
595
+ skillsKeptWithConsumerFiles: [],
596
+ prettierignore: "skipped",
442
597
  },
443
598
  nextSteps: [
444
599
  { action: `Fix forge.yaml: ${(err as Error).message}`, kind: "required" },
@@ -451,23 +606,43 @@ export async function runUpgrade(
451
606
  const fromVersion = config.forge?.syncedVersion ?? null;
452
607
 
453
608
  if (fromVersion === toVersion) {
454
- // Noop — versions match
609
+ // Noop version-wise — but the RFC-1154 reconcile (markers, stale-dir prune,
610
+ // managed .prettierignore) still runs so a same-version upgrade fixes lint debt.
611
+ const reconcile = await reconcileGeneratedSurface(
612
+ workspaceRoot,
613
+ forgeRoot,
614
+ config,
615
+ isDryRun,
616
+ fio,
617
+ );
618
+ for (const kept of reconcile.prune.keptWithConsumerFiles) {
619
+ context.logger.warn(`forge.upgrade: kept stale skill dir ${kept}`);
620
+ }
621
+ for (const warn of reconcile.warnings) {
622
+ context.logger.warn(`forge.upgrade: ${warn}`);
623
+ }
455
624
  return {
456
625
  data: {
457
626
  command: "forge.upgrade",
458
627
  status: "noop",
459
628
  fromVersion,
460
629
  toVersion,
461
- skillsUpdated: [],
630
+ skillsUpdated: reconcile.updated,
462
631
  bindingsAdded: [],
463
- skippedSkills: [],
632
+ skippedSkills: reconcile.skipped,
464
633
  nestedAgentsGenerated: [],
634
+ nestedAgentsPlanned: [],
635
+ nestedAgentsPreserved: [],
636
+ nestedAgentsSkipped: [],
465
637
  memoryScaffold: { created: [], gitignoreUpdated: false, skipped: [] },
466
638
  npmUpdated: npmUpdated,
467
639
  npmUpdateSkipped: npmUpdateSkipped,
468
640
  latestNpmVersion: npmCheck.latest,
469
641
  npmVersionWarning: npmCheck.warning,
470
642
  doctorReport: null,
643
+ skillsPruned: reconcile.prune.pruned,
644
+ skillsKeptWithConsumerFiles: reconcile.prune.keptWithConsumerFiles,
645
+ prettierignore: reconcile.prettierignore,
471
646
  },
472
647
  nextSteps: npmCheck.warning
473
648
  ? [{ action: npmCheck.warning, kind: "optional" }]
@@ -479,40 +654,26 @@ export async function runUpgrade(
479
654
  };
480
655
  }
481
656
 
482
- // Step 3: Sync forge skills
483
- const forgeSkillsResult = syncForgeSkills(
657
+ // Step 3: RFC-1154 reconcile — sync forge+pack skills + shared knowledge,
658
+ // write .forge-managed markers, prune stale marked dirs, reconcile the
659
+ // managed .prettierignore block.
660
+ const reconcile = await reconcileGeneratedSurface(
484
661
  workspaceRoot,
485
662
  forgeRoot,
486
- config.paths.skillsDir,
487
- isDryRun,
488
- );
489
-
490
- // Step 3b: Sync pack skills
491
- const packResult = syncPackSkills(
492
- workspaceRoot,
493
663
  config,
494
- config.paths.skillsDir,
495
664
  isDryRun,
665
+ fio,
496
666
  );
497
667
 
498
- // Step 3c: Sync shared knowledge layer (RFC-0663)
499
- const sharedKnowledgeResult = syncSharedKnowledge(
500
- workspaceRoot,
501
- forgeRoot,
502
- config.paths.skillsDir,
503
- isDryRun,
504
- );
505
-
506
- const skillsUpdated = [
507
- ...forgeSkillsResult.updated,
508
- ...packResult.updated,
509
- ...sharedKnowledgeResult.updated,
510
- ];
511
- const knowledgeConflicts = [
512
- ...forgeSkillsResult.knowledgeConflicts,
513
- ...packResult.knowledgeConflicts,
514
- ...sharedKnowledgeResult.knowledgeConflicts,
515
- ];
668
+ const skillsUpdated = reconcile.updated;
669
+ const knowledgeConflicts = reconcile.knowledgeConflicts;
670
+ const packSkipped = reconcile.skipped;
671
+ for (const kept of reconcile.prune.keptWithConsumerFiles) {
672
+ context.logger.warn(`forge.upgrade: kept stale skill dir ${kept}`);
673
+ }
674
+ for (const warn of reconcile.warnings) {
675
+ context.logger.warn(`forge.upgrade: ${warn}`);
676
+ }
516
677
  if (knowledgeConflicts.length > 0) {
517
678
  context.logger.warn(
518
679
  `forge.upgrade: ${knowledgeConflicts.length} knowledge entr${knowledgeConflicts.length === 1 ? "y" : "ies"} ` +
@@ -528,18 +689,47 @@ export async function runUpgrade(
528
689
 
529
690
  // Step 5: Update forge.syncedVersion
530
691
  if (!isDryRun) {
531
- updateSyncedVersion(workspaceRoot, config, toVersion, false);
692
+ await updateSyncedVersion(workspaceRoot, config, toVersion, false, fio);
532
693
  }
533
694
 
534
- // Step 5b: Generate nested AGENTS.md (RFC-0611)
695
+ // Step 5b: Generate nested AGENTS.md (RFC-0611). RFC-1153: the write path
696
+ // merges at the forge:custom boundary, and --dry-run previews which guides
697
+ // would be rewritten vs. skipped (the destructive step is no longer silent).
535
698
  let nestedAgentsGenerated: string[] = [];
536
- if (!isDryRun) {
537
- try {
538
- const nestedResult = await generateNestedAgentsMd(workspaceRoot, config, false);
699
+ const nestedAgentsPlanned: string[] = [];
700
+ let nestedAgentsPreserved: string[] = [];
701
+ const nestedAgentsSkipped: string[] = [];
702
+ try {
703
+ const nestedResult = await generateNestedAgentsMd(workspaceRoot, config, isDryRun, undefined, fio);
704
+ if (isDryRun) {
705
+ for (const [relPath, rendered] of Object.entries(nestedResult.renderedFiles)) {
706
+ const absPath = path.join(workspaceRoot, relPath);
707
+ let existing: string | null = null;
708
+ if (await fio.exists(absPath)) {
709
+ try {
710
+ existing = await fio.readFile(absPath);
711
+ } catch {
712
+ // Unreadable — treat as absent; merge will emit a fresh file.
713
+ }
714
+ }
715
+ if (existing !== null && !hasGeneratedMarker(existing)) {
716
+ nestedAgentsSkipped.push(`${relPath} (hand-written)`);
717
+ continue;
718
+ }
719
+ const merged = mergeEditableGenerated(rendered, existing, relPath);
720
+ if (merged === null) {
721
+ nestedAgentsSkipped.push(`${relPath} (unmapped-customization)`);
722
+ } else if (merged !== existing) {
723
+ nestedAgentsPlanned.push(relPath);
724
+ }
725
+ }
726
+ } else {
539
727
  nestedAgentsGenerated = nestedResult.generated;
540
- } catch {
541
- // Nested generation failure is non-fatal
728
+ nestedAgentsPreserved = nestedResult.preserved;
729
+ nestedAgentsSkipped.push(...nestedResult.skipped);
542
730
  }
731
+ } catch {
732
+ // Nested generation failure is non-fatal
543
733
  }
544
734
 
545
735
  // Step 6: Run doctor
@@ -578,19 +768,25 @@ export async function runUpgrade(
578
768
  toVersion,
579
769
  skillsUpdated,
580
770
  bindingsAdded,
581
- skippedSkills: packResult.skipped,
771
+ skippedSkills: packSkipped,
582
772
  nestedAgentsGenerated,
773
+ nestedAgentsPlanned,
774
+ nestedAgentsPreserved,
775
+ nestedAgentsSkipped,
583
776
  memoryScaffold,
584
777
  npmUpdated,
585
778
  npmUpdateSkipped,
586
779
  latestNpmVersion: npmCheck.latest,
587
780
  npmVersionWarning: npmCheck.warning,
588
781
  doctorReport,
782
+ skillsPruned: reconcile.prune.pruned,
783
+ skillsKeptWithConsumerFiles: reconcile.prune.keptWithConsumerFiles,
784
+ prettierignore: reconcile.prettierignore,
589
785
  },
590
786
  nextSteps,
591
787
  exitCode: 0,
592
788
  summary: isDryRun
593
- ? `[dry-run] forge.upgrade: would sync ${skillsUpdated.length} skill(s), add ${bindingsAdded.length} binding(s), update syncedVersion to ${toVersion}`
789
+ ? `[dry-run] forge.upgrade: would sync ${skillsUpdated.length} skill(s), add ${bindingsAdded.length} binding(s), update syncedVersion to ${toVersion}, rewrite ${nestedAgentsPlanned.length} nested AGENTS.md file(s)`
594
790
  : npmCheck.warning
595
791
  ? `[forge.upgrade] OK — ${skillsUpdated.length} skill(s) synced, ${bindingsAdded.length} binding(s) added, syncedVersion → ${toVersion} — ⚠ ${npmCheck.warning}`
596
792
  : `[forge.upgrade] OK — ${skillsUpdated.length} skill(s) synced, ${bindingsAdded.length} binding(s) added, syncedVersion → ${toVersion}` +