@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
@@ -1,9 +1,26 @@
1
1
  import yaml from 'js-yaml';
2
2
  export const DEFAULT_EVIDENCE = {
3
- injected: 0, reflected: 0, negative: 0, sessions: 0, reExtracted: 0,
3
+ injected: 0,
4
+ reflected: 0,
5
+ negative: 0,
6
+ sessions: 0,
7
+ reExtracted: 0,
4
8
  };
5
- const VALID_STATUSES = ['experiment', 'candidate', 'verified', 'mature', 'retired'];
6
- const VALID_TYPES = ['pattern', 'solution', 'decision', 'troubleshoot', 'anti-pattern', 'convention'];
9
+ const VALID_STATUSES = [
10
+ 'experiment',
11
+ 'candidate',
12
+ 'verified',
13
+ 'mature',
14
+ 'retired',
15
+ ];
16
+ const VALID_TYPES = [
17
+ 'pattern',
18
+ 'solution',
19
+ 'decision',
20
+ 'troubleshoot',
21
+ 'anti-pattern',
22
+ 'convention',
23
+ ];
7
24
  // ── Helpers ──
8
25
  export function slugify(text) {
9
26
  const slug = text
@@ -31,7 +48,7 @@ export function validateFrontmatter(fm) {
31
48
  return false;
32
49
  if (typeof o.type !== 'string' || !VALID_TYPES.includes(o.type))
33
50
  return false;
34
- if (o.scope !== 'me' && o.scope !== 'team' && o.scope !== 'project')
51
+ if (o.scope !== 'me' && o.scope !== 'team' && o.scope !== 'project' && o.scope !== 'universal')
35
52
  return false;
36
53
  if (!Array.isArray(o.tags) || !o.tags.every((t) => typeof t === 'string'))
37
54
  return false;
@@ -122,7 +139,11 @@ export function parseSolutionV3(content) {
122
139
  // ── Serialization ──
123
140
  /** Serialize a SolutionV3 to a markdown string with YAML frontmatter */
124
141
  export function serializeSolutionV3(solution) {
125
- const yamlStr = yaml.dump(solution.frontmatter, { lineWidth: -1, quotingType: '"', schema: yaml.JSON_SCHEMA });
142
+ const yamlStr = yaml.dump(solution.frontmatter, {
143
+ lineWidth: -1,
144
+ quotingType: '"',
145
+ schema: yaml.JSON_SCHEMA,
146
+ });
126
147
  return `---\n${yamlStr}---\n\n## Context\n${solution.context}\n\n## Content\n${solution.content}\n`;
127
148
  }
128
149
  // ── Format Detection ──
@@ -149,34 +170,207 @@ export function isV1Format(content) {
149
170
  /** 한국어 불용어 — 태그로 의미 없는 일반 단어 */
150
171
  const KO_STOPWORDS = new Set([
151
172
  // 일반 불용어
152
- '적용', '패턴', '모든', '같은', '발견', '다른', '사용', '경우', '위해',
153
- '통해', '대한', '이후', '때문', '하는', '있는', '없는', '되는', '관련',
154
- '해야', '하고', '있다', '없다', '한다', '이런', '그런', '저런', '매우',
155
- '항상', '모두', '각각', '대해', '여러', '시작', '그것', '이것', '저것',
156
- '아주', '정말', '너무', '많이', '자주', '가장', '먼저', '이미', '아직',
157
- '그냥', '바로', '다시', '함께', '위한', '따라', '부분', '전체', '방법',
158
- '내용', '결과', '문제', '시점', '설정', '작업', '확인', '수행', '처리',
159
- '기본', '추가', '변경', '제거', '포함', '생성', '실행', '완료', '필요',
173
+ '적용',
174
+ '패턴',
175
+ '모든',
176
+ '같은',
177
+ '발견',
178
+ '다른',
179
+ '사용',
180
+ '경우',
181
+ '위해',
182
+ '통해',
183
+ '대한',
184
+ '이후',
185
+ '때문',
186
+ '하는',
187
+ '있는',
188
+ '없는',
189
+ '되는',
190
+ '관련',
191
+ '해야',
192
+ '하고',
193
+ '있다',
194
+ '없다',
195
+ '한다',
196
+ '이런',
197
+ '그런',
198
+ '저런',
199
+ '매우',
200
+ '항상',
201
+ '모두',
202
+ '각각',
203
+ '대해',
204
+ '여러',
205
+ '시작',
206
+ '그것',
207
+ '이것',
208
+ '저것',
209
+ '아주',
210
+ '정말',
211
+ '너무',
212
+ '많이',
213
+ '자주',
214
+ '가장',
215
+ '먼저',
216
+ '이미',
217
+ '아직',
218
+ '그냥',
219
+ '바로',
220
+ '다시',
221
+ '함께',
222
+ '위한',
223
+ '따라',
224
+ '부분',
225
+ '전체',
226
+ '방법',
227
+ '내용',
228
+ '결과',
229
+ '문제',
230
+ '시점',
231
+ '설정',
232
+ '작업',
233
+ '확인',
234
+ '수행',
235
+ '처리',
236
+ '기본',
237
+ '추가',
238
+ '변경',
239
+ '제거',
240
+ '포함',
241
+ '생성',
242
+ '실행',
243
+ '완료',
244
+ '필요',
160
245
  // 조사/어미/접속사 — Jaccard 분모 희석 방지
161
- '에서', '으로', '에게', '에는', '에도', '까지', '부터', '보다', '처럼',
162
- '만큼', '대로', '밖에', '뿐만', '이나', '이고', '이면', '이라', '인데',
163
- '했는데', '됐는데', '있으면', '없으면', '하면', '되면', '하지', '되지',
164
- '하며', '되며', '에서의', '으로의', '라는', '라고', '이라고', '때문에',
165
- '아니라', '하지만', '그러나', '그래서', '따라서', '그리고', '그러면',
166
- '만약', '비록', '하여', '않고', '않은', '않는', '해서', '해도', '해야',
246
+ '에서',
247
+ '으로',
248
+ '에게',
249
+ '에는',
250
+ '에도',
251
+ '까지',
252
+ '부터',
253
+ '보다',
254
+ '처럼',
255
+ '만큼',
256
+ '대로',
257
+ '밖에',
258
+ '뿐만',
259
+ '이나',
260
+ '이고',
261
+ '이면',
262
+ '이라',
263
+ '인데',
264
+ '했는데',
265
+ '됐는데',
266
+ '있으면',
267
+ '없으면',
268
+ '하면',
269
+ '되면',
270
+ '하지',
271
+ '되지',
272
+ '하며',
273
+ '되며',
274
+ '에서의',
275
+ '으로의',
276
+ '라는',
277
+ '라고',
278
+ '이라고',
279
+ '때문에',
280
+ '아니라',
281
+ '하지만',
282
+ '그러나',
283
+ '그래서',
284
+ '따라서',
285
+ '그리고',
286
+ '그러면',
287
+ '만약',
288
+ '비록',
289
+ '하여',
290
+ '않고',
291
+ '않은',
292
+ '않는',
293
+ '해서',
294
+ '해도',
295
+ '해야',
167
296
  // 일반 동사/형용사 어간 — 의미 없는 고빈도 단어
168
- '가능', '상태', '이유', '방지', '의존', '의존성', '즉시', '원칙', '근거',
169
- '수정', '제안', '기능', '구현', '구조', '단계', '목적', '상황', '조건',
170
- '규칙', '동작', '활성', '비활성', '원래', '현재', '이전', '다음', '최종',
297
+ '가능',
298
+ '상태',
299
+ '이유',
300
+ '방지',
301
+ '의존',
302
+ '의존성',
303
+ '즉시',
304
+ '원칙',
305
+ '근거',
306
+ '수정',
307
+ '제안',
308
+ '기능',
309
+ '구현',
310
+ '구조',
311
+ '단계',
312
+ '목적',
313
+ '상황',
314
+ '조건',
315
+ '규칙',
316
+ '동작',
317
+ '활성',
318
+ '비활성',
319
+ '원래',
320
+ '현재',
321
+ '이전',
322
+ '다음',
323
+ '최종',
171
324
  ]);
172
325
  /** 영어 불용어 */
173
326
  const EN_STOPWORDS = new Set([
174
- 'the', 'and', 'for', 'that', 'this', 'with', 'from', 'are', 'was',
175
- 'were', 'been', 'have', 'has', 'had', 'not', 'but', 'all', 'can',
176
- 'will', 'use', 'used', 'using', 'when', 'each', 'which', 'their',
177
- 'also', 'into', 'more', 'some', 'than', 'other', 'should', 'would',
178
- 'could', 'about', 'after', 'before', 'between', 'does', 'only',
179
- 'across', 'just', 'detected', 'based', 'sessions', 'prompts',
327
+ 'the',
328
+ 'and',
329
+ 'for',
330
+ 'that',
331
+ 'this',
332
+ 'with',
333
+ 'from',
334
+ 'are',
335
+ 'was',
336
+ 'were',
337
+ 'been',
338
+ 'have',
339
+ 'has',
340
+ 'had',
341
+ 'not',
342
+ 'but',
343
+ 'all',
344
+ 'can',
345
+ 'will',
346
+ 'use',
347
+ 'used',
348
+ 'using',
349
+ 'when',
350
+ 'each',
351
+ 'which',
352
+ 'their',
353
+ 'also',
354
+ 'into',
355
+ 'more',
356
+ 'some',
357
+ 'than',
358
+ 'other',
359
+ 'should',
360
+ 'would',
361
+ 'could',
362
+ 'about',
363
+ 'after',
364
+ 'before',
365
+ 'between',
366
+ 'does',
367
+ 'only',
368
+ 'across',
369
+ 'just',
370
+ 'detected',
371
+ 'based',
372
+ 'sessions',
373
+ 'prompts',
180
374
  ]);
181
375
  /** 한국어 일반 조사/어미 — strip 대상 (긴 것부터 매칭)
182
376
  *
@@ -189,9 +383,32 @@ const EN_STOPWORDS = new Set([
189
383
  * term-matcher의 `KO_VERBAL_SUFFIXES`에 따로 둔다.
190
384
  */
191
385
  export const KO_SUFFIXES = [
192
- '했습니다', '있습니다', '합니다', '입니다', '됩니다',
193
- '에서', '까지', '으로', '하는', '하고', '했다', '된다', '한다',
194
- '', '를', '이', '가', '은', '는', '의', '에', '와', '과', '도', '만', '로',
386
+ '했습니다',
387
+ '있습니다',
388
+ '합니다',
389
+ '입니다',
390
+ '됩니다',
391
+ '에서',
392
+ '까지',
393
+ '으로',
394
+ '하는',
395
+ '하고',
396
+ '했다',
397
+ '된다',
398
+ '한다',
399
+ '을',
400
+ '를',
401
+ '이',
402
+ '가',
403
+ '은',
404
+ '는',
405
+ '의',
406
+ '에',
407
+ '와',
408
+ '과',
409
+ '도',
410
+ '만',
411
+ '로',
195
412
  ];
196
413
  export function stripKoSuffix(word) {
197
414
  for (const suffix of KO_SUFFIXES) {
@@ -220,9 +437,7 @@ const MAX_TAGS = 8;
220
437
  * a fresh `ROUND3_BASELINE` measurement on every downstream PR.
221
438
  */
222
439
  export function extractTags(text) {
223
- const cleaned = text
224
- .toLowerCase()
225
- .replace(/[^가-힣a-z0-9\s]/g, ' ');
440
+ const cleaned = text.toLowerCase().replace(/[^가-힣a-z0-9\s]/g, ' ');
226
441
  const words = cleaned.split(/\s+/).filter(Boolean);
227
442
  const freq = new Map();
228
443
  for (const w of words) {
@@ -1,7 +1,7 @@
1
1
  import type { SolutionIndexEntry } from './solution-format.js';
2
2
  export interface SolutionDirConfig {
3
3
  dir: string;
4
- scope: 'me' | 'team' | 'project';
4
+ scope: 'me' | 'team' | 'project' | 'universal';
5
5
  }
6
6
  export interface SolutionIndex {
7
7
  entries: SolutionIndexEntry[];
@@ -6,12 +6,33 @@ import type { SolutionStatus, SolutionType } from './solution-format.js';
6
6
  * `synonym-tfidf.test.ts` and any external consumers.
7
7
  */
8
8
  export declare function expandTagsWithSynonyms(tags: string[]): string[];
9
+ /**
10
+ * Compute the Dice coefficient between two strings using character bigrams.
11
+ *
12
+ * Dice = 2 * |intersection| / (|A| + |B|)
13
+ *
14
+ * Both strings are lowercased and whitespace-stripped before bigram generation.
15
+ * Returns 0 for empty strings or single-character strings (no bigrams possible).
16
+ * Returns 1.0 for identical non-trivial strings.
17
+ *
18
+ * This is used as a lightweight fuzzy matching signal for borderline cases
19
+ * where the TF-IDF tag intersection produces a low score but the query and
20
+ * solution tags are character-similar (e.g., "database" vs "데이터베이스"
21
+ * won't match, but "database" vs "databse" will get a high score).
22
+ */
23
+ export declare function bigramSimilarity(a: string, b: string): number;
24
+ /**
25
+ * Simplified BM25 score for a single query-document pair.
26
+ * Uses tag overlap with term frequency normalization.
27
+ * k1=1.2, b=0.75 (standard BM25 parameters).
28
+ */
29
+ export declare function bm25Score(queryTags: string[], docTags: string[], avgDocLength: number): number;
9
30
  /** Apply IDF-like weight: common tags get reduced weight */
10
31
  export declare function tagWeight(tag: string): number;
11
32
  export interface SolutionMatch {
12
33
  name: string;
13
34
  path: string;
14
- scope: 'me' | 'team' | 'project';
35
+ scope: 'me' | 'team' | 'project' | 'universal';
15
36
  relevance: number;
16
37
  summary: string;
17
38
  status: SolutionStatus;
@@ -42,6 +63,14 @@ export interface CalculateRelevanceOptions {
42
63
  * pair — `solutionTagsExpanded` MUST be a superset of `solutionTags`.
43
64
  */
44
65
  solutionTagsExpanded?: string[];
66
+ /** Average document (solution) tag count for BM25 normalization. Defaults to 6. */
67
+ avgDocLength?: number;
68
+ /** Meta-learning: dynamic ensemble weights (sum must equal 1.0). Defaults to {tfidf:0.5, bm25:0.3, bigram:0.2}. */
69
+ ensembleWeights?: {
70
+ tfidf: number;
71
+ bm25: number;
72
+ bigram: number;
73
+ };
45
74
  }
46
75
  export declare function calculateRelevance(promptTags: string[], solutionTags: string[], confidence: number, options?: CalculateRelevanceOptions): {
47
76
  relevance: number;