@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.
- package/.claude-plugin/plugin.json +20 -0
- package/CHANGELOG.md +353 -0
- package/CONTRIBUTING.md +98 -0
- package/LICENSE +21 -0
- package/README.ja.md +469 -0
- package/README.ko.md +469 -0
- package/README.md +483 -0
- package/README.zh.md +469 -0
- package/agents/analyst.md +98 -0
- package/agents/architect.md +62 -0
- package/agents/code-reviewer.md +120 -0
- package/agents/code-simplifier.md +197 -0
- package/agents/critic.md +70 -0
- package/agents/debugger.md +117 -0
- package/agents/designer.md +131 -0
- package/agents/executor.md +54 -0
- package/agents/explore.md +145 -0
- package/agents/git-master.md +212 -0
- package/agents/performance-reviewer.md +172 -0
- package/agents/planner.md +29 -0
- package/agents/qa-tester.md +158 -0
- package/agents/refactoring-expert.md +168 -0
- package/agents/scientist.md +144 -0
- package/agents/security-reviewer.md +137 -0
- package/agents/test-engineer.md +153 -0
- package/agents/verifier.md +133 -0
- package/agents/writer.md +184 -0
- package/commands/api-design.md +268 -0
- package/commands/architecture-decision.md +314 -0
- package/commands/ci-cd.md +270 -0
- package/commands/code-review.md +233 -0
- package/commands/compound.md +117 -0
- package/commands/database.md +263 -0
- package/commands/debug-detective.md +99 -0
- package/commands/docker.md +274 -0
- package/commands/documentation.md +276 -0
- package/commands/ecomode.md +51 -0
- package/commands/frontend.md +271 -0
- package/commands/git-master.md +90 -0
- package/commands/incident-response.md +292 -0
- package/commands/migrate.md +101 -0
- package/commands/performance.md +288 -0
- package/commands/refactor.md +105 -0
- package/commands/security-review.md +288 -0
- package/commands/tdd.md +183 -0
- package/commands/testing-strategy.md +265 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +295 -0
- package/dist/core/auto-compound-runner.d.ts +12 -0
- package/dist/core/auto-compound-runner.js +460 -0
- package/dist/core/config-hooks.d.ts +10 -0
- package/dist/core/config-hooks.js +112 -0
- package/dist/core/config-injector.d.ts +50 -0
- package/dist/core/config-injector.js +455 -0
- package/dist/core/doctor.d.ts +1 -0
- package/dist/core/doctor.js +163 -0
- package/dist/core/errors.d.ts +81 -0
- package/dist/core/errors.js +133 -0
- package/dist/core/global-config.d.ts +43 -0
- package/dist/core/global-config.js +25 -0
- package/dist/core/harness.d.ts +24 -0
- package/dist/core/harness.js +621 -0
- package/dist/core/init.d.ts +7 -0
- package/dist/core/init.js +37 -0
- package/dist/core/inspect-cli.d.ts +7 -0
- package/dist/core/inspect-cli.js +47 -0
- package/dist/core/legacy-detector.d.ts +33 -0
- package/dist/core/legacy-detector.js +66 -0
- package/dist/core/logger.d.ts +34 -0
- package/dist/core/logger.js +121 -0
- package/dist/core/mcp-config.d.ts +44 -0
- package/dist/core/mcp-config.js +177 -0
- package/dist/core/notepad.d.ts +31 -0
- package/dist/core/notepad.js +88 -0
- package/dist/core/paths.d.ts +85 -0
- package/dist/core/paths.js +101 -0
- package/dist/core/plugin-detector.d.ts +44 -0
- package/dist/core/plugin-detector.js +226 -0
- package/dist/core/runtime-detector.d.ts +8 -0
- package/dist/core/runtime-detector.js +49 -0
- package/dist/core/scope-resolver.d.ts +8 -0
- package/dist/core/scope-resolver.js +45 -0
- package/dist/core/session-logger.d.ts +6 -0
- package/dist/core/session-logger.js +111 -0
- package/dist/core/session-store.d.ts +28 -0
- package/dist/core/session-store.js +218 -0
- package/dist/core/settings-lock.d.ts +18 -0
- package/dist/core/settings-lock.js +125 -0
- package/dist/core/spawn.d.ts +3 -0
- package/dist/core/spawn.js +135 -0
- package/dist/core/types.d.ts +108 -0
- package/dist/core/types.js +1 -0
- package/dist/core/uninstall.d.ts +4 -0
- package/dist/core/uninstall.js +307 -0
- package/dist/core/v1-bootstrap.d.ts +26 -0
- package/dist/core/v1-bootstrap.js +155 -0
- package/dist/engine/compound-cli.d.ts +24 -0
- package/dist/engine/compound-cli.js +250 -0
- package/dist/engine/compound-extractor.d.ts +68 -0
- package/dist/engine/compound-extractor.js +860 -0
- package/dist/engine/compound-lifecycle.d.ts +32 -0
- package/dist/engine/compound-lifecycle.js +305 -0
- package/dist/engine/compound-loop.d.ts +32 -0
- package/dist/engine/compound-loop.js +511 -0
- package/dist/engine/match-eval-log.d.ts +139 -0
- package/dist/engine/match-eval-log.js +270 -0
- package/dist/engine/phrase-blocklist.d.ts +119 -0
- package/dist/engine/phrase-blocklist.js +208 -0
- package/dist/engine/skill-promoter.d.ts +20 -0
- package/dist/engine/skill-promoter.js +115 -0
- package/dist/engine/solution-format.d.ts +160 -0
- package/dist/engine/solution-format.js +432 -0
- package/dist/engine/solution-index.d.ts +13 -0
- package/dist/engine/solution-index.js +252 -0
- package/dist/engine/solution-matcher.d.ts +364 -0
- package/dist/engine/solution-matcher.js +656 -0
- package/dist/engine/solution-writer.d.ts +76 -0
- package/dist/engine/solution-writer.js +157 -0
- package/dist/engine/term-matcher.d.ts +81 -0
- package/dist/engine/term-matcher.js +268 -0
- package/dist/engine/term-normalizer.d.ts +116 -0
- package/dist/engine/term-normalizer.js +171 -0
- package/dist/fgx.d.ts +6 -0
- package/dist/fgx.js +42 -0
- package/dist/forge/cli.d.ts +11 -0
- package/dist/forge/cli.js +100 -0
- package/dist/forge/evidence-processor.d.ts +21 -0
- package/dist/forge/evidence-processor.js +87 -0
- package/dist/forge/mismatch-detector.d.ts +44 -0
- package/dist/forge/mismatch-detector.js +83 -0
- package/dist/forge/onboarding-cli.d.ts +6 -0
- package/dist/forge/onboarding-cli.js +89 -0
- package/dist/forge/onboarding.d.ts +25 -0
- package/dist/forge/onboarding.js +122 -0
- package/dist/hooks/compound-reflection.d.ts +45 -0
- package/dist/hooks/compound-reflection.js +82 -0
- package/dist/hooks/context-guard.d.ts +24 -0
- package/dist/hooks/context-guard.js +156 -0
- package/dist/hooks/dangerous-patterns.json +18 -0
- package/dist/hooks/db-guard.d.ts +17 -0
- package/dist/hooks/db-guard.js +105 -0
- package/dist/hooks/hook-config.d.ts +29 -0
- package/dist/hooks/hook-config.js +92 -0
- package/dist/hooks/hook-registry.d.ts +43 -0
- package/dist/hooks/hook-registry.js +31 -0
- package/dist/hooks/hooks-generator.d.ts +49 -0
- package/dist/hooks/hooks-generator.js +99 -0
- package/dist/hooks/intent-classifier.d.ts +12 -0
- package/dist/hooks/intent-classifier.js +62 -0
- package/dist/hooks/keyword-detector.d.ts +25 -0
- package/dist/hooks/keyword-detector.js +389 -0
- package/dist/hooks/notepad-injector.d.ts +18 -0
- package/dist/hooks/notepad-injector.js +51 -0
- package/dist/hooks/permission-handler.d.ts +14 -0
- package/dist/hooks/permission-handler.js +114 -0
- package/dist/hooks/post-tool-failure.d.ts +11 -0
- package/dist/hooks/post-tool-failure.js +118 -0
- package/dist/hooks/post-tool-handlers.d.ts +17 -0
- package/dist/hooks/post-tool-handlers.js +115 -0
- package/dist/hooks/post-tool-use.d.ts +29 -0
- package/dist/hooks/post-tool-use.js +151 -0
- package/dist/hooks/pre-compact.d.ts +10 -0
- package/dist/hooks/pre-compact.js +165 -0
- package/dist/hooks/pre-tool-use.d.ts +31 -0
- package/dist/hooks/pre-tool-use.js +325 -0
- package/dist/hooks/prompt-injection-filter.d.ts +56 -0
- package/dist/hooks/prompt-injection-filter.js +287 -0
- package/dist/hooks/rate-limiter.d.ts +21 -0
- package/dist/hooks/rate-limiter.js +86 -0
- package/dist/hooks/secret-filter.d.ts +14 -0
- package/dist/hooks/secret-filter.js +65 -0
- package/dist/hooks/session-recovery.d.ts +27 -0
- package/dist/hooks/session-recovery.js +406 -0
- package/dist/hooks/shared/atomic-write.d.ts +41 -0
- package/dist/hooks/shared/atomic-write.js +148 -0
- package/dist/hooks/shared/context-budget.d.ts +37 -0
- package/dist/hooks/shared/context-budget.js +45 -0
- package/dist/hooks/shared/file-lock.d.ts +56 -0
- package/dist/hooks/shared/file-lock.js +253 -0
- package/dist/hooks/shared/hook-response.d.ts +33 -0
- package/dist/hooks/shared/hook-response.js +62 -0
- package/dist/hooks/shared/injection-caps.d.ts +39 -0
- package/dist/hooks/shared/injection-caps.js +52 -0
- package/dist/hooks/shared/plugin-signal.d.ts +23 -0
- package/dist/hooks/shared/plugin-signal.js +104 -0
- package/dist/hooks/shared/read-stdin.d.ts +8 -0
- package/dist/hooks/shared/read-stdin.js +63 -0
- package/dist/hooks/shared/sanitize-id.d.ts +7 -0
- package/dist/hooks/shared/sanitize-id.js +9 -0
- package/dist/hooks/shared/sanitize.d.ts +7 -0
- package/dist/hooks/shared/sanitize.js +22 -0
- package/dist/hooks/skill-injector.d.ts +38 -0
- package/dist/hooks/skill-injector.js +285 -0
- package/dist/hooks/slop-detector.d.ts +18 -0
- package/dist/hooks/slop-detector.js +93 -0
- package/dist/hooks/solution-injector.d.ts +58 -0
- package/dist/hooks/solution-injector.js +436 -0
- package/dist/hooks/subagent-tracker.d.ts +10 -0
- package/dist/hooks/subagent-tracker.js +90 -0
- package/dist/i18n/index.d.ts +43 -0
- package/dist/i18n/index.js +224 -0
- package/dist/lib.d.ts +14 -0
- package/dist/lib.js +14 -0
- package/dist/mcp/server.d.ts +8 -0
- package/dist/mcp/server.js +40 -0
- package/dist/mcp/solution-reader.d.ts +90 -0
- package/dist/mcp/solution-reader.js +273 -0
- package/dist/mcp/tools.d.ts +16 -0
- package/dist/mcp/tools.js +302 -0
- package/dist/preset/facet-catalog.d.ts +17 -0
- package/dist/preset/facet-catalog.js +46 -0
- package/dist/preset/preset-manager.d.ts +31 -0
- package/dist/preset/preset-manager.js +111 -0
- package/dist/renderer/inspect-renderer.d.ts +11 -0
- package/dist/renderer/inspect-renderer.js +123 -0
- package/dist/renderer/rule-renderer.d.ts +18 -0
- package/dist/renderer/rule-renderer.js +159 -0
- package/dist/store/evidence-store.d.ts +23 -0
- package/dist/store/evidence-store.js +58 -0
- package/dist/store/profile-store.d.ts +12 -0
- package/dist/store/profile-store.js +53 -0
- package/dist/store/recommendation-store.d.ts +22 -0
- package/dist/store/recommendation-store.js +64 -0
- package/dist/store/rule-store.d.ts +22 -0
- package/dist/store/rule-store.js +62 -0
- package/dist/store/session-state-store.d.ts +11 -0
- package/dist/store/session-state-store.js +44 -0
- package/dist/store/types.d.ts +159 -0
- package/dist/store/types.js +7 -0
- package/hooks/hook-registry.json +21 -0
- package/hooks/hooks.json +185 -0
- package/package.json +89 -0
- package/plugin.json +20 -0
- package/scripts/postinstall.js +826 -0
- package/skills/api-design/SKILL.md +262 -0
- package/skills/architecture-decision/SKILL.md +309 -0
- package/skills/ci-cd/SKILL.md +264 -0
- package/skills/code-review/SKILL.md +228 -0
- package/skills/compound/SKILL.md +101 -0
- package/skills/database/SKILL.md +257 -0
- package/skills/debug-detective/SKILL.md +95 -0
- package/skills/docker/SKILL.md +268 -0
- package/skills/documentation/SKILL.md +270 -0
- package/skills/ecomode/SKILL.md +46 -0
- package/skills/frontend/SKILL.md +265 -0
- package/skills/git-master/SKILL.md +86 -0
- package/skills/incident-response/SKILL.md +286 -0
- package/skills/migrate/SKILL.md +96 -0
- package/skills/performance/SKILL.md +282 -0
- package/skills/refactor/SKILL.md +100 -0
- package/skills/security-review/SKILL.md +282 -0
- package/skills/tdd/SKILL.md +178 -0
- package/skills/testing-strategy/SKILL.md +260 -0
- package/starter-pack/solutions/starter-api-error-responses.md +37 -0
- package/starter-pack/solutions/starter-async-patterns.md +40 -0
- package/starter-pack/solutions/starter-caching-strategy.md +40 -0
- package/starter-pack/solutions/starter-code-review-checklist.md +39 -0
- package/starter-pack/solutions/starter-debugging-systematic.md +40 -0
- package/starter-pack/solutions/starter-dependency-injection.md +40 -0
- package/starter-pack/solutions/starter-error-handling-patterns.md +38 -0
- package/starter-pack/solutions/starter-git-atomic-commits.md +36 -0
- package/starter-pack/solutions/starter-input-validation.md +40 -0
- package/starter-pack/solutions/starter-n-plus-one-queries.md +37 -0
- package/starter-pack/solutions/starter-refactor-safely.md +38 -0
- package/starter-pack/solutions/starter-secret-management.md +37 -0
- package/starter-pack/solutions/starter-separation-of-concerns.md +36 -0
- package/starter-pack/solutions/starter-tdd-red-green-refactor.md +40 -0
- 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 {};
|