@entelligentsia/forgecli 0.21.0 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (364) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/README.md +31 -33
  3. package/dist/CHANGELOG-forge-plugin.md +142 -0
  4. package/dist/CHANGELOG-pi.md +24 -1
  5. package/dist/bin/forgecli.d.ts +2 -0
  6. package/dist/bin/forgecli.js +6 -0
  7. package/dist/bin/forgecli.js.map +1 -0
  8. package/dist/extensions/forgecli/add-pipeline.js +1 -1
  9. package/dist/extensions/forgecli/add-pipeline.js.map +1 -1
  10. package/dist/extensions/forgecli/add-task.js +1 -1
  11. package/dist/extensions/forgecli/add-task.js.map +1 -1
  12. package/dist/extensions/forgecli/approve.js +17 -2
  13. package/dist/extensions/forgecli/approve.js.map +1 -1
  14. package/dist/extensions/forgecli/audience-gate.js +1 -1
  15. package/dist/extensions/forgecli/audience-gate.js.map +1 -1
  16. package/dist/extensions/forgecli/calibrate.js +11 -8
  17. package/dist/extensions/forgecli/calibrate.js.map +1 -1
  18. package/dist/extensions/forgecli/collate.js +1 -1
  19. package/dist/extensions/forgecli/collate.js.map +1 -1
  20. package/dist/extensions/forgecli/commit.js +17 -2
  21. package/dist/extensions/forgecli/commit.js.map +1 -1
  22. package/dist/extensions/forgecli/enhance.js +1 -1
  23. package/dist/extensions/forgecli/enhance.js.map +1 -1
  24. package/dist/extensions/forgecli/fix-bug.d.ts +1 -2
  25. package/dist/extensions/forgecli/fix-bug.js +678 -609
  26. package/dist/extensions/forgecli/fix-bug.js.map +1 -1
  27. package/dist/extensions/forgecli/forge-artifact-tool.d.ts +6 -8
  28. package/dist/extensions/forgecli/forge-artifact-tool.js +94 -197
  29. package/dist/extensions/forgecli/forge-artifact-tool.js.map +1 -1
  30. package/dist/extensions/forgecli/forge-commands.js +57 -18
  31. package/dist/extensions/forgecli/forge-commands.js.map +1 -1
  32. package/dist/extensions/forgecli/forge-init/phase4-register.js +6 -7
  33. package/dist/extensions/forgecli/forge-init/phase4-register.js.map +1 -1
  34. package/dist/extensions/forgecli/forge-init/run-phases.d.ts +4 -0
  35. package/dist/extensions/forgecli/forge-init/run-phases.js +304 -0
  36. package/dist/extensions/forgecli/forge-init/run-phases.js.map +1 -0
  37. package/dist/extensions/forgecli/forge-init/verifiers.d.ts +14 -5
  38. package/dist/extensions/forgecli/forge-init/verifiers.js +79 -62
  39. package/dist/extensions/forgecli/forge-init/verifiers.js.map +1 -1
  40. package/dist/extensions/forgecli/forge-init.js +131 -76
  41. package/dist/extensions/forgecli/forge-init.js.map +1 -1
  42. package/dist/extensions/forgecli/forge-subagent.d.ts +26 -0
  43. package/dist/extensions/forgecli/forge-subagent.js +42 -18
  44. package/dist/extensions/forgecli/forge-subagent.js.map +1 -1
  45. package/dist/extensions/forgecli/forge-tools.d.ts +34 -4
  46. package/dist/extensions/forgecli/forge-tools.js +191 -79
  47. package/dist/extensions/forgecli/forge-tools.js.map +1 -1
  48. package/dist/extensions/forgecli/health-check.js +3 -3
  49. package/dist/extensions/forgecli/health-check.js.map +1 -1
  50. package/dist/extensions/forgecli/hook-dispatcher.d.ts +1 -1
  51. package/dist/extensions/forgecli/hooks/check-update.d.ts +8 -0
  52. package/dist/extensions/forgecli/hooks/check-update.js +29 -1
  53. package/dist/extensions/forgecli/hooks/check-update.js.map +1 -1
  54. package/dist/extensions/forgecli/hooks/post-init-hook.js +6 -6
  55. package/dist/extensions/forgecli/hooks/post-init-hook.js.map +1 -1
  56. package/dist/extensions/forgecli/hooks/post-sprint-hook.js +6 -6
  57. package/dist/extensions/forgecli/hooks/post-sprint-hook.js.map +1 -1
  58. package/dist/extensions/forgecli/hooks/triage-error.js +1 -0
  59. package/dist/extensions/forgecli/hooks/triage-error.js.map +1 -1
  60. package/dist/extensions/forgecli/implement.js +20 -2
  61. package/dist/extensions/forgecli/implement.js.map +1 -1
  62. package/dist/extensions/forgecli/index.js +39 -32
  63. package/dist/extensions/forgecli/index.js.map +1 -1
  64. package/dist/extensions/forgecli/lib/pipeline-guard.d.ts +41 -0
  65. package/dist/extensions/forgecli/lib/pipeline-guard.js +100 -0
  66. package/dist/extensions/forgecli/lib/pipeline-guard.js.map +1 -0
  67. package/dist/extensions/forgecli/loaders/persona-skill-loader.js +2 -2
  68. package/dist/extensions/forgecli/loaders/persona-skill-loader.js.map +1 -1
  69. package/dist/extensions/forgecli/migrate.d.ts +3 -0
  70. package/dist/extensions/forgecli/migrate.js +4 -2
  71. package/dist/extensions/forgecli/migrate.js.map +1 -1
  72. package/dist/extensions/forgecli/plan.js +21 -2
  73. package/dist/extensions/forgecli/plan.js.map +1 -1
  74. package/dist/extensions/forgecli/quiz-agent.js +7 -7
  75. package/dist/extensions/forgecli/quiz-agent.js.map +1 -1
  76. package/dist/extensions/forgecli/regenerate.js +49 -18
  77. package/dist/extensions/forgecli/regenerate.js.map +1 -1
  78. package/dist/extensions/forgecli/remove-command.js +1 -1
  79. package/dist/extensions/forgecli/remove-command.js.map +1 -1
  80. package/dist/extensions/forgecli/report-bug.js +1 -1
  81. package/dist/extensions/forgecli/report-bug.js.map +1 -1
  82. package/dist/extensions/forgecli/retrospective.js +9 -9
  83. package/dist/extensions/forgecli/retrospective.js.map +1 -1
  84. package/dist/extensions/forgecli/review-code.d.ts +13 -0
  85. package/dist/extensions/forgecli/review-code.js +62 -3
  86. package/dist/extensions/forgecli/review-code.js.map +1 -1
  87. package/dist/extensions/forgecli/review-plan.d.ts +13 -0
  88. package/dist/extensions/forgecli/review-plan.js +65 -3
  89. package/dist/extensions/forgecli/review-plan.js.map +1 -1
  90. package/dist/extensions/forgecli/run-task.js +461 -391
  91. package/dist/extensions/forgecli/run-task.js.map +1 -1
  92. package/dist/extensions/forgecli/session-registry.d.ts +12 -0
  93. package/dist/extensions/forgecli/session-registry.js +23 -0
  94. package/dist/extensions/forgecli/session-registry.js.map +1 -1
  95. package/dist/extensions/forgecli/skill-curator-subagent.d.ts +2 -1
  96. package/dist/extensions/forgecli/skill-curator-subagent.js +2 -1
  97. package/dist/extensions/forgecli/skill-curator-subagent.js.map +1 -1
  98. package/dist/extensions/forgecli/sprint-intake.js +6 -6
  99. package/dist/extensions/forgecli/sprint-intake.js.map +1 -1
  100. package/dist/extensions/forgecli/sprint-plan.js +9 -9
  101. package/dist/extensions/forgecli/sprint-plan.js.map +1 -1
  102. package/dist/extensions/forgecli/status-command.js +1 -1
  103. package/dist/extensions/forgecli/status-command.js.map +1 -1
  104. package/dist/extensions/forgecli/store-query.js +11 -11
  105. package/dist/extensions/forgecli/store-query.js.map +1 -1
  106. package/dist/extensions/forgecli/store-repair.js +7 -7
  107. package/dist/extensions/forgecli/store-repair.js.map +1 -1
  108. package/dist/extensions/forgecli/subagent/caller-context.d.ts +35 -11
  109. package/dist/extensions/forgecli/subagent/caller-context.js +49 -21
  110. package/dist/extensions/forgecli/subagent/caller-context.js.map +1 -1
  111. package/dist/extensions/forgecli/subagent/orchestrator-transcript.d.ts +66 -0
  112. package/dist/extensions/forgecli/subagent/orchestrator-transcript.js +66 -0
  113. package/dist/extensions/forgecli/subagent/orchestrator-transcript.js.map +1 -0
  114. package/dist/extensions/forgecli/subagent/phase-guard.d.ts +34 -0
  115. package/dist/extensions/forgecli/subagent/phase-guard.js +139 -0
  116. package/dist/extensions/forgecli/subagent/phase-guard.js.map +1 -0
  117. package/dist/extensions/forgecli/subagent/phase-summary-map.d.ts +1 -0
  118. package/dist/extensions/forgecli/subagent/phase-summary-map.js +22 -0
  119. package/dist/extensions/forgecli/subagent/phase-summary-map.js.map +1 -0
  120. package/dist/extensions/forgecli/thread-switcher.js +2 -2
  121. package/dist/extensions/forgecli/thread-switcher.js.map +1 -1
  122. package/dist/extensions/forgecli/validate.js +17 -2
  123. package/dist/extensions/forgecli/validate.js.map +1 -1
  124. package/dist/extensions/forgecli/viewport-events.d.ts +4 -0
  125. package/dist/extensions/forgecli/viewport-events.js +18 -1
  126. package/dist/extensions/forgecli/viewport-events.js.map +1 -1
  127. package/dist/extensions/forgecli/viewport-renderer.d.ts +12 -2
  128. package/dist/extensions/forgecli/viewport-renderer.js +8 -6
  129. package/dist/extensions/forgecli/viewport-renderer.js.map +1 -1
  130. package/dist/forge-payload/.base-pack/commands/check-agent.md +22 -0
  131. package/dist/forge-payload/.base-pack/commands/new-sprint.md +22 -0
  132. package/dist/forge-payload/.base-pack/commands/plan-sprint.md +22 -0
  133. package/dist/forge-payload/.base-pack/commands/quiz-agent.md +2 -18
  134. package/dist/forge-payload/.base-pack/commands/retro.md +22 -0
  135. package/dist/forge-payload/.base-pack/commands/retrospective.md +2 -18
  136. package/dist/forge-payload/.base-pack/commands/sprint-intake.md +2 -18
  137. package/dist/forge-payload/.base-pack/commands/sprint-plan.md +2 -18
  138. package/dist/forge-payload/.base-pack/workflows/_fragments/friction-emit.md +2 -2
  139. package/dist/forge-payload/.base-pack/workflows/_fragments/generation-instructions.md +4 -4
  140. package/dist/forge-payload/.base-pack/workflows/_fragments/iron-laws.md +1 -1
  141. package/dist/forge-payload/.base-pack/workflows/architect_approve.md +13 -1
  142. package/dist/forge-payload/.base-pack/workflows/commit_task.md +12 -1
  143. package/dist/forge-payload/.base-pack/workflows/enhance.md +6 -6
  144. package/dist/forge-payload/.base-pack/workflows/fix_bug.md +11 -29
  145. package/dist/forge-payload/.base-pack/workflows/implement_plan.md +14 -2
  146. package/dist/forge-payload/.base-pack/workflows/migrate_structural.md +2 -2
  147. package/dist/forge-payload/.base-pack/workflows/orchestrate_task.md +20 -4
  148. package/dist/forge-payload/.base-pack/workflows/plan_task.md +14 -2
  149. package/dist/forge-payload/.base-pack/workflows/review_code.md +37 -7
  150. package/dist/forge-payload/.base-pack/workflows/review_plan.md +36 -6
  151. package/dist/forge-payload/.base-pack/workflows/run_sprint.md +2 -2
  152. package/dist/forge-payload/.base-pack/workflows/triage.md +190 -0
  153. package/dist/forge-payload/.base-pack/workflows/validate_task.md +37 -7
  154. package/dist/forge-payload/.claude-plugin/plugin.json +1 -1
  155. package/dist/forge-payload/.schemas/config.schema.json +0 -5
  156. package/dist/forge-payload/.schemas/enum-catalog.json +9 -13
  157. package/dist/forge-payload/.schemas/migrations.json +63 -0
  158. package/dist/forge-payload/agents/tomoshibi.md +150 -6
  159. package/dist/forge-payload/commands/add-pipeline.md +1 -1
  160. package/dist/forge-payload/commands/add-task.md +1 -1
  161. package/dist/forge-payload/commands/calibrate.md +4 -350
  162. package/dist/forge-payload/commands/check-agent.md +38 -0
  163. package/dist/forge-payload/commands/config.md +3 -113
  164. package/dist/forge-payload/commands/enhance.md +5 -32
  165. package/dist/forge-payload/commands/health.md +155 -13
  166. package/dist/forge-payload/commands/init.md +25 -31
  167. package/dist/forge-payload/commands/migrate.md +6 -154
  168. package/dist/forge-payload/commands/quiz-agent.md +2 -34
  169. package/dist/forge-payload/commands/rebuild.md +664 -0
  170. package/dist/forge-payload/commands/regenerate.md +2 -774
  171. package/dist/forge-payload/commands/remove.md +10 -13
  172. package/dist/forge-payload/commands/repair.md +187 -0
  173. package/dist/forge-payload/commands/search.md +73 -0
  174. package/dist/forge-payload/commands/status.md +105 -0
  175. package/dist/forge-payload/commands/store-query.md +2 -69
  176. package/dist/forge-payload/commands/store-repair.md +2 -183
  177. package/dist/forge-payload/commands/update-tools.md +4 -50
  178. package/dist/forge-payload/commands/update.md +64 -58
  179. package/dist/forge-payload/hooks/check-update.cjs +1 -1
  180. package/dist/forge-payload/hooks/post-init.cjs +2 -2
  181. package/dist/forge-payload/hooks/post-sprint.cjs +2 -2
  182. package/dist/forge-payload/hooks/triage-error.cjs +11 -10
  183. package/dist/forge-payload/init/phases/phase-1-collect.md +138 -0
  184. package/dist/forge-payload/init/phases/phase-2-discover.md +127 -0
  185. package/dist/forge-payload/init/phases/phase-3-materialize.md +113 -0
  186. package/dist/forge-payload/init/phases/phase-4-register.md +159 -0
  187. package/dist/forge-payload/integrity.json +21 -24
  188. package/dist/forge-payload/meta/fragments/tool-discipline.md +41 -0
  189. package/dist/forge-payload/meta/templates/meta-retro.md +28 -0
  190. package/dist/forge-payload/meta/workflows/_fragments/friction-emit.md +2 -2
  191. package/dist/forge-payload/meta/workflows/_fragments/generation-instructions.md +4 -4
  192. package/dist/forge-payload/meta/workflows/_fragments/iron-laws.md +1 -1
  193. package/dist/forge-payload/meta/workflows/meta-approve.md +13 -1
  194. package/dist/forge-payload/meta/workflows/meta-bug-triage.md +210 -0
  195. package/dist/forge-payload/meta/workflows/meta-check-agent.md +138 -0
  196. package/dist/forge-payload/meta/workflows/meta-commit.md +12 -1
  197. package/dist/forge-payload/meta/workflows/meta-enhance.md +5 -5
  198. package/dist/forge-payload/meta/workflows/meta-fix-bug.md +11 -29
  199. package/dist/forge-payload/meta/workflows/meta-implement.md +13 -1
  200. package/dist/forge-payload/meta/workflows/meta-migrate.md +3 -3
  201. package/dist/forge-payload/meta/workflows/meta-new-sprint.md +84 -0
  202. package/dist/forge-payload/meta/workflows/meta-orchestrate.md +20 -4
  203. package/dist/forge-payload/meta/workflows/meta-plan-sprint.md +152 -0
  204. package/dist/forge-payload/meta/workflows/meta-plan-task.md +13 -1
  205. package/dist/forge-payload/meta/workflows/meta-retro.md +73 -0
  206. package/dist/forge-payload/meta/workflows/meta-review-implementation.md +37 -7
  207. package/dist/forge-payload/meta/workflows/meta-review-plan.md +36 -6
  208. package/dist/forge-payload/meta/workflows/meta-validate.md +37 -7
  209. package/dist/forge-payload/schemas/config.schema.json +0 -5
  210. package/dist/forge-payload/schemas/enum-catalog.json +9 -13
  211. package/dist/forge-payload/schemas/structure-manifest.json +25 -8
  212. package/dist/forge-payload/tools/artifact.cjs +324 -0
  213. package/dist/forge-payload/tools/banners.cjs +3 -4
  214. package/dist/forge-payload/tools/build-context-pack.cjs +1 -1
  215. package/dist/forge-payload/tools/check-structure.cjs +8 -3
  216. package/dist/forge-payload/tools/store-cli.cjs +67 -7
  217. package/dist/forge-payload/tools/substitute-placeholders.cjs +1 -1
  218. package/dist/forge-payload/tools/verify-apply.cjs +75 -0
  219. package/dist/forge-payload/tools/verify-phase.cjs +259 -0
  220. package/node_modules/@earendil-works/pi-agent-core/package.json +2 -2
  221. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts +0 -17
  222. package/node_modules/@earendil-works/pi-ai/dist/models.generated.d.ts.map +1 -1
  223. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js +21 -38
  224. package/node_modules/@earendil-works/pi-ai/dist/models.generated.js.map +1 -1
  225. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.d.ts.map +1 -1
  226. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js +5 -4
  227. package/node_modules/@earendil-works/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
  228. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +2 -1
  229. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  230. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +5 -2
  231. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  232. package/node_modules/@earendil-works/pi-ai/package.json +1 -1
  233. package/node_modules/@earendil-works/pi-coding-agent/CHANGELOG.md +24 -1
  234. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.d.ts.map +1 -1
  235. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js +2 -3
  236. package/node_modules/@earendil-works/pi-coding-agent/dist/cli/file-processor.js.map +1 -1
  237. package/node_modules/@earendil-works/pi-coding-agent/dist/core/output-guard.d.ts +1 -0
  238. package/node_modules/@earendil-works/pi-coding-agent/dist/core/output-guard.d.ts.map +1 -1
  239. package/node_modules/@earendil-works/pi-coding-agent/dist/core/output-guard.js +52 -22
  240. package/node_modules/@earendil-works/pi-coding-agent/dist/core/output-guard.js.map +1 -1
  241. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  242. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js +16 -4
  243. package/node_modules/@earendil-works/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  244. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.d.ts.map +1 -1
  245. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js +45 -50
  246. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/bash.js.map +1 -1
  247. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.d.ts.map +1 -1
  248. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js +43 -81
  249. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/edit.js.map +1 -1
  250. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  251. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/file-mutation-queue.js +27 -12
  252. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/file-mutation-queue.js.map +1 -1
  253. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.d.ts.map +1 -1
  254. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js +2 -3
  255. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/find.js.map +1 -1
  256. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.d.ts.map +1 -1
  257. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js +3 -3
  258. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/grep.js.map +1 -1
  259. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.d.ts.map +1 -1
  260. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js +5 -5
  261. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/ls.js.map +1 -1
  262. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/path-utils.d.ts +2 -0
  263. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/path-utils.d.ts.map +1 -1
  264. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js +37 -0
  265. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/path-utils.js.map +1 -1
  266. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.d.ts.map +1 -1
  267. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js +9 -8
  268. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/read.js.map +1 -1
  269. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.d.ts.map +1 -1
  270. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js +20 -35
  271. package/node_modules/@earendil-works/pi-coding-agent/dist/core/tools/write.js.map +1 -1
  272. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts +3 -0
  273. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
  274. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js +64 -7
  275. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
  276. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
  277. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js +15 -3
  278. package/node_modules/@earendil-works/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
  279. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/clipboard-native.d.ts +3 -1
  280. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/clipboard-native.d.ts.map +1 -1
  281. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/clipboard-native.js +14 -8
  282. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/clipboard-native.js.map +1 -1
  283. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-core.d.ts +30 -0
  284. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-core.d.ts.map +1 -0
  285. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-core.js +124 -0
  286. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-core.js.map +1 -0
  287. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-worker.d.ts +2 -0
  288. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-worker.d.ts.map +1 -0
  289. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-worker.js +31 -0
  290. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize-worker.js.map +1 -0
  291. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize.d.ts +7 -27
  292. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize.d.ts.map +1 -1
  293. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize.js +75 -115
  294. package/node_modules/@earendil-works/pi-coding-agent/dist/utils/image-resize.js.map +1 -1
  295. package/node_modules/@earendil-works/pi-coding-agent/docs/terminal-setup.md +6 -0
  296. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-anthropic/package.json +1 -1
  297. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  298. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/sandbox/package.json +1 -1
  299. package/node_modules/@earendil-works/pi-coding-agent/examples/extensions/with-deps/package.json +1 -1
  300. package/node_modules/@earendil-works/pi-coding-agent/npm-shrinkwrap.json +12 -14
  301. package/node_modules/@earendil-works/pi-coding-agent/package.json +5 -5
  302. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts +3 -0
  303. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.d.ts.map +1 -0
  304. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js +53 -0
  305. package/node_modules/@earendil-works/pi-tui/dist/native-modifiers.js.map +1 -0
  306. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.d.ts.map +1 -1
  307. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js +4 -1
  308. package/node_modules/@earendil-works/pi-tui/dist/terminal-image.js.map +1 -1
  309. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts +2 -0
  310. package/node_modules/@earendil-works/pi-tui/dist/terminal.d.ts.map +1 -1
  311. package/node_modules/@earendil-works/pi-tui/dist/terminal.js +13 -1
  312. package/node_modules/@earendil-works/pi-tui/dist/terminal.js.map +1 -1
  313. package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-arm64/darwin-modifiers.node +0 -0
  314. package/node_modules/@earendil-works/pi-tui/native/darwin/prebuilds/darwin-x64/darwin-modifiers.node +0 -0
  315. package/node_modules/@earendil-works/pi-tui/package.json +2 -2
  316. package/node_modules/@entelligentsia/forge-compress/dist/compressor.d.ts +6 -0
  317. package/node_modules/@entelligentsia/forge-compress/dist/compressor.js +137 -0
  318. package/node_modules/@entelligentsia/forge-compress/dist/entropy.d.ts +3 -0
  319. package/node_modules/@entelligentsia/forge-compress/dist/entropy.js +99 -0
  320. package/node_modules/@entelligentsia/forge-compress/dist/forge/entity.d.ts +8 -0
  321. package/node_modules/@entelligentsia/forge-compress/dist/forge/entity.js +149 -0
  322. package/node_modules/@entelligentsia/forge-compress/dist/forge/index.d.ts +7 -0
  323. package/node_modules/@entelligentsia/forge-compress/dist/forge/index.js +4 -0
  324. package/node_modules/@entelligentsia/forge-compress/dist/forge/markdown.d.ts +5 -0
  325. package/node_modules/@entelligentsia/forge-compress/dist/forge/markdown.js +92 -0
  326. package/node_modules/@entelligentsia/forge-compress/dist/forge/query.d.ts +7 -0
  327. package/node_modules/@entelligentsia/forge-compress/dist/forge/query.js +60 -0
  328. package/node_modules/@entelligentsia/forge-compress/dist/forge/validate.d.ts +1 -0
  329. package/node_modules/@entelligentsia/forge-compress/dist/forge/validate.js +82 -0
  330. package/node_modules/@entelligentsia/forge-compress/dist/index.d.ts +6 -0
  331. package/node_modules/@entelligentsia/forge-compress/dist/index.js +5 -0
  332. package/node_modules/@entelligentsia/forge-compress/dist/progressive.d.ts +1 -0
  333. package/node_modules/@entelligentsia/forge-compress/dist/progressive.js +108 -0
  334. package/node_modules/@entelligentsia/forge-compress/dist/strip.d.ts +4 -0
  335. package/node_modules/@entelligentsia/forge-compress/dist/strip.js +55 -0
  336. package/node_modules/@entelligentsia/forge-compress/dist/tokens.d.ts +2 -0
  337. package/node_modules/@entelligentsia/forge-compress/dist/tokens.js +17 -0
  338. package/node_modules/@entelligentsia/forge-compress/package.json +45 -0
  339. package/node_modules/@entelligentsia/forge-compress/src/__tests__/compress.test.ts +409 -0
  340. package/node_modules/@entelligentsia/forge-compress/src/compressor.ts +147 -0
  341. package/node_modules/@entelligentsia/forge-compress/src/entropy.ts +105 -0
  342. package/node_modules/@entelligentsia/forge-compress/src/forge/entity.ts +184 -0
  343. package/node_modules/@entelligentsia/forge-compress/src/forge/index.ts +10 -0
  344. package/node_modules/@entelligentsia/forge-compress/src/forge/markdown.ts +122 -0
  345. package/node_modules/@entelligentsia/forge-compress/src/forge/query.ts +105 -0
  346. package/node_modules/@entelligentsia/forge-compress/src/forge/validate.ts +86 -0
  347. package/node_modules/@entelligentsia/forge-compress/src/index.ts +22 -0
  348. package/node_modules/@entelligentsia/forge-compress/src/progressive.ts +123 -0
  349. package/node_modules/@entelligentsia/forge-compress/src/strip.ts +58 -0
  350. package/node_modules/@entelligentsia/forge-compress/src/tokens.ts +19 -0
  351. package/package.json +10 -15
  352. package/dist/extensions/forgecli/forge-init/phase-descriptors.d.ts +0 -72
  353. package/dist/extensions/forgecli/forge-init/phase-descriptors.js +0 -359
  354. package/dist/extensions/forgecli/forge-init/phase-descriptors.js.map +0 -1
  355. package/dist/extensions/forgecli/forge-init/prompts.d.ts +0 -10
  356. package/dist/extensions/forgecli/forge-init/prompts.js +0 -91
  357. package/dist/extensions/forgecli/forge-init/prompts.js.map +0 -1
  358. package/dist/extensions/forgecli/lib/store-error-remediation.d.ts +0 -65
  359. package/dist/extensions/forgecli/lib/store-error-remediation.js +0 -298
  360. package/dist/extensions/forgecli/lib/store-error-remediation.js.map +0 -1
  361. package/dist/forge-payload/hooks/check-update.js +0 -378
  362. package/dist/forge-payload/hooks/forge-permissions.js +0 -164
  363. package/dist/forge-payload/hooks/triage-error.js +0 -77
  364. package/dist/forge-payload/hooks/validate-write.js +0 -250
