@wooojin/forgen 0.4.7 → 0.4.9
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 +1 -1
- package/CHANGELOG.md +40 -0
- package/assets/dev-guide/be/README.md +226 -0
- package/assets/dev-guide/be/adapters/build-agents-md.sh +63 -0
- package/assets/dev-guide/be/principles/common.md +433 -0
- package/assets/dev-guide/be/principles/go.md +469 -0
- package/assets/dev-guide/be/principles/node.md +388 -0
- package/assets/dev-guide/be/skills/go/be-build/SKILL.md +262 -0
- package/assets/dev-guide/be/skills/go/be-perf/SKILL.md +308 -0
- package/assets/dev-guide/be/skills/go/be-review/SKILL.md +119 -0
- package/assets/dev-guide/be/skills/go/be-security/SKILL.md +362 -0
- package/assets/dev-guide/be/skills/node/be-build/SKILL.md +239 -0
- package/assets/dev-guide/be/skills/node/be-perf/SKILL.md +272 -0
- package/assets/dev-guide/be/skills/node/be-review/SKILL.md +118 -0
- package/assets/dev-guide/be/skills/node/be-security/SKILL.md +355 -0
- package/assets/dev-guide/be/sources/12factor/INDEX.md +53 -0
- package/assets/dev-guide/be/sources/api-design/INDEX.md +56 -0
- package/assets/dev-guide/be/sources/ddia/INDEX.md +55 -0
- package/assets/dev-guide/be/sources/go-runtime/INDEX.md +62 -0
- package/assets/dev-guide/be/sources/node-runtime/INDEX.md +60 -0
- package/assets/dev-guide/be/sources/otel/INDEX.md +53 -0
- package/assets/dev-guide/be/sources/owasp-api/INDEX.md +52 -0
- package/assets/dev-guide/be/sources/postgres/INDEX.md +55 -0
- package/assets/dev-guide/be/sources/sre-book/INDEX.md +48 -0
- package/assets/dev-guide/fe/README.md +197 -0
- package/assets/dev-guide/fe/adapters/build-agents-md.sh +63 -0
- package/assets/dev-guide/fe/adapters/refresh.sh +68 -0
- package/assets/dev-guide/fe/principles/common.md +160 -0
- package/assets/dev-guide/fe/principles/react.md +183 -0
- package/assets/dev-guide/fe/principles/vue.md +196 -0
- package/assets/dev-guide/fe/skills/react/fe-build/SKILL.md +139 -0
- package/assets/dev-guide/fe/skills/react/fe-perf/SKILL.md +179 -0
- package/assets/dev-guide/fe/skills/react/fe-review/SKILL.md +141 -0
- package/assets/dev-guide/fe/skills/vue/fe-build/SKILL.md +148 -0
- package/assets/dev-guide/fe/skills/vue/fe-perf/SKILL.md +163 -0
- package/assets/dev-guide/fe/skills/vue/fe-review/SKILL.md +136 -0
- package/assets/dev-guide/fe/sources/a11y-dx/INDEX.md +41 -0
- package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-memory.md +150 -0
- package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-performance.md +99 -0
- package/assets/dev-guide/fe/sources/a11y-dx/lighthouse-audits.md +146 -0
- package/assets/dev-guide/fe/sources/a11y-dx/react-devtools-profiler.md +128 -0
- package/assets/dev-guide/fe/sources/a11y-dx/wcag22-new-criteria.md +174 -0
- package/assets/dev-guide/fe/sources/perf/01-core-web-vitals.md +58 -0
- package/assets/dev-guide/fe/sources/perf/02-inp.md +83 -0
- package/assets/dev-guide/fe/sources/perf/03-lcp-cls.md +130 -0
- package/assets/dev-guide/fe/sources/perf/04-speculation-rules.md +148 -0
- package/assets/dev-guide/fe/sources/perf/05-view-transitions.md +153 -0
- package/assets/dev-guide/fe/sources/perf/06-nextjs-caching.md +188 -0
- package/assets/dev-guide/fe/sources/perf/07-server-components.md +181 -0
- package/assets/dev-guide/fe/sources/perf/08-ppr.md +133 -0
- package/assets/dev-guide/fe/sources/perf/09-nextjs-image.md +200 -0
- package/assets/dev-guide/fe/sources/perf/10-optimize-lcp.md +201 -0
- package/assets/dev-guide/fe/sources/perf/INDEX.md +88 -0
- package/assets/dev-guide/fe/sources/react/INDEX.md +41 -0
- package/assets/dev-guide/fe/sources/react/keeping-components-pure.md +135 -0
- package/assets/dev-guide/fe/sources/react/no-effect-patterns.md +183 -0
- package/assets/dev-guide/fe/sources/react/react-compiler.md +182 -0
- package/assets/dev-guide/fe/sources/react/server-components.md +194 -0
- package/assets/dev-guide/fe/sources/react/server-functions.md +192 -0
- package/assets/dev-guide/fe/sources/react/suspense.md +218 -0
- package/assets/dev-guide/fe/sources/react/use-action-state.md +123 -0
- package/assets/dev-guide/fe/sources/react/use-form-status.md +158 -0
- package/assets/dev-guide/fe/sources/react/use-hook.md +153 -0
- package/assets/dev-guide/fe/sources/react/use-optimistic.md +194 -0
- package/assets/dev-guide/fe/sources/toss-ff/INDEX.md +58 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-code-directory.md +79 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-form-fields.md +110 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-magic-number.md +47 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-item-edit-modal.md +124 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-use-bottom-sheet.md +57 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-use-page-state.md +71 -0
- package/assets/dev-guide/fe/sources/toss-ff/overview-4-principles.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-hidden-logic.md +59 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-http.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-use-user.md +110 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-comparison-order.md +52 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-condition-name.md +64 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-login-start-page.md +183 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-magic-number.md +53 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-submit-button.md +73 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-ternary-operator.md +38 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-use-page-state.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-user-policy.md +98 -0
- package/assets/dev-guide/fe/sources/vue/INDEX.md +17 -0
- package/assets/dev-guide/fe/sources/vue/composition-api.md +251 -0
- package/assets/dev-guide/fe/sources/vue/nuxt-data-fetching.md +232 -0
- package/assets/dev-guide/fe/sources/vue/pinia-state-management.md +134 -0
- package/assets/dev-guide/fe/sources/vue/reactivity-pitfalls.md +261 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-a.md +117 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-b.md +231 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-c.md +86 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-d.md +72 -0
- package/dist/checks/self-score-deflation.js +6 -4
- package/dist/cli.js +47 -2
- package/dist/core/auto-compound-runner.js +6 -2
- package/dist/core/dashboard-cli.d.ts +12 -0
- package/dist/core/dashboard-cli.js +226 -0
- package/dist/core/dashboard.js +2 -2
- package/dist/core/dev-guide-injector.d.ts +26 -0
- package/dist/core/dev-guide-injector.js +137 -0
- package/dist/core/doctor.d.ts +10 -0
- package/dist/core/doctor.js +49 -8
- package/dist/core/harness.js +8 -2
- package/dist/core/init.js +53 -0
- package/dist/core/inspect-cli.js +4 -4
- package/dist/core/lifecycle-classifier.d.ts +23 -0
- package/dist/core/lifecycle-classifier.js +104 -0
- package/dist/core/migrate-evidence-host.js +1 -1
- package/dist/core/notify.js +7 -0
- package/dist/core/observability-backfill.d.ts +31 -0
- package/dist/core/observability-backfill.js +178 -0
- package/dist/core/observability-store.d.ts +58 -0
- package/dist/core/observability-store.js +195 -0
- package/dist/core/paths.d.ts +16 -2
- package/dist/core/paths.js +16 -2
- package/dist/core/session-store.d.ts +12 -1
- package/dist/core/session-store.js +77 -1
- package/dist/core/spawn.d.ts +17 -0
- package/dist/core/spawn.js +191 -8
- package/dist/core/statusline-cli.js +34 -1
- package/dist/core/v1-bootstrap.d.ts +7 -0
- package/dist/core/v1-bootstrap.js +28 -6
- package/dist/engine/compound-extractor.js +40 -1
- package/dist/engine/compound-loop.js +6 -0
- package/dist/engine/compound-retire.d.ts +20 -0
- package/dist/engine/compound-retire.js +85 -0
- package/dist/engine/learn-cli.js +2 -2
- package/dist/engine/lifecycle/bypass-detector.js +3 -2
- package/dist/engine/lifecycle/meta-reclassifier.js +1 -1
- package/dist/engine/lifecycle/signals.js +2 -2
- package/dist/engine/lifecycle/trigger-t1-correction.js +1 -1
- package/dist/engine/solution-candidate.js +1 -1
- package/dist/engine/solution-outcomes.js +1 -1
- package/dist/engine/solution-quarantine.js +1 -1
- package/dist/engine/solution-weakness.js +8 -2
- package/dist/forge/cli.js +1 -1
- package/dist/hooks/context-guard.js +25 -1
- package/dist/hooks/keyword-detector.js +1 -1
- package/dist/hooks/post-tool-use.js +48 -0
- package/dist/hooks/secret-filter.js +2 -2
- package/dist/hooks/shared/hook-response.js +1 -1
- package/dist/hooks/shared/hook-timing.js +3 -3
- package/dist/hooks/solution-injector.js +94 -1
- package/dist/hooks/stop-guard.js +3 -3
- package/dist/host/install-claude.d.ts +6 -2
- package/dist/host/install-claude.js +74 -2
- package/dist/host/install-codex.d.ts +4 -0
- package/dist/host/install-codex.js +72 -1
- package/dist/host/install-orchestrator.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/preset/facet-catalog.js +2 -2
- package/dist/renderer/rule-renderer.js +7 -7
- package/dist/store/compound-usage-store.js +1 -1
- package/dist/store/implicit-feedback-store.js +2 -2
- package/dist/store/profile-store.d.ts +11 -0
- package/dist/store/profile-store.js +23 -0
- package/package.json +6 -6
- package/plugin.json +1 -1
- package/scripts/postinstall.js +134 -0
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude InstallPlan — feat/codex-support Phase 1 (P1-2)
|
|
3
3
|
*
|
|
4
|
-
* `npm install` postinstall.js 의 *Claude 측*
|
|
4
|
+
* `npm install` postinstall.js 의 *Claude 측* 5 작업을 module 로 분리.
|
|
5
5
|
* `forgen install claude` CLI 가 호출 + (P1-6 에서) postinstall.js 도 위임.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* 5 작업:
|
|
8
8
|
* 1. Plugin cache: ~/.claude/plugins/cache/forgen-local/forgen/<ver>/ 작성 + installed_plugins.json 등록
|
|
9
9
|
* 2. Slash commands: ~/.claude/commands/forgen/*.md 생성 (forgen-managed marker)
|
|
10
10
|
* 3. Settings hooks injection: ~/.claude/settings.json 의 hooks 머지 (forgen entry idempotent)
|
|
11
11
|
* 4. MCP register: ~/.claude.json 에 mcpServers.forgen-compound 추가
|
|
12
|
+
* 5. Dev-guide skills: ~/.claude/skills/forgen-<stack>-<skill>/ 설치 (forgen-managed only)
|
|
12
13
|
*
|
|
13
14
|
* 사용자 비-forgen 자산 보존 + 재실행 idempotent.
|
|
14
15
|
*/
|
|
@@ -31,5 +32,8 @@ export interface ClaudeInstallResult {
|
|
|
31
32
|
hooksInjected: number;
|
|
32
33
|
mcpRegistered: boolean;
|
|
33
34
|
mcpAlreadyPresent: boolean;
|
|
35
|
+
skillsPath: string;
|
|
36
|
+
skillsInstalled: number;
|
|
37
|
+
skillsRemoved: number;
|
|
34
38
|
}
|
|
35
39
|
export declare function planClaudeInstall(opts: ClaudeInstallOptions): ClaudeInstallResult;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Claude InstallPlan — feat/codex-support Phase 1 (P1-2)
|
|
3
3
|
*
|
|
4
|
-
* `npm install` postinstall.js 의 *Claude 측*
|
|
4
|
+
* `npm install` postinstall.js 의 *Claude 측* 5 작업을 module 로 분리.
|
|
5
5
|
* `forgen install claude` CLI 가 호출 + (P1-6 에서) postinstall.js 도 위임.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* 5 작업:
|
|
8
8
|
* 1. Plugin cache: ~/.claude/plugins/cache/forgen-local/forgen/<ver>/ 작성 + installed_plugins.json 등록
|
|
9
9
|
* 2. Slash commands: ~/.claude/commands/forgen/*.md 생성 (forgen-managed marker)
|
|
10
10
|
* 3. Settings hooks injection: ~/.claude/settings.json 의 hooks 머지 (forgen entry idempotent)
|
|
11
11
|
* 4. MCP register: ~/.claude.json 에 mcpServers.forgen-compound 추가
|
|
12
|
+
* 5. Dev-guide skills: ~/.claude/skills/forgen-<stack>-<skill>/ 설치 (forgen-managed only)
|
|
12
13
|
*
|
|
13
14
|
* 사용자 비-forgen 자산 보존 + 재실행 idempotent.
|
|
14
15
|
*/
|
|
@@ -234,6 +235,72 @@ function registerMcpInClaudeJson(opts) {
|
|
|
234
235
|
fs.writeFileSync(claudeJsonPath, `${JSON.stringify(claudeJson, null, 2)}\n`);
|
|
235
236
|
return { registered: !alreadyPresent, alreadyPresent };
|
|
236
237
|
}
|
|
238
|
+
// ── 5. Dev-guide skills ────────────────────────────────────────────────
|
|
239
|
+
const FORGEN_SKILL_PREFIX = 'forgen-';
|
|
240
|
+
function installDevGuideSkills(opts) {
|
|
241
|
+
const { pkgRoot, skillsDir, dryRun } = opts;
|
|
242
|
+
const devGuideRoot = path.join(pkgRoot, 'assets', 'dev-guide');
|
|
243
|
+
// Collect all SKILL.md entries: assets/dev-guide/{tier}/skills/{stack}/{skill}/SKILL.md
|
|
244
|
+
const entries = [];
|
|
245
|
+
if (fs.existsSync(devGuideRoot)) {
|
|
246
|
+
for (const tier of fs.readdirSync(devGuideRoot)) {
|
|
247
|
+
const skillsBase = path.join(devGuideRoot, tier, 'skills');
|
|
248
|
+
if (!fs.existsSync(skillsBase))
|
|
249
|
+
continue;
|
|
250
|
+
for (const stack of fs.readdirSync(skillsBase)) {
|
|
251
|
+
const stackDir = path.join(skillsBase, stack);
|
|
252
|
+
if (!fs.statSync(stackDir).isDirectory())
|
|
253
|
+
continue;
|
|
254
|
+
for (const skill of fs.readdirSync(stackDir)) {
|
|
255
|
+
const skillMd = path.join(stackDir, skill, 'SKILL.md');
|
|
256
|
+
if (fs.existsSync(skillMd)) {
|
|
257
|
+
entries.push({ name: `${FORGEN_SKILL_PREFIX}${stack}-${skill}`, src: skillMd });
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (dryRun) {
|
|
264
|
+
return { skillsPath: skillsDir, skillsInstalled: entries.length, skillsRemoved: 0 };
|
|
265
|
+
}
|
|
266
|
+
fs.mkdirSync(skillsDir, { recursive: true });
|
|
267
|
+
// Remove stale forgen-* skill dirs (idempotent re-install, do not touch user's own skills)
|
|
268
|
+
let removed = 0;
|
|
269
|
+
for (const entry of fs.readdirSync(skillsDir)) {
|
|
270
|
+
if (!entry.startsWith(FORGEN_SKILL_PREFIX))
|
|
271
|
+
continue;
|
|
272
|
+
const fullPath = path.join(skillsDir, entry);
|
|
273
|
+
if (fs.statSync(fullPath).isDirectory()) {
|
|
274
|
+
fs.rmSync(fullPath, { recursive: true, force: true });
|
|
275
|
+
removed += 1;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
// Install each skill via symlink → cpSync fallback (mirrors plugin cache pattern)
|
|
279
|
+
let installed = 0;
|
|
280
|
+
for (const { name, src } of entries) {
|
|
281
|
+
const targetDir = path.join(skillsDir, name);
|
|
282
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
283
|
+
const targetFile = path.join(targetDir, 'SKILL.md');
|
|
284
|
+
let linked = false;
|
|
285
|
+
let symlinkErr = null;
|
|
286
|
+
try {
|
|
287
|
+
fs.symlinkSync(src, targetFile, 'file');
|
|
288
|
+
linked = true;
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
symlinkErr = e;
|
|
292
|
+
}
|
|
293
|
+
if (!linked && symlinkErr) {
|
|
294
|
+
const code = symlinkErr.code ?? 'UNKNOWN';
|
|
295
|
+
process.stderr.write(`[forgen] symlink ${src} → ${targetFile} failed (${code}); falling back to copyFile.\n`);
|
|
296
|
+
}
|
|
297
|
+
if (!linked) {
|
|
298
|
+
fs.copyFileSync(src, targetFile);
|
|
299
|
+
}
|
|
300
|
+
installed += 1;
|
|
301
|
+
}
|
|
302
|
+
return { skillsPath: skillsDir, skillsInstalled: installed, skillsRemoved: removed };
|
|
303
|
+
}
|
|
237
304
|
// ── public ─────────────────────────────────────────────────────────────
|
|
238
305
|
export function planClaudeInstall(opts) {
|
|
239
306
|
if (!opts.pkgRoot || !fs.existsSync(opts.pkgRoot)) {
|
|
@@ -249,12 +316,14 @@ export function planClaudeInstall(opts) {
|
|
|
249
316
|
const slashCommandsDir = path.join(claudeDir, 'commands', 'forgen');
|
|
250
317
|
const settingsPath = path.join(claudeDir, 'settings.json');
|
|
251
318
|
const claudeJsonPath = path.join(homeDir, '.claude.json');
|
|
319
|
+
const skillsDir = path.join(claudeDir, 'skills');
|
|
252
320
|
const pluginCacheWritten = writePluginCache({ pkgRoot: opts.pkgRoot, cacheDir, pluginsDir, version, dryRun });
|
|
253
321
|
const slashCommandsCount = writeSlashCommands({ pkgRoot: opts.pkgRoot, targetDir: slashCommandsDir, dryRun });
|
|
254
322
|
const hooksInjected = injectHooksIntoSettings({ pkgRoot: opts.pkgRoot, settingsPath, dryRun });
|
|
255
323
|
const mcp = registerMcp
|
|
256
324
|
? registerMcpInClaudeJson({ pkgRoot: opts.pkgRoot, claudeJsonPath, dryRun })
|
|
257
325
|
: { registered: false, alreadyPresent: false };
|
|
326
|
+
const skills = installDevGuideSkills({ pkgRoot: opts.pkgRoot, skillsDir, dryRun });
|
|
258
327
|
return {
|
|
259
328
|
homeDir,
|
|
260
329
|
pluginCachePath: cacheDir,
|
|
@@ -265,5 +334,8 @@ export function planClaudeInstall(opts) {
|
|
|
265
334
|
hooksInjected,
|
|
266
335
|
mcpRegistered: mcp.registered,
|
|
267
336
|
mcpAlreadyPresent: mcp.alreadyPresent,
|
|
337
|
+
skillsPath: skills.skillsPath,
|
|
338
|
+
skillsInstalled: skills.skillsInstalled,
|
|
339
|
+
skillsRemoved: skills.skillsRemoved,
|
|
268
340
|
};
|
|
269
341
|
}
|
|
@@ -40,5 +40,9 @@ export interface CodexInstallResult {
|
|
|
40
40
|
/** P3-3: AGENTS.md (cwd) 에 forgen rule block 인젝션 여부 */
|
|
41
41
|
agentsMdPath: string;
|
|
42
42
|
agentsMdInjected: boolean;
|
|
43
|
+
/** v0.4.9: dev-guide skills (~/.codex/skills) 설치 결과 */
|
|
44
|
+
devGuideSkillsPath: string;
|
|
45
|
+
devGuideSkillsInstalled: number;
|
|
46
|
+
devGuideSkillsRemoved: number;
|
|
43
47
|
}
|
|
44
48
|
export declare function planCodexInstall(opts: CodexInstallOptions): CodexInstallResult;
|
|
@@ -150,6 +150,12 @@ export function planCodexInstall(opts) {
|
|
|
150
150
|
// pkgRoot 의 git repo root 의 AGENTS.md, 또는 explicit override.
|
|
151
151
|
const agentsMdPath = opts.agentsMdPath ?? resolveAgentsMdPath(opts.pkgRoot);
|
|
152
152
|
const agentsResult = upsertForgenRulesInAgentsMd({ agentsMdPath, pkgRoot: opts.pkgRoot, dryRun: opts.dryRun ?? false });
|
|
153
|
+
// 8) v0.4.9: dev-guide skills → ~/.codex/skills/forgen-<stack>-<skill>/SKILL.md
|
|
154
|
+
const devGuideResult = installDevGuideSkillsToCodex({
|
|
155
|
+
pkgRoot: opts.pkgRoot,
|
|
156
|
+
codexHome,
|
|
157
|
+
dryRun: opts.dryRun ?? false,
|
|
158
|
+
});
|
|
153
159
|
return {
|
|
154
160
|
codexHome,
|
|
155
161
|
hooksPath,
|
|
@@ -163,8 +169,73 @@ export function planCodexInstall(opts) {
|
|
|
163
169
|
skillsPath,
|
|
164
170
|
agentsMdPath,
|
|
165
171
|
agentsMdInjected: agentsResult.injected,
|
|
172
|
+
devGuideSkillsPath: devGuideResult.devGuideSkillsPath,
|
|
173
|
+
devGuideSkillsInstalled: devGuideResult.devGuideSkillsInstalled,
|
|
174
|
+
devGuideSkillsRemoved: devGuideResult.devGuideSkillsRemoved,
|
|
166
175
|
};
|
|
167
176
|
}
|
|
177
|
+
// ── v0.4.9: dev-guide skills → ~/.codex/skills ────────────────────────
|
|
178
|
+
// dev-guide prefix pattern: forgen-<stack>-<skill> (e.g. forgen-react-fe-build)
|
|
179
|
+
// 반드시 stack 이 react|vue|node|go 인 것만 매칭 — forgen 자체 commands 보존
|
|
180
|
+
const DEV_GUIDE_SKILL_PATTERN = /^forgen-(react|vue|node|go)-/;
|
|
181
|
+
function installDevGuideSkillsToCodex(opts) {
|
|
182
|
+
const devGuideRoot = path.join(opts.pkgRoot, 'assets', 'dev-guide');
|
|
183
|
+
const codexSkillsDir = path.join(opts.codexHome, 'skills');
|
|
184
|
+
if (!fs.existsSync(devGuideRoot)) {
|
|
185
|
+
return { devGuideSkillsPath: codexSkillsDir, devGuideSkillsInstalled: 0, devGuideSkillsRemoved: 0 };
|
|
186
|
+
}
|
|
187
|
+
// Collect entries: assets/dev-guide/{tier}/skills/{stack}/{skill}/SKILL.md
|
|
188
|
+
const entries = [];
|
|
189
|
+
for (const tier of fs.readdirSync(devGuideRoot)) {
|
|
190
|
+
const skillsBase = path.join(devGuideRoot, tier, 'skills');
|
|
191
|
+
if (!fs.existsSync(skillsBase))
|
|
192
|
+
continue;
|
|
193
|
+
for (const stack of fs.readdirSync(skillsBase)) {
|
|
194
|
+
const stackDir = path.join(skillsBase, stack);
|
|
195
|
+
if (!fs.statSync(stackDir).isDirectory())
|
|
196
|
+
continue;
|
|
197
|
+
for (const skill of fs.readdirSync(stackDir)) {
|
|
198
|
+
const skillMd = path.join(stackDir, skill, 'SKILL.md');
|
|
199
|
+
if (fs.existsSync(skillMd)) {
|
|
200
|
+
entries.push({ name: `forgen-${stack}-${skill}`, src: skillMd });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
if (opts.dryRun) {
|
|
206
|
+
return { devGuideSkillsPath: codexSkillsDir, devGuideSkillsInstalled: entries.length, devGuideSkillsRemoved: 0 };
|
|
207
|
+
}
|
|
208
|
+
fs.mkdirSync(codexSkillsDir, { recursive: true });
|
|
209
|
+
// Stale cleanup: dev-guide pattern 만 정리 (forgen 자체 commands 보존)
|
|
210
|
+
let removed = 0;
|
|
211
|
+
for (const entry of fs.readdirSync(codexSkillsDir)) {
|
|
212
|
+
if (DEV_GUIDE_SKILL_PATTERN.test(entry)) {
|
|
213
|
+
try {
|
|
214
|
+
fs.rmSync(path.join(codexSkillsDir, entry), { recursive: true, force: true });
|
|
215
|
+
removed++;
|
|
216
|
+
}
|
|
217
|
+
catch { /* best-effort */ }
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
// Install via symlink → copyFileSync fallback
|
|
221
|
+
let installed = 0;
|
|
222
|
+
for (const { name, src } of entries) {
|
|
223
|
+
const dstDir = path.join(codexSkillsDir, name);
|
|
224
|
+
fs.mkdirSync(dstDir, { recursive: true });
|
|
225
|
+
const dst = path.join(dstDir, 'SKILL.md');
|
|
226
|
+
let linked = false;
|
|
227
|
+
try {
|
|
228
|
+
fs.symlinkSync(src, dst, 'file');
|
|
229
|
+
linked = true;
|
|
230
|
+
}
|
|
231
|
+
catch { /* fallback */ }
|
|
232
|
+
if (!linked) {
|
|
233
|
+
fs.copyFileSync(src, dst);
|
|
234
|
+
}
|
|
235
|
+
installed++;
|
|
236
|
+
}
|
|
237
|
+
return { devGuideSkillsPath: codexSkillsDir, devGuideSkillsInstalled: installed, devGuideSkillsRemoved: removed };
|
|
238
|
+
}
|
|
168
239
|
// ── P3-3: Codex skills install ────────────────────────────────────────
|
|
169
240
|
function installCodexSkills(opts) {
|
|
170
241
|
const { sourceDir, targetDir, dryRun } = opts;
|
|
@@ -202,7 +273,7 @@ function installCodexSkills(opts) {
|
|
|
202
273
|
return { installed: count };
|
|
203
274
|
}
|
|
204
275
|
// ── P3-3: AGENTS.md inject ────────────────────────────────────────────
|
|
205
|
-
function resolveAgentsMdPath(
|
|
276
|
+
function resolveAgentsMdPath(_pkgRoot) {
|
|
206
277
|
// Phase 3 critic fix: pkgRoot 기반 walk-up 은 `npm install -g` 시 시스템 디렉토리
|
|
207
278
|
// (예: /usr/local/lib/node_modules/forgen) 에 fallback AGENTS.md 작성 위험.
|
|
208
279
|
// *cwd 기반* 으로 변경 — 사용자 작업 디렉토리의 git root, 없으면 cwd 자체.
|
|
@@ -109,6 +109,7 @@ export function renderResult(result, dryRun) {
|
|
|
109
109
|
lines.push(` slash commands: ${result.claude.slashCommandsCount} → ${result.claude.slashCommandsPath}`);
|
|
110
110
|
lines.push(` settings.json hooks: ${result.claude.hooksInjected}`);
|
|
111
111
|
lines.push(` MCP: ${result.claude.mcpAlreadyPresent ? 'already present' : (result.claude.mcpRegistered ? 'registered' : 'skipped')}`);
|
|
112
|
+
lines.push(` skills: ${result.claude.skillsInstalled ?? 0} installed → ${result.claude.skillsPath ?? ''}`);
|
|
112
113
|
}
|
|
113
114
|
if (result.codex) {
|
|
114
115
|
lines.push('');
|
package/dist/mcp/tools.js
CHANGED
|
@@ -29,8 +29,8 @@ export const COMMUNICATION_CENTROIDS = {
|
|
|
29
29
|
'상세형': { verbosity: 0.85, structure: 0.80, teaching_bias: 0.80 },
|
|
30
30
|
};
|
|
31
31
|
// ── Defaults (backward compat) ──
|
|
32
|
-
export const DEFAULT_JUDGMENT_FACETS = JUDGMENT_CENTROIDS
|
|
33
|
-
export const DEFAULT_COMMUNICATION_FACETS = COMMUNICATION_CENTROIDS
|
|
32
|
+
export const DEFAULT_JUDGMENT_FACETS = JUDGMENT_CENTROIDS.균형형;
|
|
33
|
+
export const DEFAULT_COMMUNICATION_FACETS = COMMUNICATION_CENTROIDS.균형형;
|
|
34
34
|
// ── Utilities ──
|
|
35
35
|
export function qualityCentroid(pack) {
|
|
36
36
|
return { ...QUALITY_CENTROIDS[pack] };
|
|
@@ -171,28 +171,28 @@ export function renderRules(rules, state, profile, ctx = DEFAULT_CONTEXT) {
|
|
|
171
171
|
for (const name of SECTION_ORDER)
|
|
172
172
|
sections.set(name, []);
|
|
173
173
|
for (const rule of hardRules) {
|
|
174
|
-
sections.get('Must Not')
|
|
174
|
+
sections.get('Must Not')?.push(ruleToText(rule));
|
|
175
175
|
}
|
|
176
176
|
for (const rule of otherRules) {
|
|
177
177
|
const section = CATEGORY_TO_SECTION[rule.category] ?? 'Working Defaults';
|
|
178
|
-
sections.get(section)
|
|
178
|
+
sections.get(section)?.push(ruleToText(rule));
|
|
179
179
|
}
|
|
180
180
|
// 5. trust policy + pack 기본 규칙 주입
|
|
181
181
|
if (ctx.include_pack_summary) {
|
|
182
|
-
sections.get('Working Defaults')
|
|
182
|
+
sections.get('Working Defaults')?.unshift(`Trust: ${trustPolicySummary(state.effective_trust_policy)}`);
|
|
183
183
|
// judgment pack 기본 규칙
|
|
184
184
|
for (const rule of judgmentPackRules(state.judgment_pack)) {
|
|
185
|
-
sections.get('Working Defaults')
|
|
185
|
+
sections.get('Working Defaults')?.push(rule);
|
|
186
186
|
}
|
|
187
187
|
// communication pack 기본 규칙
|
|
188
188
|
for (const rule of communicationPackRules(state.communication_pack)) {
|
|
189
|
-
sections.get('How To Report')
|
|
189
|
+
sections.get('How To Report')?.push(rule);
|
|
190
190
|
}
|
|
191
191
|
// 4축 facet 극단값 → 추가 규칙 (12-bucket pack 위에 연속 값 차별화).
|
|
192
192
|
// 각 facet 0.5 default 이고 자동 갱신 ±0.1 단위이므로, 0.85/0.15 임계값은
|
|
193
193
|
// 여러 세션에 걸친 강한 신호 누적 후에만 발화한다.
|
|
194
194
|
for (const fr of facetDrivenRules(profile)) {
|
|
195
|
-
sections.get(fr.section)
|
|
195
|
+
sections.get(fr.section)?.push(fr.rule);
|
|
196
196
|
}
|
|
197
197
|
}
|
|
198
198
|
// 6. 섹션 조립 (AI-optimized: 간결한 태그 형식)
|
|
@@ -200,7 +200,7 @@ export function renderRules(rules, state, profile, ctx = DEFAULT_CONTEXT) {
|
|
|
200
200
|
let totalChars = 0;
|
|
201
201
|
let totalRules = 0;
|
|
202
202
|
for (const name of SECTION_ORDER) {
|
|
203
|
-
const items = sections.get(name);
|
|
203
|
+
const items = sections.get(name) ?? [];
|
|
204
204
|
if (items.length === 0)
|
|
205
205
|
continue;
|
|
206
206
|
const header = `## ${name}`;
|
|
@@ -27,7 +27,7 @@ export function recordUsage(name, via = 'mcp') {
|
|
|
27
27
|
try {
|
|
28
28
|
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
29
29
|
const entry = { at: new Date().toISOString(), name, via };
|
|
30
|
-
fs.appendFileSync(COMPOUND_USAGE_LOG, JSON.stringify(entry)
|
|
30
|
+
fs.appendFileSync(COMPOUND_USAGE_LOG, `${JSON.stringify(entry)}\n`);
|
|
31
31
|
}
|
|
32
32
|
catch {
|
|
33
33
|
// fail-open: 신호 수집 실패가 사용자 경험을 방해하면 안 됨
|
|
@@ -67,7 +67,7 @@ export function appendImplicitFeedback(entry) {
|
|
|
67
67
|
return false;
|
|
68
68
|
try {
|
|
69
69
|
fs.mkdirSync(STATE_DIR, { recursive: true });
|
|
70
|
-
fs.appendFileSync(IMPLICIT_FEEDBACK_LOG, JSON.stringify(normalized)
|
|
70
|
+
fs.appendFileSync(IMPLICIT_FEEDBACK_LOG, `${JSON.stringify(normalized)}\n`);
|
|
71
71
|
return true;
|
|
72
72
|
}
|
|
73
73
|
catch {
|
|
@@ -147,7 +147,7 @@ export function migrateImplicitFeedbackLog() {
|
|
|
147
147
|
}
|
|
148
148
|
// atomic replace via temp file
|
|
149
149
|
const tmp = `${IMPLICIT_FEEDBACK_LOG}.migrate.${process.pid}`;
|
|
150
|
-
fs.writeFileSync(tmp, out.length > 0 ? out.join('\n')
|
|
150
|
+
fs.writeFileSync(tmp, out.length > 0 ? `${out.join('\n')}\n` : '');
|
|
151
151
|
fs.renameSync(tmp, IMPLICIT_FEEDBACK_LOG);
|
|
152
152
|
return { migrated, dropped };
|
|
153
153
|
}
|
|
@@ -18,6 +18,17 @@ export declare function saveProfile(profile: Profile): void;
|
|
|
18
18
|
* whether to run `runLegacyCutover`).
|
|
19
19
|
*/
|
|
20
20
|
export declare function profileExists(): boolean;
|
|
21
|
+
/**
|
|
22
|
+
* profile.json 이 존재하지만 parse 실패 / v1 shape 위반인 경우, 사용자가
|
|
23
|
+
* 다음 onboarding 으로 매끄럽게 복구되도록 corrupt 파일을 timestamp 백업
|
|
24
|
+
* 으로 옆에 치워둔다. 백업 경로를 반환.
|
|
25
|
+
*
|
|
26
|
+
* v0.4.8 — bootstrapV1Session 의 loadProfile()=null early-return 보강.
|
|
27
|
+
* 이전엔 needsOnboarding=true 만 반환했고 corrupt 파일이 그대로 남아
|
|
28
|
+
* 다음 실행 때도 동일 분기로 빠지면서 사용자가 정체 원인을 모른 채
|
|
29
|
+
* onboarding 안내만 반복 받는 패턴이었음.
|
|
30
|
+
*/
|
|
31
|
+
export declare function backupCorruptProfile(): string | null;
|
|
21
32
|
export declare function isV1Profile(data: unknown): data is Profile;
|
|
22
33
|
/**
|
|
23
34
|
* D2 fix (2026-04-27): explicit_correction 누적 시 해당 축의 confidence 를 점진
|
|
@@ -66,6 +66,29 @@ export function saveProfile(profile) {
|
|
|
66
66
|
export function profileExists() {
|
|
67
67
|
return fs.existsSync(FORGE_PROFILE);
|
|
68
68
|
}
|
|
69
|
+
/**
|
|
70
|
+
* profile.json 이 존재하지만 parse 실패 / v1 shape 위반인 경우, 사용자가
|
|
71
|
+
* 다음 onboarding 으로 매끄럽게 복구되도록 corrupt 파일을 timestamp 백업
|
|
72
|
+
* 으로 옆에 치워둔다. 백업 경로를 반환.
|
|
73
|
+
*
|
|
74
|
+
* v0.4.8 — bootstrapV1Session 의 loadProfile()=null early-return 보강.
|
|
75
|
+
* 이전엔 needsOnboarding=true 만 반환했고 corrupt 파일이 그대로 남아
|
|
76
|
+
* 다음 실행 때도 동일 분기로 빠지면서 사용자가 정체 원인을 모른 채
|
|
77
|
+
* onboarding 안내만 반복 받는 패턴이었음.
|
|
78
|
+
*/
|
|
79
|
+
export function backupCorruptProfile() {
|
|
80
|
+
if (!fs.existsSync(FORGE_PROFILE))
|
|
81
|
+
return null;
|
|
82
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-');
|
|
83
|
+
const backupPath = `${FORGE_PROFILE}.corrupt-${ts}`;
|
|
84
|
+
try {
|
|
85
|
+
fs.renameSync(FORGE_PROFILE, backupPath);
|
|
86
|
+
return backupPath;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
69
92
|
export function isV1Profile(data) {
|
|
70
93
|
if (!data || typeof data !== 'object')
|
|
71
94
|
return false;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wooojin/forgen",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.9",
|
|
4
4
|
"preferGlobal": true,
|
|
5
5
|
"main": "dist/lib.js",
|
|
6
6
|
"types": "./dist/lib.d.ts",
|
|
@@ -47,19 +47,19 @@
|
|
|
47
47
|
],
|
|
48
48
|
"repository": {
|
|
49
49
|
"type": "git",
|
|
50
|
-
"url": "https://github.com/forgen-team/forgen.git"
|
|
50
|
+
"url": "git+https://github.com/forgen-team/forgen.git"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
|
-
"node": ">=
|
|
53
|
+
"node": ">=22.0.0"
|
|
54
54
|
},
|
|
55
55
|
"type": "module",
|
|
56
56
|
"workspaces": [
|
|
57
57
|
"packages/*"
|
|
58
58
|
],
|
|
59
59
|
"bin": {
|
|
60
|
-
"forgen": "
|
|
61
|
-
"fgx": "
|
|
62
|
-
"forgen-mcp": "
|
|
60
|
+
"forgen": "dist/cli.js",
|
|
61
|
+
"fgx": "dist/fgx.js",
|
|
62
|
+
"forgen-mcp": "dist/mcp/server.js"
|
|
63
63
|
},
|
|
64
64
|
"files": [
|
|
65
65
|
"dist/",
|
package/plugin.json
CHANGED
package/scripts/postinstall.js
CHANGED
|
@@ -746,6 +746,125 @@ function cleanLegacyMcpFromSettings(settings) {
|
|
|
746
746
|
}
|
|
747
747
|
}
|
|
748
748
|
|
|
749
|
+
/**
|
|
750
|
+
* dev-guide 스킬 14개 자동 설치 — assets/dev-guide/{fe,be}/skills/{stack}/{skill}/SKILL.md
|
|
751
|
+
* → ~/.claude/skills/forgen-<stack>-<skill>/SKILL.md
|
|
752
|
+
*
|
|
753
|
+
* forgen- prefix 로 사용자 own skills 와 격리. idempotent 재실행 시 forgen-* 만 정리 후 재설치.
|
|
754
|
+
*/
|
|
755
|
+
function installDevGuideSkills(home) {
|
|
756
|
+
const devGuideRoot = join(PKG_ROOT, 'assets', 'dev-guide');
|
|
757
|
+
if (!existsSync(devGuideRoot)) {
|
|
758
|
+
console.log('[forgen] dev-guide assets not found — skipping skill install');
|
|
759
|
+
return 0;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const userSkillsDir = join(home, '.claude', 'skills');
|
|
763
|
+
mkdirSync(userSkillsDir, { recursive: true });
|
|
764
|
+
|
|
765
|
+
// 1. 기존 forgen-* 디렉토리 정리 (사용자 own 보존)
|
|
766
|
+
let removed = 0;
|
|
767
|
+
for (const entry of readdirSync(userSkillsDir)) {
|
|
768
|
+
if (entry.startsWith('forgen-')) {
|
|
769
|
+
try { rmSync(join(userSkillsDir, entry), { recursive: true, force: true }); removed++; } catch { /* best-effort */ }
|
|
770
|
+
}
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
// 2. assets/dev-guide/{side}/skills/{stack}/{skill}/SKILL.md 수집
|
|
774
|
+
let installed = 0;
|
|
775
|
+
for (const side of ['fe', 'be']) {
|
|
776
|
+
const sideSkillsDir = join(devGuideRoot, side, 'skills');
|
|
777
|
+
if (!existsSync(sideSkillsDir)) continue;
|
|
778
|
+
for (const stack of readdirSync(sideSkillsDir)) {
|
|
779
|
+
const stackDir = join(sideSkillsDir, stack);
|
|
780
|
+
if (!statSync(stackDir).isDirectory()) continue;
|
|
781
|
+
for (const skill of readdirSync(stackDir)) {
|
|
782
|
+
const skillSrc = join(stackDir, skill);
|
|
783
|
+
const skillFile = join(skillSrc, 'SKILL.md');
|
|
784
|
+
if (!existsSync(skillFile)) continue;
|
|
785
|
+
|
|
786
|
+
// 대상: ~/.claude/skills/forgen-<stack>-<skill>/SKILL.md
|
|
787
|
+
const dstDir = join(userSkillsDir, `forgen-${stack}-${skill}`);
|
|
788
|
+
mkdirSync(dstDir, { recursive: true });
|
|
789
|
+
const dst = join(dstDir, 'SKILL.md');
|
|
790
|
+
|
|
791
|
+
// symlink → fallback cpSync
|
|
792
|
+
try {
|
|
793
|
+
if (existsSync(dst)) rmSync(dst);
|
|
794
|
+
symlinkSync(skillFile, dst, 'file');
|
|
795
|
+
} catch {
|
|
796
|
+
cpSync(skillFile, dst);
|
|
797
|
+
}
|
|
798
|
+
installed++;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
// 3. sudo 케이스 ownership 회복
|
|
804
|
+
fixOwnership(userSkillsDir);
|
|
805
|
+
|
|
806
|
+
console.log(`[forgen] dev-guide skills: ${installed} installed${removed > 0 ? ` (${removed} stale removed)` : ''}`);
|
|
807
|
+
return installed;
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
/**
|
|
811
|
+
* v0.4.9: dev-guide 14 skills → ~/.codex/skills/forgen-<stack>-<skill>/SKILL.md
|
|
812
|
+
* forgen 자체 commands(forgen-compound 등) 와 prefix 겹치지 않도록
|
|
813
|
+
* DEV_GUIDE_SKILL_PATTERN(/^forgen-(react|vue|node|go)-/) 으로 stale 정리.
|
|
814
|
+
*/
|
|
815
|
+
const DEV_GUIDE_SKILL_PATTERN = /^forgen-(react|vue|node|go)-/;
|
|
816
|
+
|
|
817
|
+
function installDevGuideSkillsToCodex(home) {
|
|
818
|
+
const devGuideRoot = join(PKG_ROOT, 'assets', 'dev-guide');
|
|
819
|
+
if (!existsSync(devGuideRoot)) {
|
|
820
|
+
console.log('[forgen] dev-guide assets not found — codex skill install skipped');
|
|
821
|
+
return 0;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
const codexSkillsDir = join(home, '.codex', 'skills');
|
|
825
|
+
mkdirSync(codexSkillsDir, { recursive: true });
|
|
826
|
+
|
|
827
|
+
// 1. stale forgen-<stack>-<skill> 정리 (forgen 자체 commands 보존)
|
|
828
|
+
let removed = 0;
|
|
829
|
+
for (const entry of readdirSync(codexSkillsDir)) {
|
|
830
|
+
if (DEV_GUIDE_SKILL_PATTERN.test(entry)) {
|
|
831
|
+
try { rmSync(join(codexSkillsDir, entry), { recursive: true, force: true }); removed++; } catch { /* best-effort */ }
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
// 2. assets/dev-guide/{tier}/skills/{stack}/{skill}/SKILL.md 수집
|
|
836
|
+
let installed = 0;
|
|
837
|
+
for (const tier of readdirSync(devGuideRoot)) {
|
|
838
|
+
const sideSkillsDir = join(devGuideRoot, tier, 'skills');
|
|
839
|
+
if (!existsSync(sideSkillsDir)) continue;
|
|
840
|
+
for (const stack of readdirSync(sideSkillsDir)) {
|
|
841
|
+
const stackDir = join(sideSkillsDir, stack);
|
|
842
|
+
if (!statSync(stackDir).isDirectory()) continue;
|
|
843
|
+
for (const skill of readdirSync(stackDir)) {
|
|
844
|
+
const skillFile = join(stackDir, skill, 'SKILL.md');
|
|
845
|
+
if (!existsSync(skillFile)) continue;
|
|
846
|
+
|
|
847
|
+
const dstDir = join(codexSkillsDir, `forgen-${stack}-${skill}`);
|
|
848
|
+
mkdirSync(dstDir, { recursive: true });
|
|
849
|
+
const dst = join(dstDir, 'SKILL.md');
|
|
850
|
+
|
|
851
|
+
// symlink → fallback cpSync
|
|
852
|
+
try {
|
|
853
|
+
if (existsSync(dst)) rmSync(dst);
|
|
854
|
+
symlinkSync(skillFile, dst, 'file');
|
|
855
|
+
} catch {
|
|
856
|
+
cpSync(skillFile, dst);
|
|
857
|
+
}
|
|
858
|
+
installed++;
|
|
859
|
+
}
|
|
860
|
+
}
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
fixOwnership(codexSkillsDir);
|
|
864
|
+
console.log(`[forgen] codex dev-guide skills: ${installed} installed${removed > 0 ? ` (${removed} stale removed)` : ''}`);
|
|
865
|
+
return installed;
|
|
866
|
+
}
|
|
867
|
+
|
|
749
868
|
// ── Main ──
|
|
750
869
|
|
|
751
870
|
/**
|
|
@@ -927,6 +1046,21 @@ async function main() {
|
|
|
927
1046
|
console.error(`[forgen] starter pack failed: ${err?.message ?? err}`);
|
|
928
1047
|
}
|
|
929
1048
|
|
|
1049
|
+
// ── 8b. dev-guide 스킬 자동 설치 (claude) ──
|
|
1050
|
+
try {
|
|
1051
|
+
installDevGuideSkills(HOME);
|
|
1052
|
+
} catch (e) {
|
|
1053
|
+
// postinstall 의 "fail-open" 원칙 — npm install 깨뜨리지 않음
|
|
1054
|
+
console.log(`[forgen] dev-guide skills 설치 스킵 (${e?.message ?? e})`);
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
// ── 8c. dev-guide 스킬 자동 설치 (codex) ──
|
|
1058
|
+
try {
|
|
1059
|
+
installDevGuideSkillsToCodex(HOME);
|
|
1060
|
+
} catch (e) {
|
|
1061
|
+
console.log(`[forgen] codex dev-guide skills 설치 스킵 (${e?.message ?? e})`);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
930
1064
|
// sudo 실행 시 파일 소유권을 실제 유저로 변경
|
|
931
1065
|
fixOwnership(join(HOME, '.claude'), join(HOME, '.forgen'));
|
|
932
1066
|
|