@wooojin/forgen 0.1.0

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 (268) hide show
  1. package/.claude-plugin/plugin.json +20 -0
  2. package/CHANGELOG.md +353 -0
  3. package/CONTRIBUTING.md +98 -0
  4. package/LICENSE +21 -0
  5. package/README.ja.md +469 -0
  6. package/README.ko.md +469 -0
  7. package/README.md +483 -0
  8. package/README.zh.md +469 -0
  9. package/agents/analyst.md +98 -0
  10. package/agents/architect.md +62 -0
  11. package/agents/code-reviewer.md +120 -0
  12. package/agents/code-simplifier.md +197 -0
  13. package/agents/critic.md +70 -0
  14. package/agents/debugger.md +117 -0
  15. package/agents/designer.md +131 -0
  16. package/agents/executor.md +54 -0
  17. package/agents/explore.md +145 -0
  18. package/agents/git-master.md +212 -0
  19. package/agents/performance-reviewer.md +172 -0
  20. package/agents/planner.md +29 -0
  21. package/agents/qa-tester.md +158 -0
  22. package/agents/refactoring-expert.md +168 -0
  23. package/agents/scientist.md +144 -0
  24. package/agents/security-reviewer.md +137 -0
  25. package/agents/test-engineer.md +153 -0
  26. package/agents/verifier.md +133 -0
  27. package/agents/writer.md +184 -0
  28. package/commands/api-design.md +268 -0
  29. package/commands/architecture-decision.md +314 -0
  30. package/commands/ci-cd.md +270 -0
  31. package/commands/code-review.md +233 -0
  32. package/commands/compound.md +117 -0
  33. package/commands/database.md +263 -0
  34. package/commands/debug-detective.md +99 -0
  35. package/commands/docker.md +274 -0
  36. package/commands/documentation.md +276 -0
  37. package/commands/ecomode.md +51 -0
  38. package/commands/frontend.md +271 -0
  39. package/commands/git-master.md +90 -0
  40. package/commands/incident-response.md +292 -0
  41. package/commands/migrate.md +101 -0
  42. package/commands/performance.md +288 -0
  43. package/commands/refactor.md +105 -0
  44. package/commands/security-review.md +288 -0
  45. package/commands/tdd.md +183 -0
  46. package/commands/testing-strategy.md +265 -0
  47. package/dist/cli.d.ts +2 -0
  48. package/dist/cli.js +295 -0
  49. package/dist/core/auto-compound-runner.d.ts +12 -0
  50. package/dist/core/auto-compound-runner.js +460 -0
  51. package/dist/core/config-hooks.d.ts +10 -0
  52. package/dist/core/config-hooks.js +112 -0
  53. package/dist/core/config-injector.d.ts +50 -0
  54. package/dist/core/config-injector.js +455 -0
  55. package/dist/core/doctor.d.ts +1 -0
  56. package/dist/core/doctor.js +163 -0
  57. package/dist/core/errors.d.ts +81 -0
  58. package/dist/core/errors.js +133 -0
  59. package/dist/core/global-config.d.ts +43 -0
  60. package/dist/core/global-config.js +25 -0
  61. package/dist/core/harness.d.ts +24 -0
  62. package/dist/core/harness.js +621 -0
  63. package/dist/core/init.d.ts +7 -0
  64. package/dist/core/init.js +37 -0
  65. package/dist/core/inspect-cli.d.ts +7 -0
  66. package/dist/core/inspect-cli.js +47 -0
  67. package/dist/core/legacy-detector.d.ts +33 -0
  68. package/dist/core/legacy-detector.js +66 -0
  69. package/dist/core/logger.d.ts +34 -0
  70. package/dist/core/logger.js +121 -0
  71. package/dist/core/mcp-config.d.ts +44 -0
  72. package/dist/core/mcp-config.js +177 -0
  73. package/dist/core/notepad.d.ts +31 -0
  74. package/dist/core/notepad.js +88 -0
  75. package/dist/core/paths.d.ts +85 -0
  76. package/dist/core/paths.js +101 -0
  77. package/dist/core/plugin-detector.d.ts +44 -0
  78. package/dist/core/plugin-detector.js +226 -0
  79. package/dist/core/runtime-detector.d.ts +8 -0
  80. package/dist/core/runtime-detector.js +49 -0
  81. package/dist/core/scope-resolver.d.ts +8 -0
  82. package/dist/core/scope-resolver.js +45 -0
  83. package/dist/core/session-logger.d.ts +6 -0
  84. package/dist/core/session-logger.js +111 -0
  85. package/dist/core/session-store.d.ts +28 -0
  86. package/dist/core/session-store.js +218 -0
  87. package/dist/core/settings-lock.d.ts +18 -0
  88. package/dist/core/settings-lock.js +125 -0
  89. package/dist/core/spawn.d.ts +3 -0
  90. package/dist/core/spawn.js +135 -0
  91. package/dist/core/types.d.ts +108 -0
  92. package/dist/core/types.js +1 -0
  93. package/dist/core/uninstall.d.ts +4 -0
  94. package/dist/core/uninstall.js +307 -0
  95. package/dist/core/v1-bootstrap.d.ts +26 -0
  96. package/dist/core/v1-bootstrap.js +155 -0
  97. package/dist/engine/compound-cli.d.ts +24 -0
  98. package/dist/engine/compound-cli.js +250 -0
  99. package/dist/engine/compound-extractor.d.ts +68 -0
  100. package/dist/engine/compound-extractor.js +860 -0
  101. package/dist/engine/compound-lifecycle.d.ts +32 -0
  102. package/dist/engine/compound-lifecycle.js +305 -0
  103. package/dist/engine/compound-loop.d.ts +32 -0
  104. package/dist/engine/compound-loop.js +511 -0
  105. package/dist/engine/match-eval-log.d.ts +139 -0
  106. package/dist/engine/match-eval-log.js +270 -0
  107. package/dist/engine/phrase-blocklist.d.ts +119 -0
  108. package/dist/engine/phrase-blocklist.js +208 -0
  109. package/dist/engine/skill-promoter.d.ts +20 -0
  110. package/dist/engine/skill-promoter.js +115 -0
  111. package/dist/engine/solution-format.d.ts +160 -0
  112. package/dist/engine/solution-format.js +432 -0
  113. package/dist/engine/solution-index.d.ts +13 -0
  114. package/dist/engine/solution-index.js +252 -0
  115. package/dist/engine/solution-matcher.d.ts +364 -0
  116. package/dist/engine/solution-matcher.js +656 -0
  117. package/dist/engine/solution-writer.d.ts +76 -0
  118. package/dist/engine/solution-writer.js +157 -0
  119. package/dist/engine/term-matcher.d.ts +81 -0
  120. package/dist/engine/term-matcher.js +268 -0
  121. package/dist/engine/term-normalizer.d.ts +116 -0
  122. package/dist/engine/term-normalizer.js +171 -0
  123. package/dist/fgx.d.ts +6 -0
  124. package/dist/fgx.js +42 -0
  125. package/dist/forge/cli.d.ts +11 -0
  126. package/dist/forge/cli.js +100 -0
  127. package/dist/forge/evidence-processor.d.ts +21 -0
  128. package/dist/forge/evidence-processor.js +87 -0
  129. package/dist/forge/mismatch-detector.d.ts +44 -0
  130. package/dist/forge/mismatch-detector.js +83 -0
  131. package/dist/forge/onboarding-cli.d.ts +6 -0
  132. package/dist/forge/onboarding-cli.js +89 -0
  133. package/dist/forge/onboarding.d.ts +25 -0
  134. package/dist/forge/onboarding.js +122 -0
  135. package/dist/hooks/compound-reflection.d.ts +45 -0
  136. package/dist/hooks/compound-reflection.js +82 -0
  137. package/dist/hooks/context-guard.d.ts +24 -0
  138. package/dist/hooks/context-guard.js +156 -0
  139. package/dist/hooks/dangerous-patterns.json +18 -0
  140. package/dist/hooks/db-guard.d.ts +17 -0
  141. package/dist/hooks/db-guard.js +105 -0
  142. package/dist/hooks/hook-config.d.ts +29 -0
  143. package/dist/hooks/hook-config.js +92 -0
  144. package/dist/hooks/hook-registry.d.ts +43 -0
  145. package/dist/hooks/hook-registry.js +31 -0
  146. package/dist/hooks/hooks-generator.d.ts +49 -0
  147. package/dist/hooks/hooks-generator.js +99 -0
  148. package/dist/hooks/intent-classifier.d.ts +12 -0
  149. package/dist/hooks/intent-classifier.js +62 -0
  150. package/dist/hooks/keyword-detector.d.ts +25 -0
  151. package/dist/hooks/keyword-detector.js +389 -0
  152. package/dist/hooks/notepad-injector.d.ts +18 -0
  153. package/dist/hooks/notepad-injector.js +51 -0
  154. package/dist/hooks/permission-handler.d.ts +14 -0
  155. package/dist/hooks/permission-handler.js +114 -0
  156. package/dist/hooks/post-tool-failure.d.ts +11 -0
  157. package/dist/hooks/post-tool-failure.js +118 -0
  158. package/dist/hooks/post-tool-handlers.d.ts +17 -0
  159. package/dist/hooks/post-tool-handlers.js +115 -0
  160. package/dist/hooks/post-tool-use.d.ts +29 -0
  161. package/dist/hooks/post-tool-use.js +151 -0
  162. package/dist/hooks/pre-compact.d.ts +10 -0
  163. package/dist/hooks/pre-compact.js +165 -0
  164. package/dist/hooks/pre-tool-use.d.ts +31 -0
  165. package/dist/hooks/pre-tool-use.js +325 -0
  166. package/dist/hooks/prompt-injection-filter.d.ts +56 -0
  167. package/dist/hooks/prompt-injection-filter.js +287 -0
  168. package/dist/hooks/rate-limiter.d.ts +21 -0
  169. package/dist/hooks/rate-limiter.js +86 -0
  170. package/dist/hooks/secret-filter.d.ts +14 -0
  171. package/dist/hooks/secret-filter.js +65 -0
  172. package/dist/hooks/session-recovery.d.ts +27 -0
  173. package/dist/hooks/session-recovery.js +406 -0
  174. package/dist/hooks/shared/atomic-write.d.ts +41 -0
  175. package/dist/hooks/shared/atomic-write.js +148 -0
  176. package/dist/hooks/shared/context-budget.d.ts +37 -0
  177. package/dist/hooks/shared/context-budget.js +45 -0
  178. package/dist/hooks/shared/file-lock.d.ts +56 -0
  179. package/dist/hooks/shared/file-lock.js +253 -0
  180. package/dist/hooks/shared/hook-response.d.ts +33 -0
  181. package/dist/hooks/shared/hook-response.js +62 -0
  182. package/dist/hooks/shared/injection-caps.d.ts +39 -0
  183. package/dist/hooks/shared/injection-caps.js +52 -0
  184. package/dist/hooks/shared/plugin-signal.d.ts +23 -0
  185. package/dist/hooks/shared/plugin-signal.js +104 -0
  186. package/dist/hooks/shared/read-stdin.d.ts +8 -0
  187. package/dist/hooks/shared/read-stdin.js +63 -0
  188. package/dist/hooks/shared/sanitize-id.d.ts +7 -0
  189. package/dist/hooks/shared/sanitize-id.js +9 -0
  190. package/dist/hooks/shared/sanitize.d.ts +7 -0
  191. package/dist/hooks/shared/sanitize.js +22 -0
  192. package/dist/hooks/skill-injector.d.ts +38 -0
  193. package/dist/hooks/skill-injector.js +285 -0
  194. package/dist/hooks/slop-detector.d.ts +18 -0
  195. package/dist/hooks/slop-detector.js +93 -0
  196. package/dist/hooks/solution-injector.d.ts +58 -0
  197. package/dist/hooks/solution-injector.js +436 -0
  198. package/dist/hooks/subagent-tracker.d.ts +10 -0
  199. package/dist/hooks/subagent-tracker.js +90 -0
  200. package/dist/i18n/index.d.ts +43 -0
  201. package/dist/i18n/index.js +224 -0
  202. package/dist/lib.d.ts +14 -0
  203. package/dist/lib.js +14 -0
  204. package/dist/mcp/server.d.ts +8 -0
  205. package/dist/mcp/server.js +40 -0
  206. package/dist/mcp/solution-reader.d.ts +90 -0
  207. package/dist/mcp/solution-reader.js +273 -0
  208. package/dist/mcp/tools.d.ts +16 -0
  209. package/dist/mcp/tools.js +302 -0
  210. package/dist/preset/facet-catalog.d.ts +17 -0
  211. package/dist/preset/facet-catalog.js +46 -0
  212. package/dist/preset/preset-manager.d.ts +31 -0
  213. package/dist/preset/preset-manager.js +111 -0
  214. package/dist/renderer/inspect-renderer.d.ts +11 -0
  215. package/dist/renderer/inspect-renderer.js +123 -0
  216. package/dist/renderer/rule-renderer.d.ts +18 -0
  217. package/dist/renderer/rule-renderer.js +159 -0
  218. package/dist/store/evidence-store.d.ts +23 -0
  219. package/dist/store/evidence-store.js +58 -0
  220. package/dist/store/profile-store.d.ts +12 -0
  221. package/dist/store/profile-store.js +53 -0
  222. package/dist/store/recommendation-store.d.ts +22 -0
  223. package/dist/store/recommendation-store.js +64 -0
  224. package/dist/store/rule-store.d.ts +22 -0
  225. package/dist/store/rule-store.js +62 -0
  226. package/dist/store/session-state-store.d.ts +11 -0
  227. package/dist/store/session-state-store.js +44 -0
  228. package/dist/store/types.d.ts +159 -0
  229. package/dist/store/types.js +7 -0
  230. package/hooks/hook-registry.json +21 -0
  231. package/hooks/hooks.json +185 -0
  232. package/package.json +89 -0
  233. package/plugin.json +20 -0
  234. package/scripts/postinstall.js +826 -0
  235. package/skills/api-design/SKILL.md +262 -0
  236. package/skills/architecture-decision/SKILL.md +309 -0
  237. package/skills/ci-cd/SKILL.md +264 -0
  238. package/skills/code-review/SKILL.md +228 -0
  239. package/skills/compound/SKILL.md +101 -0
  240. package/skills/database/SKILL.md +257 -0
  241. package/skills/debug-detective/SKILL.md +95 -0
  242. package/skills/docker/SKILL.md +268 -0
  243. package/skills/documentation/SKILL.md +270 -0
  244. package/skills/ecomode/SKILL.md +46 -0
  245. package/skills/frontend/SKILL.md +265 -0
  246. package/skills/git-master/SKILL.md +86 -0
  247. package/skills/incident-response/SKILL.md +286 -0
  248. package/skills/migrate/SKILL.md +96 -0
  249. package/skills/performance/SKILL.md +282 -0
  250. package/skills/refactor/SKILL.md +100 -0
  251. package/skills/security-review/SKILL.md +282 -0
  252. package/skills/tdd/SKILL.md +178 -0
  253. package/skills/testing-strategy/SKILL.md +260 -0
  254. package/starter-pack/solutions/starter-api-error-responses.md +37 -0
  255. package/starter-pack/solutions/starter-async-patterns.md +40 -0
  256. package/starter-pack/solutions/starter-caching-strategy.md +40 -0
  257. package/starter-pack/solutions/starter-code-review-checklist.md +39 -0
  258. package/starter-pack/solutions/starter-debugging-systematic.md +40 -0
  259. package/starter-pack/solutions/starter-dependency-injection.md +40 -0
  260. package/starter-pack/solutions/starter-error-handling-patterns.md +38 -0
  261. package/starter-pack/solutions/starter-git-atomic-commits.md +36 -0
  262. package/starter-pack/solutions/starter-input-validation.md +40 -0
  263. package/starter-pack/solutions/starter-n-plus-one-queries.md +37 -0
  264. package/starter-pack/solutions/starter-refactor-safely.md +38 -0
  265. package/starter-pack/solutions/starter-secret-management.md +37 -0
  266. package/starter-pack/solutions/starter-separation-of-concerns.md +36 -0
  267. package/starter-pack/solutions/starter-tdd-red-green-refactor.md +40 -0
  268. package/starter-pack/solutions/starter-typescript-strict-types.md +39 -0