@@ -0,0 +1,55 @@
1
+ const ANSI_RE = /\x1b\[[0-9;]*[a-zA-Z]/g;
2
+ export function stripAnsi(s) {
3
+ if (!s.includes("\x1b"))
4
+ return s;
5
+ return s.replace(ANSI_RE, "");
6
+ }
7
+ const TS_RE = /\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?/g;
8
+ const HASH_RE = /\b[0-9a-f]{32,64}\b/g;
9
+ export function stripTimestampsAndHashes(line) {
10
+ return line.replace(TS_RE, "[TS]").replace(HASH_RE, "[HASH]");
11
+ }
12
+ export function normalizeWhitespace(line) {
13
+ let result = "";
14
+ let prevSpace = false;
15
+ for (const ch of line) {
16
+ if (ch === " " || ch === "\t") {
17
+ if (!prevSpace) {
18
+ result += " ";
19
+ prevSpace = true;
20
+ }
21
+ }
22
+ else {
23
+ result += ch;
24
+ prevSpace = false;
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+ const BOILERPLATE_PREFIXES = [
30
+ "copyright",
31
+ "licensed under",
32
+ "license:",
33
+ "all rights reserved",
34
+ "generated by",
35
+ "auto-generated",
36
+ ];
37
+ const SEPARATOR_CHARS = new Set(["=", "-", "*", "─", "━"]);
38
+ export function isBoilerplate(line) {
39
+ const lower = line.toLowerCase();
40
+ if (BOILERPLATE_PREFIXES.some((p) => lower.startsWith(p)))
41
+ return true;
42
+ if (line.length >= 4) {
43
+ const first = line[0];
44
+ if (SEPARATOR_CHARS.has(first)) {
45
+ let same = 0;
46
+ for (const c of line) {
47
+ if (c === first)
48
+ same++;
49
+ }
50
+ if (same / line.length > 0.8)
51
+ return true;
52
+ }
53
+ }
54
+ return false;
55
+ }
@@ -0,0 +1,2 @@
1
+ export declare function countTokens(text: string): number;
2
+ export declare function truncateToTokenBudget(text: string, maxTokens: number): string;
@@ -0,0 +1,17 @@
1
+ const CHARS_PER_TOKEN = 3.7;
2
+ export function countTokens(text) {
3
+ if (!text)
4
+ return 0;
5
+ return Math.ceil(text.length / CHARS_PER_TOKEN);
6
+ }
7
+ export function truncateToTokenBudget(text, maxTokens) {
8
+ if (maxTokens <= 0)
9
+ return "";
10
+ if (countTokens(text) <= maxTokens)
11
+ return text;
12
+ const charBudget = Math.floor(maxTokens * CHARS_PER_TOKEN);
13
+ const truncated = text.slice(0, charBudget);
14
+ const lastNewline = truncated.lastIndexOf("\n");
15
+ const clean = lastNewline > 0 ? truncated.slice(0, lastNewline) : truncated;
16
+ return `${clean} …`;
17
+ }
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@entelligentsia/forge-compress",
3
+ "repository": {
4
+ "type": "git",
5
+ "url": "https://github.com/Entelligentsia/forge-compress.git"
6
+ },
7
+ "version": "0.1.0",
8
+ "description": "Token-efficient compression for Forge SDLC tool outputs — store queries, entities, artifacts",
9
+ "type": "module",
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "files": [
19
+ "dist",
20
+ "src"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc",
24
+ "test": "vitest run",
25
+ "test:watch": "vitest",
26
+ "lint": "biome check src/",
27
+ "prepublishOnly": "npm run build"
28
+ },
29
+ "keywords": [
30
+ "compression",
31
+ "forge",
32
+ "sdlc",
33
+ "ai-agent",
34
+ "token-reduction"
35
+ ],
36
+ "license": "MIT",
37
+ "devDependencies": {
38
+ "typescript": "^5.7.0",
39
+ "vitest": "^3.0.0",
40
+ "@biomejs/biome": "^1.9.0"
41
+ },
42
+ "engines": {
43
+ "node": ">=18"
44
+ }
45
+ }
@@ -0,0 +1,409 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import {
3
+ compressStoreQuery,
4
+ compressEntity,
5
+ compressEntityList,
6
+ compressMarkdown,
7
+ compressValidateStore,
8
+ countTokens,
9
+ compressIb,
10
+ compressProgressive,
11
+ truncateToTokenBudget,
12
+ lightweightCleanup,
13
+ verbatimCompact,
14
+ stripAnsi,
15
+ } from "../index.js";
16
+
17
+ // ── store-query compressor ──────────────────────────────────
18
+
19
+ const SAMPLE_QUERY = JSON.stringify(
20
+ {
21
+ query: "open tasks in S12",
22
+ path: "intent-nlp",
23
+ traversalTrace: [
24
+ "parsed intent: entity=task, status=open, sprint=S12",
25
+ "resolved S12 → PROJ-S12",
26
+ "loaded 8 task records",
27
+ "filtered by status: 5 matched",
28
+ ],
29
+ results: [
30
+ {
31
+ id: "PROJ-S12-T01",
32
+ title: "Add auth middleware",
33
+ status: "implementing",
34
+ type: "task",
35
+ relationships: { sprintId: "PROJ-S12", featureId: "F-AUTH", blockedBy: null },
36
+ fileRefs: { json: ".forge/store/tasks/PROJ-S12-T01.json", md: ".forge/sprints/S12/tasks/T01/INDEX.md" },
37
+ storeRef: ".forge/store/tasks/PROJ-S12-T01.json",
38
+ indexRef: ".forge/sprints/S12/tasks/T01/INDEX.md",
39
+ excerpt: "# T01: Add auth middleware\n\n## Status: implementing\n\n## Plan\nImplement JWT-based auth middleware...\n\n## Key Changes\n- Added middleware handler\n- Updated route config\n- Added token validation\n\n## Notes\nBlocked by upstream API changes.\nNeed to coordinate with team B.\nAlso requires DB migration.\nSee PR #42 for details.\nReviewed by @alice.",
40
+ },
41
+ {
42
+ id: "PROJ-S12-T02",
43
+ title: "Fix rate limiter",
44
+ status: "planned",
45
+ type: "task",
46
+ relationships: { sprintId: "PROJ-S12" },
47
+ fileRefs: { json: ".forge/store/tasks/PROJ-S12-T02.json", md: ".forge/sprints/S12/tasks/T02/INDEX.md" },
48
+ storeRef: ".forge/store/tasks/PROJ-S12-T02.json",
49
+ indexRef: ".forge/sprints/S12/tasks/T02/INDEX.md",
50
+ excerpt: "# T02: Fix rate limiter\n\n## Status: planned\n\nRate limiter is dropping legitimate requests under load.",
51
+ },
52
+ ],
53
+ relatedFileRefs: [".forge/sprints/S12/INDEX.md", ".forge/features/F-AUTH.md"],
54
+ config: { store: ".forge/store", engineering: ".forge/engineering" },
55
+ meta: { mode: "intent-nlp", engineVersion: "1.2.0", totalTimeMs: 42 },
56
+ },
57
+ null,
58
+ 2,
59
+ );
60
+
61
+ describe("compressStoreQuery", () => {
62
+ it("strips envelope fields", () => {
63
+ const result = compressStoreQuery(SAMPLE_QUERY);
64
+ const parsed = JSON.parse(result);
65
+ expect(parsed.traversalTrace).toBeUndefined();
66
+ expect(parsed.config).toBeUndefined();
67
+ expect(parsed.meta).toBeUndefined();
68
+ expect(parsed.relatedFileRefs).toBeUndefined();
69
+ expect(parsed.results).toHaveLength(2);
70
+ });
71
+
72
+ it("strips fileRefs and storeRef from results", () => {
73
+ const result = compressStoreQuery(SAMPLE_QUERY);
74
+ const parsed = JSON.parse(result);
75
+ expect(parsed.results[0].fileRefs).toBeUndefined();
76
+ expect(parsed.results[0].storeRef).toBeUndefined();
77
+ expect(parsed.results[0].indexRef).toBeUndefined();
78
+ });
79
+
80
+ it("strips excerpts by default", () => {
81
+ const result = compressStoreQuery(SAMPLE_QUERY);
82
+ const parsed = JSON.parse(result);
83
+ expect(parsed.results[0].excerpt).toBeUndefined();
84
+ });
85
+
86
+ it("keeps truncated excerpts when requested", () => {
87
+ const result = compressStoreQuery(SAMPLE_QUERY, {
88
+ keepExcerpts: true,
89
+ maxExcerptLines: 3,
90
+ });
91
+ const parsed = JSON.parse(result);
92
+ expect(parsed.results[0].excerpt).toContain("Add auth middleware");
93
+ expect(parsed.results[0].excerpt).toContain("more lines");
94
+ });
95
+
96
+ it("preserves query and path", () => {
97
+ const result = compressStoreQuery(SAMPLE_QUERY);
98
+ const parsed = JSON.parse(result);
99
+ expect(parsed.query).toBe("open tasks in S12");
100
+ expect(parsed.path).toBe("intent-nlp");
101
+ });
102
+
103
+ it("limits results count", () => {
104
+ const result = compressStoreQuery(SAMPLE_QUERY, { maxResults: 1 });
105
+ const parsed = JSON.parse(result);
106
+ expect(parsed.results).toHaveLength(1);
107
+ expect(parsed.truncated).toBe(1);
108
+ });
109
+
110
+ it("strips null relationships", () => {
111
+ const result = compressStoreQuery(SAMPLE_QUERY);
112
+ const parsed = JSON.parse(result);
113
+ expect(parsed.results[0].relationships.blockedBy).toBeUndefined();
114
+ });
115
+
116
+ it("is significantly smaller than input", () => {
117
+ const result = compressStoreQuery(SAMPLE_QUERY);
118
+ expect(countTokens(result)).toBeLessThan(countTokens(SAMPLE_QUERY) * 0.5);
119
+ });
120
+
121
+ it("passes through non-JSON input", () => {
122
+ expect(compressStoreQuery("not json")).toBe("not json");
123
+ });
124
+ });
125
+
126
+ // ── entity compressor ───────────────────────────────────────
127
+
128
+ const SAMPLE_TASK = JSON.stringify(
129
+ {
130
+ taskId: "PROJ-S12-T01",
131
+ sprintId: "PROJ-S12",
132
+ title: "Add auth middleware",
133
+ status: "implementing",
134
+ path: "sprints/S12/tasks/T01",
135
+ feature_id: "F-AUTH",
136
+ description: "Implement JWT-based authentication middleware for all API routes.",
137
+ estimate: "M",
138
+ dependencies: ["PROJ-S12-T03"],
139
+ knowledgeUpdates: [{ file: "auth.md", delta: "Added JWT flow" }],
140
+ planIterations: 2,
141
+ codeReviewIterations: 1,
142
+ assignedModel: "claude-opus-4-7",
143
+ pipeline: "standard",
144
+ summaries: {
145
+ plan: {
146
+ objective: "Design JWT middleware architecture",
147
+ written_at: "2026-05-20T10:00:00Z",
148
+ key_changes: ["Created middleware handler", "Updated route config", "Added token validation", "Set up refresh flow"],
149
+ findings: ["Need to handle expired tokens gracefully"],
150
+ verdict: "approved",
151
+ artifact_ref: "PLAN.md",
152
+ },
153
+ review_plan: {
154
+ objective: "Review plan for auth middleware",
155
+ written_at: "2026-05-20T14:00:00Z",
156
+ key_changes: ["Approved with minor suggestions"],
157
+ verdict: "approved",
158
+ },
159
+ implementation: {
160
+ objective: "Implement auth middleware per plan",
161
+ written_at: "2026-05-21T09:00:00Z",
162
+ key_changes: ["Implemented JWT parsing", "Added route guards", "Unit tests passing", "Integration tests added", "Error handling complete"],
163
+ verdict: "approved",
164
+ artifact_ref: "PROGRESS.md",
165
+ },
166
+ },
167
+ },
168
+ null,
169
+ 2,
170
+ );
171
+
172
+ describe("compressEntity", () => {
173
+ it("keeps essential fields", () => {
174
+ const result = compressEntity(SAMPLE_TASK);
175
+ const parsed = JSON.parse(result);
176
+ expect(parsed.taskId).toBe("PROJ-S12-T01");
177
+ expect(parsed.title).toBe("Add auth middleware");
178
+ expect(parsed.status).toBe("implementing");
179
+ expect(parsed.sprintId).toBe("PROJ-S12");
180
+ });
181
+
182
+ it("drops verbose fields by default", () => {
183
+ const result = compressEntity(SAMPLE_TASK);
184
+ const parsed = JSON.parse(result);
185
+ expect(parsed.description).toBeUndefined();
186
+ expect(parsed.knowledgeUpdates).toBeUndefined();
187
+ expect(parsed.assignedModel).toBeUndefined();
188
+ expect(parsed.path).toBeUndefined();
189
+ expect(parsed.planIterations).toBeUndefined();
190
+ });
191
+
192
+ it("keeps only latest summary by default", () => {
193
+ const result = compressEntity(SAMPLE_TASK);
194
+ const parsed = JSON.parse(result);
195
+ expect(parsed.summaries).toBeUndefined();
196
+ expect(parsed.latestPhase).toBe("implementation");
197
+ expect(parsed.latestSummary.objective).toBe("Implement auth middleware per plan");
198
+ expect(parsed.latestSummary.verdict).toBe("approved");
199
+ });
200
+
201
+ it("truncates key_changes in summary", () => {
202
+ const result = compressEntity(SAMPLE_TASK, { maxKeyChanges: 2 });
203
+ const parsed = JSON.parse(result);
204
+ expect(parsed.latestSummary.key_changes).toHaveLength(3);
205
+ expect(parsed.latestSummary.key_changes[2]).toContain("+3 more");
206
+ });
207
+
208
+ it("keeps all summaries when requested", () => {
209
+ const result = compressEntity(SAMPLE_TASK, { keepSummaries: "all" });
210
+ const parsed = JSON.parse(result);
211
+ expect(parsed.summaries).toBeDefined();
212
+ expect(parsed.summaries.plan).toBeDefined();
213
+ expect(parsed.summaries.implementation).toBeDefined();
214
+ });
215
+
216
+ it("produces flat format", () => {
217
+ const result = compressEntity(SAMPLE_TASK, { flatFormat: true });
218
+ expect(result).toContain("taskId: PROJ-S12-T01");
219
+ expect(result).toContain("status: implementing");
220
+ expect(result).not.toContain("{");
221
+ });
222
+
223
+ it("is smaller than input", () => {
224
+ const result = compressEntity(SAMPLE_TASK);
225
+ expect(countTokens(result)).toBeLessThan(countTokens(SAMPLE_TASK) * 0.5);
226
+ });
227
+ });
228
+
229
+ describe("compressEntityList", () => {
230
+ it("compresses array of entities", () => {
231
+ const list = JSON.stringify([JSON.parse(SAMPLE_TASK), JSON.parse(SAMPLE_TASK)], null, 2);
232
+ const result = compressEntityList(list);
233
+ const parsed = JSON.parse(result);
234
+ expect(parsed).toHaveLength(2);
235
+ expect(parsed[0].taskId).toBe("PROJ-S12-T01");
236
+ expect(parsed[0].description).toBeUndefined();
237
+ });
238
+ });
239
+
240
+ // ── markdown compressor ─────────────────────────────────────
241
+
242
+ const SAMPLE_MD = `---
243
+ title: Sprint S12 Progress
244
+ date: 2026-05-25
245
+ ---
246
+
247
+ # Sprint S12: Auth & Rate Limiting
248
+
249
+ ## Overview
250
+ This sprint covers the implementation of JWT authentication
251
+ middleware and fixing the rate limiter under high load.
252
+ There are multiple sub-tasks involved.
253
+
254
+ ## Tasks
255
+
256
+ ### T01: Add auth middleware
257
+ - **Status**: implementing
258
+ - **Estimate**: M
259
+ - Started implementation on May 20th
260
+ - Plan was approved after 2 iterations
261
+ - Currently blocked on upstream API
262
+
263
+ ### T02: Fix rate limiter
264
+ - **Status**: planned
265
+ - **Estimate**: S
266
+ - Root cause identified as connection pool exhaustion
267
+
268
+ ## Risks
269
+ - Upstream API team may not deliver on time
270
+ - Rate limiter fix requires load testing infrastructure
271
+ - JWT library has a known CVE that needs patching
272
+
273
+ ## Timeline
274
+ | Milestone | Date | Status |
275
+ |-----------|------|--------|
276
+ | Plan approval | May 20 | Done |
277
+ | Auth impl | May 25 | In progress |
278
+ | Rate limiter | May 28 | Not started |
279
+ `;
280
+
281
+ describe("compressMarkdown", () => {
282
+ it("map mode keeps headings and key lines", () => {
283
+ const result = compressMarkdown(SAMPLE_MD, { mode: "map" });
284
+ expect(result).toContain("# Sprint S12");
285
+ expect(result).toContain("### T01");
286
+ expect(result).toContain("### T02");
287
+ expect(result).toContain("**Status**");
288
+ expect(result).toContain("body lines omitted");
289
+ expect(countTokens(result)).toBeLessThan(countTokens(SAMPLE_MD) * 0.7);
290
+ });
291
+
292
+ it("headings mode extracts structure only", () => {
293
+ const result = compressMarkdown(SAMPLE_MD, { mode: "headings" });
294
+ expect(result).toContain("Sprint S12");
295
+ expect(result).toContain("T01: Add auth middleware");
296
+ expect(result).toContain("sections");
297
+ expect(result).not.toContain("implementing");
298
+ });
299
+
300
+ it("truncate mode respects token budget", () => {
301
+ const result = compressMarkdown(SAMPLE_MD, { mode: "truncate", maxTokens: 50 });
302
+ expect(countTokens(result)).toBeLessThanOrEqual(60);
303
+ });
304
+
305
+ it("skips frontmatter", () => {
306
+ const result = compressMarkdown(SAMPLE_MD, { mode: "map" });
307
+ expect(result).not.toContain("title: Sprint S12 Progress");
308
+ expect(result).not.toContain("date: 2026");
309
+ });
310
+
311
+ it("handles empty input", () => {
312
+ expect(compressMarkdown("")).toBe("");
313
+ expect(compressMarkdown(" ")).toBe("");
314
+ });
315
+ });
316
+
317
+ // ── validate-store compressor ───────────────────────────────
318
+
319
+ describe("compressValidateStore", () => {
320
+ it("compresses JSON report", () => {
321
+ const report = JSON.stringify({
322
+ ok: false,
323
+ errors: [
324
+ { entity: "task", id: "PROJ-S12-T01", category: "missing-required", field: "title", message: "title is required" },
325
+ { entity: "task", id: "PROJ-S12-T02", category: "missing-required", field: "status", message: "status is required" },
326
+ { entity: "bug", id: "PROJ-B01", category: "orphaned-fk", field: "sprintId", message: "references non-existent sprint" },
327
+ ],
328
+ warnings: [
329
+ { entity: "task", id: "PROJ-S12-T03", category: "stale-path", message: "path does not match filesystem" },
330
+ ],
331
+ fixes: [
332
+ { entity: "task", id: "PROJ-S12-T01", category: "backfill", message: "backfilled path", applied: true },
333
+ ],
334
+ summary: { sprints: 2, tasks: 5, bugs: 1, features: 0, errors: 3, warnings: 1, fixes: 1 },
335
+ });
336
+ const result = compressValidateStore(report);
337
+ expect(result).toContain("missing-required (2)");
338
+ expect(result).toContain("orphaned-fk (1)");
339
+ expect(result).toContain("1 warnings");
340
+ expect(result).toContain("1 fixes (1 applied)");
341
+ expect(countTokens(result)).toBeLessThan(countTokens(report) * 0.6);
342
+ });
343
+
344
+ it("returns ok for valid store", () => {
345
+ const report = JSON.stringify({
346
+ ok: true,
347
+ errors: [],
348
+ warnings: [],
349
+ fixes: [],
350
+ summary: { sprints: 3, tasks: 12, bugs: 2, features: 1, errors: 0, warnings: 0, fixes: 0 },
351
+ });
352
+ const result = compressValidateStore(report);
353
+ expect(result).toContain("3 sprints");
354
+ expect(result).toContain("12 tasks");
355
+ });
356
+
357
+ it("compresses plain text validation output", () => {
358
+ const lines = Array.from({ length: 20 }, (_, i) =>
359
+ `ERROR PROJ-S12-T${String(i).padStart(2, "0")}: missing required field`
360
+ ).join("\n");
361
+ const result = compressValidateStore(lines);
362
+ expect(result).toContain("20 errors");
363
+ expect(result).toContain("... +15 more");
364
+ });
365
+ });
366
+
367
+ // ── core utilities ──────────────────────────────────────────
368
+
369
+ describe("core utilities", () => {
370
+ it("countTokens", () => {
371
+ expect(countTokens("")).toBe(0);
372
+ expect(countTokens("hello world")).toBeGreaterThan(0);
373
+ });
374
+
375
+ it("stripAnsi", () => {
376
+ expect(stripAnsi("\x1b[31mERROR\x1b[0m")).toBe("ERROR");
377
+ expect(stripAnsi("clean")).toBe("clean");
378
+ });
379
+
380
+ it("truncateToTokenBudget", () => {
381
+ const long = "word ".repeat(1000);
382
+ const result = truncateToTokenBudget(long, 50);
383
+ expect(countTokens(result)).toBeLessThanOrEqual(60);
384
+ });
385
+
386
+ it("compressIb preserves high-entropy lines", () => {
387
+ let boring = "aaa bbb aaa bbb\n".repeat(30);
388
+ boring += "unique_identifier_xyz_quartz\n";
389
+ const result = compressIb(boring, 0.15);
390
+ expect(result).toContain("unique_identifier_xyz_quartz");
391
+ });
392
+
393
+ it("compressProgressive respects budget", () => {
394
+ const segs = Array.from({ length: 4 }, (_, i) => `segment ${i} content\n`);
395
+ const out = compressProgressive(segs, 80);
396
+ const total = out.reduce((acc, s) => acc + countTokens(s), 0);
397
+ expect(total).toBeLessThanOrEqual(80);
398
+ });
399
+
400
+ it("lightweightCleanup collapses blanks", () => {
401
+ const result = lightweightCleanup("a\n\n\n\n\nb");
402
+ expect(result.match(/\n/g)?.length ?? 0).toBeLessThanOrEqual(3);
403
+ });
404
+
405
+ it("verbatimCompact deduplicates", () => {
406
+ const result = verbatimCompact("same\n".repeat(20));
407
+ expect(result).toContain("[20x]");
408
+ });
409
+ });
@@ -0,0 +1,147 @@
1
+ import { countTokens } from "./tokens.js";
2
+ import {
3
+ stripAnsi,
4
+ stripTimestampsAndHashes,
5
+ normalizeWhitespace,
6
+ isBoilerplate,
7
+ } from "./strip.js";
8
+
9
+ export function lightweightCleanup(content: string): string {
10
+ const lines = content.split("\n");
11
+ const total = lines.length;
12
+ const result: string[] = [];
13
+ let blankCount = 0;
14
+ let braceRun: string[] = [];
15
+
16
+ const flushBraceRun = () => {
17
+ if (total <= 200 || braceRun.length <= 5) {
18
+ result.push(...braceRun);
19
+ } else {
20
+ result.push(braceRun[0], braceRun[1]);
21
+ result.push(`[${braceRun.length - 2} brace-only lines collapsed]`);
22
+ }
23
+ braceRun = [];
24
+ };
25
+
26
+ for (const line of lines) {
27
+ const trimmed = line.trim();
28
+ if (!trimmed) {
29
+ flushBraceRun();
30
+ blankCount++;
31
+ if (blankCount <= 1) result.push("");
32
+ continue;
33
+ }
34
+ blankCount = 0;
35
+ if (/^[}\]);]+$/.test(trimmed)) {
36
+ braceRun.push(trimmed);
37
+ continue;
38
+ }
39
+ flushBraceRun();
40
+ result.push(line);
41
+ }
42
+ flushBraceRun();
43
+ return result.join("\n");
44
+ }
45
+
46
+ export function verbatimCompact(text: string): string {
47
+ const lines: string[] = [];
48
+ let blankCount = 0;
49
+ let prevLine: string | null = null;
50
+ let repeatCount = 0;
51
+
52
+ const flushRepeats = () => {
53
+ if (repeatCount > 1 && prevLine !== null) {
54
+ const lastIdx = lines.length - 1;
55
+ if (lastIdx >= 0) {
56
+ lines[lastIdx] = `[${repeatCount}x] ${prevLine}`;
57
+ }
58
+ }
59
+ repeatCount = 0;
60
+ prevLine = null;
61
+ };
62
+
63
+ for (const line of text.split("\n")) {
64
+ const trimmed = line.trim();
65
+ if (!trimmed) {
66
+ blankCount++;
67
+ if (blankCount <= 1) {
68
+ flushRepeats();
69
+ lines.push("");
70
+ }
71
+ continue;
72
+ }
73
+ blankCount = 0;
74
+ if (isBoilerplate(trimmed)) continue;
75
+
76
+ const normalized = normalizeWhitespace(trimmed);
77
+ const stripped = stripTimestampsAndHashes(normalized);
78
+
79
+ if (prevLine !== null && prevLine === stripped) {
80
+ repeatCount++;
81
+ continue;
82
+ }
83
+ flushRepeats();
84
+ prevLine = stripped;
85
+ repeatCount = 1;
86
+ lines.push(stripped);
87
+ }
88
+ flushRepeats();
89
+ return lines.join("\n");
90
+ }
91
+
92
+ export function aggressiveCompress(
93
+ content: string,
94
+ ext?: string,
95
+ ): string {
96
+ const result: string[] = [];
97
+ const isPython = ext === "py";
98
+ const isHtml = ext === "html" || ext === "htm" || ext === "xml" || ext === "svg";
99
+ const isSql = ext === "sql";
100
+ const isShell = ext === "sh" || ext === "bash" || ext === "zsh" || ext === "fish";
101
+ let inBlockComment = false;
102
+
103
+ for (const line of content.split("\n")) {
104
+ const trimmed = line.trim();
105
+ if (!trimmed) continue;
106
+
107
+ if (inBlockComment) {
108
+ if (trimmed.includes("*/") || (isHtml && trimmed.includes("-->"))) {
109
+ inBlockComment = false;
110
+ }
111
+ continue;
112
+ }
113
+ if (trimmed.startsWith("/*") || (isHtml && trimmed.startsWith("<!--"))) {
114
+ if (!(trimmed.includes("*/") || trimmed.includes("-->"))) {
115
+ inBlockComment = true;
116
+ }
117
+ continue;
118
+ }
119
+ if (trimmed.startsWith("//") && !trimmed.startsWith("///")) continue;
120
+ if (trimmed.startsWith("*") || trimmed.startsWith("*/")) continue;
121
+ if (isPython && trimmed.startsWith("#")) continue;
122
+ if (isSql && trimmed.startsWith("--")) continue;
123
+ if (isShell && trimmed.startsWith("#") && !trimmed.startsWith("#!")) continue;
124
+
125
+ if (/^[}\]);]+$/.test(trimmed)) {
126
+ const last = result[result.length - 1];
127
+ if (last && /^[}\]);]+$/.test(last.trim())) {
128
+ result[result.length - 1] = last + trimmed;
129
+ continue;
130
+ }
131
+ }
132
+ result.push(trimmed);
133
+ }
134
+ return result.join("\n");
135
+ }
136
+
137
+ export function safeguardRatio(original: string, compressed: string): string {
138
+ const origTokens = countTokens(original);
139
+ const compTokens = countTokens(compressed);
140
+ if (origTokens === 0) return compressed;
141
+ if (compTokens > origTokens) return original;
142
+ const ratio = compTokens / origTokens;
143
+ if (ratio < 0.05 && origTokens < 2000) return original;
144
+ return compressed;
145
+ }
146
+
147
+ export { stripAnsi };