@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.
Files changed (145) hide show
  1. package/CHANGELOG.md +76 -0
  2. package/README.ko.md +25 -14
  3. package/README.md +61 -17
  4. package/agents/analyst.md +48 -4
  5. package/agents/architect.md +39 -4
  6. package/agents/code-reviewer.md +107 -77
  7. package/agents/critic.md +47 -4
  8. package/agents/debugger.md +46 -4
  9. package/agents/designer.md +40 -4
  10. package/agents/executor.md +112 -30
  11. package/agents/explore.md +45 -5
  12. package/agents/git-master.md +48 -4
  13. package/agents/planner.md +121 -18
  14. package/agents/solution-evolver.md +115 -0
  15. package/agents/test-engineer.md +58 -4
  16. package/agents/verifier.md +92 -77
  17. package/commands/architecture-decision.md +127 -258
  18. package/commands/calibrate.md +225 -0
  19. package/commands/code-review.md +163 -178
  20. package/commands/compound.md +127 -68
  21. package/commands/deep-interview.md +212 -110
  22. package/commands/docker.md +68 -178
  23. package/commands/forge-loop.md +215 -0
  24. package/commands/learn.md +231 -0
  25. package/commands/retro.md +215 -0
  26. package/commands/ship.md +277 -0
  27. package/dist/cli.js +25 -9
  28. package/dist/core/auto-compound-runner.js +14 -0
  29. package/dist/core/config-injector.d.ts +2 -1
  30. package/dist/core/config-injector.js +2 -1
  31. package/dist/core/dashboard.d.ts +17 -0
  32. package/dist/core/dashboard.js +158 -2
  33. package/dist/core/harness.d.ts +6 -1
  34. package/dist/core/harness.js +75 -19
  35. package/dist/core/paths.d.ts +31 -1
  36. package/dist/core/paths.js +43 -2
  37. package/dist/core/spawn.d.ts +3 -2
  38. package/dist/core/spawn.js +27 -8
  39. package/dist/core/types.d.ts +34 -0
  40. package/dist/engine/compound-lifecycle.d.ts +4 -3
  41. package/dist/engine/compound-lifecycle.js +91 -46
  42. package/dist/engine/learn-cli.d.ts +1 -0
  43. package/dist/engine/learn-cli.js +182 -0
  44. package/dist/engine/meta-learning/adaptive-thresholds.d.ts +20 -0
  45. package/dist/engine/meta-learning/adaptive-thresholds.js +126 -0
  46. package/dist/engine/meta-learning/extraction-tuner.d.ts +15 -0
  47. package/dist/engine/meta-learning/extraction-tuner.js +99 -0
  48. package/dist/engine/meta-learning/matcher-weight-tuner.d.ts +21 -0
  49. package/dist/engine/meta-learning/matcher-weight-tuner.js +151 -0
  50. package/dist/engine/meta-learning/runner.d.ts +14 -0
  51. package/dist/engine/meta-learning/runner.js +90 -0
  52. package/dist/engine/meta-learning/scope-promoter.d.ts +21 -0
  53. package/dist/engine/meta-learning/scope-promoter.js +84 -0
  54. package/dist/engine/meta-learning/session-quality-scorer.d.ts +61 -0
  55. package/dist/engine/meta-learning/session-quality-scorer.js +166 -0
  56. package/dist/engine/meta-learning/types.d.ts +114 -0
  57. package/dist/engine/meta-learning/types.js +43 -0
  58. package/dist/engine/solution-candidate.d.ts +30 -0
  59. package/dist/engine/solution-candidate.js +124 -0
  60. package/dist/engine/solution-fitness.d.ts +52 -0
  61. package/dist/engine/solution-fitness.js +95 -0
  62. package/dist/engine/solution-fixup.d.ts +30 -0
  63. package/dist/engine/solution-fixup.js +116 -0
  64. package/dist/engine/solution-format.d.ts +10 -2
  65. package/dist/engine/solution-format.js +287 -57
  66. package/dist/engine/solution-index.d.ts +1 -1
  67. package/dist/engine/solution-index.js +10 -0
  68. package/dist/engine/solution-matcher.d.ts +7 -1
  69. package/dist/engine/solution-matcher.js +137 -37
  70. package/dist/engine/solution-outcomes.d.ts +70 -0
  71. package/dist/engine/solution-outcomes.js +242 -0
  72. package/dist/engine/solution-quarantine.d.ts +36 -0
  73. package/dist/engine/solution-quarantine.js +172 -0
  74. package/dist/engine/solution-weakness.d.ts +45 -0
  75. package/dist/engine/solution-weakness.js +225 -0
  76. package/dist/engine/solution-writer.d.ts +5 -0
  77. package/dist/engine/solution-writer.js +18 -0
  78. package/dist/fgx.js +12 -8
  79. package/dist/hooks/context-guard.d.ts +5 -0
  80. package/dist/hooks/context-guard.js +118 -2
  81. package/dist/hooks/hooks-generator.d.ts +3 -0
  82. package/dist/hooks/hooks-generator.js +23 -6
  83. package/dist/hooks/keyword-detector.js +16 -100
  84. package/dist/hooks/post-tool-failure.js +7 -0
  85. package/dist/hooks/skill-injector.d.ts +4 -3
  86. package/dist/hooks/skill-injector.js +6 -4
  87. package/dist/hooks/solution-injector.js +20 -0
  88. package/dist/host/codex-adapter.d.ts +10 -0
  89. package/dist/host/codex-adapter.js +154 -0
  90. package/dist/mcp/solution-reader.d.ts +5 -5
  91. package/dist/mcp/solution-reader.js +34 -24
  92. package/dist/mcp/tools.js +8 -0
  93. package/dist/services/session.d.ts +19 -0
  94. package/dist/services/session.js +62 -0
  95. package/hooks/hooks.json +2 -2
  96. package/package.json +2 -1
  97. package/skills/architecture-decision/SKILL.md +113 -257
  98. package/skills/calibrate/SKILL.md +207 -0
  99. package/skills/code-review/SKILL.md +151 -178
  100. package/skills/compound/SKILL.md +126 -68
  101. package/skills/deep-interview/SKILL.md +210 -110
  102. package/skills/docker/SKILL.md +57 -179
  103. package/skills/forge-loop/SKILL.md +198 -0
  104. package/skills/learn/SKILL.md +216 -0
  105. package/skills/retro/SKILL.md +199 -0
  106. package/skills/ship/SKILL.md +259 -0
  107. package/agents/code-simplifier.md +0 -197
  108. package/agents/performance-reviewer.md +0 -172
  109. package/agents/qa-tester.md +0 -158
  110. package/agents/refactoring-expert.md +0 -168
  111. package/agents/scientist.md +0 -144
  112. package/agents/security-reviewer.md +0 -137
  113. package/agents/writer.md +0 -184
  114. package/commands/api-design.md +0 -268
  115. package/commands/ci-cd.md +0 -270
  116. package/commands/database.md +0 -263
  117. package/commands/debug-detective.md +0 -99
  118. package/commands/documentation.md +0 -276
  119. package/commands/ecomode.md +0 -51
  120. package/commands/frontend.md +0 -271
  121. package/commands/git-master.md +0 -90
  122. package/commands/incident-response.md +0 -292
  123. package/commands/migrate.md +0 -101
  124. package/commands/performance.md +0 -288
  125. package/commands/refactor.md +0 -105
  126. package/commands/security-review.md +0 -288
  127. package/commands/specify.md +0 -128
  128. package/commands/tdd.md +0 -183
  129. package/commands/testing-strategy.md +0 -265
  130. package/skills/api-design/SKILL.md +0 -262
  131. package/skills/ci-cd/SKILL.md +0 -264
  132. package/skills/database/SKILL.md +0 -257
  133. package/skills/debug-detective/SKILL.md +0 -95
  134. package/skills/documentation/SKILL.md +0 -270
  135. package/skills/ecomode/SKILL.md +0 -46
  136. package/skills/frontend/SKILL.md +0 -265
  137. package/skills/git-master/SKILL.md +0 -86
  138. package/skills/incident-response/SKILL.md +0 -286
  139. package/skills/migrate/SKILL.md +0 -96
  140. package/skills/performance/SKILL.md +0 -282
  141. package/skills/refactor/SKILL.md +0 -100
  142. package/skills/security-review/SKILL.md +0 -282
  143. package/skills/specify/SKILL.md +0 -122
  144. package/skills/tdd/SKILL.md +0 -178
  145. 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 { getOrBuildIndex } from '../engine/solution-index.js';
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 { mutateSolutionFile } from '../engine/solution-writer.js';
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, { normalizedPromptTags, solutionTagsExpanded: entryTagsExpanded });
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
- && tagMatches.length > 0
126
- && shouldRejectByR4T3Rules(queryTags, tagMatches)) {
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: [...tagMatches, ...queryTags.filter(t => nameWords.includes(t) && !tagMatches.includes(t))],
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: { pattern: 0, solution: 0, decision: 0, troubleshoot: 0, 'anti-pattern': 0, convention: 0 },
262
- byScope: { me: 0, team: 0, project: 0 },
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
  ]
package/package.json CHANGED
@@ -1,6 +1,7 @@
1
1
  {
2
2
  "name": "@wooojin/forgen",
3
- "version": "0.2.1",
3
+ "version": "0.3.1",
4
+ "preferGlobal": true,
4
5
  "main": "dist/lib.js",
5
6
  "types": "./dist/lib.d.ts",
6
7
  "exports": {