@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.
Files changed (268) hide show
  1. package/.claude-plugin/plugin.json +20 -0
  2. package/CHANGELOG.md +353 -0
  3. package/CONTRIBUTING.md +98 -0
  4. package/LICENSE +21 -0
  5. package/README.ja.md +469 -0
  6. package/README.ko.md +469 -0
  7. package/README.md +483 -0
  8. package/README.zh.md +469 -0
  9. package/agents/analyst.md +98 -0
  10. package/agents/architect.md +62 -0
  11. package/agents/code-reviewer.md +120 -0
  12. package/agents/code-simplifier.md +197 -0
  13. package/agents/critic.md +70 -0
  14. package/agents/debugger.md +117 -0
  15. package/agents/designer.md +131 -0
  16. package/agents/executor.md +54 -0
  17. package/agents/explore.md +145 -0
  18. package/agents/git-master.md +212 -0
  19. package/agents/performance-reviewer.md +172 -0
  20. package/agents/planner.md +29 -0
  21. package/agents/qa-tester.md +158 -0
  22. package/agents/refactoring-expert.md +168 -0
  23. package/agents/scientist.md +144 -0
  24. package/agents/security-reviewer.md +137 -0
  25. package/agents/test-engineer.md +153 -0
  26. package/agents/verifier.md +133 -0
  27. package/agents/writer.md +184 -0
  28. package/commands/api-design.md +268 -0
  29. package/commands/architecture-decision.md +314 -0
  30. package/commands/ci-cd.md +270 -0
  31. package/commands/code-review.md +233 -0
  32. package/commands/compound.md +117 -0
  33. package/commands/database.md +263 -0
  34. package/commands/debug-detective.md +99 -0
  35. package/commands/docker.md +274 -0
  36. package/commands/documentation.md +276 -0
  37. package/commands/ecomode.md +51 -0
  38. package/commands/frontend.md +271 -0
  39. package/commands/git-master.md +90 -0
  40. package/commands/incident-response.md +292 -0
  41. package/commands/migrate.md +101 -0
  42. package/commands/performance.md +288 -0
  43. package/commands/refactor.md +105 -0
  44. package/commands/security-review.md +288 -0
  45. package/commands/tdd.md +183 -0
  46. package/commands/testing-strategy.md +265 -0
  47. package/dist/cli.d.ts +2 -0
  48. package/dist/cli.js +295 -0
  49. package/dist/core/auto-compound-runner.d.ts +12 -0
  50. package/dist/core/auto-compound-runner.js +460 -0
  51. package/dist/core/config-hooks.d.ts +10 -0
  52. package/dist/core/config-hooks.js +112 -0
  53. package/dist/core/config-injector.d.ts +50 -0
  54. package/dist/core/config-injector.js +455 -0
  55. package/dist/core/doctor.d.ts +1 -0
  56. package/dist/core/doctor.js +163 -0
  57. package/dist/core/errors.d.ts +81 -0
  58. package/dist/core/errors.js +133 -0
  59. package/dist/core/global-config.d.ts +43 -0
  60. package/dist/core/global-config.js +25 -0
  61. package/dist/core/harness.d.ts +24 -0
  62. package/dist/core/harness.js +621 -0
  63. package/dist/core/init.d.ts +7 -0
  64. package/dist/core/init.js +37 -0
  65. package/dist/core/inspect-cli.d.ts +7 -0
  66. package/dist/core/inspect-cli.js +47 -0
  67. package/dist/core/legacy-detector.d.ts +33 -0
  68. package/dist/core/legacy-detector.js +66 -0
  69. package/dist/core/logger.d.ts +34 -0
  70. package/dist/core/logger.js +121 -0
  71. package/dist/core/mcp-config.d.ts +44 -0
  72. package/dist/core/mcp-config.js +177 -0
  73. package/dist/core/notepad.d.ts +31 -0
  74. package/dist/core/notepad.js +88 -0
  75. package/dist/core/paths.d.ts +85 -0
  76. package/dist/core/paths.js +101 -0
  77. package/dist/core/plugin-detector.d.ts +44 -0
  78. package/dist/core/plugin-detector.js +226 -0
  79. package/dist/core/runtime-detector.d.ts +8 -0
  80. package/dist/core/runtime-detector.js +49 -0
  81. package/dist/core/scope-resolver.d.ts +8 -0
  82. package/dist/core/scope-resolver.js +45 -0
  83. package/dist/core/session-logger.d.ts +6 -0
  84. package/dist/core/session-logger.js +111 -0
  85. package/dist/core/session-store.d.ts +28 -0
  86. package/dist/core/session-store.js +218 -0
  87. package/dist/core/settings-lock.d.ts +18 -0
  88. package/dist/core/settings-lock.js +125 -0
  89. package/dist/core/spawn.d.ts +3 -0
  90. package/dist/core/spawn.js +135 -0
  91. package/dist/core/types.d.ts +108 -0
  92. package/dist/core/types.js +1 -0
  93. package/dist/core/uninstall.d.ts +4 -0
  94. package/dist/core/uninstall.js +307 -0
  95. package/dist/core/v1-bootstrap.d.ts +26 -0
  96. package/dist/core/v1-bootstrap.js +155 -0
  97. package/dist/engine/compound-cli.d.ts +24 -0
  98. package/dist/engine/compound-cli.js +250 -0
  99. package/dist/engine/compound-extractor.d.ts +68 -0
  100. package/dist/engine/compound-extractor.js +860 -0
  101. package/dist/engine/compound-lifecycle.d.ts +32 -0
  102. package/dist/engine/compound-lifecycle.js +305 -0
  103. package/dist/engine/compound-loop.d.ts +32 -0
  104. package/dist/engine/compound-loop.js +511 -0
  105. package/dist/engine/match-eval-log.d.ts +139 -0
  106. package/dist/engine/match-eval-log.js +270 -0
  107. package/dist/engine/phrase-blocklist.d.ts +119 -0
  108. package/dist/engine/phrase-blocklist.js +208 -0
  109. package/dist/engine/skill-promoter.d.ts +20 -0
  110. package/dist/engine/skill-promoter.js +115 -0
  111. package/dist/engine/solution-format.d.ts +160 -0
  112. package/dist/engine/solution-format.js +432 -0
  113. package/dist/engine/solution-index.d.ts +13 -0
  114. package/dist/engine/solution-index.js +252 -0
  115. package/dist/engine/solution-matcher.d.ts +364 -0
  116. package/dist/engine/solution-matcher.js +656 -0
  117. package/dist/engine/solution-writer.d.ts +76 -0
  118. package/dist/engine/solution-writer.js +157 -0
  119. package/dist/engine/term-matcher.d.ts +81 -0
  120. package/dist/engine/term-matcher.js +268 -0
  121. package/dist/engine/term-normalizer.d.ts +116 -0
  122. package/dist/engine/term-normalizer.js +171 -0
  123. package/dist/fgx.d.ts +6 -0
  124. package/dist/fgx.js +42 -0
  125. package/dist/forge/cli.d.ts +11 -0
  126. package/dist/forge/cli.js +100 -0
  127. package/dist/forge/evidence-processor.d.ts +21 -0
  128. package/dist/forge/evidence-processor.js +87 -0
  129. package/dist/forge/mismatch-detector.d.ts +44 -0
  130. package/dist/forge/mismatch-detector.js +83 -0
  131. package/dist/forge/onboarding-cli.d.ts +6 -0
  132. package/dist/forge/onboarding-cli.js +89 -0
  133. package/dist/forge/onboarding.d.ts +25 -0
  134. package/dist/forge/onboarding.js +122 -0
  135. package/dist/hooks/compound-reflection.d.ts +45 -0
  136. package/dist/hooks/compound-reflection.js +82 -0
  137. package/dist/hooks/context-guard.d.ts +24 -0
  138. package/dist/hooks/context-guard.js +156 -0
  139. package/dist/hooks/dangerous-patterns.json +18 -0
  140. package/dist/hooks/db-guard.d.ts +17 -0
  141. package/dist/hooks/db-guard.js +105 -0
  142. package/dist/hooks/hook-config.d.ts +29 -0
  143. package/dist/hooks/hook-config.js +92 -0
  144. package/dist/hooks/hook-registry.d.ts +43 -0
  145. package/dist/hooks/hook-registry.js +31 -0
  146. package/dist/hooks/hooks-generator.d.ts +49 -0
  147. package/dist/hooks/hooks-generator.js +99 -0
  148. package/dist/hooks/intent-classifier.d.ts +12 -0
  149. package/dist/hooks/intent-classifier.js +62 -0
  150. package/dist/hooks/keyword-detector.d.ts +25 -0
  151. package/dist/hooks/keyword-detector.js +389 -0
  152. package/dist/hooks/notepad-injector.d.ts +18 -0
  153. package/dist/hooks/notepad-injector.js +51 -0
  154. package/dist/hooks/permission-handler.d.ts +14 -0
  155. package/dist/hooks/permission-handler.js +114 -0
  156. package/dist/hooks/post-tool-failure.d.ts +11 -0
  157. package/dist/hooks/post-tool-failure.js +118 -0
  158. package/dist/hooks/post-tool-handlers.d.ts +17 -0
  159. package/dist/hooks/post-tool-handlers.js +115 -0
  160. package/dist/hooks/post-tool-use.d.ts +29 -0
  161. package/dist/hooks/post-tool-use.js +151 -0
  162. package/dist/hooks/pre-compact.d.ts +10 -0
  163. package/dist/hooks/pre-compact.js +165 -0
  164. package/dist/hooks/pre-tool-use.d.ts +31 -0
  165. package/dist/hooks/pre-tool-use.js +325 -0
  166. package/dist/hooks/prompt-injection-filter.d.ts +56 -0
  167. package/dist/hooks/prompt-injection-filter.js +287 -0
  168. package/dist/hooks/rate-limiter.d.ts +21 -0
  169. package/dist/hooks/rate-limiter.js +86 -0
  170. package/dist/hooks/secret-filter.d.ts +14 -0
  171. package/dist/hooks/secret-filter.js +65 -0
  172. package/dist/hooks/session-recovery.d.ts +27 -0
  173. package/dist/hooks/session-recovery.js +406 -0
  174. package/dist/hooks/shared/atomic-write.d.ts +41 -0
  175. package/dist/hooks/shared/atomic-write.js +148 -0
  176. package/dist/hooks/shared/context-budget.d.ts +37 -0
  177. package/dist/hooks/shared/context-budget.js +45 -0
  178. package/dist/hooks/shared/file-lock.d.ts +56 -0
  179. package/dist/hooks/shared/file-lock.js +253 -0
  180. package/dist/hooks/shared/hook-response.d.ts +33 -0
  181. package/dist/hooks/shared/hook-response.js +62 -0
  182. package/dist/hooks/shared/injection-caps.d.ts +39 -0
  183. package/dist/hooks/shared/injection-caps.js +52 -0
  184. package/dist/hooks/shared/plugin-signal.d.ts +23 -0
  185. package/dist/hooks/shared/plugin-signal.js +104 -0
  186. package/dist/hooks/shared/read-stdin.d.ts +8 -0
  187. package/dist/hooks/shared/read-stdin.js +63 -0
  188. package/dist/hooks/shared/sanitize-id.d.ts +7 -0
  189. package/dist/hooks/shared/sanitize-id.js +9 -0
  190. package/dist/hooks/shared/sanitize.d.ts +7 -0
  191. package/dist/hooks/shared/sanitize.js +22 -0
  192. package/dist/hooks/skill-injector.d.ts +38 -0
  193. package/dist/hooks/skill-injector.js +285 -0
  194. package/dist/hooks/slop-detector.d.ts +18 -0
  195. package/dist/hooks/slop-detector.js +93 -0
  196. package/dist/hooks/solution-injector.d.ts +58 -0
  197. package/dist/hooks/solution-injector.js +436 -0
  198. package/dist/hooks/subagent-tracker.d.ts +10 -0
  199. package/dist/hooks/subagent-tracker.js +90 -0
  200. package/dist/i18n/index.d.ts +43 -0
  201. package/dist/i18n/index.js +224 -0
  202. package/dist/lib.d.ts +14 -0
  203. package/dist/lib.js +14 -0
  204. package/dist/mcp/server.d.ts +8 -0
  205. package/dist/mcp/server.js +40 -0
  206. package/dist/mcp/solution-reader.d.ts +90 -0
  207. package/dist/mcp/solution-reader.js +273 -0
  208. package/dist/mcp/tools.d.ts +16 -0
  209. package/dist/mcp/tools.js +302 -0
  210. package/dist/preset/facet-catalog.d.ts +17 -0
  211. package/dist/preset/facet-catalog.js +46 -0
  212. package/dist/preset/preset-manager.d.ts +31 -0
  213. package/dist/preset/preset-manager.js +111 -0
  214. package/dist/renderer/inspect-renderer.d.ts +11 -0
  215. package/dist/renderer/inspect-renderer.js +123 -0
  216. package/dist/renderer/rule-renderer.d.ts +18 -0
  217. package/dist/renderer/rule-renderer.js +159 -0
  218. package/dist/store/evidence-store.d.ts +23 -0
  219. package/dist/store/evidence-store.js +58 -0
  220. package/dist/store/profile-store.d.ts +12 -0
  221. package/dist/store/profile-store.js +53 -0
  222. package/dist/store/recommendation-store.d.ts +22 -0
  223. package/dist/store/recommendation-store.js +64 -0
  224. package/dist/store/rule-store.d.ts +22 -0
  225. package/dist/store/rule-store.js +62 -0
  226. package/dist/store/session-state-store.d.ts +11 -0
  227. package/dist/store/session-state-store.js +44 -0
  228. package/dist/store/types.d.ts +159 -0
  229. package/dist/store/types.js +7 -0
  230. package/hooks/hook-registry.json +21 -0
  231. package/hooks/hooks.json +185 -0
  232. package/package.json +89 -0
  233. package/plugin.json +20 -0
  234. package/scripts/postinstall.js +826 -0
  235. package/skills/api-design/SKILL.md +262 -0
  236. package/skills/architecture-decision/SKILL.md +309 -0
  237. package/skills/ci-cd/SKILL.md +264 -0
  238. package/skills/code-review/SKILL.md +228 -0
  239. package/skills/compound/SKILL.md +101 -0
  240. package/skills/database/SKILL.md +257 -0
  241. package/skills/debug-detective/SKILL.md +95 -0
  242. package/skills/docker/SKILL.md +268 -0
  243. package/skills/documentation/SKILL.md +270 -0
  244. package/skills/ecomode/SKILL.md +46 -0
  245. package/skills/frontend/SKILL.md +265 -0
  246. package/skills/git-master/SKILL.md +86 -0
  247. package/skills/incident-response/SKILL.md +286 -0
  248. package/skills/migrate/SKILL.md +96 -0
  249. package/skills/performance/SKILL.md +282 -0
  250. package/skills/refactor/SKILL.md +100 -0
  251. package/skills/security-review/SKILL.md +282 -0
  252. package/skills/tdd/SKILL.md +178 -0
  253. package/skills/testing-strategy/SKILL.md +260 -0
  254. package/starter-pack/solutions/starter-api-error-responses.md +37 -0
  255. package/starter-pack/solutions/starter-async-patterns.md +40 -0
  256. package/starter-pack/solutions/starter-caching-strategy.md +40 -0
  257. package/starter-pack/solutions/starter-code-review-checklist.md +39 -0
  258. package/starter-pack/solutions/starter-debugging-systematic.md +40 -0
  259. package/starter-pack/solutions/starter-dependency-injection.md +40 -0
  260. package/starter-pack/solutions/starter-error-handling-patterns.md +38 -0
  261. package/starter-pack/solutions/starter-git-atomic-commits.md +36 -0
  262. package/starter-pack/solutions/starter-input-validation.md +40 -0
  263. package/starter-pack/solutions/starter-n-plus-one-queries.md +37 -0
  264. package/starter-pack/solutions/starter-refactor-safely.md +38 -0
  265. package/starter-pack/solutions/starter-secret-management.md +37 -0
  266. package/starter-pack/solutions/starter-separation-of-concerns.md +36 -0
  267. package/starter-pack/solutions/starter-tdd-red-green-refactor.md +40 -0
  268. package/starter-pack/solutions/starter-typescript-strict-types.md +39 -0
