@wooojin/forgen 0.2.0 → 0.3.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 (158) hide show
  1. package/CHANGELOG.md +72 -0
  2. package/README.ja.md +79 -14
  3. package/README.ko.md +100 -14
  4. package/README.md +124 -17
  5. package/README.zh.md +79 -14
  6. package/agents/analyst.md +48 -4
  7. package/agents/architect.md +39 -4
  8. package/agents/code-reviewer.md +107 -77
  9. package/agents/critic.md +47 -4
  10. package/agents/debugger.md +46 -4
  11. package/agents/designer.md +40 -4
  12. package/agents/executor.md +112 -30
  13. package/agents/explore.md +45 -5
  14. package/agents/git-master.md +48 -4
  15. package/agents/planner.md +121 -18
  16. package/agents/test-engineer.md +58 -4
  17. package/agents/verifier.md +92 -77
  18. package/commands/architecture-decision.md +127 -258
  19. package/commands/calibrate.md +225 -0
  20. package/commands/code-review.md +163 -178
  21. package/commands/compound.md +127 -68
  22. package/commands/deep-interview.md +273 -0
  23. package/commands/docker.md +68 -178
  24. package/commands/forge-loop.md +215 -0
  25. package/commands/learn.md +231 -0
  26. package/commands/retro.md +215 -0
  27. package/commands/ship.md +277 -0
  28. package/dist/cli.js +26 -9
  29. package/dist/core/auto-compound-runner.js +14 -0
  30. package/dist/core/config-injector.d.ts +2 -1
  31. package/dist/core/config-injector.js +2 -1
  32. package/dist/core/dashboard.d.ts +108 -0
  33. package/dist/core/dashboard.js +495 -0
  34. package/dist/core/doctor.js +151 -21
  35. package/dist/core/drift-score.d.ts +49 -0
  36. package/dist/core/drift-score.js +87 -0
  37. package/dist/core/harness.d.ts +6 -1
  38. package/dist/core/harness.js +75 -19
  39. package/dist/core/mcp-config.d.ts +2 -0
  40. package/dist/core/mcp-config.js +6 -1
  41. package/dist/core/paths.d.ts +6 -1
  42. package/dist/core/paths.js +18 -2
  43. package/dist/core/spawn.d.ts +3 -2
  44. package/dist/core/spawn.js +27 -8
  45. package/dist/core/types.d.ts +34 -0
  46. package/dist/engine/compound-export.d.ts +41 -0
  47. package/dist/engine/compound-export.js +169 -0
  48. package/dist/engine/compound-lifecycle.d.ts +4 -3
  49. package/dist/engine/compound-lifecycle.js +91 -46
  50. package/dist/engine/compound-loop.js +18 -0
  51. package/dist/engine/meta-learning/adaptive-thresholds.d.ts +20 -0
  52. package/dist/engine/meta-learning/adaptive-thresholds.js +126 -0
  53. package/dist/engine/meta-learning/extraction-tuner.d.ts +15 -0
  54. package/dist/engine/meta-learning/extraction-tuner.js +99 -0
  55. package/dist/engine/meta-learning/matcher-weight-tuner.d.ts +21 -0
  56. package/dist/engine/meta-learning/matcher-weight-tuner.js +151 -0
  57. package/dist/engine/meta-learning/runner.d.ts +14 -0
  58. package/dist/engine/meta-learning/runner.js +90 -0
  59. package/dist/engine/meta-learning/scope-promoter.d.ts +21 -0
  60. package/dist/engine/meta-learning/scope-promoter.js +84 -0
  61. package/dist/engine/meta-learning/session-quality-scorer.d.ts +61 -0
  62. package/dist/engine/meta-learning/session-quality-scorer.js +166 -0
  63. package/dist/engine/meta-learning/types.d.ts +114 -0
  64. package/dist/engine/meta-learning/types.js +43 -0
  65. package/dist/engine/solution-format.d.ts +2 -2
  66. package/dist/engine/solution-format.js +249 -34
  67. package/dist/engine/solution-index.d.ts +1 -1
  68. package/dist/engine/solution-matcher.d.ts +30 -1
  69. package/dist/engine/solution-matcher.js +235 -45
  70. package/dist/fgx.js +12 -8
  71. package/dist/hooks/context-guard.d.ts +15 -0
  72. package/dist/hooks/context-guard.js +218 -56
  73. package/dist/hooks/db-guard.js +2 -2
  74. package/dist/hooks/hook-config.d.ts +27 -1
  75. package/dist/hooks/hook-config.js +72 -12
  76. package/dist/hooks/hooks-generator.d.ts +3 -0
  77. package/dist/hooks/hooks-generator.js +23 -6
  78. package/dist/hooks/intent-classifier.d.ts +0 -2
  79. package/dist/hooks/intent-classifier.js +32 -18
  80. package/dist/hooks/keyword-detector.js +126 -204
  81. package/dist/hooks/notepad-injector.js +2 -2
  82. package/dist/hooks/permission-handler.js +2 -2
  83. package/dist/hooks/post-tool-failure.js +12 -6
  84. package/dist/hooks/post-tool-handlers.d.ts +1 -1
  85. package/dist/hooks/post-tool-handlers.js +14 -11
  86. package/dist/hooks/post-tool-use.d.ts +11 -0
  87. package/dist/hooks/post-tool-use.js +184 -71
  88. package/dist/hooks/pre-compact.d.ts +11 -1
  89. package/dist/hooks/pre-compact.js +112 -37
  90. package/dist/hooks/pre-tool-use.js +86 -56
  91. package/dist/hooks/rate-limiter.js +3 -3
  92. package/dist/hooks/secret-filter.js +2 -2
  93. package/dist/hooks/session-recovery.js +256 -236
  94. package/dist/hooks/shared/hook-response.d.ts +4 -4
  95. package/dist/hooks/shared/hook-response.js +13 -24
  96. package/dist/hooks/shared/hook-timing.d.ts +15 -0
  97. package/dist/hooks/shared/hook-timing.js +64 -0
  98. package/dist/hooks/skill-injector.d.ts +4 -3
  99. package/dist/hooks/skill-injector.js +47 -16
  100. package/dist/hooks/slop-detector.js +3 -3
  101. package/dist/hooks/solution-injector.js +224 -197
  102. package/dist/hooks/subagent-tracker.js +2 -2
  103. package/dist/host/codex-adapter.d.ts +10 -0
  104. package/dist/host/codex-adapter.js +154 -0
  105. package/dist/mcp/solution-reader.d.ts +5 -5
  106. package/dist/mcp/solution-reader.js +34 -24
  107. package/dist/renderer/rule-renderer.js +9 -11
  108. package/dist/services/session.d.ts +19 -0
  109. package/dist/services/session.js +62 -0
  110. package/hooks/hooks.json +2 -2
  111. package/package.json +2 -1
  112. package/skills/architecture-decision/SKILL.md +113 -257
  113. package/skills/calibrate/SKILL.md +207 -0
  114. package/skills/code-review/SKILL.md +151 -178
  115. package/skills/compound/SKILL.md +126 -68
  116. package/skills/deep-interview/SKILL.md +266 -0
  117. package/skills/docker/SKILL.md +57 -179
  118. package/skills/forge-loop/SKILL.md +198 -0
  119. package/skills/learn/SKILL.md +216 -0
  120. package/skills/retro/SKILL.md +199 -0
  121. package/skills/ship/SKILL.md +259 -0
  122. package/agents/code-simplifier.md +0 -197
  123. package/agents/performance-reviewer.md +0 -172
  124. package/agents/qa-tester.md +0 -158
  125. package/agents/refactoring-expert.md +0 -168
  126. package/agents/scientist.md +0 -144
  127. package/agents/security-reviewer.md +0 -137
  128. package/agents/writer.md +0 -184
  129. package/commands/api-design.md +0 -268
  130. package/commands/ci-cd.md +0 -270
  131. package/commands/database.md +0 -263
  132. package/commands/debug-detective.md +0 -99
  133. package/commands/documentation.md +0 -276
  134. package/commands/ecomode.md +0 -51
  135. package/commands/frontend.md +0 -271
  136. package/commands/git-master.md +0 -90
  137. package/commands/incident-response.md +0 -292
  138. package/commands/migrate.md +0 -101
  139. package/commands/performance.md +0 -288
  140. package/commands/refactor.md +0 -105
  141. package/commands/security-review.md +0 -288
  142. package/commands/tdd.md +0 -183
  143. package/commands/testing-strategy.md +0 -265
  144. package/skills/api-design/SKILL.md +0 -262
  145. package/skills/ci-cd/SKILL.md +0 -264
  146. package/skills/database/SKILL.md +0 -257
  147. package/skills/debug-detective/SKILL.md +0 -95
  148. package/skills/documentation/SKILL.md +0 -270
  149. package/skills/ecomode/SKILL.md +0 -46
  150. package/skills/frontend/SKILL.md +0 -265
  151. package/skills/git-master/SKILL.md +0 -86
  152. package/skills/incident-response/SKILL.md +0 -286
  153. package/skills/migrate/SKILL.md +0 -96
  154. package/skills/performance/SKILL.md +0 -282
  155. package/skills/refactor/SKILL.md +0 -100
  156. package/skills/security-review/SKILL.md +0 -282
  157. package/skills/tdd/SKILL.md +0 -178
  158. package/skills/testing-strategy/SKILL.md +0 -260
