@wooojin/forgen 0.2.1 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +76 -0
- package/README.ko.md +25 -14
- package/README.md +61 -17
- package/agents/analyst.md +48 -4
- package/agents/architect.md +39 -4
- package/agents/code-reviewer.md +107 -77
- package/agents/critic.md +47 -4
- package/agents/debugger.md +46 -4
- package/agents/designer.md +40 -4
- package/agents/executor.md +112 -30
- package/agents/explore.md +45 -5
- package/agents/git-master.md +48 -4
- package/agents/planner.md +121 -18
- package/agents/solution-evolver.md +115 -0
- package/agents/test-engineer.md +58 -4
- package/agents/verifier.md +92 -77
- package/commands/architecture-decision.md +127 -258
- package/commands/calibrate.md +225 -0
- package/commands/code-review.md +163 -178
- package/commands/compound.md +127 -68
- package/commands/deep-interview.md +212 -110
- package/commands/docker.md +68 -178
- package/commands/forge-loop.md +215 -0
- package/commands/learn.md +231 -0
- package/commands/retro.md +215 -0
- package/commands/ship.md +277 -0
- package/dist/cli.js +25 -9
- package/dist/core/auto-compound-runner.js +14 -0
- package/dist/core/config-injector.d.ts +2 -1
- package/dist/core/config-injector.js +2 -1
- package/dist/core/dashboard.d.ts +17 -0
- package/dist/core/dashboard.js +158 -2
- package/dist/core/harness.d.ts +6 -1
- package/dist/core/harness.js +75 -19
- package/dist/core/paths.d.ts +31 -1
- package/dist/core/paths.js +43 -2
- package/dist/core/spawn.d.ts +3 -2
- package/dist/core/spawn.js +27 -8
- package/dist/core/types.d.ts +34 -0
- package/dist/engine/compound-lifecycle.d.ts +4 -3
- package/dist/engine/compound-lifecycle.js +91 -46
- package/dist/engine/learn-cli.d.ts +1 -0
- package/dist/engine/learn-cli.js +182 -0
- package/dist/engine/meta-learning/adaptive-thresholds.d.ts +20 -0
- package/dist/engine/meta-learning/adaptive-thresholds.js +126 -0
- package/dist/engine/meta-learning/extraction-tuner.d.ts +15 -0
- package/dist/engine/meta-learning/extraction-tuner.js +99 -0
- package/dist/engine/meta-learning/matcher-weight-tuner.d.ts +21 -0
- package/dist/engine/meta-learning/matcher-weight-tuner.js +151 -0
- package/dist/engine/meta-learning/runner.d.ts +14 -0
- package/dist/engine/meta-learning/runner.js +90 -0
- package/dist/engine/meta-learning/scope-promoter.d.ts +21 -0
- package/dist/engine/meta-learning/scope-promoter.js +84 -0
- package/dist/engine/meta-learning/session-quality-scorer.d.ts +61 -0
- package/dist/engine/meta-learning/session-quality-scorer.js +166 -0
- package/dist/engine/meta-learning/types.d.ts +114 -0
- package/dist/engine/meta-learning/types.js +43 -0
- package/dist/engine/solution-candidate.d.ts +30 -0
- package/dist/engine/solution-candidate.js +124 -0
- package/dist/engine/solution-fitness.d.ts +52 -0
- package/dist/engine/solution-fitness.js +95 -0
- package/dist/engine/solution-fixup.d.ts +30 -0
- package/dist/engine/solution-fixup.js +116 -0
- package/dist/engine/solution-format.d.ts +10 -2
- package/dist/engine/solution-format.js +287 -57
- package/dist/engine/solution-index.d.ts +1 -1
- package/dist/engine/solution-index.js +10 -0
- package/dist/engine/solution-matcher.d.ts +7 -1
- package/dist/engine/solution-matcher.js +137 -37
- package/dist/engine/solution-outcomes.d.ts +70 -0
- package/dist/engine/solution-outcomes.js +242 -0
- package/dist/engine/solution-quarantine.d.ts +36 -0
- package/dist/engine/solution-quarantine.js +172 -0
- package/dist/engine/solution-weakness.d.ts +45 -0
- package/dist/engine/solution-weakness.js +225 -0
- package/dist/engine/solution-writer.d.ts +5 -0
- package/dist/engine/solution-writer.js +18 -0
- package/dist/fgx.js +12 -8
- package/dist/hooks/context-guard.d.ts +5 -0
- package/dist/hooks/context-guard.js +118 -2
- package/dist/hooks/hooks-generator.d.ts +3 -0
- package/dist/hooks/hooks-generator.js +23 -6
- package/dist/hooks/keyword-detector.js +16 -100
- package/dist/hooks/post-tool-failure.js +7 -0
- package/dist/hooks/skill-injector.d.ts +4 -3
- package/dist/hooks/skill-injector.js +6 -4
- package/dist/hooks/solution-injector.js +20 -0
- package/dist/host/codex-adapter.d.ts +10 -0
- package/dist/host/codex-adapter.js +154 -0
- package/dist/mcp/solution-reader.d.ts +5 -5
- package/dist/mcp/solution-reader.js +34 -24
- package/dist/mcp/tools.js +8 -0
- package/dist/services/session.d.ts +19 -0
- package/dist/services/session.js +62 -0
- package/hooks/hooks.json +2 -2
- package/package.json +2 -1
- package/skills/architecture-decision/SKILL.md +113 -257
- package/skills/calibrate/SKILL.md +207 -0
- package/skills/code-review/SKILL.md +151 -178
- package/skills/compound/SKILL.md +126 -68
- package/skills/deep-interview/SKILL.md +210 -110
- package/skills/docker/SKILL.md +57 -179
- package/skills/forge-loop/SKILL.md +198 -0
- package/skills/learn/SKILL.md +216 -0
- package/skills/retro/SKILL.md +199 -0
- package/skills/ship/SKILL.md +259 -0
- package/agents/code-simplifier.md +0 -197
- package/agents/performance-reviewer.md +0 -172
- package/agents/qa-tester.md +0 -158
- package/agents/refactoring-expert.md +0 -168
- package/agents/scientist.md +0 -144
- package/agents/security-reviewer.md +0 -137
- package/agents/writer.md +0 -184
- package/commands/api-design.md +0 -268
- package/commands/ci-cd.md +0 -270
- package/commands/database.md +0 -263
- package/commands/debug-detective.md +0 -99
- package/commands/documentation.md +0 -276
- package/commands/ecomode.md +0 -51
- package/commands/frontend.md +0 -271
- package/commands/git-master.md +0 -90
- package/commands/incident-response.md +0 -292
- package/commands/migrate.md +0 -101
- package/commands/performance.md +0 -288
- package/commands/refactor.md +0 -105
- package/commands/security-review.md +0 -288
- package/commands/specify.md +0 -128
- package/commands/tdd.md +0 -183
- package/commands/testing-strategy.md +0 -265
- package/skills/api-design/SKILL.md +0 -262
- package/skills/ci-cd/SKILL.md +0 -264
- package/skills/database/SKILL.md +0 -257
- package/skills/debug-detective/SKILL.md +0 -95
- package/skills/documentation/SKILL.md +0 -270
- package/skills/ecomode/SKILL.md +0 -46
- package/skills/frontend/SKILL.md +0 -265
- package/skills/git-master/SKILL.md +0 -86
- package/skills/incident-response/SKILL.md +0 -286
- package/skills/migrate/SKILL.md +0 -96
- package/skills/performance/SKILL.md +0 -282
- package/skills/refactor/SKILL.md +0 -100
- package/skills/security-review/SKILL.md +0 -282
- package/skills/specify/SKILL.md +0 -122
- package/skills/tdd/SKILL.md +0 -178
- package/skills/testing-strategy/SKILL.md +0 -260
|
@@ -16,14 +16,13 @@
|
|
|
16
16
|
import * as fs from 'node:fs';
|
|
17
17
|
import * as path from 'node:path';
|
|
18
18
|
import { ME_SOLUTIONS, PACKS_DIR } from '../core/paths.js';
|
|
19
|
-
import {
|
|
20
|
-
import { extractTags, expandCompoundTags, expandQueryBigrams } from '../engine/solution-format.js';
|
|
21
|
-
import { parseSolutionV3 } from '../engine/solution-format.js';
|
|
19
|
+
import { logMatchDecision } from '../engine/match-eval-log.js';
|
|
22
20
|
import { maskBlockedTokens } from '../engine/phrase-blocklist.js';
|
|
23
|
-
import {
|
|
21
|
+
import { expandCompoundTags, expandQueryBigrams, extractTags, parseSolutionV3, } from '../engine/solution-format.js';
|
|
22
|
+
import { getOrBuildIndex } from '../engine/solution-index.js';
|
|
24
23
|
import { calculateRelevance, shouldRejectByR4T3Rules } from '../engine/solution-matcher.js';
|
|
24
|
+
import { mutateSolutionFile } from '../engine/solution-writer.js';
|
|
25
25
|
import { defaultNormalizer } from '../engine/term-normalizer.js';
|
|
26
|
-
import { logMatchDecision } from '../engine/match-eval-log.js';
|
|
27
26
|
import { filterSolutionContent } from '../hooks/prompt-injection-filter.js';
|
|
28
27
|
// ── 디렉토리 해석 ──
|
|
29
28
|
/**
|
|
@@ -31,9 +30,7 @@ import { filterSolutionContent } from '../hooks/prompt-injection-filter.js';
|
|
|
31
30
|
* MCP 서버에서 cwd를 전달받으면 project 스코프도 포함.
|
|
32
31
|
*/
|
|
33
32
|
export function defaultSolutionDirs(cwd) {
|
|
34
|
-
const dirs = [
|
|
35
|
-
{ dir: ME_SOLUTIONS, scope: 'me' },
|
|
36
|
-
];
|
|
33
|
+
const dirs = [{ dir: ME_SOLUTIONS, scope: 'me' }];
|
|
37
34
|
// 팩 디렉토리 스캔 — 하위에 solutions/ 디렉토리가 있는 팩만 포함
|
|
38
35
|
// PR2c-2 (M7 fix): readdirSync 결과를 정렬해 결정적 순서 보장.
|
|
39
36
|
// 정렬 안 하면 같은 팩 집합도 파일시스템 순서에 따라 다른 cache key/precedence
|
|
@@ -107,12 +104,15 @@ export function searchSolutions(query, options) {
|
|
|
107
104
|
// R4-T2: pass `queryTags` (already masked above) so the union
|
|
108
105
|
// denominator inside calculateRelevance uses the post-mask set, in
|
|
109
106
|
// sync with the matching step.
|
|
110
|
-
const result = calculateRelevance(queryTags, entry.tags, entry.confidence, {
|
|
107
|
+
const result = calculateRelevance(queryTags, entry.tags, entry.confidence, {
|
|
108
|
+
normalizedPromptTags,
|
|
109
|
+
solutionTagsExpanded: entryTagsExpanded,
|
|
110
|
+
});
|
|
111
111
|
// 태그 매칭 + 이름 매칭: 솔루션 이름에 쿼리 단어가 포함되면 boost
|
|
112
112
|
// Compute name match FIRST so R4-T3 cannot silently drop a candidate
|
|
113
113
|
// with strong name-match evidence.
|
|
114
114
|
const nameWords = entry.name.toLowerCase().split(/[-_]/);
|
|
115
|
-
const nameMatchCount = queryTags.filter(t => nameWords.includes(t)).length;
|
|
115
|
+
const nameMatchCount = queryTags.filter((t) => nameWords.includes(t)).length;
|
|
116
116
|
const nameBoost = nameMatchCount * 0.1;
|
|
117
117
|
// R4-T3: orchestration-layer specificity guards (mirror of the
|
|
118
118
|
// matching block in solution-matcher.rankCandidates). Reject single-
|
|
@@ -121,9 +121,9 @@ export function searchSolutions(query, options) {
|
|
|
121
121
|
// bypass the R4-T3 gate — a nameMatchCount > 0 is strong evidence.
|
|
122
122
|
let tagRelevance = result.relevance;
|
|
123
123
|
let tagMatches = result.matchedTags;
|
|
124
|
-
if (nameMatchCount === 0
|
|
125
|
-
|
|
126
|
-
|
|
124
|
+
if (nameMatchCount === 0 &&
|
|
125
|
+
tagMatches.length > 0 &&
|
|
126
|
+
shouldRejectByR4T3Rules(queryTags, tagMatches)) {
|
|
127
127
|
tagRelevance = 0;
|
|
128
128
|
tagMatches = [];
|
|
129
129
|
}
|
|
@@ -137,7 +137,10 @@ export function searchSolutions(query, options) {
|
|
|
137
137
|
scope: entry.scope,
|
|
138
138
|
tags: entry.tags,
|
|
139
139
|
relevance: tagRelevance + nameBoost,
|
|
140
|
-
matchedTags: [
|
|
140
|
+
matchedTags: [
|
|
141
|
+
...tagMatches,
|
|
142
|
+
...queryTags.filter((t) => nameWords.includes(t) && !tagMatches.includes(t)),
|
|
143
|
+
],
|
|
141
144
|
});
|
|
142
145
|
}
|
|
143
146
|
results.sort((a, b) => b.relevance - a.relevance);
|
|
@@ -152,12 +155,12 @@ export function searchSolutions(query, options) {
|
|
|
152
155
|
source: 'mcp',
|
|
153
156
|
rawQuery: query,
|
|
154
157
|
normalizedQuery: normalizedPromptTags,
|
|
155
|
-
candidates: top.map(r => ({
|
|
158
|
+
candidates: top.map((r) => ({
|
|
156
159
|
name: r.name,
|
|
157
160
|
relevance: r.relevance,
|
|
158
161
|
matchedTerms: r.matchedTags,
|
|
159
162
|
})),
|
|
160
|
-
rankedTopN: top.slice(0, 5).map(r => r.name),
|
|
163
|
+
rankedTopN: top.slice(0, 5).map((r) => r.name),
|
|
161
164
|
});
|
|
162
165
|
}
|
|
163
166
|
return top;
|
|
@@ -167,7 +170,7 @@ export function searchSolutions(query, options) {
|
|
|
167
170
|
export function listSolutions(options) {
|
|
168
171
|
const dirs = options?.dirs ?? defaultSolutionDirs();
|
|
169
172
|
const index = getOrBuildIndex(dirs);
|
|
170
|
-
let entries = index.entries.map(e => ({
|
|
173
|
+
let entries = index.entries.map((e) => ({
|
|
171
174
|
name: e.name,
|
|
172
175
|
status: e.status,
|
|
173
176
|
confidence: e.confidence,
|
|
@@ -176,11 +179,11 @@ export function listSolutions(options) {
|
|
|
176
179
|
tags: e.tags,
|
|
177
180
|
}));
|
|
178
181
|
if (options?.status)
|
|
179
|
-
entries = entries.filter(e => e.status === options.status);
|
|
182
|
+
entries = entries.filter((e) => e.status === options.status);
|
|
180
183
|
if (options?.type)
|
|
181
|
-
entries = entries.filter(e => e.type === options.type);
|
|
184
|
+
entries = entries.filter((e) => e.type === options.type);
|
|
182
185
|
if (options?.scope)
|
|
183
|
-
entries = entries.filter(e => e.scope === options.scope);
|
|
186
|
+
entries = entries.filter((e) => e.scope === options.scope);
|
|
184
187
|
const sort = options?.sort ?? 'confidence';
|
|
185
188
|
if (sort === 'confidence') {
|
|
186
189
|
entries.sort((a, b) => b.confidence - a.confidence);
|
|
@@ -201,7 +204,7 @@ export function listSolutions(options) {
|
|
|
201
204
|
export function readSolution(name, options) {
|
|
202
205
|
const dirs = options?.dirs ?? defaultSolutionDirs();
|
|
203
206
|
const index = getOrBuildIndex(dirs);
|
|
204
|
-
const entry = index.entries.find(e => e.name === name);
|
|
207
|
+
const entry = index.entries.find((e) => e.name === name);
|
|
205
208
|
if (!entry)
|
|
206
209
|
return null;
|
|
207
210
|
let fileContent;
|
|
@@ -231,7 +234,7 @@ export function readSolution(name, options) {
|
|
|
231
234
|
// Pull(MCP) 경로: evidence에 기여 — sessions + reflected 카운트 증가
|
|
232
235
|
// PR2b: solution-writer.mutateSolutionFile로 통합. lock + fresh re-read로 race 방지.
|
|
233
236
|
if (!options?.skipEvidence) {
|
|
234
|
-
mutateSolutionFile(entry.filePath, sol => {
|
|
237
|
+
mutateSolutionFile(entry.filePath, (sol) => {
|
|
235
238
|
sol.frontmatter.evidence.sessions += 1;
|
|
236
239
|
sol.frontmatter.evidence.reflected += 1;
|
|
237
240
|
return true;
|
|
@@ -258,8 +261,15 @@ export function getSolutionStats(options) {
|
|
|
258
261
|
total: index.entries.length,
|
|
259
262
|
// retired는 인덱스에서 제외되므로 항상 0 (solution-index.ts:73)
|
|
260
263
|
byStatus: { experiment: 0, candidate: 0, verified: 0, mature: 0, retired: 0 },
|
|
261
|
-
byType: {
|
|
262
|
-
|
|
264
|
+
byType: {
|
|
265
|
+
pattern: 0,
|
|
266
|
+
solution: 0,
|
|
267
|
+
decision: 0,
|
|
268
|
+
troubleshoot: 0,
|
|
269
|
+
'anti-pattern': 0,
|
|
270
|
+
convention: 0,
|
|
271
|
+
},
|
|
272
|
+
byScope: { me: 0, team: 0, project: 0, universal: 0 },
|
|
263
273
|
};
|
|
264
274
|
for (const entry of index.entries) {
|
|
265
275
|
if (entry.status in stats.byStatus)
|
package/dist/mcp/tools.js
CHANGED
|
@@ -273,6 +273,14 @@ export function registerTools(server) {
|
|
|
273
273
|
target,
|
|
274
274
|
axis_hint: axis_hint,
|
|
275
275
|
});
|
|
276
|
+
// Outcome tracking (Phase 1): attribute this correction to any
|
|
277
|
+
// pending injections in the session. Fail-open — attribution is a
|
|
278
|
+
// best-effort signal, never block the correction record itself.
|
|
279
|
+
try {
|
|
280
|
+
const { attributeCorrection } = await import('../engine/solution-outcomes.js');
|
|
281
|
+
attributeCorrection(effectiveSessionId);
|
|
282
|
+
}
|
|
283
|
+
catch { /* ignore */ }
|
|
276
284
|
const lines = [
|
|
277
285
|
`Evidence recorded: ${result.evidence_event_id}`,
|
|
278
286
|
];
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch-time runtime selection helpers.
|
|
3
|
+
*
|
|
4
|
+
* 기본 동작:
|
|
5
|
+
* - --runtime claude|codex 플래그 우선
|
|
6
|
+
* - 설정되지 않으면 FORGEN_RUNTIME 환경변수 사용
|
|
7
|
+
* - 환경변수 미설정 시 claude 기본값
|
|
8
|
+
*
|
|
9
|
+
* 목표:
|
|
10
|
+
* - launch context(런타임 + 정제된 args)를 단일 타입으로 통일
|
|
11
|
+
* - CLI/fgx에서 수집한 런타임 값을 Harness, Spawn, Hook Generator에 일관되게 전달
|
|
12
|
+
*/
|
|
13
|
+
import { type LaunchContext } from '../core/types.js';
|
|
14
|
+
/**
|
|
15
|
+
* CLI 인자를 파싱해 런타임 결정 + 런타임 플래그 제거
|
|
16
|
+
* - --runtime codex
|
|
17
|
+
* - --runtime=codex
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveLaunchContext(args: string[]): LaunchContext;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Launch-time runtime selection helpers.
|
|
3
|
+
*
|
|
4
|
+
* 기본 동작:
|
|
5
|
+
* - --runtime claude|codex 플래그 우선
|
|
6
|
+
* - 설정되지 않으면 FORGEN_RUNTIME 환경변수 사용
|
|
7
|
+
* - 환경변수 미설정 시 claude 기본값
|
|
8
|
+
*
|
|
9
|
+
* 목표:
|
|
10
|
+
* - launch context(런타임 + 정제된 args)를 단일 타입으로 통일
|
|
11
|
+
* - CLI/fgx에서 수집한 런타임 값을 Harness, Spawn, Hook Generator에 일관되게 전달
|
|
12
|
+
*/
|
|
13
|
+
/** 런타임 정규화: 외부 문자열을 내부 enum으로 변환 */
|
|
14
|
+
function parseRuntime(raw) {
|
|
15
|
+
if (!raw)
|
|
16
|
+
return null;
|
|
17
|
+
switch (raw.trim().toLowerCase()) {
|
|
18
|
+
case 'claude':
|
|
19
|
+
return 'claude';
|
|
20
|
+
case 'codex':
|
|
21
|
+
return 'codex';
|
|
22
|
+
default:
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
const DEFAULT_RUNTIME = 'claude';
|
|
27
|
+
/**
|
|
28
|
+
* CLI 인자를 파싱해 런타임 결정 + 런타임 플래그 제거
|
|
29
|
+
* - --runtime codex
|
|
30
|
+
* - --runtime=codex
|
|
31
|
+
*/
|
|
32
|
+
export function resolveLaunchContext(args) {
|
|
33
|
+
const runtimeFromEnv = parseRuntime(process.env.FORGEN_RUNTIME);
|
|
34
|
+
const result = {
|
|
35
|
+
runtime: runtimeFromEnv ?? DEFAULT_RUNTIME,
|
|
36
|
+
args: [],
|
|
37
|
+
runtimeSource: runtimeFromEnv ? 'env' : 'default',
|
|
38
|
+
};
|
|
39
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
40
|
+
const arg = args[i];
|
|
41
|
+
if (arg === '--runtime') {
|
|
42
|
+
const next = args[i + 1];
|
|
43
|
+
const parsed = parseRuntime(next);
|
|
44
|
+
if (parsed) {
|
|
45
|
+
result.runtime = parsed;
|
|
46
|
+
result.runtimeSource = 'flag';
|
|
47
|
+
i += 1;
|
|
48
|
+
}
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (arg.startsWith('--runtime=')) {
|
|
52
|
+
const parsed = parseRuntime(arg.slice('--runtime='.length));
|
|
53
|
+
if (parsed) {
|
|
54
|
+
result.runtime = parsed;
|
|
55
|
+
result.runtimeSource = 'flag';
|
|
56
|
+
}
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
result.args.push(arg);
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
package/hooks/hooks.json
CHANGED
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
"hooks": [
|
|
152
152
|
{
|
|
153
153
|
"type": "command",
|
|
154
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/hooks/subagent-tracker.js\" start",
|
|
154
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/hooks/subagent-tracker.js\" \"start\"",
|
|
155
155
|
"timeout": 2
|
|
156
156
|
}
|
|
157
157
|
]
|
|
@@ -163,7 +163,7 @@
|
|
|
163
163
|
"hooks": [
|
|
164
164
|
{
|
|
165
165
|
"type": "command",
|
|
166
|
-
"command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/hooks/subagent-tracker.js\" stop",
|
|
166
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/dist/hooks/subagent-tracker.js\" \"stop\"",
|
|
167
167
|
"timeout": 2
|
|
168
168
|
}
|
|
169
169
|
]
|