@warpgogol/forge 2.8.0 → 2.8.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/AGENTS.md +1 -1
  2. package/README.md +23 -13
  3. package/README.uk.md +19 -12
  4. package/dist/os/session/handlers/validate.d.ts.map +1 -1
  5. package/dist/os/session/handlers/validate.js +23 -1
  6. package/dist/os/session/handlers/validate.js.map +1 -1
  7. package/dist/os/session/types.d.ts +31 -1
  8. package/dist/os/session/types.d.ts.map +1 -1
  9. package/dist/os/session/types.js +7 -0
  10. package/dist/os/session/types.js.map +1 -1
  11. package/os/adr/adr.module.ts +158 -0
  12. package/os/adr/frontmatter-io.ts +80 -0
  13. package/os/adr/handlers/archive.ts +244 -0
  14. package/os/adr/handlers/implement-stamp.ts +331 -0
  15. package/os/adr/handlers/list-create.ts +207 -0
  16. package/os/adr/handlers/validate.test.ts +232 -0
  17. package/os/adr/handlers/validate.ts +429 -0
  18. package/os/adr/index.ts +33 -0
  19. package/os/adr/types.ts +141 -0
  20. package/os/audit/audit.module.ts +48 -0
  21. package/os/audit/frontmatter-io.ts +84 -0
  22. package/os/audit/handlers/archive.ts +239 -0
  23. package/os/audit/index.ts +23 -0
  24. package/os/audit/types.ts +29 -0
  25. package/os/compass/compass.module.ts +186 -0
  26. package/os/compass/handlers/compass-audit-handler.ts +382 -0
  27. package/os/compass/handlers/compass-change-summary-handler.ts +272 -0
  28. package/os/compass/handlers/compass-inventory-handler.ts +294 -0
  29. package/os/compass/handlers/compass-inventory.ts +521 -0
  30. package/os/compass/handlers/git-revision.ts +137 -0
  31. package/os/compass/handlers/resolve-scan-root-workpiece.test.ts +65 -0
  32. package/os/compass/handlers/resolve-scan-root.ts +79 -0
  33. package/os/compass/index.ts +33 -0
  34. package/os/core/core.module.ts +948 -0
  35. package/os/core/handlers/assets-check.ts +122 -0
  36. package/os/core/handlers/assets-helpers.ts +174 -0
  37. package/os/core/handlers/assets-list.ts +99 -0
  38. package/os/core/handlers/build.ts +155 -0
  39. package/os/core/handlers/determinism-check.ts +354 -0
  40. package/os/core/handlers/dev.ts +127 -0
  41. package/os/core/handlers/invariant-engine.test.ts +681 -0
  42. package/os/core/handlers/knowledge-compact.ts +248 -0
  43. package/os/core/handlers/lifecycle-handlers.test.ts +524 -0
  44. package/os/core/handlers/note-frontmatter-validate.test.ts +93 -0
  45. package/os/core/handlers/note-link-validate.test.ts +117 -0
  46. package/os/core/handlers/note-orphan-detect.test.ts +94 -0
  47. package/os/core/handlers/package-health.test.ts +226 -0
  48. package/os/core/handlers/package-health.ts +231 -0
  49. package/os/core/handlers/pinned-check.ts +200 -0
  50. package/os/core/handlers/pinned-init.ts +333 -0
  51. package/os/core/handlers/pinned-types.ts +50 -0
  52. package/os/core/handlers/pinned-validate.ts +301 -0
  53. package/os/core/handlers/profile-resolve.ts +92 -0
  54. package/os/core/handlers/release-prepare.ts +294 -0
  55. package/os/core/handlers/release-publish.ts +211 -0
  56. package/os/core/handlers/validate.ts +262 -0
  57. package/os/core/handlers/workspace-deps.ts +55 -0
  58. package/os/core/index.ts +13 -0
  59. package/os/exploration/exploration.module.ts +90 -0
  60. package/os/exploration/frontmatter-io.ts +77 -0
  61. package/os/exploration/handlers/archive.ts +159 -0
  62. package/os/exploration/handlers/list.ts +77 -0
  63. package/os/exploration/handlers/show.ts +107 -0
  64. package/os/exploration/index.ts +25 -0
  65. package/os/exploration/types.ts +65 -0
  66. package/os/mission/handlers/archive.test.ts +391 -0
  67. package/os/mission/handlers/archive.ts +453 -0
  68. package/os/mission/index.ts +1 -0
  69. package/os/mission/mission.module.ts +48 -0
  70. package/os/mission/types.ts +41 -0
  71. package/os/naming/index.ts +14 -0
  72. package/os/naming/naming-convention.test.ts +163 -0
  73. package/os/naming/naming-convention.ts +378 -0
  74. package/os/naming/naming.module.ts +36 -0
  75. package/os/notes/index.ts +10 -0
  76. package/os/notes/notes.module.ts +109 -0
  77. package/os/plan/frontmatter-io.ts +82 -0
  78. package/os/plan/handlers/archive.ts +238 -0
  79. package/os/plan/index.ts +23 -0
  80. package/os/plan/plan.module.ts +48 -0
  81. package/os/plan/types.ts +29 -0
  82. package/os/program/discovery.ts +294 -0
  83. package/os/program/handlers/complete.ts +419 -0
  84. package/os/program/handlers/lease.ts +433 -0
  85. package/os/program/handlers/seal.ts +332 -0
  86. package/os/program/handlers/validate.ts +213 -0
  87. package/os/program/lease.ts +156 -0
  88. package/os/program/program.module.ts +212 -0
  89. package/os/program/schemas.ts +218 -0
  90. package/os/program/state.ts +264 -0
  91. package/os/rfc/acceptance.ts +344 -0
  92. package/os/rfc/decision-log.ts +310 -0
  93. package/os/rfc/dna-trace.ts +280 -0
  94. package/os/rfc/frontmatter-io.test.ts +132 -0
  95. package/os/rfc/frontmatter-io.ts +130 -0
  96. package/os/rfc/handlers/archive.ts +300 -0
  97. package/os/rfc/handlers/check.ts +238 -0
  98. package/os/rfc/handlers/implement-stamp.ts +515 -0
  99. package/os/rfc/handlers/index-graph.ts +237 -0
  100. package/os/rfc/handlers/lifecycle.test.ts +127 -0
  101. package/os/rfc/handlers/lifecycle.ts +249 -0
  102. package/os/rfc/handlers/list-create.ts +296 -0
  103. package/os/rfc/handlers/pipeline-status.ts +173 -0
  104. package/os/rfc/handlers/shared.ts +121 -0
  105. package/os/rfc/handlers/supersede-propose.ts +241 -0
  106. package/os/rfc/handlers/validate-rules.test.ts +891 -0
  107. package/os/rfc/handlers/validate-rules.ts +984 -0
  108. package/os/rfc/handlers/validate.ts +166 -0
  109. package/os/rfc/handlers.ts +19 -0
  110. package/os/rfc/index.ts +97 -0
  111. package/os/rfc/rfc.module.ts +416 -0
  112. package/os/rfc/types.ts +596 -0
  113. package/os/rfc/verification-evidence.ts +244 -0
  114. package/os/session/atif-parser.ts +166 -0
  115. package/os/session/frontmatter-io.ts +163 -0
  116. package/os/session/handlers/archive.ts +288 -0
  117. package/os/session/handlers/list.ts +174 -0
  118. package/os/session/handlers/save.ts +348 -0
  119. package/os/session/handlers/validate.ts +250 -0
  120. package/os/session/index.ts +50 -0
  121. package/os/session/session.module.ts +126 -0
  122. package/os/session/types.ts +203 -0
  123. package/os/spec/live-spec-list-show-validate.test.ts +257 -0
  124. package/os/spec/live-spec-list.ts +78 -0
  125. package/os/spec/live-spec-merge.test.ts +261 -0
  126. package/os/spec/live-spec-merge.ts +413 -0
  127. package/os/spec/live-spec-show.ts +117 -0
  128. package/os/spec/live-spec-types.ts +93 -0
  129. package/os/spec/live-spec-validate.ts +170 -0
  130. package/os/spec/spec-materialize.ts +390 -0
  131. package/os/spec/spec-schema.ts +174 -0
  132. package/os/spec/spec-status.ts +284 -0
  133. package/os/spec/spec-validate.ts +513 -0
  134. package/os/spec/spec.module.ts +135 -0
  135. package/os/werkstatt/handlers/lock.ts +168 -0
  136. package/os/werkstatt/handlers/schema.ts +47 -0
  137. package/os/werkstatt/handlers/werkstatt-lock-recover.ts +175 -0
  138. package/os/werkstatt/handlers/werkstatt-lock-status.ts +73 -0
  139. package/os/werkstatt/handlers/werkstatt-operation-validate.ts +104 -0
  140. package/os/werkstatt/index.ts +39 -0
  141. package/os/werkstatt/werkstatt.module.ts +62 -0
  142. package/os/workflow/handlers.ts +304 -0
  143. package/os/workflow/index.ts +28 -0
  144. package/os/workflow/types.ts +99 -0
  145. package/os/workflow/workflow.module.ts +54 -0
  146. package/package.json +3 -3
  147. package/profiles/godot-csharp.yaml +4 -4
  148. package/profiles/knowledge-typescript-turborepo.yaml +110 -0
  149. package/profiles/phaser-turborepo.yaml +5 -5
  150. package/skills/_shared/fo-session-summary.md +132 -16
  151. package/skills/fo/fo-add-tests/pbt-guide.md +13 -13
  152. package/skills/fo/fo-doc-audit/SKILL.md +6 -6
  153. package/skills/fo/fo-handoff/SKILL.md +5 -0
  154. package/skills/fo/fo-idea/SKILL.md +1 -1
  155. package/skills/fo/fo-review/SKILL.md +1 -1
  156. package/skills/fo/fo-session-retro/SKILL.md +8 -0
  157. package/skills/fo/fo-session-save/SKILL.md +8 -0
  158. package/src/cli-output.ts +59 -0
  159. package/src/config/__tests__/resolve-terminology.test.ts +45 -0
  160. package/src/config/forge-config.ts +524 -0
  161. package/src/forge-module.ts +39 -0
  162. package/src/index.ts +172 -0
  163. package/src/knowledge/__tests__/promote.test.ts +219 -0
  164. package/src/knowledge/__tests__/serialize.test.ts +192 -0
  165. package/src/knowledge/budgets.ts +202 -0
  166. package/src/knowledge/compact.ts +405 -0
  167. package/src/knowledge/index.ts +53 -0
  168. package/src/knowledge/parse.ts +227 -0
  169. package/src/knowledge/promote.ts +156 -0
  170. package/src/knowledge/schema.ts +112 -0
  171. package/src/knowledge/serialize.ts +72 -0
  172. package/src/migration-adapters/__tests__/ignored-files.test.ts +51 -0
  173. package/src/migration-adapters/__tests__/registry.test.ts +131 -0
  174. package/src/migration-adapters/__tests__/types.test.ts +86 -0
  175. package/src/migration-adapters/git-utils.ts +71 -0
  176. package/src/migration-adapters/ignored-files.ts +221 -0
  177. package/src/migration-adapters/index.ts +21 -0
  178. package/src/migration-adapters/node-typescript-pnpm/index.ts +189 -0
  179. package/src/migration-adapters/phaser-pnpm/index.ts +189 -0
  180. package/src/migration-adapters/registry.ts +61 -0
  181. package/src/migration-adapters/types.ts +68 -0
  182. package/src/onboarding/__tests__/workspace-discovery.test.ts +134 -0
  183. package/src/onboarding/agents-generate.ts +443 -0
  184. package/src/onboarding/create.ts +427 -0
  185. package/src/onboarding/doctor.ts +1297 -0
  186. package/src/onboarding/init.ts +337 -0
  187. package/src/onboarding/invariant-engine.ts +448 -0
  188. package/src/onboarding/memory-scaffold.ts +189 -0
  189. package/src/onboarding/nested-agents-generate.ts +93 -0
  190. package/src/onboarding/nested-agents-templates.ts +233 -0
  191. package/src/onboarding/profile-validate.ts +139 -0
  192. package/src/onboarding/scaffold-project.ts +299 -0
  193. package/src/onboarding/scaffold.ts +143 -0
  194. package/src/onboarding/upgrade.ts +558 -0
  195. package/src/onboarding/workspace-discovery.ts +167 -0
  196. package/src/profiles/__tests__/profile-schema.test.ts +228 -0
  197. package/src/profiles/__tests__/stack-profile.test.ts +190 -0
  198. package/src/profiles/__tests__/terminology-utils.test.ts +71 -0
  199. package/src/profiles/profile-schema.ts +350 -0
  200. package/src/profiles/stack-profile.ts +188 -0
  201. package/src/profiles/terminology-utils.ts +36 -0
  202. package/src/registry.ts +185 -0
  203. package/src/skill-schema.ts +37 -0
  204. package/src/tests/acceptance-criteria.test.ts +99 -0
  205. package/src/tests/adr-implement-stamp.test.ts +368 -0
  206. package/src/tests/agents-generate-domain.test.ts +317 -0
  207. package/src/tests/agents-generate.test.ts +458 -0
  208. package/src/tests/bindings-schema.test.ts +314 -0
  209. package/src/tests/budgets.test.ts +213 -0
  210. package/src/tests/cli-output.test.ts +196 -0
  211. package/src/tests/compact.test.ts +796 -0
  212. package/src/tests/create.test.ts +289 -0
  213. package/src/tests/doctor-autonomy.test.ts +98 -0
  214. package/src/tests/doctor-bindings.test.ts +286 -0
  215. package/src/tests/doctor-domain.test.ts +113 -0
  216. package/src/tests/exploration-handlers.test.ts +256 -0
  217. package/src/tests/fixtures/agents-generate-business-before.txt +288 -0
  218. package/src/tests/forge-config.test.ts +420 -0
  219. package/src/tests/fs-atomic.test.ts +96 -0
  220. package/src/tests/fs.test.ts +103 -0
  221. package/src/tests/generated-marker.test.ts +161 -0
  222. package/src/tests/hash.test.ts +37 -0
  223. package/src/tests/implement-stamp.test.ts +505 -0
  224. package/src/tests/init-bindings.test.ts +183 -0
  225. package/src/tests/knowledge-parse.test.ts +307 -0
  226. package/src/tests/knowledge-pbt.test.ts +164 -0
  227. package/src/tests/memory-scaffold.test.ts +192 -0
  228. package/src/tests/migration-adapters.test.ts +480 -0
  229. package/src/tests/package-files.test.ts +54 -0
  230. package/src/tests/pinned-check.test.ts +194 -0
  231. package/src/tests/pinned-init.test.ts +126 -0
  232. package/src/tests/profile-schema.test.ts +194 -0
  233. package/src/tests/profile-validate.test.ts +120 -0
  234. package/src/tests/program-lease.test.ts +245 -0
  235. package/src/tests/program-paths-extended.test.ts +117 -0
  236. package/src/tests/program-paths.test.ts +98 -0
  237. package/src/tests/program-property.test.ts +139 -0
  238. package/src/tests/program-schemas.test.ts +445 -0
  239. package/src/tests/program-spec-node.test.ts +250 -0
  240. package/src/tests/promote.test.ts +481 -0
  241. package/src/tests/registry.test.ts +73 -0
  242. package/src/tests/scaffold-project.test.ts +150 -0
  243. package/src/tests/session-handlers.test.ts +503 -0
  244. package/src/tests/session-pbt.test.ts +190 -0
  245. package/src/tests/skill-schema.test.ts +146 -0
  246. package/src/tests/skill-validate-knowledge.test.ts +26 -0
  247. package/src/tests/skill-validate.test.ts +254 -0
  248. package/src/tests/stack-profile.test.ts +221 -0
  249. package/src/tests/string-utils.test.ts +42 -0
  250. package/src/tests/upgrade.test.ts +428 -0
  251. package/src/tests/werkstatt-lock.test.ts +343 -0
  252. package/src/tests/workspace-discovery-domain.test.ts +112 -0
  253. package/src/tests/workspace-discovery.test.ts +135 -0
  254. package/src/types.ts +221 -0
  255. package/src/utils/__tests__/fs-idempotent.test.ts +73 -0
  256. package/src/utils/__tests__/fs-trash.test.ts +45 -0
  257. package/src/utils/fs-atomic.ts +93 -0
  258. package/src/utils/fs-idempotent.ts +41 -0
  259. package/src/utils/fs-trash-sync.ts +37 -0
  260. package/src/utils/fs-trash.ts +24 -0
  261. package/src/utils/fs.ts +74 -0
  262. package/src/utils/generated-marker.ts +166 -0
  263. package/src/utils/hash.ts +19 -0
  264. package/src/utils/index.ts +29 -0
  265. package/src/utils/string-utils.ts +19 -0
  266. package/src/validators/__tests__/note-orphan-detect.test.ts +94 -0
  267. package/src/validators/note-frontmatter-validate.ts +142 -0
  268. package/src/validators/note-link-validate.ts +145 -0
  269. package/src/validators/note-orphan-detect.ts +146 -0
  270. package/src/validators/port-validate.ts +97 -0
  271. package/src/validators/skill-validate.ts +831 -0