@@ -13,8 +13,8 @@
13
13
  * - 인덱스 캐시는 isIndexStale()에 의존 (resetIndexCache 미사용)
14
14
  * → 디렉토리 mtime이 변하지 않으면 캐시 재사용 (성능)
15
15
  */
16
- import type { SolutionDirConfig } from '../engine/solution-index.js';
17
16
  import type { SolutionStatus, SolutionType } from '../engine/solution-format.js';
17
+ import type { SolutionDirConfig } from '../engine/solution-index.js';
18
18
  export interface SearchOptions {
19
19
  dirs?: SolutionDirConfig[];
20
20
  type?: SolutionType;
@@ -25,7 +25,7 @@ export interface ListOptions {
25
25
  dirs?: SolutionDirConfig[];
26
26
  status?: SolutionStatus;
27
27
  type?: SolutionType;
28
- scope?: 'me' | 'team' | 'project';
28
+ scope?: 'me' | 'team' | 'project' | 'universal';
29
29
  sort?: 'confidence' | 'updated' | 'name';
30
30
  }
31
31
  export interface SolutionSummary {
@@ -33,7 +33,7 @@ export interface SolutionSummary {
33
33
  status: SolutionStatus;
34
34
  confidence: number;
35
35
  type: SolutionType;
36
- scope: 'me' | 'team' | 'project';
36
+ scope: 'me' | 'team' | 'project' | 'universal';
37
37
  tags: string[];
38
38
  }
39
39
  export interface SearchResult extends SolutionSummary {
@@ -45,7 +45,7 @@ export interface SolutionDetail {
45
45
  status: SolutionStatus;
46
46
  confidence: number;
47
47
  type: SolutionType;
48
- scope: 'me' | 'team' | 'project';
48
+ scope: 'me' | 'team' | 'project' | 'universal';
49
49
  tags: string[];
50
50
  identifiers: string[];
51
51
  context: string;
@@ -55,7 +55,7 @@ export interface SolutionStats {
55
55
  total: number;
56
56
  byStatus: Record<SolutionStatus, number>;
57
57
  byType: Record<SolutionType, number>;
58
- byScope: Record<'me' | 'team' | 'project', number>;
58
+ byScope: Record<'me' | 'team' | 'project' | 'universal', number>;
59
59
  }
60
60
  /**
61
61
  * 기본 솔루션 디렉토리 목록 생성.
@@ -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)
@@ -6,12 +6,12 @@
6
6
  *
7
7
  * 파이프라인: filter → dedupe(render_key) → group(category) → order → template → budget
8
8
  */
9
- import { initLocaleFromConfig, getLocale, qualityName, autonomyName, judgmentName, communicationName, RULE_RENDERER } from '../i18n/index.js';
9
+ import { initLocaleFromConfig, getLocale, RULE_RENDERER } from '../i18n/index.js';
10
10
  export const DEFAULT_CONTEXT = {
11
11
  surface: 'session_start',
12
12
  max_rules: 30,
13
13
  max_chars: 4000,
14
- include_pack_summary: true,
14
+ include_pack_summary: false,
15
15
  };
16
16
  // ── Output Sections ──
17
17
  const SECTION_ORDER = ['Must Not', 'Working Defaults', 'When To Ask', 'How To Validate', 'How To Report'];
@@ -61,9 +61,11 @@ function dedupeByRenderKey(rules) {
61
61
  }
62
62
  // ── Template ──
63
63
  function ruleToText(rule) {
64
- // policy 필드가 이미 사람이 읽을 수 있는 문장이면 그대로 사용
65
- // render_key로 템플릿을 찾을 수도 있지만 v1은 policy 직접 사용
66
- return rule.policy;
64
+ // AI-optimized: [category|strength] 태그로 축약하여 토큰 절감
65
+ // hard 강도는 태그 생략 (Must Not 섹션이 이미 의미를 전달)
66
+ if (rule.strength === 'hard')
67
+ return rule.policy;
68
+ return `[${rule.category}|${rule.strength}] ${rule.policy}`;
67
69
  }
68
70
  function trustPolicySummary(policy) {
69
71
  const s = RULE_RENDERER[getLocale()];
@@ -121,13 +123,9 @@ export function renderRules(rules, state, _profile, ctx = DEFAULT_CONTEXT) {
121
123
  sections.get('How To Report').push(rule);
122
124
  }
123
125
  }
124
- // 6. 섹션 조립
126
+ // 6. 섹션 조립 (AI-optimized: 간결한 태그 형식)
125
127
  const parts = [];
126
- if (ctx.include_pack_summary) {
127
- const l = getLocale();
128
- parts.push(`[${qualityName(state.quality_pack, l)} quality / ${autonomyName(state.autonomy_pack, l)} autonomy / ${judgmentName(state.judgment_pack, l)} judgment / ${communicationName(state.communication_pack, l)} communication]`);
129
- }
130
- let totalChars = parts.reduce((sum, p) => sum + p.length, 0);
128
+ let totalChars = 0;
131
129
  let totalRules = 0;
132
130
  for (const name of SECTION_ORDER) {
133
131
  const items = sections.get(name);
@@ -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.0",
3
+ "version": "0.3.0",
4
+ "preferGlobal": true,
4
5
  "main": "dist/lib.js",
5
6
  "types": "./dist/lib.d.ts",
6
7
  "exports": {