@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,115 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen — Skill Promoter
|
|
3
|
+
*
|
|
4
|
+
* verified/mature 솔루션을 .forgen/me/skills/ 스킬로 승격.
|
|
5
|
+
* 솔루션(선언적 지식) → 스킬(절차적 지식) 변환.
|
|
6
|
+
*/
|
|
7
|
+
import * as fs from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import * as os from 'node:os';
|
|
10
|
+
import { parseSolutionV3 } from './solution-format.js';
|
|
11
|
+
import { createLogger } from '../core/logger.js';
|
|
12
|
+
const log = createLogger('skill-promoter');
|
|
13
|
+
const FORGEN_HOME = path.join(os.homedir(), '.forgen');
|
|
14
|
+
const ME_SOLUTIONS = path.join(FORGEN_HOME, 'me', 'solutions');
|
|
15
|
+
const ME_SKILLS = path.join(FORGEN_HOME, 'me', 'skills');
|
|
16
|
+
// Claude Code가 자동 인식하는 글로벌 스킬 경로
|
|
17
|
+
const CLAUDE_SKILLS = path.join(os.homedir(), '.claude', 'skills');
|
|
18
|
+
// 일반적인 태그 제외 (트리거로 부적합)
|
|
19
|
+
const GENERIC_TAGS = new Set([
|
|
20
|
+
'typescript', 'javascript', 'react', 'node', 'error', 'fix', 'code',
|
|
21
|
+
'pattern', 'solution', 'decision', 'troubleshoot', 'project', 'file',
|
|
22
|
+
]);
|
|
23
|
+
/** 솔루션을 스킬로 승격 */
|
|
24
|
+
export function promoteSolution(solutionName, triggers) {
|
|
25
|
+
// 1. 솔루션 찾기
|
|
26
|
+
const solPath = path.join(ME_SOLUTIONS, `${solutionName}.md`);
|
|
27
|
+
if (!fs.existsSync(solPath)) {
|
|
28
|
+
return { success: false, reason: `Solution not found: ${solutionName}` };
|
|
29
|
+
}
|
|
30
|
+
const content = fs.readFileSync(solPath, 'utf-8');
|
|
31
|
+
const parsed = parseSolutionV3(content);
|
|
32
|
+
if (!parsed) {
|
|
33
|
+
return { success: false, reason: `Failed to parse solution: ${solutionName}` };
|
|
34
|
+
}
|
|
35
|
+
// 2. 자격 검증: verified 이상만
|
|
36
|
+
if (!['verified', 'mature'].includes(parsed.frontmatter.status)) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
reason: `Only verified/mature solutions can be promoted. Current: ${parsed.frontmatter.status}. Use 'forgen compound --verify ${solutionName}' first.`,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// 3. 중복 체크
|
|
43
|
+
const skillPath = path.join(ME_SKILLS, `${solutionName}.md`);
|
|
44
|
+
if (fs.existsSync(skillPath)) {
|
|
45
|
+
return { success: false, reason: `Skill already exists: ${solutionName}` };
|
|
46
|
+
}
|
|
47
|
+
// 4. 트리거 결정
|
|
48
|
+
const effectiveTriggers = triggers ?? deriveTriggersFromTags(parsed.frontmatter.tags);
|
|
49
|
+
if (effectiveTriggers.length === 0) {
|
|
50
|
+
return { success: false, reason: 'No triggers could be derived. Provide --trigger manually.' };
|
|
51
|
+
}
|
|
52
|
+
// 5. 스킬 파일 생성
|
|
53
|
+
const description = parsed.context
|
|
54
|
+
? parsed.context.split('\n')[0].slice(0, 100)
|
|
55
|
+
: `${solutionName} 패턴 적용`;
|
|
56
|
+
const skillContent = [
|
|
57
|
+
'---',
|
|
58
|
+
`name: ${solutionName}`,
|
|
59
|
+
`description: ${description}`,
|
|
60
|
+
'triggers:',
|
|
61
|
+
...effectiveTriggers.map(t => ` - "${t}"`),
|
|
62
|
+
`promoted_from: solution/${solutionName}`,
|
|
63
|
+
`promoted_at: "${new Date().toISOString().split('T')[0]}"`,
|
|
64
|
+
'status: candidate',
|
|
65
|
+
'usage_count: 0',
|
|
66
|
+
'---',
|
|
67
|
+
'',
|
|
68
|
+
'<Purpose>',
|
|
69
|
+
parsed.context || `${solutionName} 패턴을 적용합니다.`,
|
|
70
|
+
'</Purpose>',
|
|
71
|
+
'',
|
|
72
|
+
'<Steps>',
|
|
73
|
+
parsed.content,
|
|
74
|
+
'</Steps>',
|
|
75
|
+
].join('\n');
|
|
76
|
+
fs.mkdirSync(ME_SKILLS, { recursive: true });
|
|
77
|
+
fs.writeFileSync(skillPath, skillContent);
|
|
78
|
+
// Claude Code 네이티브 스킬로도 등록 (~/.claude/skills/{name}/SKILL.md)
|
|
79
|
+
const claudeSkillDir = path.join(CLAUDE_SKILLS, solutionName);
|
|
80
|
+
fs.mkdirSync(claudeSkillDir, { recursive: true });
|
|
81
|
+
fs.writeFileSync(path.join(claudeSkillDir, 'SKILL.md'), skillContent);
|
|
82
|
+
log.debug(`스킬 승격 완료: ${solutionName} → ${skillPath} + ${claudeSkillDir}`);
|
|
83
|
+
return { success: true, skillPath };
|
|
84
|
+
}
|
|
85
|
+
/** 태그에서 트리거 키워드 자동 추출 */
|
|
86
|
+
function deriveTriggersFromTags(tags) {
|
|
87
|
+
return tags
|
|
88
|
+
.filter(t => t.length >= 3 && !GENERIC_TAGS.has(t.toLowerCase()))
|
|
89
|
+
.slice(0, 3);
|
|
90
|
+
}
|
|
91
|
+
/** 스킬 목록 조회 */
|
|
92
|
+
export function listSkills() {
|
|
93
|
+
if (!fs.existsSync(ME_SKILLS))
|
|
94
|
+
return [];
|
|
95
|
+
return fs.readdirSync(ME_SKILLS)
|
|
96
|
+
.filter(f => f.endsWith('.md'))
|
|
97
|
+
.map(f => {
|
|
98
|
+
try {
|
|
99
|
+
const content = fs.readFileSync(path.join(ME_SKILLS, f), 'utf-8');
|
|
100
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
101
|
+
if (!fmMatch)
|
|
102
|
+
return null;
|
|
103
|
+
const fm = fmMatch[1];
|
|
104
|
+
const name = fm.match(/name:\s*(.+)/)?.[1]?.trim() ?? f.replace('.md', '');
|
|
105
|
+
const status = fm.match(/status:\s*(.+)/)?.[1]?.trim() ?? 'unknown';
|
|
106
|
+
const promotedFrom = fm.match(/promoted_from:\s*(.+)/)?.[1]?.trim();
|
|
107
|
+
const triggers = [...fm.matchAll(/- "(.+)"/g)].map(m => m[1]);
|
|
108
|
+
return { name, status, promotedFrom, triggers };
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
})
|
|
114
|
+
.filter((s) => s !== null);
|
|
115
|
+
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
export type SolutionStatus = 'experiment' | 'candidate' | 'verified' | 'mature' | 'retired';
|
|
2
|
+
export type SolutionType = 'pattern' | 'solution' | 'decision' | 'troubleshoot' | 'anti-pattern' | 'convention';
|
|
3
|
+
export interface SolutionEvidence {
|
|
4
|
+
injected: number;
|
|
5
|
+
reflected: number;
|
|
6
|
+
negative: number;
|
|
7
|
+
sessions: number;
|
|
8
|
+
reExtracted: number;
|
|
9
|
+
}
|
|
10
|
+
export interface SolutionFrontmatter {
|
|
11
|
+
name: string;
|
|
12
|
+
version: number;
|
|
13
|
+
status: SolutionStatus;
|
|
14
|
+
confidence: number;
|
|
15
|
+
type: SolutionType;
|
|
16
|
+
scope: 'me' | 'team' | 'project';
|
|
17
|
+
tags: string[];
|
|
18
|
+
identifiers: string[];
|
|
19
|
+
evidence: SolutionEvidence;
|
|
20
|
+
created: string;
|
|
21
|
+
updated: string;
|
|
22
|
+
supersedes: string | null;
|
|
23
|
+
extractedBy: 'auto' | 'manual';
|
|
24
|
+
}
|
|
25
|
+
export interface SolutionV3 {
|
|
26
|
+
frontmatter: SolutionFrontmatter;
|
|
27
|
+
context: string;
|
|
28
|
+
content: string;
|
|
29
|
+
filePath?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface SolutionIndexEntry {
|
|
32
|
+
name: string;
|
|
33
|
+
status: SolutionStatus;
|
|
34
|
+
confidence: number;
|
|
35
|
+
type: SolutionType;
|
|
36
|
+
scope: 'me' | 'team' | 'project';
|
|
37
|
+
tags: string[];
|
|
38
|
+
/**
|
|
39
|
+
* Pre-expanded tag set, computed at index build time via the term normalizer.
|
|
40
|
+
* Contains `tags` plus every related match term from the canonical families
|
|
41
|
+
* they belong to.
|
|
42
|
+
*
|
|
43
|
+
* T2 scope (current): populated as forward-looking metadata for T3's
|
|
44
|
+
* ranking-decision log (which records raw + normalized query/solution
|
|
45
|
+
* terms for offline explainability) and T4's BM25 term-frequency stats.
|
|
46
|
+
* **NOT consumed by the current matcher** — `rankCandidates` and
|
|
47
|
+
* `calculateRelevance` still use raw `tags` for intersection to preserve
|
|
48
|
+
* the Round 3 baseline (bidirectional expansion would inflate recall 5-10×
|
|
49
|
+
* and invalidate fixture metrics). A future PR that uses `normalizedTags`
|
|
50
|
+
* in scoring must update `ROUND3_BASELINE` in the same commit.
|
|
51
|
+
*
|
|
52
|
+
* Not persisted — recomputed on every index build. Safe to regenerate whenever
|
|
53
|
+
* `DEFAULT_MATCH_TERMS` changes.
|
|
54
|
+
*/
|
|
55
|
+
normalizedTags: string[];
|
|
56
|
+
identifiers: string[];
|
|
57
|
+
filePath: string;
|
|
58
|
+
}
|
|
59
|
+
export declare const DEFAULT_EVIDENCE: SolutionEvidence;
|
|
60
|
+
export declare function slugify(text: string): string;
|
|
61
|
+
/** Runtime type guard for SolutionFrontmatter */
|
|
62
|
+
export declare function validateFrontmatter(fm: unknown): fm is SolutionFrontmatter;
|
|
63
|
+
/** Parse YAML frontmatter from solution file content */
|
|
64
|
+
export declare function parseFrontmatterOnly(content: string): SolutionFrontmatter | null;
|
|
65
|
+
/** Parse a full V3 solution file into its components */
|
|
66
|
+
export declare function parseSolutionV3(content: string): SolutionV3 | null;
|
|
67
|
+
/** Serialize a SolutionV3 to a markdown string with YAML frontmatter */
|
|
68
|
+
export declare function serializeSolutionV3(solution: SolutionV3): string;
|
|
69
|
+
/** Check if content is in V3 format (YAML frontmatter) */
|
|
70
|
+
export declare function isV3Format(content: string): boolean;
|
|
71
|
+
/** Check if content is in V1 format (# Title + > Type: pattern) */
|
|
72
|
+
export declare function isV1Format(content: string): boolean;
|
|
73
|
+
/** 한국어 일반 조사/어미 — strip 대상 (긴 것부터 매칭)
|
|
74
|
+
*
|
|
75
|
+
* term-matcher에서 재사용 가능하도록 export — 매칭 시점과 추출 시점의 stripping
|
|
76
|
+
* 규칙을 단일 source of truth로 유지해 한국어 stem 비교 정합성 보장.
|
|
77
|
+
*
|
|
78
|
+
* 주의: 이 리스트는 **추출 시점에도 적용**되므로 1글자 suffix를 추가할 때
|
|
79
|
+
* `집중`→`집`, `시도`→`시` 같은 한자어 명사가 깨지지 않도록 극도로 보수적으로
|
|
80
|
+
* 유지한다. 동사 활용형(`리팩토링중`, `배포시`)처럼 매칭 전용 suffix가 필요하면
|
|
81
|
+
* term-matcher의 `KO_VERBAL_SUFFIXES`에 따로 둔다.
|
|
82
|
+
*/
|
|
83
|
+
export declare const KO_SUFFIXES: string[];
|
|
84
|
+
export declare function stripKoSuffix(word: string): string;
|
|
85
|
+
/**
|
|
86
|
+
* Extract tags from text.
|
|
87
|
+
* Korean 2-char words preserved (e.g. "에러", "배포"), stopwords filtered.
|
|
88
|
+
* English words require 3+ chars, stopwords filtered.
|
|
89
|
+
* Tags capped at MAX_TAGS, ranked by frequency.
|
|
90
|
+
*
|
|
91
|
+
* NOTE on hyphens: this function strips `-` to a space (`api-key` query token
|
|
92
|
+
* becomes `api` and `key` separately). Solution-side compound tags are
|
|
93
|
+
* recovered downstream by `expandCompoundTags`, and query-side bigram
|
|
94
|
+
* recovery is done by `expandQueryBigrams`. Both ship as part of R4-T1
|
|
95
|
+
* (compound-tag tokenizer fix) — see `docs/plans/2026-04-08-t4-bm25-skip-adr.md`
|
|
96
|
+
* "Round 4 candidates" section for the rationale. Changing this regex
|
|
97
|
+
* directly was considered but rejected: it would silently shift the index
|
|
98
|
+
* representation of every existing solution, requiring an index rebuild and
|
|
99
|
+
* a fresh `ROUND3_BASELINE` measurement on every downstream PR.
|
|
100
|
+
*/
|
|
101
|
+
export declare function extractTags(text: string): string[];
|
|
102
|
+
/**
|
|
103
|
+
* Expand a solution tag list with hyphen-split alternatives.
|
|
104
|
+
*
|
|
105
|
+
* Each input tag is preserved verbatim, and any tag containing `-` also
|
|
106
|
+
* contributes its parts (length ≥ 3 each) as additional tags. The output
|
|
107
|
+
* is deduplicated.
|
|
108
|
+
*
|
|
109
|
+
* Examples:
|
|
110
|
+
* - `['api-key', 'security']` → `['api-key', 'api', 'key', 'security']`
|
|
111
|
+
* - `['code-review', 'quality']` → `['code-review', 'code', 'review', 'quality']`
|
|
112
|
+
* - `['n+1', 'database']` → `['n+1', 'database']` (no hyphen, n+1 unchanged)
|
|
113
|
+
* - `['red-green-refactor']` → `['red-green-refactor', 'red', 'green', 'refactor']`
|
|
114
|
+
* - `['typescript']` → `['typescript']` (no hyphen, no expansion)
|
|
115
|
+
*
|
|
116
|
+
* Korean compound tags (`API에러`, `테스트주도개발`) are preserved verbatim
|
|
117
|
+
* because they contain no `-`. The expansion is intentionally English-
|
|
118
|
+
* compound-aware only — Korean compound recovery is not in scope for R4-T1
|
|
119
|
+
* (the existing `term-normalizer` family expansion handles Korean ↔ English
|
|
120
|
+
* cross-mapping).
|
|
121
|
+
*
|
|
122
|
+
* The output ordering is insertion order: original tags first, then split
|
|
123
|
+
* parts in left-to-right order. Stable across runs (Set + Array dedup).
|
|
124
|
+
*/
|
|
125
|
+
export declare function expandCompoundTags(tags: readonly string[]): string[];
|
|
126
|
+
/**
|
|
127
|
+
* Expand a query tag list with adjacent-token bigram alternatives.
|
|
128
|
+
*
|
|
129
|
+
* For each adjacent (a, b) pair where both tokens are length ≥ 3, the
|
|
130
|
+
* function adds:
|
|
131
|
+
* - `a-b` (hyphen-joined form, e.g. `api-key`)
|
|
132
|
+
* - `ab` (concatenated form, e.g. `apikey`)
|
|
133
|
+
* - `a-b'` (singular stem of b, only if b ends in `s` and length > 3)
|
|
134
|
+
* - `ab'` (concatenated singular stem)
|
|
135
|
+
*
|
|
136
|
+
* Examples:
|
|
137
|
+
* - `['api', 'keys']` → `['api', 'keys', 'api-key', 'apikey', 'api-keys', 'apikeys']`
|
|
138
|
+
* - `['code', 'review']` → `['code', 'review', 'code-review', 'codereview']`
|
|
139
|
+
* - `['red', 'green', 'refactor']` → `[..., 'red-green', 'redgreen', 'green-refactor', 'greenrefactor']`
|
|
140
|
+
*
|
|
141
|
+
* Plural→singular stem is intentionally minimal: only `s`-suffix removal,
|
|
142
|
+
* no `es`/`ies` handling. The cost-benefit is asymmetric — `apis → api`
|
|
143
|
+
* is the highest-value case and is handled correctly; `classes → classe`
|
|
144
|
+
* is wrong but doesn't matter because no solution tag is `classe`.
|
|
145
|
+
*
|
|
146
|
+
* Why both `-` and concatenated forms: solution tag conventions vary
|
|
147
|
+
* across packs (`api-key` vs `apikey`), and this expansion is cheap.
|
|
148
|
+
* The downstream intersection check is O(M) per solution where M = expanded
|
|
149
|
+
* query tag count, so even doubling the query tag count is well within
|
|
150
|
+
* the matcher's hot-path budget for the corpus sizes Forgen targets
|
|
151
|
+
* (N ≤ 200 solutions).
|
|
152
|
+
*
|
|
153
|
+
* Korean tokens (`/[가-힣]/`) are passed through verbatim: bigram
|
|
154
|
+
* concatenation of Korean compound words is meaningless because the
|
|
155
|
+
* boundary is lexical, not whitespace-driven (`디버깅` is one word, not
|
|
156
|
+
* two adjacent tokens). Only ASCII-letter pairs participate.
|
|
157
|
+
*/
|
|
158
|
+
export declare function expandQueryBigrams(tags: readonly string[]): string[];
|
|
159
|
+
/** Migrate a V1-format solution file to V3 format */
|
|
160
|
+
export declare function migrateV1toV3(content: string, filePath: string): string;
|