@@ -0,0 +1,250 @@
1
+ import * as fs from 'node:fs';
2
+ import * as path from 'node:path';
3
+ import { extractTags, parseFrontmatterOnly, parseSolutionV3 } from './solution-format.js';
4
+ import { mutateSolutionFile } from './solution-writer.js';
5
+ import { ME_SOLUTIONS, ME_RULES } from '../core/paths.js';
6
+ /** Scan saved compound entries and return summaries */
7
+ function scanEntries() {
8
+ const summaries = [];
9
+ const dirs = [
10
+ { dir: ME_SOLUTIONS, category: 'solution' },
11
+ { dir: ME_RULES, category: 'rule' },
12
+ ];
13
+ for (const { dir, category } of dirs) {
14
+ if (!fs.existsSync(dir))
15
+ continue;
16
+ try {
17
+ const files = fs.readdirSync(dir).filter(f => f.endsWith('.md'));
18
+ for (const file of files) {
19
+ const filePath = path.join(dir, file);
20
+ const content = fs.readFileSync(filePath, 'utf-8');
21
+ const fm = parseFrontmatterOnly(content);
22
+ if (!fm)
23
+ continue;
24
+ summaries.push({
25
+ name: fm.name,
26
+ status: fm.status,
27
+ confidence: fm.confidence,
28
+ type: fm.type,
29
+ category,
30
+ tags: fm.tags,
31
+ evidence: fm.evidence,
32
+ created: fm.created,
33
+ filePath,
34
+ });
35
+ }
36
+ }
37
+ catch { /* 개별 솔루션 파일 파싱 실패 무시 — 손상된 파일은 건너뛰기 */ }
38
+ }
39
+ return summaries;
40
+ }
41
+ /** Status icon */
42
+ function statusIcon(status) {
43
+ switch (status) {
44
+ case 'mature': return 'M';
45
+ case 'verified': return 'V';
46
+ case 'candidate': return 'C';
47
+ case 'experiment': return 'E';
48
+ case 'retired': return 'R';
49
+ default: return '?';
50
+ }
51
+ }
52
+ /** List all solutions with status summary */
53
+ export function listSolutions() {
54
+ const entries = scanEntries();
55
+ if (entries.length === 0) {
56
+ console.log('\n No compound entries found.\n');
57
+ return;
58
+ }
59
+ // Group by status
60
+ const groups = {};
61
+ for (const entry of entries) {
62
+ if (!groups[entry.status])
63
+ groups[entry.status] = [];
64
+ groups[entry.status].push(entry);
65
+ }
66
+ const order = ['mature', 'verified', 'candidate', 'experiment', 'retired'];
67
+ console.log('\n Compound Entries\n');
68
+ let total = 0;
69
+ for (const status of order) {
70
+ const group = groups[status];
71
+ if (!group || group.length === 0)
72
+ continue;
73
+ total += group.length;
74
+ console.log(` [${statusIcon(status)}] ${status} (${group.length})`);
75
+ for (const entry of group) {
76
+ const ev = entry.evidence;
77
+ const evStr = `inj:${ev.injected} ref:${ev.reflected} neg:${ev.negative}`;
78
+ console.log(` ${entry.name} [${entry.category}] (${entry.confidence.toFixed(2)}) ${evStr} [${entry.tags.slice(0, 3).join(', ')}]`);
79
+ }
80
+ }
81
+ console.log(`\n Total: ${total} entries\n`);
82
+ }
83
+ /** Inspect a single saved entry in detail */
84
+ export function inspectSolution(name) {
85
+ const entries = scanEntries();
86
+ const entry = entries.find(s => s.name === name);
87
+ if (!entry) {
88
+ console.log(`\n Entry "${name}" not found.\n`);
89
+ return;
90
+ }
91
+ // Read full content
92
+ const content = fs.readFileSync(entry.filePath, 'utf-8');
93
+ const full = parseSolutionV3(content);
94
+ console.log(`\n Entry: ${entry.name}`);
95
+ console.log(` Category: ${entry.category}`);
96
+ console.log(` Status: ${entry.status} (confidence: ${entry.confidence.toFixed(2)})`);
97
+ console.log(` Type: ${entry.type}`);
98
+ console.log(` Tags: [${entry.tags.join(', ')}]`);
99
+ console.log(` Created: ${entry.created}`);
100
+ console.log(` Evidence:`);
101
+ console.log(` injected: ${entry.evidence.injected}`);
102
+ console.log(` reflected: ${entry.evidence.reflected}`);
103
+ console.log(` negative: ${entry.evidence.negative}`);
104
+ console.log(` sessions: ${entry.evidence.sessions}`);
105
+ console.log(` reExtracted: ${entry.evidence.reExtracted}`);
106
+ if (full) {
107
+ if (full.context)
108
+ console.log(`\n Context: ${full.context}`);
109
+ if (full.content)
110
+ console.log(`\n Content:\n ${full.content.split('\n').join('\n ')}`);
111
+ }
112
+ console.log(`\n File: ${entry.filePath}\n`);
113
+ }
114
+ /** Remove a saved entry by name */
115
+ export function removeSolution(name) {
116
+ const entries = scanEntries();
117
+ const entry = entries.find(s => s.name === name);
118
+ if (!entry) {
119
+ console.log(`\n Entry "${name}" not found.\n`);
120
+ return;
121
+ }
122
+ try {
123
+ fs.unlinkSync(entry.filePath);
124
+ console.log(`\n Removed: ${name} [${entry.category}] (${entry.filePath})\n`);
125
+ }
126
+ catch (e) {
127
+ console.log(`\n Failed to remove: ${e.message}\n`);
128
+ }
129
+ }
130
+ /**
131
+ * Names of extractors that have been removed from the compound pipeline.
132
+ * Any solution file on disk whose `name` matches one of these entries is
133
+ * an artifact of a removed extractor and should be retired.
134
+ *
135
+ * Current list (added by C4 cleanup, 2026-04-09):
136
+ * - `recurring-task-pattern`: word-frequency histogram, not a real
137
+ * pattern. Observed injected 105+ times in production.
138
+ * - `modification-hotspot`: directory modification count with generic
139
+ * "maybe refactor this" advice.
140
+ *
141
+ * Add future entries here whenever an extractor is removed from
142
+ * `compound-extractor.ts` so users can clean up their stores.
143
+ */
144
+ const STALE_EXTRACTOR_NAMES = [
145
+ 'recurring-task-pattern',
146
+ 'modification-hotspot',
147
+ ];
148
+ /**
149
+ * Retire solutions whose names match extractors that have been removed
150
+ * from the compound pipeline. Retired solutions are excluded from the
151
+ * index (see `solution-index.ts:142`) so they stop being surfaced in
152
+ * MCP search and hook injection, but the file stays on disk for
153
+ * audit / undo purposes.
154
+ *
155
+ * M-3 (2026-04-09): the C4 extractor cleanup left orphaned files in
156
+ * users' `~/.forgen/me/solutions/` directories. Without this migration,
157
+ * files like `recurring-task-pattern.md` (which had injected=113 on
158
+ * the author's own machine at fix time) continue to pollute matching
159
+ * results until the user manually deletes them.
160
+ */
161
+ export function cleanStaleSolutions() {
162
+ const entries = scanEntries().filter(e => e.category === 'solution');
163
+ const stale = entries.filter(e => STALE_EXTRACTOR_NAMES.includes(e.name));
164
+ if (stale.length === 0) {
165
+ console.log('\n No stale extractor artifacts found.\n');
166
+ return;
167
+ }
168
+ console.log(`\n Found ${stale.length} stale extractor artifact(s):`);
169
+ for (const entry of stale) {
170
+ console.log(` - ${entry.name} (status: ${entry.status}, injected: ${entry.evidence.injected})`);
171
+ }
172
+ console.log();
173
+ let retired = 0;
174
+ for (const entry of stale) {
175
+ // Skip already-retired files — idempotent.
176
+ if (entry.status === 'retired') {
177
+ console.log(` ✓ ${entry.name} already retired, skipping`);
178
+ continue;
179
+ }
180
+ const ok = mutateSolutionFile(entry.filePath, sol => {
181
+ sol.frontmatter.status = 'retired';
182
+ sol.frontmatter.updated = new Date().toISOString().split('T')[0];
183
+ return true;
184
+ });
185
+ if (ok) {
186
+ retired++;
187
+ console.log(` ✗ ${entry.name} retired`);
188
+ }
189
+ else {
190
+ console.log(` ! ${entry.name} failed to update`);
191
+ }
192
+ }
193
+ console.log(`\n Retired ${retired}/${stale.length} stale artifact(s).\n`);
194
+ console.log(' Tip: retired files are excluded from index + MCP search but');
195
+ console.log(' remain on disk. Use `forgen compound remove <name>` to delete.\n');
196
+ }
197
+ /** Retag all solutions using improved extractTags */
198
+ export function retagSolutions() {
199
+ const entries = scanEntries().filter(e => e.category === 'solution');
200
+ if (entries.length === 0) {
201
+ console.log('\n No solutions to retag.\n');
202
+ return;
203
+ }
204
+ // PR2b: solution-writer.mutateSolutionFile로 통합. lock + fresh re-read.
205
+ // 동시 hook이 같은 .md를 update해도 retag 결과가 손실되지 않는다.
206
+ let retagged = 0;
207
+ for (const entry of entries) {
208
+ const ok = mutateSolutionFile(entry.filePath, sol => {
209
+ const source = [sol.context, sol.content].filter(Boolean).join(' ');
210
+ const newTags = extractTags(source);
211
+ sol.frontmatter.tags = newTags;
212
+ return true;
213
+ });
214
+ if (ok)
215
+ retagged++;
216
+ else
217
+ console.log(` Failed: ${entry.name}`);
218
+ }
219
+ console.log(`\n Retagged ${retagged}/${entries.length} solutions.\n`);
220
+ }
221
+ /** Rollback auto-extracted solutions since a given date */
222
+ export function rollbackSolutions(sinceDate) {
223
+ const since = new Date(sinceDate);
224
+ if (Number.isNaN(since.getTime())) {
225
+ console.log(`\n Invalid date: ${sinceDate}\n`);
226
+ return;
227
+ }
228
+ const solutions = scanEntries().filter((entry) => entry.category === 'solution');
229
+ const toRemove = solutions.filter((solution) => {
230
+ if (solution.evidence.reflected > 0 || solution.evidence.sessions > 0)
231
+ return false; // keep used ones
232
+ const created = new Date(solution.created);
233
+ return created >= since;
234
+ });
235
+ if (toRemove.length === 0) {
236
+ console.log(`\n No solutions to rollback since ${sinceDate}.\n`);
237
+ return;
238
+ }
239
+ console.log(`\n Rolling back ${toRemove.length} solutions since ${sinceDate}:\n`);
240
+ for (const sol of toRemove) {
241
+ try {
242
+ fs.unlinkSync(sol.filePath);
243
+ console.log(` Removed: ${sol.name}`);
244
+ }
245
+ catch {
246
+ console.log(` Failed: ${sol.name}`);
247
+ }
248
+ }
249
+ console.log();
250
+ }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Forgen — Compound Knowledge Extractor
3
+ *
4
+ * Extracts reusable patterns and decisions from git history and session context.
5
+ * Runs quality gates (structure, toxicity, trivial, dedup) before persisting solutions.
6
+ *
7
+ * Module Structure:
8
+ * - Lines 1-50: Imports, constants, SHA validation, LastExtraction/ExtractedSolution interfaces
9
+ * - Lines 50-115: Git helpers — getNewCommits, getCommitMessages, getGitDiff, getDiffStats
10
+ * - Lines 115-190: Quality Gates — gate0 (worth extracting), gate1 (structure), gate2 (toxicity),
11
+ * gateTrivial (trivial rejection), gate3 (dedup)
12
+ * - Lines 190-275: extractFromDiff — pattern extraction from git diff (modules, errors, imports, commits)
13
+ * - Lines 275-395: extractFromSessionContext — prompt/write history analysis (actions, hotspots, tech)
14
+ * - Lines 396-475: saveExtractedSolution, updateReExtractedCounter — solution persistence
15
+ * - Lines 477-555: runExtraction — main entry point orchestrating gates + extraction + state
16
+ * - Lines 557-634: processExtractionResults, isExtractionPaused, pauseExtraction, resumeExtraction
17
+ */
18
+ import type { SolutionType } from './solution-format.js';
19
+ interface ExtractedSolution {
20
+ name: string;
21
+ type: SolutionType;
22
+ tags: string[];
23
+ identifiers: string[];
24
+ context: string;
25
+ content: string;
26
+ }
27
+ interface WriteContextEntry {
28
+ filePath: string;
29
+ contentSnippet: string;
30
+ fileExtension: string;
31
+ }
32
+ /**
33
+ * Load Claude session prompts + writes correlated to `cwd`.
34
+ *
35
+ * Exported primarily for test assertions (the `claude-session-context`
36
+ * tests need to verify that correlation picks the right project's
37
+ * sessions and ignores unrelated ones). Before C4 the tests could
38
+ * observe this indirectly via the now-removed `recurring-task-pattern`
39
+ * extractor; now they check this loader directly. Not intended for
40
+ * production callers outside the extractor pipeline.
41
+ */
42
+ export declare function loadClaudeProjectSessionContext(cwd: string, lastExtractedAt: string): {
43
+ prompts: string[];
44
+ writes: WriteContextEntry[];
45
+ };
46
+ export declare function previewExtraction(cwd: string): Promise<{
47
+ preview: ExtractedSolution[];
48
+ skipped: string[];
49
+ reason?: string;
50
+ }>;
51
+ /** Main extraction function — called from SessionStart or CLI */
52
+ export declare function runExtraction(cwd: string, sessionId: string): Promise<{
53
+ extracted: string[];
54
+ skipped: string[];
55
+ reason?: string;
56
+ }>;
57
+ /** Process LLM extraction results (called after LLM returns) */
58
+ export declare function processExtractionResults(rawJson: string, sessionId: string): {
59
+ saved: string[];
60
+ skipped: string[];
61
+ };
62
+ /** Check if extraction is paused */
63
+ export declare function isExtractionPaused(): boolean;
64
+ /** Pause auto-extraction */
65
+ export declare function pauseExtraction(): void;
66
+ /** Resume auto-extraction */
67
+ export declare function resumeExtraction(): void;
68
+ export {};