@@ -0,0 +1,248 @@
1
+ /*
2
+ <MODULE_CONTRACT>
3
+ <purpose>Command handler for forge.skill.knowledge.compact — wraps pure compaction functions in the kernel command contract (RFC-0662).</purpose>
4
+ <non-goals>
5
+ <item>Do not implement planning logic — that lives in src/knowledge/compact.ts.</item>
6
+ <item>Do not import from @warpgogol/* — os/core may use dynamic imports but this handler stays forge-internal.</item>
7
+ </non-goals>
8
+ </MODULE_CONTRACT>
9
+ <CHANGE_SUMMARY>
10
+ <item>RFC-0662: initial knowledge compaction command handler.</item>
11
+ </CHANGE_SUMMARY>
12
+ */
13
+
14
+ import fs from "node:fs";
15
+ import path from "node:path";
16
+ import type {
17
+ ForgeCommandInput,
18
+ ForgeCommandResult,
19
+ ForgeRuntimeContext,
20
+ } from "../../../src/types.ts";
21
+ import { FORGE_SKILLS, discoverPackSkills } from "../../../src/registry.ts";
22
+ import { loadForgeConfig } from "../../../src/config/forge-config.ts";
23
+ import { resolveForgeRoot } from "../../../src/config/forge-config.ts";
24
+ import {
25
+ planCompaction,
26
+ executeCompaction,
27
+ resolveRetentionDays,
28
+ resolveStaleDays,
29
+ type CompactReport,
30
+ } from "../../../src/knowledge/compact.ts";
31
+ import { parseKnowledgeFile } from "../../../src/knowledge/parse.ts";
32
+ import type { ParsedKnowledgeFile } from "../../../src/knowledge/schema.ts";
33
+
34
+ interface SkillKnowledgeFiles {
35
+ skill: string;
36
+ files: string[];
37
+ }
38
+
39
+ function discoverSkillKnowledgeFiles(
40
+ workspaceRoot: string,
41
+ skillName: string | undefined,
42
+ all: boolean,
43
+ ): SkillKnowledgeFiles[] {
44
+ const result: SkillKnowledgeFiles[] = [];
45
+ const config = loadForgeConfig(workspaceRoot);
46
+
47
+ // Resolve forge root
48
+ let forgeRoot: string;
49
+ try {
50
+ forgeRoot = resolveForgeRoot(workspaceRoot);
51
+ } catch {
52
+ return result;
53
+ }
54
+
55
+ // Collect forge skills. skill.path is relative to forgeRoot and already
56
+ // includes the "skills/" prefix (see init.ts / skill-validate.ts).
57
+ for (const skill of FORGE_SKILLS) {
58
+ if (skillName && skill.name !== skillName) continue;
59
+ if (!skill.knowledge || skill.knowledge.length === 0) continue;
60
+
61
+ const skillDir = path.dirname(path.join(forgeRoot, skill.path));
62
+ const files = skill.knowledge
63
+ .map((f) => path.join(skillDir, f))
64
+ .filter((f) => {
65
+ try {
66
+ return fs.existsSync(f);
67
+ } catch {
68
+ return false;
69
+ }
70
+ });
71
+
72
+ if (files.length > 0) {
73
+ result.push({ skill: skill.name, files });
74
+ }
75
+ }
76
+
77
+ // Collect pack skills if --all or if --skill matches a pack skill
78
+ if (all || (skillName && !result.some((r) => r.skill === skillName))) {
79
+ const packSkills = discoverPackSkills(workspaceRoot, config);
80
+ for (const skill of packSkills) {
81
+ if (skillName && skill.name !== skillName) continue;
82
+ if (!skill.knowledge || skill.knowledge.length === 0) continue;
83
+
84
+ const packDir = path.resolve(workspaceRoot, skill.dir);
85
+ const skillDir = path.dirname(path.join(packDir, skill.path));
86
+ const files = skill.knowledge
87
+ .map((f) => path.join(skillDir, f))
88
+ .filter((f) => {
89
+ try {
90
+ return fs.existsSync(f);
91
+ } catch {
92
+ return false;
93
+ }
94
+ });
95
+
96
+ if (files.length > 0) {
97
+ result.push({ skill: skill.name, files });
98
+ }
99
+ }
100
+ }
101
+
102
+ return result;
103
+ }
104
+
105
+ export async function runKnowledgeCompact(
106
+ input: ForgeCommandInput,
107
+ context: ForgeRuntimeContext,
108
+ ): Promise<ForgeCommandResult<CompactReport>> {
109
+ const { flags } = input;
110
+ const skillName = typeof flags["skill"] === "string" ? flags["skill"] : undefined;
111
+ // --all is consumed by the site-kernel CLI (consumeCommonFlags) before the
112
+ // command runs, so the command-level switch is --all-skills.
113
+ const all = flags["all-skills"] === true;
114
+ const dryRun = context.dryRun || flags["dry-run"] === true;
115
+ const json = flags["json"] === true;
116
+
117
+ // Validate exactly one of --skill / --all
118
+ if (!skillName && !all) {
119
+ return {
120
+ data: {
121
+ command: "forge.skill.knowledge.compact",
122
+ status: "fail",
123
+ dryRun,
124
+ files: [],
125
+ totals: { archived: 0, markedStale: 0, legacyFiles: 0 },
126
+ errors: ["Exactly one of --skill <name> or --all-skills is required."],
127
+ },
128
+ exitCode: 1,
129
+ summary: "Error: --skill or --all-skills is required.",
130
+ };
131
+ }
132
+
133
+ if (skillName && all) {
134
+ return {
135
+ data: {
136
+ command: "forge.skill.knowledge.compact",
137
+ status: "fail",
138
+ dryRun,
139
+ files: [],
140
+ totals: { archived: 0, markedStale: 0, legacyFiles: 0 },
141
+ errors: ["--skill and --all-skills are mutually exclusive."],
142
+ },
143
+ exitCode: 1,
144
+ summary: "Error: --skill and --all-skills are mutually exclusive.",
145
+ };
146
+ }
147
+
148
+ // Resolve retention and stale days
149
+ const flagRetention =
150
+ typeof flags["retention-days"] === "string" ? Number(flags["retention-days"]) : undefined;
151
+ const flagStale =
152
+ typeof flags["stale-days"] === "string" ? Number(flags["stale-days"]) : undefined;
153
+
154
+ const retentionDays =
155
+ flagRetention !== undefined && flagRetention > 0 && Number.isInteger(flagRetention)
156
+ ? flagRetention
157
+ : resolveRetentionDays(context.workspaceRoot);
158
+ const staleDays =
159
+ flagStale !== undefined && flagStale > 0 && Number.isInteger(flagStale)
160
+ ? flagStale
161
+ : resolveStaleDays(context.workspaceRoot);
162
+
163
+ // Discover target skills and their knowledge files
164
+ const skillFiles = discoverSkillKnowledgeFiles(context.workspaceRoot, skillName, all);
165
+
166
+ if (skillFiles.length === 0) {
167
+ const summary = skillName
168
+ ? `Skill "${skillName}" has no knowledge files to compact.`
169
+ : "No skills with knowledge files found.";
170
+ return {
171
+ data: {
172
+ command: "forge.skill.knowledge.compact",
173
+ status: "pass",
174
+ dryRun,
175
+ files: [],
176
+ totals: { archived: 0, markedStale: 0, legacyFiles: 0 },
177
+ },
178
+ exitCode: 0,
179
+ summary,
180
+ };
181
+ }
182
+
183
+ // Parse all knowledge files
184
+ const today = new Date().toISOString().slice(0, 10);
185
+ const allParsed: ParsedKnowledgeFile[] = [];
186
+ const fileToSkill = new Map<string, string>();
187
+
188
+ for (const { skill, files } of skillFiles) {
189
+ for (const file of files) {
190
+ const parsed = parseKnowledgeFile(file);
191
+ allParsed.push(parsed);
192
+ fileToSkill.set(file, skill);
193
+ }
194
+ }
195
+
196
+ // Plan compaction
197
+ const plans = planCompaction(allParsed, { retentionDays, staleDays, today });
198
+
199
+ // Execute compaction
200
+ const report = executeCompaction(plans, dryRun);
201
+
202
+ // Group by skill for output
203
+ const skillsOutput = skillFiles.map(({ skill, files }) => {
204
+ const skillFileResults = report.files.filter((r) => files.includes(r.file));
205
+ return {
206
+ skill,
207
+ files: skillFileResults.map((r) => ({
208
+ file: path.basename(r.file),
209
+ actions: r.actions,
210
+ legacySectionCount: r.legacySectionCount,
211
+ })),
212
+ };
213
+ });
214
+
215
+ const groupedReport = {
216
+ ...report,
217
+ skills: skillsOutput,
218
+ };
219
+
220
+ const exitCode = report.status === "pass" ? 0 : 1;
221
+ const summary = dryRun
222
+ ? `[dry-run] forge.skill.knowledge.compact: ${report.totals.archived} would archive, ${report.totals.markedStale} would mark stale, ${report.totals.legacyFiles} legacy file(s)`
223
+ : `forge.skill.knowledge.compact: ${report.totals.archived} archived, ${report.totals.markedStale} marked stale, ${report.totals.legacyFiles} legacy file(s)`;
224
+
225
+ if (context.outputFormat === "pretty" && !json) {
226
+ context.logger.section("Knowledge Compaction");
227
+ for (const skill of skillsOutput) {
228
+ context.logger.info(`Skill: ${skill.skill}`);
229
+ for (const file of skill.files) {
230
+ context.logger.info(
231
+ ` ${file.file}: ${file.actions.length} action(s), ${file.legacySectionCount} legacy section(s)`,
232
+ );
233
+ for (const action of file.actions) {
234
+ context.logger.info(` ${action.kind}: ${action.entryId} — ${action.reason}`);
235
+ }
236
+ }
237
+ }
238
+ context.logger.info(
239
+ `Totals: ${report.totals.archived} archived, ${report.totals.markedStale} marked stale, ${report.totals.legacyFiles} legacy file(s)`,
240
+ );
241
+ }
242
+
243
+ return {
244
+ data: groupedReport,
245
+ exitCode,
246
+ summary,
247
+ };
248
+ }