@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,108 @@
1
+ export interface Principle {
2
+ belief: string;
3
+ /**
4
+ * generates 항목 유형:
5
+ * - string: 일반 텍스트 규칙
6
+ * - routing: 모델 라우팅 지시 (예: "explore → Sonnet, implement → Opus")
7
+ * - alert: 경고 메시지 (임계값 초과 시 표시)
8
+ * - step: 단계 설명
9
+ * - hook: @planned — 향후 자동 훅 등록 기능에서 사용 예정. 현재 소비자 없음.
10
+ */
11
+ generates: Array<string | {
12
+ hook?: string;
13
+ routing?: string;
14
+ alert?: string;
15
+ step?: string;
16
+ }>;
17
+ }
18
+ export interface Philosophy {
19
+ name: string;
20
+ version: string;
21
+ author: string;
22
+ description?: string;
23
+ /** 중앙 관리 팩에서 상속 (예: "pack:emr-standard") */
24
+ extends?: string;
25
+ principles: Record<string, Principle>;
26
+ }
27
+ /** 팩이 요구하는 외부 의존성 */
28
+ export interface PackRequirement {
29
+ /** MCP 서버 */
30
+ mcpServers?: Array<{
31
+ name: string;
32
+ installCmd?: string;
33
+ npm?: string;
34
+ pip?: string;
35
+ description?: string;
36
+ }>;
37
+ /** CLI 도구 */
38
+ tools?: Array<{
39
+ name: string;
40
+ installCmd?: string;
41
+ description?: string;
42
+ }>;
43
+ /** 환경변수 */
44
+ envVars?: Array<{
45
+ name: string;
46
+ description?: string;
47
+ required?: boolean;
48
+ }>;
49
+ }
50
+ export interface PackMeta {
51
+ name: string;
52
+ version: string;
53
+ remote?: {
54
+ type: 'github' | 'gdrive' | 's3' | 'local';
55
+ url: string;
56
+ auto_sync?: boolean;
57
+ };
58
+ provides?: {
59
+ atoms?: number;
60
+ manuals?: number;
61
+ solutions?: number;
62
+ rules?: number;
63
+ skills?: number;
64
+ agents?: number;
65
+ workflows?: number;
66
+ };
67
+ /** 팩이 요구하는 외부 의존성 */
68
+ requires?: PackRequirement;
69
+ }
70
+ export interface ScopeInfo {
71
+ me: {
72
+ philosophyPath: string;
73
+ solutionCount: number;
74
+ ruleCount: number;
75
+ };
76
+ team?: {
77
+ name: string;
78
+ version: string;
79
+ packPath: string;
80
+ solutionCount: number;
81
+ ruleCount: number;
82
+ syncStatus: 'synced' | 'outdated' | 'unknown';
83
+ };
84
+ project: {
85
+ path: string;
86
+ solutionCount: number;
87
+ };
88
+ summary: string;
89
+ }
90
+ /** 태스크 → 추천 모델 라우팅 테이블 */
91
+ export interface ModelRoutingTable {
92
+ /** Task type → recommended model */
93
+ routes: Record<string, 'opus' | 'sonnet' | 'haiku'>;
94
+ }
95
+ export interface HarnessContext {
96
+ philosophy: Philosophy;
97
+ /** 철학 로드 소스: project(프로젝트별), global(글로벌), default(기본값) */
98
+ philosophySource: 'project' | 'global' | 'default';
99
+ scope: ScopeInfo;
100
+ cwd: string;
101
+ inTmux: boolean;
102
+ /** Philosophy 기반 모델 라우팅 테이블 */
103
+ modelRouting?: Record<string, string[]>;
104
+ /** 신호 기반 하이브리드 라우팅 활성 여부 */
105
+ signalRoutingEnabled?: boolean;
106
+ /** 모델 라우팅 프리셋 (default, cost-saving, max-quality) */
107
+ routingPreset?: string;
108
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,4 @@
1
+ /** forgen uninstall 메인 */
2
+ export declare function handleUninstall(cwd: string, options: {
3
+ force?: boolean;
4
+ }): Promise<void>;
@@ -0,0 +1,307 @@
1
+ import * as fs from 'node:fs';
2
+ import * as os from 'node:os';
3
+ import * as path from 'node:path';
4
+ import * as readline from 'node:readline';
5
+ import { SETTINGS_PATH, acquireLock, releaseLock, atomicWriteFileSync, } from './settings-lock.js';
6
+ /** 플러그인 제거 (plugin-installer.ts 삭제 후 인라인) */
7
+ function uninstallPlugin() {
8
+ const pluginDir = path.join(os.homedir(), '.claude', 'plugins', 'forgen');
9
+ try {
10
+ if (fs.existsSync(pluginDir)) {
11
+ fs.rmSync(pluginDir, { recursive: true, force: true });
12
+ }
13
+ const settingsPath = path.join(os.homedir(), '.claude', 'settings.json');
14
+ if (fs.existsSync(settingsPath)) {
15
+ const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
16
+ if (Array.isArray(settings.plugins)) {
17
+ settings.plugins = settings.plugins.filter(p => !p.includes('forgen'));
18
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
19
+ }
20
+ }
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ }
27
+ /** 플러그인 관련 아티팩트 정리 */
28
+ function cleanPluginArtifacts() {
29
+ const claudeDir = path.join(os.homedir(), '.claude');
30
+ const pluginsDir = path.join(claudeDir, 'plugins');
31
+ // 1. ~/.claude/plugins/cache/forgen-local/ 삭제
32
+ try {
33
+ const cacheDir = path.join(pluginsDir, 'cache', 'forgen-local');
34
+ if (fs.existsSync(cacheDir)) {
35
+ fs.rmSync(cacheDir, { recursive: true, force: true });
36
+ console.log(' ✓ Removed plugin cache (~/.claude/plugins/cache/forgen-local/)');
37
+ }
38
+ }
39
+ catch (e) {
40
+ console.error(' ✗ Failed to remove plugin cache:', e instanceof Error ? e.message : String(e));
41
+ }
42
+ // 2. ~/.claude/plugins/installed_plugins.json에서 forgen@forgen-local 제거
43
+ try {
44
+ const installedPluginsPath = path.join(pluginsDir, 'installed_plugins.json');
45
+ if (fs.existsSync(installedPluginsPath)) {
46
+ const data = JSON.parse(fs.readFileSync(installedPluginsPath, 'utf-8'));
47
+ if (data.plugins && 'forgen@forgen-local' in data.plugins) {
48
+ delete data.plugins['forgen@forgen-local'];
49
+ fs.writeFileSync(installedPluginsPath, JSON.stringify(data, null, 2));
50
+ console.log(' ✓ Removed forgen@forgen-local from installed_plugins.json');
51
+ }
52
+ }
53
+ }
54
+ catch (e) {
55
+ console.error(' ✗ Failed to update installed_plugins.json:', e instanceof Error ? e.message : String(e));
56
+ }
57
+ // 3. ~/.claude/plugins/forgen/ 삭제 (plugin-installer 경로)
58
+ try {
59
+ const removed = uninstallPlugin();
60
+ if (removed) {
61
+ console.log(' ✓ Removed plugin directory (~/.claude/plugins/forgen/)');
62
+ }
63
+ }
64
+ catch (e) {
65
+ console.error(' ✗ Failed to remove plugin directory:', e instanceof Error ? e.message : String(e));
66
+ }
67
+ }
68
+ /** ~/.claude/commands/forgen/ 슬래시 명령 파일 제거 */
69
+ function cleanSlashCommands() {
70
+ const commandsDir = path.join(os.homedir(), '.claude', 'commands', 'forgen');
71
+ if (!fs.existsSync(commandsDir)) {
72
+ console.log(' - No slash command directory found');
73
+ return;
74
+ }
75
+ let removed = 0;
76
+ for (const file of fs.readdirSync(commandsDir).filter((f) => f.endsWith('.md'))) {
77
+ const filePath = path.join(commandsDir, file);
78
+ const content = fs.readFileSync(filePath, 'utf-8');
79
+ if (content.includes('<!-- forgen-managed -->')) {
80
+ fs.unlinkSync(filePath);
81
+ removed++;
82
+ }
83
+ }
84
+ // 디렉토리가 비었으면 삭제
85
+ try {
86
+ const remaining = fs.readdirSync(commandsDir);
87
+ if (remaining.length === 0) {
88
+ fs.rmdirSync(commandsDir);
89
+ }
90
+ }
91
+ catch { /* ignore */ }
92
+ if (removed > 0) {
93
+ console.log(` ✓ Removed ${removed} slash command(s) (~/.claude/commands/forgen/)`);
94
+ }
95
+ else {
96
+ console.log(' - No forgen-managed slash commands found');
97
+ }
98
+ }
99
+ /** 사용자에게 y/n 확인 */
100
+ function confirm(message) {
101
+ return new Promise((resolve) => {
102
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
103
+ rl.question(`${message} (y/N) `, (answer) => {
104
+ rl.close();
105
+ resolve(answer.trim().toLowerCase() === 'y');
106
+ });
107
+ });
108
+ }
109
+ /** settings.json에서 CH 관련 항목 제거 */
110
+ function cleanSettings() {
111
+ if (!fs.existsSync(SETTINGS_PATH))
112
+ return;
113
+ let settings;
114
+ try {
115
+ settings = JSON.parse(fs.readFileSync(SETTINGS_PATH, 'utf-8'));
116
+ }
117
+ catch {
118
+ console.error('[forgen] Failed to parse settings.json — skipping.');
119
+ return;
120
+ }
121
+ // env에서 COMPOUND_ 접두어 키 제거
122
+ const env = settings.env;
123
+ if (env) {
124
+ for (const key of Object.keys(env)) {
125
+ if (key.startsWith('COMPOUND_'))
126
+ delete env[key];
127
+ }
128
+ if (Object.keys(env).length === 0) {
129
+ delete settings.env;
130
+ }
131
+ }
132
+ // hooks에서 forgen 관련 엔트리 제거
133
+ const hookMarkers = ['forgen', 'compound-harness'];
134
+ function isCHCommand(cmd) {
135
+ return hookMarkers.some(m => cmd.includes(m));
136
+ }
137
+ function isCHHookEntry(entry) {
138
+ // 직접 형식: { type, command }
139
+ if (typeof entry.command === 'string' && isCHCommand(entry.command))
140
+ return true;
141
+ // 래핑 형식: { matcher, hooks: [{ command }] }
142
+ const innerHooks = entry.hooks;
143
+ if (Array.isArray(innerHooks)) {
144
+ return innerHooks.some(h => typeof h.command === 'string' && isCHCommand(h.command));
145
+ }
146
+ return false;
147
+ }
148
+ const hooks = settings.hooks;
149
+ if (hooks) {
150
+ for (const [hookType, entries] of Object.entries(hooks)) {
151
+ if (!Array.isArray(entries))
152
+ continue;
153
+ const filtered = entries.filter((h) => !isCHHookEntry(h));
154
+ if (filtered.length === 0) {
155
+ delete hooks[hookType];
156
+ }
157
+ else {
158
+ hooks[hookType] = filtered;
159
+ }
160
+ }
161
+ if (Object.keys(hooks).length === 0) {
162
+ delete settings.hooks;
163
+ }
164
+ }
165
+ // statusLine이 forgen status면 제거
166
+ const statusLine = settings.statusLine;
167
+ if (statusLine?.command === 'forgen status') {
168
+ delete settings.statusLine;
169
+ }
170
+ // enabledPlugins에서 forgen@forgen-local 제거
171
+ const enabledPlugins = settings.enabledPlugins;
172
+ if (enabledPlugins && 'forgen@forgen-local' in enabledPlugins) {
173
+ delete enabledPlugins['forgen@forgen-local'];
174
+ if (Object.keys(enabledPlugins).length === 0) {
175
+ delete settings.enabledPlugins;
176
+ }
177
+ }
178
+ // mcpServers에서 forgen-compound 제거
179
+ const mcpServers = settings.mcpServers;
180
+ if (mcpServers && 'forgen-compound' in mcpServers) {
181
+ delete mcpServers['forgen-compound'];
182
+ if (Object.keys(mcpServers).length === 0) {
183
+ delete settings.mcpServers;
184
+ }
185
+ }
186
+ acquireLock();
187
+ try {
188
+ atomicWriteFileSync(SETTINGS_PATH, JSON.stringify(settings, null, 2));
189
+ }
190
+ finally {
191
+ releaseLock();
192
+ }
193
+ console.log(' ✓ Removed CH entries from settings.json');
194
+ }
195
+ /** 프로젝트 .claude/agents/ch-*.md 삭제 (커스터마이즈된 파일은 보호) */
196
+ function cleanAgents(cwd) {
197
+ const agentsDir = path.join(cwd, '.claude', 'agents');
198
+ if (!fs.existsSync(agentsDir))
199
+ return;
200
+ let removed = 0;
201
+ let preserved = 0;
202
+ for (const file of fs.readdirSync(agentsDir)) {
203
+ if (file.startsWith('ch-') && file.endsWith('.md')) {
204
+ const filePath = path.join(agentsDir, file);
205
+ const content = fs.readFileSync(filePath, 'utf-8');
206
+ if (!content.includes('<!-- forgen-managed -->')) {
207
+ // 사용자가 커스터마이즈한 파일 → 보존
208
+ preserved++;
209
+ continue;
210
+ }
211
+ fs.unlinkSync(filePath);
212
+ removed++;
213
+ }
214
+ }
215
+ if (removed > 0) {
216
+ console.log(` ✓ Removed ${removed} agent file(s) (.claude/agents/ch-*.md)`);
217
+ }
218
+ if (preserved > 0) {
219
+ console.log(` ⚠ Preserved ${preserved} customized agent file(s) (manual deletion required)`);
220
+ }
221
+ if (removed === 0 && preserved === 0) {
222
+ console.log(' - No agent files found');
223
+ }
224
+ }
225
+ /** .claude/rules/ 의 forgen 규칙 파일 및 레거시 compound-rules.md 제거 */
226
+ function cleanCompoundRules(cwd) {
227
+ const ruleFiles = [
228
+ // v4.1+ consolidated
229
+ 'project-context.md',
230
+ 'routing.md',
231
+ // legacy (v4.0 and earlier)
232
+ 'security.md',
233
+ 'golden-principles.md',
234
+ 'anti-pattern.md',
235
+ 'compound.md',
236
+ ];
237
+ const rulesDir = path.join(cwd, '.claude', 'rules');
238
+ let removedCount = 0;
239
+ for (const file of ruleFiles) {
240
+ const p = path.join(rulesDir, file);
241
+ if (fs.existsSync(p)) {
242
+ fs.unlinkSync(p);
243
+ removedCount++;
244
+ }
245
+ }
246
+ // 레거시 경로
247
+ const legacyPath = path.join(cwd, '.claude', 'compound-rules.md');
248
+ if (fs.existsSync(legacyPath)) {
249
+ fs.unlinkSync(legacyPath);
250
+ removedCount++;
251
+ }
252
+ if (removedCount > 0) {
253
+ console.log(` ✓ Removed ${removedCount} rule file(s)`);
254
+ }
255
+ else {
256
+ console.log(' - No rule files found');
257
+ }
258
+ }
259
+ /** CLAUDE.md에서 forgen 블록 제거 */
260
+ function cleanClaudeMd(cwd) {
261
+ const claudeMdPath = path.join(cwd, 'CLAUDE.md');
262
+ if (!fs.existsSync(claudeMdPath))
263
+ return;
264
+ const content = fs.readFileSync(claudeMdPath, 'utf-8');
265
+ const marker = '<!-- forgen:start -->';
266
+ const endMarker = '<!-- forgen:end -->';
267
+ if (!content.includes(marker)) {
268
+ console.log(' - No CH block found in CLAUDE.md');
269
+ return;
270
+ }
271
+ const regex = new RegExp(`\\n?${marker}[\\s\\S]*?${endMarker}\\n?`, 'g');
272
+ const cleaned = content.replace(regex, '\n');
273
+ fs.writeFileSync(claudeMdPath, `${cleaned.replace(/\n{3,}/g, '\n\n').trim()}\n`);
274
+ console.log(' ✓ Removed CH block from CLAUDE.md');
275
+ }
276
+ /** forgen uninstall 메인 */
277
+ export async function handleUninstall(cwd, options) {
278
+ console.log('\n[forgen] Uninstalling Forgen\n');
279
+ console.log('The following items will be cleaned up:');
280
+ console.log(' 1. Remove CH env vars/hooks/statusLine/enabledPlugins from ~/.claude/settings.json');
281
+ console.log(' 2. Delete .claude/agents/ch-*.md agent files');
282
+ console.log(' 3. Delete .claude/rules/ rule files (project-context, routing, forge-*)');
283
+ console.log(' 4. Remove forgen block from CLAUDE.md');
284
+ console.log(' 5. Remove slash commands (~/.claude/commands/forgen/)');
285
+ console.log(' 6. Remove plugin artifacts (cache, installed_plugins.json, plugin directory)');
286
+ console.log('');
287
+ console.log('Note: ~/.forgen/ directory is preserved (manual deletion: rm -rf ~/.forgen)\n');
288
+ if (!options.force) {
289
+ if (!process.stdin.isTTY) {
290
+ console.error('[forgen] Use --force flag in non-interactive environments.');
291
+ process.exit(1);
292
+ }
293
+ const ok = await confirm('Do you want to continue?');
294
+ if (!ok) {
295
+ console.log('Cancelled.');
296
+ return;
297
+ }
298
+ console.log('');
299
+ }
300
+ cleanSettings();
301
+ cleanAgents(cwd);
302
+ cleanCompoundRules(cwd);
303
+ cleanClaudeMd(cwd);
304
+ cleanSlashCommands();
305
+ cleanPluginArtifacts();
306
+ console.log('\n[forgen] Uninstall complete. Restart Claude Code for a clean state.\n');
307
+ }
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Forgen v1 — Session Bootstrap
3
+ *
4
+ * v1 설계의 세션 시작 흐름을 구현.
5
+ * Authoritative: docs/plans/2026-04-03-forgen-lifecycle-design.md §6
6
+ * docs/plans/2026-04-03-forgen-component-interface-design.md §4
7
+ *
8
+ * 흐름:
9
+ * 1. Legacy 감지 → cutover 필요 시 backup + fresh onboarding
10
+ * 2. Profile 로드 (없으면 onboarding 필요)
11
+ * 3. Runtime capability 감지
12
+ * 4. Preset Manager가 SessionEffectiveState 합성
13
+ * 5. Rule Renderer가 자연어 규칙 세트 생성
14
+ */
15
+ import { type MismatchResult } from '../forge/mismatch-detector.js';
16
+ import type { SessionEffectiveState, Profile } from '../store/types.js';
17
+ export declare function ensureV1Directories(): void;
18
+ export interface V1BootstrapResult {
19
+ needsOnboarding: boolean;
20
+ legacyBackupPath: string | null;
21
+ session: SessionEffectiveState | null;
22
+ renderedRules: string | null;
23
+ profile: Profile | null;
24
+ mismatch: MismatchResult | null;
25
+ }
26
+ export declare function bootstrapV1Session(): V1BootstrapResult;
@@ -0,0 +1,155 @@
1
+ /**
2
+ * Forgen v1 — Session Bootstrap
3
+ *
4
+ * v1 설계의 세션 시작 흐름을 구현.
5
+ * Authoritative: docs/plans/2026-04-03-forgen-lifecycle-design.md §6
6
+ * docs/plans/2026-04-03-forgen-component-interface-design.md §4
7
+ *
8
+ * 흐름:
9
+ * 1. Legacy 감지 → cutover 필요 시 backup + fresh onboarding
10
+ * 2. Profile 로드 (없으면 onboarding 필요)
11
+ * 3. Runtime capability 감지
12
+ * 4. Preset Manager가 SessionEffectiveState 합성
13
+ * 5. Rule Renderer가 자연어 규칙 세트 생성
14
+ */
15
+ import * as fs from 'node:fs';
16
+ import * as crypto from 'node:crypto';
17
+ import { FORGEN_HOME, V1_ME_DIR, V1_RULES_DIR, V1_EVIDENCE_DIR, V1_RECOMMENDATIONS_DIR, V1_SESSIONS_DIR, V1_STATE_DIR, V1_RAW_LOGS_DIR, V1_SOLUTIONS_DIR } from './paths.js';
18
+ import { checkLegacyProfile, runLegacyCutover } from './legacy-detector.js';
19
+ import { detectRuntimeCapability } from './runtime-detector.js';
20
+ import { loadProfile, profileExists } from '../store/profile-store.js';
21
+ import { loadActiveRules } from '../store/rule-store.js';
22
+ import { composeSession } from '../preset/preset-manager.js';
23
+ import { renderRules, DEFAULT_CONTEXT } from '../renderer/rule-renderer.js';
24
+ import { saveSessionState, loadRecentSessions } from '../store/session-state-store.js';
25
+ import { loadEvidenceBySession } from '../store/evidence-store.js';
26
+ import { computeSessionSignals, detectMismatch } from '../forge/mismatch-detector.js';
27
+ import { createRecommendation, saveRecommendation } from '../store/recommendation-store.js';
28
+ // ── Directory Initialization ──
29
+ const V1_DIRS = [FORGEN_HOME, V1_ME_DIR, V1_RULES_DIR, V1_EVIDENCE_DIR, V1_RECOMMENDATIONS_DIR, V1_STATE_DIR, V1_SESSIONS_DIR, V1_RAW_LOGS_DIR, V1_SOLUTIONS_DIR];
30
+ export function ensureV1Directories() {
31
+ for (const dir of V1_DIRS) {
32
+ fs.mkdirSync(dir, { recursive: true });
33
+ }
34
+ }
35
+ // ── Main Bootstrap ──
36
+ export function bootstrapV1Session() {
37
+ // 0. 디렉토리 준비
38
+ ensureV1Directories();
39
+ // 1. Legacy 감지
40
+ const legacyCheck = checkLegacyProfile();
41
+ let legacyBackupPath = null;
42
+ if (legacyCheck.isLegacy) {
43
+ legacyBackupPath = runLegacyCutover();
44
+ }
45
+ // 2. Profile 로드
46
+ if (!profileExists()) {
47
+ return {
48
+ needsOnboarding: true,
49
+ legacyBackupPath,
50
+ session: null,
51
+ renderedRules: null,
52
+ profile: null,
53
+ mismatch: null,
54
+ };
55
+ }
56
+ const profile = loadProfile();
57
+ if (!profile) {
58
+ return { needsOnboarding: true, legacyBackupPath, session: null, renderedRules: null, profile: null, mismatch: null };
59
+ }
60
+ // 3. Runtime capability 감지
61
+ const runtime = detectRuntimeCapability();
62
+ // 4. Rules 로드 + Session 합성
63
+ const personalRules = loadActiveRules();
64
+ const sessionId = crypto.randomUUID();
65
+ const session = composeSession({
66
+ session_id: sessionId,
67
+ profile,
68
+ personalRules,
69
+ sessionOverlays: [],
70
+ runtime,
71
+ });
72
+ // 5. Session state 저장
73
+ saveSessionState(session);
74
+ // 6. Rule 렌더링
75
+ const allRules = [...personalRules];
76
+ const renderedRules = renderRules(allRules, session, profile, DEFAULT_CONTEXT);
77
+ // 7. Mismatch 감지 (최근 3세션 rolling)
78
+ let mismatchResult = null;
79
+ try {
80
+ const recentSessions = loadRecentSessions(3);
81
+ if (recentSessions.length >= 2) {
82
+ const allSignals = [];
83
+ for (const prevSession of recentSessions) {
84
+ const sessionEvidence = loadEvidenceBySession(prevSession.session_id);
85
+ const corrections = sessionEvidence.filter(e => e.type === 'explicit_correction');
86
+ const summaries = sessionEvidence.filter(e => e.type === 'session_summary');
87
+ const strongRules = personalRules.filter(r => r.strength === 'strong' && r.evidence_refs.some(ref => sessionEvidence.some(e => e.evidence_id === ref)));
88
+ const signals = computeSessionSignals(prevSession.session_id, corrections, summaries, strongRules, profile.base_packs.quality_pack, profile.base_packs.autonomy_pack);
89
+ allSignals.push(...signals);
90
+ }
91
+ if (allSignals.length > 0) {
92
+ mismatchResult = detectMismatch(allSignals);
93
+ // mismatch 감지 시 재추천 생성
94
+ if (mismatchResult.quality_mismatch || mismatchResult.autonomy_mismatch) {
95
+ session.warnings.push(`Pack mismatch 감지: quality=${mismatchResult.quality_score}, autonomy=${mismatchResult.autonomy_score}. forgen forge --reset soft 로 재설정하거나 forgen onboarding 으로 재추천을 받으세요.`);
96
+ const rec = createRecommendation({
97
+ source: 'mismatch_recommendation',
98
+ quality_pack: mismatchResult.quality_mismatch
99
+ ? (profile.base_packs.quality_pack === '보수형' ? '속도형' : '보수형')
100
+ : profile.base_packs.quality_pack,
101
+ autonomy_pack: mismatchResult.autonomy_mismatch
102
+ ? (profile.base_packs.autonomy_pack === '확인 우선형' ? '자율 실행형' : '확인 우선형')
103
+ : profile.base_packs.autonomy_pack,
104
+ suggested_trust_policy: profile.trust_preferences.desired_policy,
105
+ confidence: 0.6,
106
+ reason_summary: `Rolling 3세션 mismatch: quality=${mismatchResult.quality_score}, autonomy=${mismatchResult.autonomy_score}`,
107
+ });
108
+ saveRecommendation(rec);
109
+ }
110
+ }
111
+ }
112
+ }
113
+ catch {
114
+ // mismatch 감지 실패는 세션 시작을 막지 않음
115
+ }
116
+ // 8. Raw Log 기록 + TTL sweep (7일)
117
+ try {
118
+ // 세션 시작 로그
119
+ const rawLogPath = require('node:path').join(V1_RAW_LOGS_DIR, `${sessionId}.jsonl`);
120
+ fs.appendFileSync(rawLogPath, JSON.stringify({
121
+ event: 'session-started',
122
+ session_id: sessionId,
123
+ timestamp: new Date().toISOString(),
124
+ quality_pack: profile.base_packs.quality_pack,
125
+ autonomy_pack: profile.base_packs.autonomy_pack,
126
+ judgment_pack: profile.base_packs.judgment_pack,
127
+ communication_pack: profile.base_packs.communication_pack,
128
+ effective_trust: session.effective_trust_policy,
129
+ }) + '\n');
130
+ // TTL sweep: 7일 이상 된 raw log 파일 삭제
131
+ const TTL_MS = 7 * 24 * 60 * 60 * 1000;
132
+ const now = Date.now();
133
+ for (const file of fs.readdirSync(V1_RAW_LOGS_DIR)) {
134
+ const filePath = require('node:path').join(V1_RAW_LOGS_DIR, file);
135
+ try {
136
+ const stat = fs.statSync(filePath);
137
+ if (now - stat.mtimeMs > TTL_MS) {
138
+ fs.unlinkSync(filePath);
139
+ }
140
+ }
141
+ catch { /* skip */ }
142
+ }
143
+ }
144
+ catch {
145
+ // raw log 실패는 세션 시작을 막지 않음
146
+ }
147
+ return {
148
+ needsOnboarding: false,
149
+ legacyBackupPath,
150
+ session,
151
+ renderedRules,
152
+ profile,
153
+ mismatch: mismatchResult,
154
+ };
155
+ }
@@ -0,0 +1,24 @@
1
+ /** List all solutions with status summary */
2
+ export declare function listSolutions(): void;
3
+ /** Inspect a single saved entry in detail */
4
+ export declare function inspectSolution(name: string): void;
5
+ /** Remove a saved entry by name */
6
+ export declare function removeSolution(name: string): void;
7
+ /**
8
+ * Retire solutions whose names match extractors that have been removed
9
+ * from the compound pipeline. Retired solutions are excluded from the
10
+ * index (see `solution-index.ts:142`) so they stop being surfaced in
11
+ * MCP search and hook injection, but the file stays on disk for
12
+ * audit / undo purposes.
13
+ *
14
+ * M-3 (2026-04-09): the C4 extractor cleanup left orphaned files in
15
+ * users' `~/.forgen/me/solutions/` directories. Without this migration,
16
+ * files like `recurring-task-pattern.md` (which had injected=113 on
17
+ * the author's own machine at fix time) continue to pollute matching
18
+ * results until the user manually deletes them.
19
+ */
20
+ export declare function cleanStaleSolutions(): void;
21
+ /** Retag all solutions using improved extractTags */
22
+ export declare function retagSolutions(): void;
23
+ /** Rollback auto-extracted solutions since a given date */
24
+ export declare function rollbackSolutions(sinceDate: string): void;