cli-jaw 0.1.10 → 0.1.12

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 (67) hide show
  1. package/README.ko.md +44 -13
  2. package/README.md +12 -11
  3. package/README.zh-CN.md +43 -12
  4. package/dist/bin/commands/doctor.js +13 -2
  5. package/dist/bin/commands/doctor.js.map +1 -1
  6. package/dist/bin/commands/mcp.js +15 -18
  7. package/dist/bin/commands/mcp.js.map +1 -1
  8. package/dist/bin/commands/serve.js +3 -28
  9. package/dist/bin/commands/serve.js.map +1 -1
  10. package/dist/bin/commands/skill.js +9 -6
  11. package/dist/bin/commands/skill.js.map +1 -1
  12. package/dist/lib/mcp-sync.js +123 -31
  13. package/dist/lib/mcp-sync.js.map +1 -1
  14. package/{scripts → dist/scripts}/check-copilot-gap.js +24 -17
  15. package/dist/scripts/check-copilot-gap.js.map +1 -0
  16. package/{scripts/check-deps-offline.mjs → dist/scripts/check-deps-offline.js} +24 -20
  17. package/dist/scripts/check-deps-offline.js.map +1 -0
  18. package/dist/scripts/fresh-install-smoke.js +120 -0
  19. package/dist/scripts/fresh-install-smoke.js.map +1 -0
  20. package/{scripts/i18n-registry.py → dist/scripts/i18n-registry.js} +115 -122
  21. package/dist/scripts/i18n-registry.js.map +1 -0
  22. package/dist/server.js +34 -26
  23. package/dist/server.js.map +1 -1
  24. package/dist/src/agent/spawn.js +22 -8
  25. package/dist/src/agent/spawn.js.map +1 -1
  26. package/dist/src/cli/command-context.js +13 -3
  27. package/dist/src/cli/command-context.js.map +1 -1
  28. package/dist/src/prompt/builder.js +28 -1
  29. package/dist/src/prompt/builder.js.map +1 -1
  30. package/dist/src/telegram/bot.js +1 -1
  31. package/dist/src/telegram/bot.js.map +1 -1
  32. package/package.json +9 -5
  33. package/public/dist/bundle.js +72 -77
  34. package/public/dist/bundle.js.map +4 -4
  35. package/public/index.html +1 -3
  36. package/public/js/{api.js → api.ts} +18 -12
  37. package/public/js/{constants.js → constants.ts} +44 -24
  38. package/public/js/features/{appname.js → appname.ts} +13 -12
  39. package/public/js/features/{chat.js → chat.ts} +46 -37
  40. package/public/js/features/{employees.js → employees.ts} +67 -38
  41. package/public/js/features/heartbeat.ts +90 -0
  42. package/public/js/features/{i18n.js → i18n.ts} +20 -20
  43. package/public/js/features/memory.ts +125 -0
  44. package/public/js/features/{settings.js → settings.ts} +125 -93
  45. package/public/js/features/{sidebar.js → sidebar.ts} +15 -16
  46. package/public/js/features/{skills.js → skills.ts} +29 -16
  47. package/public/js/features/{slash-commands.js → slash-commands.ts} +34 -29
  48. package/public/js/features/{theme.js → theme.ts} +4 -4
  49. package/public/js/{locale.js → locale.ts} +3 -3
  50. package/public/js/main.ts +280 -0
  51. package/public/js/{render.js → render.ts} +34 -107
  52. package/public/js/state.ts +38 -0
  53. package/public/js/{ui.js → ui.ts} +60 -63
  54. package/public/js/{ws.js → ws.ts} +46 -20
  55. package/public/locales/en.json +1 -0
  56. package/public/locales/ko.json +1 -0
  57. package/scripts/check-copilot-gap.ts +75 -0
  58. package/scripts/check-deps-offline.ts +98 -0
  59. package/scripts/fresh-install-smoke.ts +130 -0
  60. package/scripts/i18n-registry.ts +230 -0
  61. package/scripts/postinstall-guard.cjs +5 -0
  62. package/dist/bin/cli-claw.js +0 -96
  63. package/dist/bin/cli-claw.js.map +0 -1
  64. package/public/js/features/heartbeat.js +0 -80
  65. package/public/js/features/memory.js +0 -85
  66. package/public/js/main.js +0 -278
  67. package/public/js/state.js +0 -16
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+
6
+ interface CheckItem {
7
+ name: string;
8
+ file: string;
9
+ needle: string;
10
+ }
11
+
12
+ const __filename: string = fileURLToPath(import.meta.url);
13
+ const __dirname: string = path.dirname(__filename);
14
+ const ROOT = path.resolve(__dirname, '..');
15
+
16
+ const checks: CheckItem[] = [
17
+ {
18
+ name: 'CLI registry has copilot',
19
+ file: 'src/cli/registry.ts',
20
+ needle: 'copilot:',
21
+ },
22
+ {
23
+ name: 'Server exposes /api/cli-registry',
24
+ file: 'server.ts',
25
+ needle: "app.get('/api/cli-registry'",
26
+ },
27
+ {
28
+ name: 'Settings page includes copilot model select',
29
+ file: 'public/index.html',
30
+ needle: 'id="modelCopilot"',
31
+ },
32
+ {
33
+ name: 'Frontend loads registry from API',
34
+ file: 'public/js/constants.ts',
35
+ needle: "'/api/cli-registry')",
36
+ },
37
+ {
38
+ name: 'Employees UI uses dynamic CLI keys',
39
+ file: 'public/js/features/employees',
40
+ needle: 'getCliKeys()',
41
+ },
42
+ ];
43
+
44
+ const rows = checks.map((item: CheckItem) => {
45
+ // Support both .js and .ts (frontend migration may change extensions)
46
+ const candidates = item.file.includes('.')
47
+ ? [item.file]
48
+ : [`${item.file}.ts`, `${item.file}.js`];
49
+ let content = '';
50
+ let resolvedFile = item.file;
51
+ for (const f of candidates) {
52
+ const fullPath = path.join(ROOT, f);
53
+ if (fs.existsSync(fullPath)) {
54
+ content = fs.readFileSync(fullPath, 'utf8');
55
+ resolvedFile = f;
56
+ break;
57
+ }
58
+ }
59
+ const ok: boolean = content.includes(item.needle);
60
+ return { ...item, file: resolvedFile, ok };
61
+ });
62
+
63
+ console.log('\nCopilot gap check\n');
64
+ for (const row of rows) {
65
+ const icon = row.ok ? 'OK' : 'MISSING';
66
+ console.log(`- ${icon}: ${row.name} (${row.file})`);
67
+ }
68
+
69
+ const failed = rows.filter(r => !r.ok);
70
+ if (failed.length) {
71
+ console.error(`\nMissing ${failed.length} required item(s).`);
72
+ process.exit(1);
73
+ }
74
+
75
+ console.log('\nAll checks passed.');
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env node
2
+ // ─── Offline Dependency Check ────────────────────────
3
+ // Phase 9.7 — package-lock.json 기반 오프라인 취약 버전 검증
4
+ // 네트워크 없이도 알려진 advisory 범위와 비교 가능
5
+
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+
9
+ type SemVer = [number, number, number];
10
+
11
+ interface PackageLock {
12
+ packages?: Record<string, { version?: string }>;
13
+ }
14
+
15
+ interface Rule {
16
+ pkg: string;
17
+ test: (v: string) => boolean;
18
+ adv: string;
19
+ why: string;
20
+ }
21
+
22
+ const lockPath = path.resolve('package-lock.json');
23
+ if (!fs.existsSync(lockPath)) {
24
+ console.error('[deps] package-lock.json not found');
25
+ process.exit(2);
26
+ }
27
+
28
+ const lock: PackageLock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
29
+ const pkgs = lock.packages ?? {};
30
+
31
+ function ver(p: string): string | null { return pkgs[p]?.version ?? null; }
32
+
33
+ function semver(v: string | null): SemVer | null {
34
+ const m = String(v ?? '').match(/^(\d+)\.(\d+)\.(\d+)/);
35
+ return m ? [+m[1]!, +m[2]!, +m[3]!] as SemVer : null;
36
+ }
37
+
38
+ function lt(a: SemVer, b: SemVer): boolean {
39
+ for (let i = 0; i < 3; i++) { if (a[i]! !== b[i]!) return a[i]! < b[i]!; }
40
+ return false;
41
+ }
42
+
43
+ function gte(a: SemVer, b: SemVer): boolean { return !lt(a, b); }
44
+
45
+ function inRange(v: string, lo: string, hi: string): boolean {
46
+ const sv = semver(v);
47
+ const loSv = semver(lo);
48
+ const hiSv = semver(hi);
49
+ return sv !== null && loSv !== null && hiSv !== null && gte(sv, loSv) && lt(sv, hiSv);
50
+ }
51
+
52
+ // ─── Advisory Rules ──────────────────────────────────
53
+ const rules: Rule[] = [
54
+ {
55
+ pkg: 'node_modules/ws',
56
+ test: (v: string) => inRange(v, '8.0.0', '8.17.1'),
57
+ adv: 'GHSA-3h5v-q93c-6h6q',
58
+ why: 'DoS via infinite loop',
59
+ },
60
+ {
61
+ pkg: 'node_modules/node-fetch',
62
+ test: (v: string) => {
63
+ const sv = semver(v);
64
+ const target = semver('2.6.7');
65
+ return inRange(v, '3.0.0', '3.1.1') || (sv !== null && target !== null && lt(sv, target));
66
+ },
67
+ adv: 'GHSA-r683-j2x4-v87g',
68
+ why: 'header forwarding to third-party',
69
+ },
70
+ {
71
+ pkg: 'node_modules/grammy/node_modules/node-fetch',
72
+ test: (v: string) => {
73
+ const sv = semver(v);
74
+ const target = semver('2.6.7');
75
+ return sv !== null && target !== null && lt(sv, target);
76
+ },
77
+ adv: 'GHSA-r683-j2x4-v87g',
78
+ why: 'transitive dependency',
79
+ },
80
+ ];
81
+
82
+ // ─── Check ───────────────────────────────────────────
83
+ let fail = 0;
84
+ for (const r of rules) {
85
+ const v = ver(r.pkg);
86
+ if (!v) {
87
+ console.log(`SKIP ${r.pkg} (not installed)`);
88
+ continue;
89
+ }
90
+ if (r.test(v)) {
91
+ fail++;
92
+ console.error(`FAIL ${r.pkg}@${v} → ${r.adv} (${r.why})`);
93
+ } else {
94
+ console.log(`PASS ${r.pkg}@${v}`);
95
+ }
96
+ }
97
+
98
+ process.exit(fail > 0 ? 1 : 0);
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { execFileSync, spawn, type ChildProcess, type ExecFileSyncOptions } from 'node:child_process';
6
+
7
+ const root: string = process.cwd();
8
+
9
+ function run(cmd: string, args: string[], opts: ExecFileSyncOptions = {}): string {
10
+ return execFileSync(cmd, args, {
11
+ cwd: root,
12
+ encoding: 'utf8',
13
+ stdio: ['ignore', 'pipe', 'pipe'],
14
+ ...opts,
15
+ }) as string;
16
+ }
17
+
18
+ function sleep(ms: number): Promise<void> {
19
+ return new Promise(resolve => setTimeout(resolve, ms));
20
+ }
21
+
22
+ async function waitFor(url: string, timeoutMs: number = 15000): Promise<boolean> {
23
+ const started = Date.now();
24
+ while (Date.now() - started < timeoutMs) {
25
+ try {
26
+ const res = await fetch(url);
27
+ if (res.ok) return true;
28
+ } catch {
29
+ // retry
30
+ }
31
+ await sleep(300);
32
+ }
33
+ return false;
34
+ }
35
+
36
+ function resolveInstalledPackage(prefix: string): string {
37
+ const candidates = [
38
+ path.join(prefix, 'lib', 'node_modules', 'cli-jaw'),
39
+ path.join(prefix, 'node_modules', 'cli-jaw'),
40
+ ];
41
+ for (const c of candidates) {
42
+ if (fs.existsSync(c)) return c;
43
+ }
44
+ throw new Error(`installed package path not found under prefix: ${prefix}`);
45
+ }
46
+
47
+ interface NpmPackResult {
48
+ filename?: string;
49
+ }
50
+
51
+ async function main(): Promise<void> {
52
+ let tarballPath: string | null = null;
53
+ let tmp: string | null = null;
54
+ let server: ChildProcess | null = null;
55
+
56
+ try {
57
+ const packOut = run('npm', ['pack', '--json']);
58
+ const pack: NpmPackResult[] = JSON.parse(packOut);
59
+ const tarballName = pack[0]?.filename;
60
+ if (!tarballName) throw new Error('npm pack did not return filename');
61
+
62
+ tarballPath = path.join(root, tarballName);
63
+ if (!fs.existsSync(tarballPath)) throw new Error(`tarball not found: ${tarballPath}`);
64
+
65
+ tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'jaw-fresh-'));
66
+ const prefix = path.join(tmp, 'prefix');
67
+ const jawHome = path.join(tmp, 'jaw-home');
68
+ fs.mkdirSync(prefix, { recursive: true });
69
+
70
+ const installEnv: NodeJS.ProcessEnv = { ...process.env, JAW_SAFE: '1', npm_config_loglevel: 'error' };
71
+ run('npm', ['i', '-g', tarballPath, '--prefix', prefix], { env: installEnv });
72
+
73
+ const pkgDir = resolveInstalledPackage(prefix);
74
+ const jawEntry = path.join(pkgDir, 'dist', 'bin', 'cli-jaw.js');
75
+ if (!fs.existsSync(jawEntry)) throw new Error(`cli entry not found: ${jawEntry}`);
76
+
77
+ const jawEnv: NodeJS.ProcessEnv = { ...process.env, CLI_JAW_HOME: jawHome };
78
+
79
+ const version = run(process.execPath, [jawEntry, '--version'], { env: jawEnv }).trim();
80
+ if (!version.toLowerCase().includes('cli-jaw')) throw new Error(`unexpected version output: ${version}`);
81
+
82
+ const doctorRaw = run(process.execPath, [jawEntry, '--home', jawHome, 'doctor', '--json'], { env: jawEnv });
83
+ const doctor = JSON.parse(doctorRaw) as { checks?: unknown[] };
84
+ if (!Array.isArray(doctor?.checks) || doctor.checks.length === 0) {
85
+ throw new Error('doctor --json returned empty checks');
86
+ }
87
+
88
+ const port = 30000 + Math.floor(Math.random() * 20000);
89
+ server = spawn(process.execPath, [jawEntry, '--home', jawHome, 'serve', '--port', String(port)], {
90
+ cwd: root,
91
+ env: jawEnv,
92
+ stdio: 'pipe',
93
+ });
94
+
95
+ const ready = await waitFor(`http://127.0.0.1:${port}/api/session`, 20000);
96
+ if (!ready) throw new Error('server did not become ready in time');
97
+
98
+ const cliRes = await fetch(`http://127.0.0.1:${port}/api/cli-status`);
99
+ if (!cliRes.ok) throw new Error(`/api/cli-status HTTP ${cliRes.status}`);
100
+ const cliJson = (await cliRes.json()) as Record<string, unknown> | null;
101
+ const keys = Object.keys(cliJson ?? {});
102
+ const required: string[] = ['claude', 'codex', 'gemini', 'copilot', 'opencode'];
103
+ for (const k of required) {
104
+ if (!keys.includes(k)) throw new Error(`missing cli key in status: ${k}`);
105
+ }
106
+
107
+ console.log('[fresh-install-smoke] PASS');
108
+ console.log(`[fresh-install-smoke] version=${version}`);
109
+ console.log(`[fresh-install-smoke] checks=${doctor.checks.length}`);
110
+ console.log(`[fresh-install-smoke] cli-status keys=${keys.join(',')}`);
111
+ } finally {
112
+ if (server && !server.killed) {
113
+ server.kill('SIGTERM');
114
+ await sleep(500);
115
+ if (!server.killed) server.kill('SIGKILL');
116
+ }
117
+ if (tarballPath && fs.existsSync(tarballPath)) {
118
+ fs.unlinkSync(tarballPath);
119
+ }
120
+ if (tmp && fs.existsSync(tmp)) {
121
+ fs.rmSync(tmp, { recursive: true, force: true });
122
+ }
123
+ }
124
+ }
125
+
126
+ main().catch((err: unknown) => {
127
+ console.error('[fresh-install-smoke] FAIL');
128
+ console.error((err as Error)?.stack || String(err));
129
+ process.exit(1);
130
+ });
@@ -0,0 +1,230 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Add dual-key i18n fields to skills registry.
4
+ *
5
+ * For each skill:
6
+ * - name_ko = current name
7
+ * - name_en = English name
8
+ * - desc_ko = current description
9
+ * - desc_en = English description
10
+ * - Preserves original name/description for backward compat
11
+ */
12
+ import fs from 'node:fs';
13
+ import path from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ // ─── Types ───────────────────────────────────────────
17
+ interface Skill {
18
+ name?: string;
19
+ description?: string;
20
+ name_ko?: string;
21
+ name_en?: string;
22
+ desc_ko?: string;
23
+ desc_en?: string;
24
+ [key: string]: unknown;
25
+ }
26
+
27
+ interface Registry {
28
+ skills: Record<string, Skill>;
29
+ [key: string]: unknown;
30
+ }
31
+
32
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
33
+ const REGISTRY = path.resolve(__dirname, '..', 'skills_ref', 'registry.json');
34
+
35
+ // Manual name translations (19 skills with Korean names)
36
+ const NAME_EN: Record<string, string> = {
37
+ himalaya: 'Email (Himalaya)',
38
+ github: 'GitHub',
39
+ 'skill-creator': 'Skill Creator',
40
+ weather: 'Weather',
41
+ 'video-frames': 'Video Frames',
42
+ summarize: 'URL Summarizer',
43
+ goplaces: 'Places Search',
44
+ 'nano-banana-pro': 'Image Gen (Gemini)',
45
+ browser: 'Browser Control',
46
+ 'develop-web-game': 'Web Game Dev',
47
+ 'figma-implement-design': 'Figma → Code',
48
+ 'notion-knowledge-capture': 'Notion Knowledge Capture',
49
+ 'notion-meeting-intelligence': 'Notion Meeting Intelligence',
50
+ 'notion-research-documentation': 'Notion Research Documentation',
51
+ 'notion-spec-to-implementation': 'Notion Spec → Tasks',
52
+ sora: 'Sora Video',
53
+ transcribe: 'Speech → Text',
54
+ imagegen: 'Image Gen (OpenAI)',
55
+ pdf: 'PDF Reader/Writer',
56
+ };
57
+
58
+ // Full description translations for all 107+ skills
59
+ const DESC_EN: Record<string, string> = {
60
+ notion: 'Notion page/DB CRUD via curl API calls. Complements Codex notion-* skills.',
61
+ trello: 'Trello board/list/card management via curl REST API.',
62
+ obsidian: 'Obsidian vault note creation, search, and tag management.',
63
+ 'things-mac': 'Things 3 todo add/complete/search. AppleScript + URL scheme.',
64
+ 'apple-notes': 'Apple Notes create/search. AppleScript-based.',
65
+ 'apple-reminders': 'Apple Reminders add/complete/list management. AppleScript-based.',
66
+ 'apple-messages': 'Send iMessage/SMS via AppleScript.',
67
+ linear: 'Linear issue/project/cycle management via GraphQL API.',
68
+ bear: 'Bear note markdown editing/archiving. x-callback-url scheme.',
69
+ 'google-calendar': 'gcalcli-based Google Calendar event CRUD.',
70
+ himalaya: 'Terminal email read/write/reply/search. Gmail/Outlook supported.',
71
+ gog: 'Gmail, Calendar, Drive, Sheets, Docs integrated management.',
72
+ xurl: 'Tweet post/search/reply/DM/media upload.',
73
+ 'telegram-send': 'Send voice/photo/document directly via Telegram local API.',
74
+ github: 'GitHub gh CLI integration: issues, PRs, CI, code review, API + PR comments, CI debugging, auto-fix.',
75
+ tmux: 'tmux session remote control. Send keystrokes + read output.',
76
+ 'skill-creator': 'Auto-generate new SKILL.md. Template + guidelines provided.',
77
+ weather: 'wttr.in weather/forecast lookup. No API key needed.',
78
+ 'video-frames': 'Extract video frames/segments with ffmpeg.',
79
+ summarize: 'Summarize URLs, YouTube videos, and files to text.',
80
+ goplaces: 'Google Places API for location, reviews, and business hours search.',
81
+ '1password': '1Password CLI for password/document/OTP lookup.',
82
+ 'nano-banana-pro': 'Generate/edit images with Gemini 3 Pro. Different model from Codex imagegen.',
83
+ 'spotify-player': 'Spotify play/pause/search/playlist management.',
84
+ openhue: 'Hue light/scene control. "Dim living room to 50%"',
85
+ browser: 'Chrome browser automation. Identify elements via ref snapshots → click/type.',
86
+ 'vision-click': 'Vision-based coordinate clicking. Codex CLI only. Screenshot → AI coordinate → pixel click.',
87
+ tts: 'macOS say command text-to-speech. Multi-language, file output.',
88
+ 'screen-capture': 'macOS screenshot/webcam/recording. Full/region/window/multi-monitor. Default fallback when tool-specific capture unavailable.',
89
+ atlas: 'Control ChatGPT Atlas app. macOS only.',
90
+ 'cloudflare-deploy': 'Deploy to Cloudflare Workers/Pages. wrangler CLI.',
91
+ 'develop-web-game': 'Web game development + Playwright test loop.',
92
+ 'figma-implement-design': 'Convert Figma designs to 1:1 code. Requires Figma MCP.',
93
+ 'jupyter-notebook': '.ipynb create/edit. Bundled Python script.',
94
+ 'netlify-deploy': 'Deploy Netlify sites. netlify CLI.',
95
+ 'notion-knowledge-capture': 'Conversation → Notion wiki/FAQ/HOW-TO capture. Notion MCP.',
96
+ 'notion-meeting-intelligence': 'Meeting prep (per-attendee context, agenda). Notion MCP.',
97
+ 'notion-research-documentation': 'Notion multi-source → report/comparison synthesis. Notion MCP.',
98
+ 'notion-spec-to-implementation': 'PRD/spec → implementation plan + auto task creation. Notion MCP.',
99
+ 'render-deploy': 'Deploy Render services. Blueprint YAML.',
100
+ sentry: 'Sentry issue/event lookup. Bundled Python script.',
101
+ sora: 'Sora video generation/management. OpenAI API.',
102
+ speech: 'OpenAI TTS voice synthesis. Bundled Python script.',
103
+ transcribe: 'OpenAI Whisper speech-to-text + speaker diarization.',
104
+ 'vercel-deploy': 'Vercel project deployment.',
105
+ memory: 'Long-term memory across sessions. Stored in markdown files, grep search.',
106
+ imagegen: 'Generate/edit images via OpenAI Images API.',
107
+ 'openai-docs': 'OpenAI product/API official documentation reference. Build guides.',
108
+ pdf: 'PDF read/create/edit/review. reportlab/pdfplumber/pypdf + nano-pdf natural language editing.',
109
+ 'frontend-design': 'Unique, production-grade frontend UI/page design and implementation.',
110
+ docx: '.docx document create/edit/read. Visual verification (soffice→PDF→PNG), tracked changes, python-docx.',
111
+ xlsx: '.xlsx/.xlsm/.csv/.tsv file create/edit/analyze/format. Includes pandas data analysis.',
112
+ 'webapp-testing': 'Playwright-based web app interaction/verification/debugging test skill.',
113
+ 'mcp-builder': 'Design/implement MCP servers for external API integration.',
114
+ pptx: 'Presentation (.pptx) create/edit/analyze skill.',
115
+ 'doc-coauthoring': 'Structured planning/spec/document co-authoring workflow skill.',
116
+ 'web-artifacts-builder': 'React/Tailwind complex web artifact creation skill.',
117
+ 'theme-factory': 'Apply reusable themes to document/slide/HTML outputs.',
118
+ 'web-routing': 'Guide skill for routing browser requests to browser/webapp-testing.',
119
+ 'algorithmic-art': 'p5.js generative art. Algorithm-based visual artwork creation.',
120
+ 'canvas-design': 'Create PNG/PDF visual designs via Canvas API.',
121
+ 'react-best-practices': 'React code patterns, performance optimization, component design best practices.',
122
+ 'web-perf': 'Core Web Vitals audit. Lighthouse/CrUX-based performance analysis.',
123
+ 'agents-sdk': 'Cloudflare Workers AI Agents SDK usage guide.',
124
+ 'durable-objects': 'Cloudflare Durable Objects (RPC+SQLite+WebSocket) stateful workers.',
125
+ 'static-analysis': 'CodeQL+Semgrep+SARIF static security analysis.',
126
+ 'insecure-defaults': 'Detect hardcoded secrets, weak crypto, and insecure defaults.',
127
+ 'modern-python': 'uv+ruff+ty+pytest Python best practices.',
128
+ 'differential-review': 'Security-focused diff review. Analyze security impact of code changes.',
129
+ 'property-based-testing': 'Multi-language property-based testing (Hypothesis/fast-check etc.).',
130
+ 'security-best-practices': 'Language-specific security vulnerability pattern review.',
131
+ 'security-ownership-map': 'Codebase owner/bus-factor mapping.',
132
+ 'security-threat-model': 'Per-repo threat model generation (STRIDE/DREAD).',
133
+ 'hugging-face-cli': 'HF Hub CLI for model/dataset/space management.',
134
+ 'hugging-face-model-trainer': 'TRL: SFT/DPO/GRPO model training.',
135
+ 'hugging-face-evaluation': 'vLLM/lighteval model evaluation and benchmarks.',
136
+ 'fal-image-edit': 'fal.ai AI image editing (style transfer, object removal).',
137
+ brainstorming: 'Pre-coding idea refinement → design document creation (obra/superpowers).',
138
+ 'writing-plans': '2-5 minute task decomposition. File paths/code/verification included.',
139
+ tdd: 'RED-GREEN-REFACTOR TDD cycle enforcement.',
140
+ 'requesting-code-review': 'Internal agent code review. Severity-based blocking.',
141
+ 'receiving-code-review': 'Code review feedback reception and response patterns.',
142
+ 'dispatching-parallel-agents': 'Parallel sub-agent dispatch patterns.',
143
+ 'debugging-helpers': 'Systematic debugging helpers.',
144
+ 'git-worktrees': 'git worktree-based isolated branch workflow.',
145
+ 'codebase-orientation': 'Project entrypoint/module/build mapping. Onboarding guide.',
146
+ 'debugging-checklist': 'Reproduce → isolate → log → hypothesis verification debugging checklist.',
147
+ 'error-message-explainer': 'Compiler/runtime error → cause + fix suggestions.',
148
+ 'config-file-explainer': 'Config file structure/keys/defaults explanation.',
149
+ 'data-structure-chooser': 'Data structure time/space tradeoff recommendations.',
150
+ 'log-summarizer': 'Log grouping + first failure identification + action suggestions.',
151
+ 'linter-fix-guide': 'Lint rule explanation + pattern examples + minimal fix suggestions.',
152
+ 'dependency-install-helper': 'Platform-specific dependency installation steps + verification commands.',
153
+ 'changelog-generator': 'git commit → changelog/release notes generation.',
154
+ 'video-downloader': 'yt-dlp wrapper. YouTube/media download.',
155
+ 'email-draft-polish': 'Email draft tone adjustment/formatting.',
156
+ postgres: 'PostgreSQL read-only queries. Schema exploration.',
157
+ 'deep-research': 'Multi-step research agent. Search → analyze → summarize.',
158
+ 'context-compression': 'Context compression strategies. Long session optimization.',
159
+ 'ios-simulator': 'iOS Simulator control. App build/run/test.',
160
+ 'apple-hig-skills': 'Apple HIG 14 guides (foundations/platforms/components/patterns).',
161
+ whatsapp: 'WhatsApp message automation (automate-whatsapp).',
162
+ 'aws-skills': 'AWS infrastructure automation (CDK/CloudFormation/Lambda).',
163
+ terraform: 'HashiCorp Terraform HCL/modules/providers IaC.',
164
+ kreuzberg: '62+ format text extraction (PDF/DOCX/PPTX/images etc.).',
165
+ dev: 'Common development guide. Modular dev, self-reference patterns, skills_ref exploration, changelog.',
166
+ 'dev-frontend': 'Frontend role guide. Unique UI/UX implementation, component design, aesthetic standards.',
167
+ 'dev-backend': 'Backend role guide. Express.js patterns, SQLite, error handling, security basics.',
168
+ 'dev-data': 'Data role guide. ETL pipelines, CSV/JSON processing, SQL queries, analysis.',
169
+ 'dev-testing': 'Debugging phase only. Playwright web app testing, recon-action pattern.',
170
+ };
171
+
172
+ function main(): void {
173
+ const raw = fs.readFileSync(REGISTRY, 'utf8');
174
+ const data: Registry = JSON.parse(raw);
175
+
176
+ const skills = data.skills;
177
+ let updated = 0;
178
+
179
+ for (const [skillId, skill] of Object.entries(skills)) {
180
+ const name = skill.name ?? skillId;
181
+ const desc = skill.description ?? '';
182
+
183
+ // Set ko fields
184
+ skill.name_ko = name;
185
+ skill.desc_ko = desc;
186
+
187
+ // Set en fields
188
+ const hasKoName = /[\uac00-\ud7af]/.test(name);
189
+ skill.name_en = hasKoName ? (NAME_EN[skillId] ?? name) : name;
190
+
191
+ skill.desc_en = DESC_EN[skillId] ?? desc;
192
+ updated++;
193
+ }
194
+
195
+ // Reorder keys: keep original order, insert i18n fields after description
196
+ const newSkills: Record<string, Skill> = {};
197
+ for (const [skillId, skill] of Object.entries(skills)) {
198
+ const ordered: Record<string, unknown> = {};
199
+ for (const [k, v] of Object.entries(skill)) {
200
+ ordered[k] = v;
201
+ if (k === 'name') {
202
+ ordered.name_ko = skill.name_ko;
203
+ ordered.name_en = skill.name_en;
204
+ } else if (k === 'description') {
205
+ ordered.desc_ko = skill.desc_ko;
206
+ ordered.desc_en = skill.desc_en;
207
+ }
208
+ }
209
+ newSkills[skillId] = ordered as Skill;
210
+ }
211
+
212
+ data.skills = newSkills;
213
+
214
+ fs.writeFileSync(REGISTRY, JSON.stringify(data, null, 4) + '\n', 'utf8');
215
+
216
+ console.log(`Updated ${updated} skills with i18n fields`);
217
+
218
+ // Verify
219
+ const missingEn = Object.entries(newSkills)
220
+ .filter(([, v]) => !v.desc_en)
221
+ .map(([k]) => k);
222
+
223
+ if (missingEn.length) {
224
+ console.log(`WARNING: ${missingEn.length} skills missing desc_en: ${missingEn}`);
225
+ } else {
226
+ console.log('All skills have desc_en ✓');
227
+ }
228
+ }
229
+
230
+ main();
@@ -2,6 +2,11 @@
2
2
  /**
3
3
  * postinstall-guard.cjs — Cross-platform guard for postinstall.
4
4
  *
5
+ * ⚠️ INTENTIONALLY CommonJS (.cjs) — DO NOT convert to TypeScript.
6
+ * Reason: Runs during `npm postinstall` BEFORE any build step.
7
+ * At this point, tsx/tsc may not be available (fresh install).
8
+ * Must work with bare Node.js on all platforms (macOS/Linux/Windows).
9
+ *
5
10
  * 1. Node version check (fail-fast before any build attempt)
6
11
  * 2. dist/bin/postinstall.js exists? → exit 0
7
12
  * 3. Dev clone fallback: local tsc → build → exit 0
@@ -1,96 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * cli-claw — Phase 9.1
4
- * CLI entrypoint with subcommand routing.
5
- * No external dependencies — Node built-in only.
6
- */
7
- import { dirname, join } from 'node:path';
8
- import { fileURLToPath } from 'node:url';
9
- import { readFileSync } from 'node:fs';
10
- const __dirname = dirname(fileURLToPath(import.meta.url));
11
- let pkg;
12
- try {
13
- pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8'));
14
- }
15
- catch {
16
- pkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8'));
17
- }
18
- const command = process.argv[2];
19
- function printHelp() {
20
- console.log(`
21
- 🦞 cli-claw v${pkg.version}
22
-
23
- Usage: cli-claw <command> [options]
24
-
25
- Commands:
26
- serve 서버 시작 (포그라운드)
27
- init 초기 설정 마법사
28
- doctor 설치/설정 진단
29
- chat 터미널 채팅 (REPL)
30
- employee 직원 관리 (reset)
31
- reset 전체 초기화 (MCP/스킬/직원/세션)
32
- mcp MCP 서버 관리 (install/sync/list)
33
- skill 스킬 관리 (install/remove/info)
34
- status 서버 상태 확인
35
-
36
- Options:
37
- --help 도움말 표시
38
- --version 버전 표시
39
-
40
- Examples:
41
- cli-claw serve --port 3457
42
- cli-claw init
43
- cli-claw doctor --json
44
- cli-claw chat --raw
45
- `);
46
- }
47
- switch (command) {
48
- case 'serve':
49
- await import('./commands/serve.js');
50
- break;
51
- case 'init':
52
- await import('./commands/init.js');
53
- break;
54
- case 'doctor':
55
- await import('./commands/doctor.js');
56
- break;
57
- case 'chat':
58
- await import('./commands/chat.js');
59
- break;
60
- case 'employee':
61
- await import('./commands/employee.js');
62
- break;
63
- case 'reset':
64
- await import('./commands/reset.js');
65
- break;
66
- case 'mcp':
67
- await import('./commands/mcp.js');
68
- break;
69
- case 'skill':
70
- await import('./commands/skill.js');
71
- break;
72
- case 'status':
73
- await import('./commands/status.js');
74
- break;
75
- case 'browser':
76
- await import('./commands/browser.js');
77
- break;
78
- case 'memory':
79
- await import('./commands/memory.js');
80
- break;
81
- case '--version':
82
- case '-v':
83
- console.log(`cli-claw v${pkg.version}`);
84
- break;
85
- case '--help':
86
- case '-h':
87
- case undefined:
88
- printHelp();
89
- break;
90
- default:
91
- console.error(` ❌ Unknown command: ${command}\n`);
92
- printHelp();
93
- process.exitCode = 1;
94
- break;
95
- }
96
- //# sourceMappingURL=cli-claw.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli-claw.js","sourceRoot":"","sources":["../../bin/cli-claw.ts"],"names":[],"mappings":";AACA;;;;GAIG;AACH,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AAEvC,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AAC1D,IAAI,GAAwC,CAAC;AAC7C,IAAI,CAAC;IACD,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAClF,CAAC;AAAC,MAAM,CAAC;IACL,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,cAAc,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AACxF,CAAC;AAED,MAAM,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAEhC,SAAS,SAAS;IACd,OAAO,CAAC,GAAG,CAAC;iBACC,GAAG,CAAC,OAAO;;;;;;;;;;;;;;;;;;;;;;;;CAwB3B,CAAC,CAAC;AACH,CAAC;AAED,QAAQ,OAAO,EAAE,CAAC;IACd,KAAK,OAAO;QACR,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACpC,MAAM;IACV,KAAK,MAAM;QACP,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACnC,MAAM;IACV,KAAK,QAAQ;QACT,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACrC,MAAM;IACV,KAAK,MAAM;QACP,MAAM,MAAM,CAAC,oBAAoB,CAAC,CAAC;QACnC,MAAM;IACV,KAAK,UAAU;QACX,MAAM,MAAM,CAAC,wBAAwB,CAAC,CAAC;QACvC,MAAM;IACV,KAAK,OAAO;QACR,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACpC,MAAM;IACV,KAAK,KAAK;QACN,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;QAClC,MAAM;IACV,KAAK,OAAO;QACR,MAAM,MAAM,CAAC,qBAAqB,CAAC,CAAC;QACpC,MAAM;IACV,KAAK,QAAQ;QACT,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACrC,MAAM;IACV,KAAK,SAAS;QACV,MAAM,MAAM,CAAC,uBAAuB,CAAC,CAAC;QACtC,MAAM;IACV,KAAK,QAAQ;QACT,MAAM,MAAM,CAAC,sBAAsB,CAAC,CAAC;QACrC,MAAM;IACV,KAAK,WAAW,CAAC;IACjB,KAAK,IAAI;QACL,OAAO,CAAC,GAAG,CAAC,aAAa,GAAG,CAAC,OAAO,EAAE,CAAC,CAAC;QACxC,MAAM;IACV,KAAK,QAAQ,CAAC;IACd,KAAK,IAAI,CAAC;IACV,KAAK,SAAS;QACV,SAAS,EAAE,CAAC;QACZ,MAAM;IACV;QACI,OAAO,CAAC,KAAK,CAAC,wBAAwB,OAAO,IAAI,CAAC,CAAC;QACnD,SAAS,EAAE,CAAC;QACZ,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;QACrB,MAAM;AACd,CAAC"}