@@ -0,0 +1,116 @@
1
+ /**
2
+ * Term normalizer — indexed matchTerms registry replacing SYNONYM_MAP.
3
+ *
4
+ * Why this module exists (T2 of the Round 3 plan):
5
+ * The previous `expandTagsWithSynonyms` in solution-matcher.ts did:
6
+ * 1. forward lookup: SYNONYM_MAP[tag] → O(1)
7
+ * 2. reverse lookup: Object.entries(SYNONYM_MAP).filter(...) → O(N)
8
+ * The reverse sweep was called *inside* `calculateRelevance` which itself
9
+ * runs once per solution, so expanding a single query against N solutions
10
+ * cost O(N × M) where M is the synonym-map size. That is the hot-path
11
+ * cost the plan targets.
12
+ *
13
+ * This module pre-computes both directions as Maps at module load time,
14
+ * so every lookup is O(1). The forward map (`canonicalToTerms`) is used
15
+ * to expand a canonical into its family; the reverse map
16
+ * (`termToCanonicals`) is used to find which canonical(s) a term belongs
17
+ * to (a term may belong to more than one canonical — `mock` is both a
18
+ * testing-family term and a dependency-injection-family term).
19
+ *
20
+ * Migration rules (T2 Task 2, Step 3 of the plan):
21
+ * - The old SYNONYM_MAP had 32 top-level keys. This migration consolidates
22
+ * them into 19 canonicals by merging Korean↔English duplicates (e.g.
23
+ * `에러` was a key AND a value inside `error` — both directions now live
24
+ * under a single `error` canonical's `matchTerms`). See the registry
25
+ * block below for per-family merge notes; the `error`/`debug` split is
26
+ * documented inline because it was reverted from an earlier round after
27
+ * measurable baseline regression.
28
+ * - Exact duplicates within a single `matchTerms` array are deduplicated
29
+ * by the Set construction in `buildTermNormalizer`.
30
+ * - Korean-English cross-mappings (the big 5.1.2 hotfix) are kept intact:
31
+ * `에러` ↔ `error`, `핸들링` ↔ `handling`, `배포` ↔ `deploy`, etc.
32
+ *
33
+ * Input normalization: `normalizeTerms` NFC-normalizes each input term at
34
+ * entry. macOS HFS+ paths come back as NFD, and `fm.tags` from YAML can
35
+ * occasionally arrive that way too — matching the same NFC strategy as
36
+ * PR3's `term-matcher.ts` prevents silent lookup misses on Korean tags.
37
+ *
38
+ * Design contract (consumer-facing):
39
+ * - `normalizeTerms(input)` is the replacement for
40
+ * `expandTagsWithSynonyms(input)`. It returns a de-duplicated array.
41
+ * - `canonicalToTerms` and `termToCanonicals` are exposed for callers
42
+ * that want richer introspection (the ranking-log writer in T3 will
43
+ * record "query term → matched canonical → sibling terms" for offline
44
+ * explainability).
45
+ * - `defaultNormalizer` is a pre-built instance from `DEFAULT_MATCH_TERMS`,
46
+ * suitable for direct use in hot paths (matchSolutions, solution-reader,
47
+ * solution-index). Tests can build their own via `buildTermNormalizer`.
48
+ */
49
+ /**
50
+ * A single canonical group in the match-terms registry.
51
+ *
52
+ * `canonical` is the preferred term used for display/debug. `matchTerms`
53
+ * lists all synonyms that should pull the same family. The canonical
54
+ * itself does not need to appear in `matchTerms` — `buildTermNormalizer`
55
+ * always includes it in the expanded set.
56
+ */
57
+ export interface MatchTermEntry {
58
+ canonical: string;
59
+ matchTerms: string[];
60
+ }
61
+ /**
62
+ * Pre-built lookup shape. `buildTermNormalizer` returns this; consumers
63
+ * call `normalizeTerms` in their hot path.
64
+ */
65
+ export interface TermNormalizer {
66
+ /** canonical → full Set of terms (canonical + matchTerms). */
67
+ canonicalToTerms: Map<string, Set<string>>;
68
+ /**
69
+ * Any term (canonical OR matchTerm) → ordered list of canonicals it
70
+ * belongs to. Ordered to keep debug traces stable; a term usually
71
+ * belongs to 1 canonical, occasionally 2-3.
72
+ */
73
+ termToCanonicals: Map<string, string[]>;
74
+ /**
75
+ * Expand an input term list to the union of all related terms.
76
+ * Output is deduplicated. Unknown terms pass through unchanged.
77
+ *
78
+ * For each input term:
79
+ * 1. Include the term itself
80
+ * 2. Look up `termToCanonicals[term]` → list of canonicals
81
+ * 3. For each canonical, include `canonicalToTerms[canonical]`
82
+ * (the full family)
83
+ */
84
+ normalizeTerms(input: string[]): string[];
85
+ }
86
+ /**
87
+ * Build a term normalizer from a list of `MatchTermEntry` records.
88
+ *
89
+ * Safe for runtime use and for tests (tests can pass custom entries to
90
+ * exercise edge cases without mutating `DEFAULT_MATCH_TERMS`).
91
+ */
92
+ export declare function buildTermNormalizer(entries: MatchTermEntry[]): TermNormalizer;
93
+ /**
94
+ * Default match-terms registry, ported 1:1 from the previous
95
+ * `SYNONYM_MAP` in `solution-matcher.ts`. Each entry captures one
96
+ * semantic family.
97
+ *
98
+ * Editing guidance:
99
+ * - Adding a new canonical is safe — it extends coverage without
100
+ * shifting existing ranks.
101
+ * - Adding a matchTerm under an existing canonical pulls the new term
102
+ * into the existing family. Check the bootstrap eval (`npm test --
103
+ * solution-matcher-eval`) to confirm baseline metrics don't regress.
104
+ * - Removing a matchTerm can drop recall for the corresponding query
105
+ * shape. Update `ROUND3_BASELINE` and the plan doc if intentional.
106
+ * - Korean-English cross-mapping (`에러` ↔ `error`) must not regress —
107
+ * covered by the bilingual spot-checks in
108
+ * `tests/term-normalizer.test.ts`.
109
+ */
110
+ export declare const DEFAULT_MATCH_TERMS: MatchTermEntry[];
111
+ /**
112
+ * Pre-built normalizer for the default registry. Modules that need the
113
+ * default behaviour should import this directly rather than calling
114
+ * `buildTermNormalizer(DEFAULT_MATCH_TERMS)` on every query.
115
+ */
116
+ export declare const defaultNormalizer: TermNormalizer;
@@ -0,0 +1,171 @@
1
+ /**
2
+ * Term normalizer — indexed matchTerms registry replacing SYNONYM_MAP.
3
+ *
4
+ * Why this module exists (T2 of the Round 3 plan):
5
+ * The previous `expandTagsWithSynonyms` in solution-matcher.ts did:
6
+ * 1. forward lookup: SYNONYM_MAP[tag] → O(1)
7
+ * 2. reverse lookup: Object.entries(SYNONYM_MAP).filter(...) → O(N)
8
+ * The reverse sweep was called *inside* `calculateRelevance` which itself
9
+ * runs once per solution, so expanding a single query against N solutions
10
+ * cost O(N × M) where M is the synonym-map size. That is the hot-path
11
+ * cost the plan targets.
12
+ *
13
+ * This module pre-computes both directions as Maps at module load time,
14
+ * so every lookup is O(1). The forward map (`canonicalToTerms`) is used
15
+ * to expand a canonical into its family; the reverse map
16
+ * (`termToCanonicals`) is used to find which canonical(s) a term belongs
17
+ * to (a term may belong to more than one canonical — `mock` is both a
18
+ * testing-family term and a dependency-injection-family term).
19
+ *
20
+ * Migration rules (T2 Task 2, Step 3 of the plan):
21
+ * - The old SYNONYM_MAP had 32 top-level keys. This migration consolidates
22
+ * them into 19 canonicals by merging Korean↔English duplicates (e.g.
23
+ * `에러` was a key AND a value inside `error` — both directions now live
24
+ * under a single `error` canonical's `matchTerms`). See the registry
25
+ * block below for per-family merge notes; the `error`/`debug` split is
26
+ * documented inline because it was reverted from an earlier round after
27
+ * measurable baseline regression.
28
+ * - Exact duplicates within a single `matchTerms` array are deduplicated
29
+ * by the Set construction in `buildTermNormalizer`.
30
+ * - Korean-English cross-mappings (the big 5.1.2 hotfix) are kept intact:
31
+ * `에러` ↔ `error`, `핸들링` ↔ `handling`, `배포` ↔ `deploy`, etc.
32
+ *
33
+ * Input normalization: `normalizeTerms` NFC-normalizes each input term at
34
+ * entry. macOS HFS+ paths come back as NFD, and `fm.tags` from YAML can
35
+ * occasionally arrive that way too — matching the same NFC strategy as
36
+ * PR3's `term-matcher.ts` prevents silent lookup misses on Korean tags.
37
+ *
38
+ * Design contract (consumer-facing):
39
+ * - `normalizeTerms(input)` is the replacement for
40
+ * `expandTagsWithSynonyms(input)`. It returns a de-duplicated array.
41
+ * - `canonicalToTerms` and `termToCanonicals` are exposed for callers
42
+ * that want richer introspection (the ranking-log writer in T3 will
43
+ * record "query term → matched canonical → sibling terms" for offline
44
+ * explainability).
45
+ * - `defaultNormalizer` is a pre-built instance from `DEFAULT_MATCH_TERMS`,
46
+ * suitable for direct use in hot paths (matchSolutions, solution-reader,
47
+ * solution-index). Tests can build their own via `buildTermNormalizer`.
48
+ */
49
+ /**
50
+ * Build a term normalizer from a list of `MatchTermEntry` records.
51
+ *
52
+ * Safe for runtime use and for tests (tests can pass custom entries to
53
+ * exercise edge cases without mutating `DEFAULT_MATCH_TERMS`).
54
+ */
55
+ export function buildTermNormalizer(entries) {
56
+ const canonicalToTerms = new Map();
57
+ const termToCanonicals = new Map();
58
+ for (const entry of entries) {
59
+ // Canonical is always in its own family
60
+ const family = new Set([entry.canonical, ...entry.matchTerms]);
61
+ canonicalToTerms.set(entry.canonical, family);
62
+ // Reverse map: every term in the family points back to this canonical
63
+ for (const term of family) {
64
+ const existing = termToCanonicals.get(term) ?? [];
65
+ if (!existing.includes(entry.canonical)) {
66
+ existing.push(entry.canonical);
67
+ }
68
+ termToCanonicals.set(term, existing);
69
+ }
70
+ }
71
+ function normalizeTerms(input) {
72
+ const out = new Set();
73
+ for (const rawTerm of input) {
74
+ // NFC normalize on ingest — macOS NFD tags must not silently miss the
75
+ // NFC-encoded registry. Mirrors PR3 `term-matcher.ts` strategy.
76
+ const term = rawTerm.normalize('NFC');
77
+ out.add(term); // always include the original (post-normalize)
78
+ const canonicals = termToCanonicals.get(term);
79
+ if (!canonicals)
80
+ continue;
81
+ for (const canonical of canonicals) {
82
+ const family = canonicalToTerms.get(canonical);
83
+ if (!family)
84
+ continue;
85
+ for (const related of family)
86
+ out.add(related);
87
+ }
88
+ }
89
+ return [...out];
90
+ }
91
+ return { canonicalToTerms, termToCanonicals, normalizeTerms };
92
+ }
93
+ /**
94
+ * Default match-terms registry, ported 1:1 from the previous
95
+ * `SYNONYM_MAP` in `solution-matcher.ts`. Each entry captures one
96
+ * semantic family.
97
+ *
98
+ * Editing guidance:
99
+ * - Adding a new canonical is safe — it extends coverage without
100
+ * shifting existing ranks.
101
+ * - Adding a matchTerm under an existing canonical pulls the new term
102
+ * into the existing family. Check the bootstrap eval (`npm test --
103
+ * solution-matcher-eval`) to confirm baseline metrics don't regress.
104
+ * - Removing a matchTerm can drop recall for the corresponding query
105
+ * shape. Update `ROUND3_BASELINE` and the plan doc if intentional.
106
+ * - Korean-English cross-mapping (`에러` ↔ `error`) must not regress —
107
+ * covered by the bilingual spot-checks in
108
+ * `tests/term-normalizer.test.ts`.
109
+ */
110
+ export const DEFAULT_MATCH_TERMS = [
111
+ // ── Frameworks / UI ──
112
+ { canonical: 'react', matchTerms: ['jsx', 'component', 'hook', 'useState', 'useEffect', '컴포넌트'] },
113
+ // ── Persistence ──
114
+ { canonical: 'database', matchTerms: ['db', 'sql', 'schema', 'migration', 'query', '데이터베이스', '스키마'] },
115
+ { canonical: 'migration', matchTerms: ['migrate', 'upgrade', '마이그레이션', '업그레이드'] },
116
+ // ── Testing ──
117
+ { canonical: 'test', matchTerms: ['testing', 'spec', 'vitest', 'jest', 'mocha', '테스트', '단위테스트'] },
118
+ // ── Languages ──
119
+ { canonical: 'typescript', matchTerms: ['ts', 'type', 'interface', 'generic'] },
120
+ // ── API / Network ──
121
+ { canonical: 'api', matchTerms: ['rest', 'graphql', 'endpoint', 'route'] },
122
+ { canonical: 'auth', matchTerms: ['authentication', 'authorization', 'login', 'session', 'jwt', '인증'] },
123
+ // ── DevOps ──
124
+ { canonical: 'docker', matchTerms: ['container', 'dockerfile', 'compose'] },
125
+ { canonical: 'ci', matchTerms: ['pipeline', 'workflow', 'actions'] },
126
+ { canonical: 'deploy', matchTerms: ['deployment', 'release', 'publish', '배포'] },
127
+ // ── Error / Debug ──
128
+ //
129
+ // Two canonicals kept distinct — debug tooling ≠ the error condition
130
+ // itself. Merging them pulled `디버깅`/`debugger`/`breakpoint` into query
131
+ // expansions of `error`, which inflated `starter-debugging-systematic`'s
132
+ // relevance on any generic error-handling query and flipped the ranking
133
+ // of `"proper error handling pattern"` to the wrong solution (baseline
134
+ // mrrAt5 dropped from 1.0 → 0.988). Splitting them back restores the
135
+ // old SYNONYM_MAP semantic: `error` includes `debug` (the verb/general
136
+ // concept) but NOT the debugging toolchain terms.
137
+ //
138
+ // Query-side impact: `"에러 핸들링"` still expands densely via the
139
+ // `error` + `handling` families; `"디버깅"` queries now expand via the
140
+ // dedicated `debug` family and no longer cross-contaminate error matches.
141
+ { canonical: 'error', matchTerms: ['bug', 'fix', 'debug', 'crash', 'exception', '에러', '오류', '버그', '예외'] },
142
+ { canonical: 'debug', matchTerms: ['debugger', 'breakpoint', '디버깅', '디버그'] },
143
+ // Handling is kept distinct from `error`: not every "handling" query is
144
+ // about errors (event handling, request handling), but they often co-occur.
145
+ { canonical: 'handling', matchTerms: ['handler', 'catch', 'try', 'recovery', '핸들링', '처리', '대응'] },
146
+ // ── Performance / Cache ──
147
+ //
148
+ // Merged `performance` + `성능` + `최적화` into one family — all three
149
+ // pulled overlapping `optimize`/`profiling`/`bottleneck` terms.
150
+ { canonical: 'performance', matchTerms: ['optimize', 'profiling', 'bottleneck', 'latency', '성능', '최적화', '프로파일링', '병목'] },
151
+ { canonical: 'cache', matchTerms: ['caching', 'memoize', 'invalidate', '캐시', '캐싱'] },
152
+ // ── Security ──
153
+ //
154
+ // Merged `security` + `보안` + `인증` overlap is handled by the `auth`
155
+ // canonical above. Security keeps the vulnerability family.
156
+ { canonical: 'security', matchTerms: ['vulnerability', 'injection', 'xss', 'csrf', '보안', '취약점'] },
157
+ // ── Refactoring / Architecture ──
158
+ //
159
+ // Merged `refactor` + `리팩토링`.
160
+ { canonical: 'refactor', matchTerms: ['cleanup', 'restructure', 'simplify', 'decompose', '리팩토링', '정리', '개선', '분리'] },
161
+ // ── Validation ──
162
+ { canonical: 'validation', matchTerms: ['validate', 'check', 'sanitize', '검증', '유효성'] },
163
+ // ── Logging ──
164
+ { canonical: 'logging', matchTerms: ['log', 'trace', 'monitor', '로깅', '로그'] },
165
+ ];
166
+ /**
167
+ * Pre-built normalizer for the default registry. Modules that need the
168
+ * default behaviour should import this directly rather than calling
169
+ * `buildTermNormalizer(DEFAULT_MATCH_TERMS)` on every query.
170
+ */
171
+ export const defaultNormalizer = buildTermNormalizer(DEFAULT_MATCH_TERMS);
package/dist/fgx.d.ts ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fgx — forgen --dangerously-skip-permissions 의 단축 명령
4
+ * 모든 인자를 그대로 전달하되, --dangerously-skip-permissions 를 자동 주입
5
+ */
6
+ export {};
package/dist/fgx.js ADDED
@@ -0,0 +1,42 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * fgx — forgen --dangerously-skip-permissions 의 단축 명령
4
+ * 모든 인자를 그대로 전달하되, --dangerously-skip-permissions 를 자동 주입
5
+ */
6
+ const args = process.argv.slice(2);
7
+ // 이미 포함되어 있으면 중복 추가하지 않음
8
+ if (!args.includes('--dangerously-skip-permissions')) {
9
+ args.unshift('--dangerously-skip-permissions');
10
+ }
11
+ // cli.ts 의 main 로직을 재사용
12
+ import { prepareHarness, isFirstRun } from './core/harness.js';
13
+ import { spawnClaude } from './core/spawn.js';
14
+ async function main() {
15
+ // Security warning — fgx bypasses all Claude Code permission checks
16
+ console.warn('\n ⚠ fgx: ALL permission checks are disabled (--dangerously-skip-permissions)');
17
+ console.warn(' ⚠ Claude Code will execute tools without asking for confirmation.');
18
+ console.warn(' ⚠ Use only in trusted environments.\n');
19
+ // fgx는 서브커맨드 없이 바로 Claude Code 실행 전용
20
+ const firstRun = isFirstRun();
21
+ if (firstRun) {
22
+ console.log('\n Forgen — Setting up for the first time.\n');
23
+ console.log(' Creating ~/.forgen/ directory and default philosophy.');
24
+ console.log(' Run `forgen onboarding` afterwards to complete personalization.\n');
25
+ }
26
+ const context = await prepareHarness(process.cwd());
27
+ if (firstRun) {
28
+ console.log(' [Done] Initial setup complete.\n');
29
+ }
30
+ const v1 = context.v1;
31
+ console.log(`[forgen] Profile: ${v1.session ? `${v1.session.quality_pack}/${v1.session.autonomy_pack}` : 'onboarding needed'}`);
32
+ if (v1.session) {
33
+ console.log(`[forgen] Trust: ${v1.session.effective_trust_policy}`);
34
+ }
35
+ console.log('[forgen] Mode: dangerously-skip-permissions');
36
+ console.log('[forgen] Starting Claude Code...\n');
37
+ await spawnClaude(args, context);
38
+ }
39
+ main().catch((err) => {
40
+ console.error('[forgen] Error:', err instanceof Error ? err.message : err);
41
+ process.exit(1);
42
+ });
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Forgen Forge — v1 CLI Handler
3
+ *
4
+ * forgen forge v1 onboarding (첫 실행) 또는 profile 요약
5
+ * forgen forge --profile 현재 v1 profile 표시
6
+ * forgen forge --export Profile JSON 출력
7
+ * forgen forge --reset soft Soft reset
8
+ * forgen forge --reset learning Learning reset
9
+ * forgen forge --reset full Full reset
10
+ */
11
+ export declare function handleForge(args: string[]): Promise<void>;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Forgen Forge — v1 CLI Handler
3
+ *
4
+ * forgen forge v1 onboarding (첫 실행) 또는 profile 요약
5
+ * forgen forge --profile 현재 v1 profile 표시
6
+ * forgen forge --export Profile JSON 출력
7
+ * forgen forge --reset soft Soft reset
8
+ * forgen forge --reset learning Learning reset
9
+ * forgen forge --reset full Full reset
10
+ */
11
+ import { loadProfile, profileExists } from '../store/profile-store.js';
12
+ import { renderProfile } from '../renderer/inspect-renderer.js';
13
+ import { runOnboarding } from './onboarding-cli.js';
14
+ export async function handleForge(args) {
15
+ if (args.includes('--profile')) {
16
+ handleShowProfile();
17
+ return;
18
+ }
19
+ if (args.includes('--export')) {
20
+ handleExport();
21
+ return;
22
+ }
23
+ if (args.includes('--reset')) {
24
+ const level = args[args.indexOf('--reset') + 1] ?? 'soft';
25
+ await handleReset(level);
26
+ return;
27
+ }
28
+ // Default: profile이 있으면 보여주고, 없으면 onboarding
29
+ if (profileExists()) {
30
+ handleShowProfile();
31
+ }
32
+ else {
33
+ await runOnboarding();
34
+ }
35
+ }
36
+ function handleShowProfile() {
37
+ const profile = loadProfile();
38
+ if (!profile) {
39
+ console.log('\n No v1 profile found. Run `forgen forge` or `forgen onboarding`.\n');
40
+ return;
41
+ }
42
+ console.log('\n' + renderProfile(profile) + '\n');
43
+ }
44
+ function handleExport() {
45
+ const profile = loadProfile();
46
+ if (!profile) {
47
+ console.log('{}');
48
+ return;
49
+ }
50
+ console.log(JSON.stringify(profile, null, 2));
51
+ }
52
+ async function handleReset(level) {
53
+ const validLevels = ['soft', 'learning', 'full'];
54
+ if (!validLevels.includes(level)) {
55
+ console.log(` Invalid reset level: ${level}`);
56
+ console.log(` Valid levels: ${validLevels.join(', ')}`);
57
+ return;
58
+ }
59
+ // 동적 import로 store 모듈 로드
60
+ const fs = await import('node:fs');
61
+ const { V1_PROFILE, V1_RULES_DIR, V1_EVIDENCE_DIR, V1_RECOMMENDATIONS_DIR, V1_SESSIONS_DIR, V1_RAW_LOGS_DIR, V1_SOLUTIONS_DIR } = await import('../core/paths.js');
62
+ const deleteDirs = (dirs) => {
63
+ for (const dir of dirs) {
64
+ try {
65
+ fs.rmSync(dir, { recursive: true, force: true });
66
+ fs.mkdirSync(dir, { recursive: true });
67
+ }
68
+ catch { /* ignore */ }
69
+ }
70
+ };
71
+ const deleteFile = (p) => {
72
+ try {
73
+ fs.unlinkSync(p);
74
+ }
75
+ catch { /* ignore */ }
76
+ };
77
+ if (level === 'soft') {
78
+ deleteFile(V1_PROFILE);
79
+ deleteDirs([V1_RULES_DIR, V1_RECOMMENDATIONS_DIR, V1_SESSIONS_DIR]);
80
+ console.log('\n Soft reset 완료. Profile + Rule + Recommendation + Session 초기화.');
81
+ }
82
+ else if (level === 'learning') {
83
+ deleteFile(V1_PROFILE);
84
+ deleteDirs([V1_RULES_DIR, V1_EVIDENCE_DIR, V1_RECOMMENDATIONS_DIR, V1_SESSIONS_DIR, V1_RAW_LOGS_DIR]);
85
+ console.log('\n Learning reset 완료. 개인 학습 전체 초기화.');
86
+ }
87
+ else if (level === 'full') {
88
+ deleteFile(V1_PROFILE);
89
+ deleteDirs([V1_RULES_DIR, V1_EVIDENCE_DIR, V1_RECOMMENDATIONS_DIR, V1_SESSIONS_DIR, V1_RAW_LOGS_DIR, V1_SOLUTIONS_DIR]);
90
+ console.log('\n Full reset 완료. Compound 포함 전체 초기화.');
91
+ }
92
+ // Reset 후 자동 온보딩 (interactive 환경에서만)
93
+ if (process.stdin.isTTY) {
94
+ console.log(' 새 프로필을 생성합니다.\n');
95
+ await runOnboarding();
96
+ }
97
+ else {
98
+ console.log(' forgen forge 또는 forgen onboarding 으로 새 프로필을 생성하세요.\n');
99
+ }
100
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Forgen v1 — Evidence Processor
3
+ *
4
+ * 교정/관찰 → Evidence 생성 + facet 조정 제안 인터페이스.
5
+ * Migration Plan §5.4: 이 모듈의 "해석" 계층은 AI(Claude 세션)가 채운다.
6
+ * 여기서는 구조화된 입출력 인터페이스와 알고리즘 적용 함수만 정의.
7
+ */
8
+ import type { CorrectionRequest, CorrectionResult, SessionLearningSummary, QualityFacets, AutonomyFacets } from '../store/types.js';
9
+ /**
10
+ * 사용자 교정을 Evidence로 기록하고, 필요 시 temporary rule 생성.
11
+ * axis_hint는 AI(Claude 세션)가 제공하는 것을 전제.
12
+ */
13
+ export declare function processCorrection(req: CorrectionRequest): CorrectionResult;
14
+ /**
15
+ * SessionLearningSummary의 profile_delta_suggestion을 현재 facet에 적용.
16
+ * delta 값은 AI가 제안한 것이고, 이 함수는 clamp만 수행.
17
+ */
18
+ export declare function applyFacetDelta(currentQuality: QualityFacets, currentAutonomy: AutonomyFacets, delta: SessionLearningSummary['profile_delta_suggestion']): {
19
+ quality: QualityFacets;
20
+ autonomy: AutonomyFacets;
21
+ };
@@ -0,0 +1,87 @@
1
+ /**
2
+ * Forgen v1 — Evidence Processor
3
+ *
4
+ * 교정/관찰 → Evidence 생성 + facet 조정 제안 인터페이스.
5
+ * Migration Plan §5.4: 이 모듈의 "해석" 계층은 AI(Claude 세션)가 채운다.
6
+ * 여기서는 구조화된 입출력 인터페이스와 알고리즘 적용 함수만 정의.
7
+ */
8
+ import { createEvidence, saveEvidence } from '../store/evidence-store.js';
9
+ import { createRule, saveRule } from '../store/rule-store.js';
10
+ // ── Correction → Evidence + Temporary Rule ──
11
+ /**
12
+ * 사용자 교정을 Evidence로 기록하고, 필요 시 temporary rule 생성.
13
+ * axis_hint는 AI(Claude 세션)가 제공하는 것을 전제.
14
+ */
15
+ export function processCorrection(req) {
16
+ // Evidence 기록
17
+ const evidence = createEvidence({
18
+ type: 'explicit_correction',
19
+ session_id: req.session_id,
20
+ source_component: 'Hooks',
21
+ summary: req.message,
22
+ axis_refs: req.axis_hint ? [req.axis_hint] : [],
23
+ confidence: 0.85, // explicit correction은 기본 높은 confidence
24
+ raw_payload: {
25
+ kind: req.kind,
26
+ target: req.target,
27
+ axis_hint: req.axis_hint,
28
+ // avoid-this는 현재 설정과 반대 방향의 교정 → mismatch 감지용
29
+ direction: req.kind === 'avoid-this' ? 'opposite' : 'same',
30
+ },
31
+ });
32
+ saveEvidence(evidence);
33
+ // fix-now, avoid-this → temporary session rule
34
+ let temporaryRule = null;
35
+ if (req.kind === 'fix-now' || req.kind === 'avoid-this') {
36
+ temporaryRule = createRule({
37
+ category: req.axis_hint === 'quality_safety' ? 'quality'
38
+ : req.axis_hint === 'autonomy' ? 'autonomy'
39
+ : 'workflow',
40
+ scope: 'session',
41
+ trigger: req.target,
42
+ policy: req.message,
43
+ strength: req.kind === 'avoid-this' ? 'strong' : 'default',
44
+ source: 'explicit_correction',
45
+ evidence_refs: [evidence.evidence_id],
46
+ render_key: `${req.axis_hint ?? 'workflow'}.${req.target.toLowerCase().replace(/\s+/g, '-').slice(0, 30)}`,
47
+ });
48
+ saveRule(temporaryRule);
49
+ }
50
+ return {
51
+ temporary_rule: temporaryRule,
52
+ evidence_event_id: evidence.evidence_id,
53
+ recompose_required: temporaryRule !== null,
54
+ promotion_candidate: req.kind === 'prefer-from-now' || req.kind === 'avoid-this',
55
+ };
56
+ }
57
+ // ── Facet Delta 적용 (알고리즘 계층) ──
58
+ /**
59
+ * SessionLearningSummary의 profile_delta_suggestion을 현재 facet에 적용.
60
+ * delta 값은 AI가 제안한 것이고, 이 함수는 clamp만 수행.
61
+ */
62
+ export function applyFacetDelta(currentQuality, currentAutonomy, delta) {
63
+ if (!delta)
64
+ return { quality: { ...currentQuality }, autonomy: { ...currentAutonomy } };
65
+ const clamp = (v) => Math.max(0.0, Math.min(1.0, v));
66
+ const quality = { ...currentQuality };
67
+ if (delta.quality_safety) {
68
+ if (delta.quality_safety.verification_depth !== undefined)
69
+ quality.verification_depth = clamp(quality.verification_depth + delta.quality_safety.verification_depth);
70
+ if (delta.quality_safety.stop_threshold !== undefined)
71
+ quality.stop_threshold = clamp(quality.stop_threshold + delta.quality_safety.stop_threshold);
72
+ if (delta.quality_safety.change_conservatism !== undefined)
73
+ quality.change_conservatism = clamp(quality.change_conservatism + delta.quality_safety.change_conservatism);
74
+ }
75
+ const autonomy = { ...currentAutonomy };
76
+ if (delta.autonomy) {
77
+ if (delta.autonomy.confirmation_independence !== undefined)
78
+ autonomy.confirmation_independence = clamp(autonomy.confirmation_independence + delta.autonomy.confirmation_independence);
79
+ if (delta.autonomy.assumption_tolerance !== undefined)
80
+ autonomy.assumption_tolerance = clamp(autonomy.assumption_tolerance + delta.autonomy.assumption_tolerance);
81
+ if (delta.autonomy.scope_expansion_tolerance !== undefined)
82
+ autonomy.scope_expansion_tolerance = clamp(autonomy.scope_expansion_tolerance + delta.autonomy.scope_expansion_tolerance);
83
+ if (delta.autonomy.approval_threshold !== undefined)
84
+ autonomy.approval_threshold = clamp(autonomy.approval_threshold + delta.autonomy.approval_threshold);
85
+ }
86
+ return { quality, autonomy };
87
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Forgen v1 — Pack Mismatch Detector
3
+ *
4
+ * rolling 3세션 mismatch score 계산.
5
+ * Authoritative spec: docs/plans/2026-04-03-forgen-onboarding-adaptation-spec.md §8
6
+ *
7
+ * 신호별 점수:
8
+ * - 반대 방향 explicit_correction: +2
9
+ * - session_summary가 반대 pack 성향 명시: +1
10
+ * - 같은 축 strong rule 2개+ 신규 생성: +1
11
+ *
12
+ * 후보 조건:
13
+ * - 최근 3세션 rolling sum >= 4 (축별)
14
+ * - 또는 최근 3세션에 같은 방향 explicit_correction 2회+
15
+ */
16
+ import type { Evidence, Rule, QualityPack, AutonomyPack } from '../store/types.js';
17
+ export interface MismatchSignal {
18
+ session_id: string;
19
+ axis: 'quality_safety' | 'autonomy';
20
+ score: number;
21
+ reason: string;
22
+ }
23
+ export interface MismatchResult {
24
+ quality_mismatch: boolean;
25
+ autonomy_mismatch: boolean;
26
+ quality_score: number;
27
+ autonomy_score: number;
28
+ signals: MismatchSignal[];
29
+ }
30
+ /**
31
+ * 단일 세션의 mismatch 신호를 계산.
32
+ *
33
+ * @param sessionId 세션 ID
34
+ * @param corrections 해당 세션의 explicit_correction evidence
35
+ * @param summaries 해당 세션의 session_summary evidence
36
+ * @param newStrongRules 해당 세션에서 신규 생성된 strong rule
37
+ * @param currentQuality 현재 quality pack
38
+ * @param currentAutonomy 현재 autonomy pack
39
+ */
40
+ export declare function computeSessionSignals(sessionId: string, corrections: Evidence[], summaries: Evidence[], newStrongRules: Rule[], _currentQuality: QualityPack, _currentAutonomy: AutonomyPack): MismatchSignal[];
41
+ /**
42
+ * 최근 3세션 rolling sum으로 mismatch 판정.
43
+ */
44
+ export declare function detectMismatch(recentSignals: MismatchSignal[]): MismatchResult;