cli-jaw 0.1.11 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.ko.md +44 -13
- package/README.md +12 -11
- package/README.zh-CN.md +43 -12
- package/dist/bin/commands/doctor.js +13 -2
- package/dist/bin/commands/doctor.js.map +1 -1
- package/dist/bin/commands/mcp.js +15 -18
- package/dist/bin/commands/mcp.js.map +1 -1
- package/dist/bin/commands/serve.js +3 -28
- package/dist/bin/commands/serve.js.map +1 -1
- package/dist/bin/commands/skill.js +9 -6
- package/dist/bin/commands/skill.js.map +1 -1
- package/dist/lib/mcp-sync.js +123 -31
- package/dist/lib/mcp-sync.js.map +1 -1
- package/{scripts → dist/scripts}/check-copilot-gap.js +24 -17
- package/dist/scripts/check-copilot-gap.js.map +1 -0
- package/{scripts/check-deps-offline.mjs → dist/scripts/check-deps-offline.js} +24 -20
- package/dist/scripts/check-deps-offline.js.map +1 -0
- package/dist/scripts/fresh-install-smoke.js +120 -0
- package/dist/scripts/fresh-install-smoke.js.map +1 -0
- package/{scripts/i18n-registry.py → dist/scripts/i18n-registry.js} +115 -122
- package/dist/scripts/i18n-registry.js.map +1 -0
- package/dist/server.js +34 -26
- package/dist/server.js.map +1 -1
- package/dist/src/cli/command-context.js +13 -3
- package/dist/src/cli/command-context.js.map +1 -1
- package/dist/src/prompt/builder.js +28 -1
- package/dist/src/prompt/builder.js.map +1 -1
- package/package.json +9 -5
- package/public/dist/bundle.js +72 -77
- package/public/dist/bundle.js.map +4 -4
- package/public/index.html +1 -3
- package/public/js/{api.js → api.ts} +18 -12
- package/public/js/{constants.js → constants.ts} +44 -24
- package/public/js/features/{appname.js → appname.ts} +13 -12
- package/public/js/features/{chat.js → chat.ts} +46 -37
- package/public/js/features/{employees.js → employees.ts} +67 -38
- package/public/js/features/heartbeat.ts +90 -0
- package/public/js/features/{i18n.js → i18n.ts} +20 -20
- package/public/js/features/memory.ts +125 -0
- package/public/js/features/{settings.js → settings.ts} +125 -93
- package/public/js/features/{sidebar.js → sidebar.ts} +15 -16
- package/public/js/features/{skills.js → skills.ts} +29 -16
- package/public/js/features/{slash-commands.js → slash-commands.ts} +34 -29
- package/public/js/features/{theme.js → theme.ts} +4 -4
- package/public/js/{locale.js → locale.ts} +3 -3
- package/public/js/main.ts +280 -0
- package/public/js/{render.js → render.ts} +34 -107
- package/public/js/state.ts +38 -0
- package/public/js/{ui.js → ui.ts} +60 -63
- package/public/js/{ws.js → ws.ts} +46 -20
- package/public/locales/en.json +1 -0
- package/public/locales/ko.json +1 -0
- package/scripts/check-copilot-gap.ts +75 -0
- package/scripts/check-deps-offline.ts +98 -0
- package/scripts/fresh-install-smoke.ts +130 -0
- package/scripts/i18n-registry.ts +230 -0
- package/scripts/postinstall-guard.cjs +5 -0
- package/dist/bin/cli-claw.js +0 -96
- package/dist/bin/cli-claw.js.map +0 -1
- package/public/js/features/heartbeat.js +0 -80
- package/public/js/features/memory.js +0 -85
- package/public/js/main.js +0 -278
- package/public/js/state.js +0 -16
|
@@ -2,60 +2,63 @@
|
|
|
2
2
|
// ─── Offline Dependency Check ────────────────────────
|
|
3
3
|
// Phase 9.7 — package-lock.json 기반 오프라인 취약 버전 검증
|
|
4
4
|
// 네트워크 없이도 알려진 advisory 범위와 비교 가능
|
|
5
|
-
|
|
6
5
|
import fs from 'node:fs';
|
|
7
6
|
import path from 'node:path';
|
|
8
|
-
|
|
9
7
|
const lockPath = path.resolve('package-lock.json');
|
|
10
8
|
if (!fs.existsSync(lockPath)) {
|
|
11
9
|
console.error('[deps] package-lock.json not found');
|
|
12
10
|
process.exit(2);
|
|
13
11
|
}
|
|
14
|
-
|
|
15
12
|
const lock = JSON.parse(fs.readFileSync(lockPath, 'utf8'));
|
|
16
|
-
const pkgs = lock.packages
|
|
17
|
-
|
|
18
|
-
function ver(p) { return pkgs[p]?.version || null; }
|
|
19
|
-
|
|
13
|
+
const pkgs = lock.packages ?? {};
|
|
14
|
+
function ver(p) { return pkgs[p]?.version ?? null; }
|
|
20
15
|
function semver(v) {
|
|
21
|
-
const m = String(v
|
|
16
|
+
const m = String(v ?? '').match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
22
17
|
return m ? [+m[1], +m[2], +m[3]] : null;
|
|
23
18
|
}
|
|
24
|
-
|
|
25
19
|
function lt(a, b) {
|
|
26
|
-
for (let i = 0; i < 3; i++) {
|
|
20
|
+
for (let i = 0; i < 3; i++) {
|
|
21
|
+
if (a[i] !== b[i])
|
|
22
|
+
return a[i] < b[i];
|
|
23
|
+
}
|
|
27
24
|
return false;
|
|
28
25
|
}
|
|
29
|
-
|
|
30
26
|
function gte(a, b) { return !lt(a, b); }
|
|
31
|
-
|
|
32
27
|
function inRange(v, lo, hi) {
|
|
33
28
|
const sv = semver(v);
|
|
34
|
-
|
|
29
|
+
const loSv = semver(lo);
|
|
30
|
+
const hiSv = semver(hi);
|
|
31
|
+
return sv !== null && loSv !== null && hiSv !== null && gte(sv, loSv) && lt(sv, hiSv);
|
|
35
32
|
}
|
|
36
|
-
|
|
37
33
|
// ─── Advisory Rules ──────────────────────────────────
|
|
38
34
|
const rules = [
|
|
39
35
|
{
|
|
40
36
|
pkg: 'node_modules/ws',
|
|
41
|
-
test: v => inRange(v, '8.0.0', '8.17.1'),
|
|
37
|
+
test: (v) => inRange(v, '8.0.0', '8.17.1'),
|
|
42
38
|
adv: 'GHSA-3h5v-q93c-6h6q',
|
|
43
39
|
why: 'DoS via infinite loop',
|
|
44
40
|
},
|
|
45
41
|
{
|
|
46
42
|
pkg: 'node_modules/node-fetch',
|
|
47
|
-
test:
|
|
43
|
+
test: (v) => {
|
|
44
|
+
const sv = semver(v);
|
|
45
|
+
const target = semver('2.6.7');
|
|
46
|
+
return inRange(v, '3.0.0', '3.1.1') || (sv !== null && target !== null && lt(sv, target));
|
|
47
|
+
},
|
|
48
48
|
adv: 'GHSA-r683-j2x4-v87g',
|
|
49
49
|
why: 'header forwarding to third-party',
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
pkg: 'node_modules/grammy/node_modules/node-fetch',
|
|
53
|
-
test: v =>
|
|
53
|
+
test: (v) => {
|
|
54
|
+
const sv = semver(v);
|
|
55
|
+
const target = semver('2.6.7');
|
|
56
|
+
return sv !== null && target !== null && lt(sv, target);
|
|
57
|
+
},
|
|
54
58
|
adv: 'GHSA-r683-j2x4-v87g',
|
|
55
59
|
why: 'transitive dependency',
|
|
56
60
|
},
|
|
57
61
|
];
|
|
58
|
-
|
|
59
62
|
// ─── Check ───────────────────────────────────────────
|
|
60
63
|
let fail = 0;
|
|
61
64
|
for (const r of rules) {
|
|
@@ -67,9 +70,10 @@ for (const r of rules) {
|
|
|
67
70
|
if (r.test(v)) {
|
|
68
71
|
fail++;
|
|
69
72
|
console.error(`FAIL ${r.pkg}@${v} → ${r.adv} (${r.why})`);
|
|
70
|
-
}
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
71
75
|
console.log(`PASS ${r.pkg}@${v}`);
|
|
72
76
|
}
|
|
73
77
|
}
|
|
74
|
-
|
|
75
78
|
process.exit(fail > 0 ? 1 : 0);
|
|
79
|
+
//# sourceMappingURL=check-deps-offline.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"check-deps-offline.js","sourceRoot":"","sources":["../../scripts/check-deps-offline.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,120 @@
|
|
|
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 } from 'node:child_process';
|
|
6
|
+
const root = process.cwd();
|
|
7
|
+
function run(cmd, args, opts = {}) {
|
|
8
|
+
return execFileSync(cmd, args, {
|
|
9
|
+
cwd: root,
|
|
10
|
+
encoding: 'utf8',
|
|
11
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
12
|
+
...opts,
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
function sleep(ms) {
|
|
16
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
17
|
+
}
|
|
18
|
+
async function waitFor(url, timeoutMs = 15000) {
|
|
19
|
+
const started = Date.now();
|
|
20
|
+
while (Date.now() - started < timeoutMs) {
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(url);
|
|
23
|
+
if (res.ok)
|
|
24
|
+
return true;
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
// retry
|
|
28
|
+
}
|
|
29
|
+
await sleep(300);
|
|
30
|
+
}
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
function resolveInstalledPackage(prefix) {
|
|
34
|
+
const candidates = [
|
|
35
|
+
path.join(prefix, 'lib', 'node_modules', 'cli-jaw'),
|
|
36
|
+
path.join(prefix, 'node_modules', 'cli-jaw'),
|
|
37
|
+
];
|
|
38
|
+
for (const c of candidates) {
|
|
39
|
+
if (fs.existsSync(c))
|
|
40
|
+
return c;
|
|
41
|
+
}
|
|
42
|
+
throw new Error(`installed package path not found under prefix: ${prefix}`);
|
|
43
|
+
}
|
|
44
|
+
async function main() {
|
|
45
|
+
let tarballPath = null;
|
|
46
|
+
let tmp = null;
|
|
47
|
+
let server = null;
|
|
48
|
+
try {
|
|
49
|
+
const packOut = run('npm', ['pack', '--json']);
|
|
50
|
+
const pack = JSON.parse(packOut);
|
|
51
|
+
const tarballName = pack[0]?.filename;
|
|
52
|
+
if (!tarballName)
|
|
53
|
+
throw new Error('npm pack did not return filename');
|
|
54
|
+
tarballPath = path.join(root, tarballName);
|
|
55
|
+
if (!fs.existsSync(tarballPath))
|
|
56
|
+
throw new Error(`tarball not found: ${tarballPath}`);
|
|
57
|
+
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'jaw-fresh-'));
|
|
58
|
+
const prefix = path.join(tmp, 'prefix');
|
|
59
|
+
const jawHome = path.join(tmp, 'jaw-home');
|
|
60
|
+
fs.mkdirSync(prefix, { recursive: true });
|
|
61
|
+
const installEnv = { ...process.env, JAW_SAFE: '1', npm_config_loglevel: 'error' };
|
|
62
|
+
run('npm', ['i', '-g', tarballPath, '--prefix', prefix], { env: installEnv });
|
|
63
|
+
const pkgDir = resolveInstalledPackage(prefix);
|
|
64
|
+
const jawEntry = path.join(pkgDir, 'dist', 'bin', 'cli-jaw.js');
|
|
65
|
+
if (!fs.existsSync(jawEntry))
|
|
66
|
+
throw new Error(`cli entry not found: ${jawEntry}`);
|
|
67
|
+
const jawEnv = { ...process.env, CLI_JAW_HOME: jawHome };
|
|
68
|
+
const version = run(process.execPath, [jawEntry, '--version'], { env: jawEnv }).trim();
|
|
69
|
+
if (!version.toLowerCase().includes('cli-jaw'))
|
|
70
|
+
throw new Error(`unexpected version output: ${version}`);
|
|
71
|
+
const doctorRaw = run(process.execPath, [jawEntry, '--home', jawHome, 'doctor', '--json'], { env: jawEnv });
|
|
72
|
+
const doctor = JSON.parse(doctorRaw);
|
|
73
|
+
if (!Array.isArray(doctor?.checks) || doctor.checks.length === 0) {
|
|
74
|
+
throw new Error('doctor --json returned empty checks');
|
|
75
|
+
}
|
|
76
|
+
const port = 30000 + Math.floor(Math.random() * 20000);
|
|
77
|
+
server = spawn(process.execPath, [jawEntry, '--home', jawHome, 'serve', '--port', String(port)], {
|
|
78
|
+
cwd: root,
|
|
79
|
+
env: jawEnv,
|
|
80
|
+
stdio: 'pipe',
|
|
81
|
+
});
|
|
82
|
+
const ready = await waitFor(`http://127.0.0.1:${port}/api/session`, 20000);
|
|
83
|
+
if (!ready)
|
|
84
|
+
throw new Error('server did not become ready in time');
|
|
85
|
+
const cliRes = await fetch(`http://127.0.0.1:${port}/api/cli-status`);
|
|
86
|
+
if (!cliRes.ok)
|
|
87
|
+
throw new Error(`/api/cli-status HTTP ${cliRes.status}`);
|
|
88
|
+
const cliJson = (await cliRes.json());
|
|
89
|
+
const keys = Object.keys(cliJson ?? {});
|
|
90
|
+
const required = ['claude', 'codex', 'gemini', 'copilot', 'opencode'];
|
|
91
|
+
for (const k of required) {
|
|
92
|
+
if (!keys.includes(k))
|
|
93
|
+
throw new Error(`missing cli key in status: ${k}`);
|
|
94
|
+
}
|
|
95
|
+
console.log('[fresh-install-smoke] PASS');
|
|
96
|
+
console.log(`[fresh-install-smoke] version=${version}`);
|
|
97
|
+
console.log(`[fresh-install-smoke] checks=${doctor.checks.length}`);
|
|
98
|
+
console.log(`[fresh-install-smoke] cli-status keys=${keys.join(',')}`);
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
if (server && !server.killed) {
|
|
102
|
+
server.kill('SIGTERM');
|
|
103
|
+
await sleep(500);
|
|
104
|
+
if (!server.killed)
|
|
105
|
+
server.kill('SIGKILL');
|
|
106
|
+
}
|
|
107
|
+
if (tarballPath && fs.existsSync(tarballPath)) {
|
|
108
|
+
fs.unlinkSync(tarballPath);
|
|
109
|
+
}
|
|
110
|
+
if (tmp && fs.existsSync(tmp)) {
|
|
111
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
main().catch((err) => {
|
|
116
|
+
console.error('[fresh-install-smoke] FAIL');
|
|
117
|
+
console.error(err?.stack || String(err));
|
|
118
|
+
process.exit(1);
|
|
119
|
+
});
|
|
120
|
+
//# sourceMappingURL=fresh-install-smoke.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fresh-install-smoke.js","sourceRoot":"","sources":["../../scripts/fresh-install-smoke.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,73 +1,73 @@
|
|
|
1
|
-
#!/usr/bin/env
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const REGISTRY = path.resolve(__dirname, '..', 'skills_ref', 'registry.json');
|
|
17
|
+
// Manual name translations (19 skills with Korean names)
|
|
18
|
+
const NAME_EN = {
|
|
19
|
+
himalaya: 'Email (Himalaya)',
|
|
20
|
+
github: 'GitHub',
|
|
20
21
|
'skill-creator': 'Skill Creator',
|
|
21
|
-
|
|
22
|
+
weather: 'Weather',
|
|
22
23
|
'video-frames': 'Video Frames',
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
summarize: 'URL Summarizer',
|
|
25
|
+
goplaces: 'Places Search',
|
|
25
26
|
'nano-banana-pro': 'Image Gen (Gemini)',
|
|
26
|
-
|
|
27
|
+
browser: 'Browser Control',
|
|
27
28
|
'develop-web-game': 'Web Game Dev',
|
|
28
29
|
'figma-implement-design': 'Figma → Code',
|
|
29
30
|
'notion-knowledge-capture': 'Notion Knowledge Capture',
|
|
30
31
|
'notion-meeting-intelligence': 'Notion Meeting Intelligence',
|
|
31
32
|
'notion-research-documentation': 'Notion Research Documentation',
|
|
32
33
|
'notion-spec-to-implementation': 'Notion Spec → Tasks',
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
'obsidian': 'Obsidian vault note creation, search, and tag management.',
|
|
34
|
+
sora: 'Sora Video',
|
|
35
|
+
transcribe: 'Speech → Text',
|
|
36
|
+
imagegen: 'Image Gen (OpenAI)',
|
|
37
|
+
pdf: 'PDF Reader/Writer',
|
|
38
|
+
};
|
|
39
|
+
// Full description translations for all 107+ skills
|
|
40
|
+
const DESC_EN = {
|
|
41
|
+
notion: 'Notion page/DB CRUD via curl API calls. Complements Codex notion-* skills.',
|
|
42
|
+
trello: 'Trello board/list/card management via curl REST API.',
|
|
43
|
+
obsidian: 'Obsidian vault note creation, search, and tag management.',
|
|
44
44
|
'things-mac': 'Things 3 todo add/complete/search. AppleScript + URL scheme.',
|
|
45
45
|
'apple-notes': 'Apple Notes create/search. AppleScript-based.',
|
|
46
46
|
'apple-reminders': 'Apple Reminders add/complete/list management. AppleScript-based.',
|
|
47
47
|
'apple-messages': 'Send iMessage/SMS via AppleScript.',
|
|
48
|
-
|
|
49
|
-
|
|
48
|
+
linear: 'Linear issue/project/cycle management via GraphQL API.',
|
|
49
|
+
bear: 'Bear note markdown editing/archiving. x-callback-url scheme.',
|
|
50
50
|
'google-calendar': 'gcalcli-based Google Calendar event CRUD.',
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
51
|
+
himalaya: 'Terminal email read/write/reply/search. Gmail/Outlook supported.',
|
|
52
|
+
gog: 'Gmail, Calendar, Drive, Sheets, Docs integrated management.',
|
|
53
|
+
xurl: 'Tweet post/search/reply/DM/media upload.',
|
|
54
54
|
'telegram-send': 'Send voice/photo/document directly via Telegram local API.',
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
github: 'GitHub gh CLI integration: issues, PRs, CI, code review, API + PR comments, CI debugging, auto-fix.',
|
|
56
|
+
tmux: 'tmux session remote control. Send keystrokes + read output.',
|
|
57
57
|
'skill-creator': 'Auto-generate new SKILL.md. Template + guidelines provided.',
|
|
58
|
-
|
|
58
|
+
weather: 'wttr.in weather/forecast lookup. No API key needed.',
|
|
59
59
|
'video-frames': 'Extract video frames/segments with ffmpeg.',
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
summarize: 'Summarize URLs, YouTube videos, and files to text.',
|
|
61
|
+
goplaces: 'Google Places API for location, reviews, and business hours search.',
|
|
62
62
|
'1password': '1Password CLI for password/document/OTP lookup.',
|
|
63
63
|
'nano-banana-pro': 'Generate/edit images with Gemini 3 Pro. Different model from Codex imagegen.',
|
|
64
64
|
'spotify-player': 'Spotify play/pause/search/playlist management.',
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
openhue: 'Hue light/scene control. "Dim living room to 50%"',
|
|
66
|
+
browser: 'Chrome browser automation. Identify elements via ref snapshots → click/type.',
|
|
67
67
|
'vision-click': 'Vision-based coordinate clicking. Codex CLI only. Screenshot → AI coordinate → pixel click.',
|
|
68
|
-
|
|
68
|
+
tts: 'macOS say command text-to-speech. Multi-language, file output.',
|
|
69
69
|
'screen-capture': 'macOS screenshot/webcam/recording. Full/region/window/multi-monitor. Default fallback when tool-specific capture unavailable.',
|
|
70
|
-
|
|
70
|
+
atlas: 'Control ChatGPT Atlas app. macOS only.',
|
|
71
71
|
'cloudflare-deploy': 'Deploy to Cloudflare Workers/Pages. wrangler CLI.',
|
|
72
72
|
'develop-web-game': 'Web game development + Playwright test loop.',
|
|
73
73
|
'figma-implement-design': 'Convert Figma designs to 1:1 code. Requires Figma MCP.',
|
|
@@ -78,21 +78,21 @@ DESC_EN = {
|
|
|
78
78
|
'notion-research-documentation': 'Notion multi-source → report/comparison synthesis. Notion MCP.',
|
|
79
79
|
'notion-spec-to-implementation': 'PRD/spec → implementation plan + auto task creation. Notion MCP.',
|
|
80
80
|
'render-deploy': 'Deploy Render services. Blueprint YAML.',
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
81
|
+
sentry: 'Sentry issue/event lookup. Bundled Python script.',
|
|
82
|
+
sora: 'Sora video generation/management. OpenAI API.',
|
|
83
|
+
speech: 'OpenAI TTS voice synthesis. Bundled Python script.',
|
|
84
|
+
transcribe: 'OpenAI Whisper speech-to-text + speaker diarization.',
|
|
85
85
|
'vercel-deploy': 'Vercel project deployment.',
|
|
86
|
-
|
|
87
|
-
|
|
86
|
+
memory: 'Long-term memory across sessions. Stored in markdown files, grep search.',
|
|
87
|
+
imagegen: 'Generate/edit images via OpenAI Images API.',
|
|
88
88
|
'openai-docs': 'OpenAI product/API official documentation reference. Build guides.',
|
|
89
|
-
|
|
89
|
+
pdf: 'PDF read/create/edit/review. reportlab/pdfplumber/pypdf + nano-pdf natural language editing.',
|
|
90
90
|
'frontend-design': 'Unique, production-grade frontend UI/page design and implementation.',
|
|
91
|
-
|
|
92
|
-
|
|
91
|
+
docx: '.docx document create/edit/read. Visual verification (soffice→PDF→PNG), tracked changes, python-docx.',
|
|
92
|
+
xlsx: '.xlsx/.xlsm/.csv/.tsv file create/edit/analyze/format. Includes pandas data analysis.',
|
|
93
93
|
'webapp-testing': 'Playwright-based web app interaction/verification/debugging test skill.',
|
|
94
94
|
'mcp-builder': 'Design/implement MCP servers for external API integration.',
|
|
95
|
-
|
|
95
|
+
pptx: 'Presentation (.pptx) create/edit/analyze skill.',
|
|
96
96
|
'doc-coauthoring': 'Structured planning/spec/document co-authoring workflow skill.',
|
|
97
97
|
'web-artifacts-builder': 'React/Tailwind complex web artifact creation skill.',
|
|
98
98
|
'theme-factory': 'Apply reusable themes to document/slide/HTML outputs.',
|
|
@@ -115,9 +115,9 @@ DESC_EN = {
|
|
|
115
115
|
'hugging-face-model-trainer': 'TRL: SFT/DPO/GRPO model training.',
|
|
116
116
|
'hugging-face-evaluation': 'vLLM/lighteval model evaluation and benchmarks.',
|
|
117
117
|
'fal-image-edit': 'fal.ai AI image editing (style transfer, object removal).',
|
|
118
|
-
|
|
118
|
+
brainstorming: 'Pre-coding idea refinement → design document creation (obra/superpowers).',
|
|
119
119
|
'writing-plans': '2-5 minute task decomposition. File paths/code/verification included.',
|
|
120
|
-
|
|
120
|
+
tdd: 'RED-GREEN-REFACTOR TDD cycle enforcement.',
|
|
121
121
|
'requesting-code-review': 'Internal agent code review. Severity-based blocking.',
|
|
122
122
|
'receiving-code-review': 'Code review feedback reception and response patterns.',
|
|
123
123
|
'dispatching-parallel-agents': 'Parallel sub-agent dispatch patterns.',
|
|
@@ -134,75 +134,68 @@ DESC_EN = {
|
|
|
134
134
|
'changelog-generator': 'git commit → changelog/release notes generation.',
|
|
135
135
|
'video-downloader': 'yt-dlp wrapper. YouTube/media download.',
|
|
136
136
|
'email-draft-polish': 'Email draft tone adjustment/formatting.',
|
|
137
|
-
|
|
137
|
+
postgres: 'PostgreSQL read-only queries. Schema exploration.',
|
|
138
138
|
'deep-research': 'Multi-step research agent. Search → analyze → summarize.',
|
|
139
139
|
'context-compression': 'Context compression strategies. Long session optimization.',
|
|
140
140
|
'ios-simulator': 'iOS Simulator control. App build/run/test.',
|
|
141
141
|
'apple-hig-skills': 'Apple HIG 14 guides (foundations/platforms/components/patterns).',
|
|
142
|
-
|
|
142
|
+
whatsapp: 'WhatsApp message automation (automate-whatsapp).',
|
|
143
143
|
'aws-skills': 'AWS infrastructure automation (CDK/CloudFormation/Lambda).',
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
terraform: 'HashiCorp Terraform HCL/modules/providers IaC.',
|
|
145
|
+
kreuzberg: '62+ format text extraction (PDF/DOCX/PPTX/images etc.).',
|
|
146
|
+
dev: 'Common development guide. Modular dev, self-reference patterns, skills_ref exploration, changelog.',
|
|
147
147
|
'dev-frontend': 'Frontend role guide. Unique UI/UX implementation, component design, aesthetic standards.',
|
|
148
148
|
'dev-backend': 'Backend role guide. Express.js patterns, SQLite, error handling, security basics.',
|
|
149
149
|
'dev-data': 'Data role guide. ETL pipelines, CSV/JSON processing, SQL queries, analysis.',
|
|
150
150
|
'dev-testing': 'Debugging phase only. Playwright web app testing, recon-action pattern.',
|
|
151
|
+
};
|
|
152
|
+
function main() {
|
|
153
|
+
const raw = fs.readFileSync(REGISTRY, 'utf8');
|
|
154
|
+
const data = JSON.parse(raw);
|
|
155
|
+
const skills = data.skills;
|
|
156
|
+
let updated = 0;
|
|
157
|
+
for (const [skillId, skill] of Object.entries(skills)) {
|
|
158
|
+
const name = skill.name ?? skillId;
|
|
159
|
+
const desc = skill.description ?? '';
|
|
160
|
+
// Set ko fields
|
|
161
|
+
skill.name_ko = name;
|
|
162
|
+
skill.desc_ko = desc;
|
|
163
|
+
// Set en fields
|
|
164
|
+
const hasKoName = /[\uac00-\ud7af]/.test(name);
|
|
165
|
+
skill.name_en = hasKoName ? (NAME_EN[skillId] ?? name) : name;
|
|
166
|
+
skill.desc_en = DESC_EN[skillId] ?? desc;
|
|
167
|
+
updated++;
|
|
168
|
+
}
|
|
169
|
+
// Reorder keys: keep original order, insert i18n fields after description
|
|
170
|
+
const newSkills = {};
|
|
171
|
+
for (const [skillId, skill] of Object.entries(skills)) {
|
|
172
|
+
const ordered = {};
|
|
173
|
+
for (const [k, v] of Object.entries(skill)) {
|
|
174
|
+
ordered[k] = v;
|
|
175
|
+
if (k === 'name') {
|
|
176
|
+
ordered.name_ko = skill.name_ko;
|
|
177
|
+
ordered.name_en = skill.name_en;
|
|
178
|
+
}
|
|
179
|
+
else if (k === 'description') {
|
|
180
|
+
ordered.desc_ko = skill.desc_ko;
|
|
181
|
+
ordered.desc_en = skill.desc_en;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
newSkills[skillId] = ordered;
|
|
185
|
+
}
|
|
186
|
+
data.skills = newSkills;
|
|
187
|
+
fs.writeFileSync(REGISTRY, JSON.stringify(data, null, 4) + '\n', 'utf8');
|
|
188
|
+
console.log(`Updated ${updated} skills with i18n fields`);
|
|
189
|
+
// Verify
|
|
190
|
+
const missingEn = Object.entries(newSkills)
|
|
191
|
+
.filter(([, v]) => !v.desc_en)
|
|
192
|
+
.map(([k]) => k);
|
|
193
|
+
if (missingEn.length) {
|
|
194
|
+
console.log(`WARNING: ${missingEn.length} skills missing desc_en: ${missingEn}`);
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
console.log('All skills have desc_en ✓');
|
|
198
|
+
}
|
|
151
199
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
with open(REGISTRY) as f:
|
|
155
|
-
data = json.load(f)
|
|
156
|
-
|
|
157
|
-
skills = data['skills']
|
|
158
|
-
updated = 0
|
|
159
|
-
|
|
160
|
-
for skill_id, skill in skills.items():
|
|
161
|
-
name = skill.get('name', skill_id)
|
|
162
|
-
desc = skill.get('description', '')
|
|
163
|
-
|
|
164
|
-
# Set ko fields
|
|
165
|
-
skill['name_ko'] = name
|
|
166
|
-
skill['desc_ko'] = desc
|
|
167
|
-
|
|
168
|
-
# Set en fields
|
|
169
|
-
has_ko_name = any('\uac00' <= c <= '\ud7af' for c in name)
|
|
170
|
-
if has_ko_name:
|
|
171
|
-
skill['name_en'] = NAME_EN.get(skill_id, name)
|
|
172
|
-
else:
|
|
173
|
-
skill['name_en'] = name
|
|
174
|
-
|
|
175
|
-
skill['desc_en'] = DESC_EN.get(skill_id, desc)
|
|
176
|
-
updated += 1
|
|
177
|
-
|
|
178
|
-
# Reorder keys: keep original order, insert i18n fields after description
|
|
179
|
-
new_skills = {}
|
|
180
|
-
for skill_id, skill in skills.items():
|
|
181
|
-
ordered = {}
|
|
182
|
-
for k, v in skill.items():
|
|
183
|
-
ordered[k] = v
|
|
184
|
-
if k == 'name':
|
|
185
|
-
ordered['name_ko'] = skill['name_ko']
|
|
186
|
-
ordered['name_en'] = skill['name_en']
|
|
187
|
-
elif k == 'description':
|
|
188
|
-
ordered['desc_ko'] = skill['desc_ko']
|
|
189
|
-
ordered['desc_en'] = skill['desc_en']
|
|
190
|
-
# Remove duplicates from natural ordering
|
|
191
|
-
new_skills[skill_id] = ordered
|
|
192
|
-
|
|
193
|
-
data['skills'] = new_skills
|
|
194
|
-
|
|
195
|
-
with open(REGISTRY, 'w') as f:
|
|
196
|
-
json.dump(data, f, indent=4, ensure_ascii=False)
|
|
197
|
-
|
|
198
|
-
print(f'Updated {updated} skills with i18n fields')
|
|
199
|
-
|
|
200
|
-
# Verify
|
|
201
|
-
missing_en = [k for k, v in new_skills.items() if not v.get('desc_en')]
|
|
202
|
-
if missing_en:
|
|
203
|
-
print(f'WARNING: {len(missing_en)} skills missing desc_en: {missing_en}')
|
|
204
|
-
else:
|
|
205
|
-
print('All skills have desc_en ✓')
|
|
206
|
-
|
|
207
|
-
if __name__ == '__main__':
|
|
208
|
-
main()
|
|
200
|
+
main();
|
|
201
|
+
//# sourceMappingURL=i18n-registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"i18n-registry.js","sourceRoot":"","sources":["../../scripts/i18n-registry.ts"],"names":[],"mappings":"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"}
|