codemini-cli 0.4.1 → 0.4.2

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/OPERATIONS.md CHANGED
@@ -202,8 +202,10 @@ or revise/discard before execution:
202
202
  ```powershell
203
203
  codemini skill list
204
204
  codemini skill inspect superpowers-lite
205
- codemini skill enable brainstorm
206
- codemini skill disable brainstorm
205
+ codemini skill install .\my-skill
206
+ codemini skill install --scope=global .\my-skill
207
+ codemini skill enable my-skill
208
+ codemini skill disable my-skill
207
209
  codemini skill reindex
208
210
  ```
209
211
 
package/README.md CHANGED
@@ -16,7 +16,7 @@ CodeMini CLI is a terminal coding assistant built for teams that want a sharper,
16
16
 
17
17
  It is designed around a deliberate idea: most coding workflows do not need a huge default tool surface or unrestricted shell behavior. Instead, CodeMini starts with a compact core, loads advanced tools on demand, and keeps the agent grounded in structured code operations, session todos, lightweight project indexing, and shell-aware safety rules.
18
18
 
19
- **Contents** — [Why CodeMini CLI](#why-codemini-cli) · [Installation](#installation) · [Quick Start](#quick-start) · [Commands](#commands) · [Personalities (Souls)](#personalities-souls) · [Tool Model](#how-the-tool-model-works) · [Core Capabilities](#core-capabilities) · [Dream Loop (Built-in Memory Evolution)](#dream-loop-built-in-memory-evolution) · [Project Index](#project-index) · [Good Fit](#good-fit) · [Documentation](#documentation) · [Development](#development) · [License](#license)
19
+ **Contents** — [Why CodeMini CLI](#why-codemini-cli) · [Installation](#installation) · [Quick Start](#quick-start) · [Commands](#commands) · [Personalities (Souls)](#personalities-souls) · [Tool Model](#how-the-tool-model-works) · [Core Capabilities](#core-capabilities) · [Reflect Skills](#reflect-skills) · [Dream Loop (Built-in Memory Evolution)](#dream-loop-built-in-memory-evolution) · [Project Index](#project-index) · [Good Fit](#good-fit) · [Documentation](#documentation) · [Development](#development) · [License](#license)
20
20
 
21
21
  ### Why CodeMini CLI
22
22
 
@@ -67,6 +67,17 @@ CodeMini CLI can optionally use `fff-mcp` as a faster backend for `grep`, `glob`
67
67
  - This means `fff-mcp` is an enhancement, not a hard dependency.
68
68
  - `codemini doctor` now reports `FFF MCP availability` so you can verify whether it is active.
69
69
 
70
+ ### Optional: Playwright Web Rendering
71
+
72
+ `web_fetch` uses a lightweight `fetch` + HTML parser path by default, so Playwright is not installed as a default dependency.
73
+
74
+ For JavaScript-rendered pages, install Playwright separately to enable richer browser-rendered fallback:
75
+
76
+ ```bash
77
+ npm install -g playwright
78
+ playwright install chromium
79
+ ```
80
+
70
81
  ### Commands
71
82
 
72
83
  | Command | Description |
@@ -106,10 +117,14 @@ Skills are reusable workflow patterns that guide how the agent approaches differ
106
117
  Skills are installed and managed via `codemini skill`:
107
118
 
108
119
  ```bash
109
- codemini skill list # List all available skills
110
- codemini skill inspect <name> # Inspect a skill's details
120
+ codemini skill list # List builtin, project, and global skills
121
+ codemini skill install <path> # Install to .codemini/skills by default
122
+ codemini skill install --scope=global <path> # Install to the global skills directory
123
+ codemini skill inspect <name> # Inspect a skill's details
111
124
  ```
112
125
 
126
+ Bundled skills are built in, always enabled, and cannot be disabled or overwritten. Third-party skills live either in the project at `.codemini/skills/<name>/SKILL.md` or globally at `<base-config-dir>/skills/<name>/SKILL.md`, matching `/reflect`.
127
+
113
128
  ### How The Tool Model Works
114
129
 
115
130
  CodeMini CLI intentionally separates tools into two layers:
@@ -149,6 +164,30 @@ Typical flow:
149
164
  - Reply language control via `ui.reply_language`
150
165
  - Safe mode enabled by default
151
166
 
167
+ ### Reflect Skills
168
+
169
+ `/reflect` turns a successful workflow from the current session into a reviewed, reusable `SKILL.md` draft.
170
+
171
+ It is separate from the dream loop: reflect creates a skill draft, waits for review, and writes only after approval. It does not write inbox memories or run dream consolidation.
172
+
173
+ Common forms:
174
+
175
+ ```text
176
+ /reflect
177
+ /reflect <what to preserve>
178
+ /reflect --scope=global <what to preserve>
179
+ ```
180
+
181
+ - `/reflect` is exploratory. CodeMini reviews recent context and proposes a skill only when there is a reusable pattern worth saving.
182
+ - `/reflect <what to preserve>` is directed. Use it when you already know which successful chain should become a skill, such as `/reflect preserve the provider tool-call recovery workflow`.
183
+ - `/reflect --scope=global <request>` writes the approved draft to the global skills directory instead of the current project.
184
+ - The draft is previewed first. Use `/yes` to write it, `/edit <feedback>` to revise it, or `/no` to discard it.
185
+
186
+ Approved skills are written to the same locations used by third-party skill install:
187
+
188
+ - Project scope: `.codemini/skills/<skill-name>/SKILL.md`
189
+ - Global scope: `<base-config-dir>/skills/<skill-name>/SKILL.md`
190
+
152
191
  ### Dream Loop (Built-in Memory Evolution)
153
192
 
154
193
  Dream loop is built into the runtime as native tools and slash commands (not a skill-only workflow).
@@ -300,6 +339,17 @@ CodeMini CLI 可以可选地使用 `fff-mcp` 作为 `grep`、`glob` 和部分 `l
300
339
  - 这意味着 `fff-mcp` 是增强项,不是硬依赖。
301
340
  - 现在可以通过 `codemini doctor` 里的 `FFF MCP availability` 看到它是否可用。
302
341
 
342
+ ### 可选:Playwright 网页渲染
343
+
344
+ `web_fetch` 默认使用轻量的 `fetch` + HTML 解析路径,因此 Playwright 不再作为默认依赖安装。
345
+
346
+ 如果经常读取 JavaScript 渲染页面,可以单独安装 Playwright,让 `web_fetch` 在需要时回退到浏览器渲染:
347
+
348
+ ```bash
349
+ npm install -g playwright
350
+ playwright install chromium
351
+ ```
352
+
303
353
  ### 命令概览
304
354
 
305
355
  | 命令 | 说明 |
@@ -339,10 +389,14 @@ Skill 是可复用的工作流模式,指导 agent 如何处理不同类型的
339
389
  通过 `codemini skill` 管理技能:
340
390
 
341
391
  ```bash
342
- codemini skill list # 列出所有可用 skill
343
- codemini skill inspect <name> # 查看某个 skill 的详细信息
392
+ codemini skill list # 列出内置、项目级、全局 skill
393
+ codemini skill install <path> # 默认安装到 .codemini/skills
394
+ codemini skill install --scope=global <path> # 安装到全局 skills 目录
395
+ codemini skill inspect <name> # 查看某个 skill 的详细信息
344
396
  ```
345
397
 
398
+ 内置 skill 是运行时能力,默认启用,不能禁用或被同名第三方 skill 覆盖。第三方 skill 分为项目级 `.codemini/skills/<name>/SKILL.md` 和全局 `<base-config-dir>/skills/<name>/SKILL.md`,与 `/reflect` 的写入位置一致。
399
+
346
400
  ### 工具模型怎么设计
347
401
 
348
402
  CodeMini CLI 把工具分成两层:
@@ -382,6 +436,30 @@ CodeMini CLI 把工具分成两层:
382
436
  - 支持通过 `ui.reply_language` 控制回复语言
383
437
  - safe mode 默认开启
384
438
 
439
+ ### Reflect Skills(复盘沉淀 Skill)
440
+
441
+ `/reflect` 可以把当前会话中已经跑通的成功链路沉淀成一个可审阅、可复用的 `SKILL.md` 草稿。
442
+
443
+ 它和 dream loop 是分开的:reflect 只生成 skill 草稿,先让用户审阅,确认后才写文件;不会写入 inbox,也不会触发 dream consolidation。
444
+
445
+ 常用形式:
446
+
447
+ ```text
448
+ /reflect
449
+ /reflect <要沉淀的用户要求>
450
+ /reflect --scope=global <要沉淀的用户要求>
451
+ ```
452
+
453
+ - `/reflect` 是探索模式。CodeMini 会查看近期上下文,只有在确实有可复用模式时才提出 skill 草稿。
454
+ - `/reflect <用户要求>` 是定向模式。适合你已经知道要沉淀哪条成功链路,例如 `/reflect 把刚才 provider tool_call 恢复链路沉淀成 skill`。
455
+ - `/reflect --scope=global <用户要求>` 会把确认后的草稿写到全局 skill 目录,而不是当前项目。
456
+ - 草稿会先预览。用 `/yes` 写入,用 `/edit <反馈>` 修改,用 `/no` 放弃。
457
+
458
+ 确认后的 skill 写入位置和第三方 skill 安装保持一致:
459
+
460
+ - 项目级:`.codemini/skills/<skill-name>/SKILL.md`
461
+ - 全局级:`<base-config-dir>/skills/<skill-name>/SKILL.md`
462
+
385
463
  ### Dream Loop(内置记忆演化)
386
464
 
387
465
  Dream loop 是运行时内置能力,不依赖 skill 才能使用。
package/deployment.md CHANGED
@@ -13,13 +13,13 @@ npm pack
13
13
  Expected output:
14
14
 
15
15
  ```text
16
- codemini-cli-0.4.1.tgz
16
+ codemini-cli-0.4.2.tgz
17
17
  ```
18
18
 
19
19
  If you want to verify the package contents:
20
20
 
21
21
  ```bash
22
- tar -tf codemini-cli-0.4.1.tgz
22
+ tar -tf codemini-cli-0.4.2.tgz
23
23
  ```
24
24
 
25
25
  ## 2. Copy To The Target Machine
@@ -34,7 +34,7 @@ Copy the generated `.tgz` file to the Win10 machine by one of these methods:
34
34
  Recommended target path:
35
35
 
36
36
  ```powershell
37
- C:\temp\codemini-cli-0.4.1.tgz
37
+ C:\temp\codemini-cli-0.4.2.tgz
38
38
  ```
39
39
 
40
40
  ## 3. Environment Requirements
@@ -58,7 +58,7 @@ npm -v
58
58
  Global install:
59
59
 
60
60
  ```powershell
61
- npm install -g C:\temp\codemini-cli-0.4.1.tgz
61
+ npm install -g C:\temp\codemini-cli-0.4.2.tgz
62
62
  ```
63
63
 
64
64
  If global install is blocked by company policy, install in a working directory instead:
@@ -66,7 +66,7 @@ If global install is blocked by company policy, install in a working directory i
66
66
  ```powershell
67
67
  mkdir C:\temp\coder-test
68
68
  cd C:\temp\coder-test
69
- npm install C:\temp\codemini-cli-0.4.1.tgz
69
+ npm install C:\temp\codemini-cli-0.4.2.tgz
70
70
  ```
71
71
 
72
72
  ## 5. Confirm Installation
@@ -158,6 +158,7 @@ Typical contents:
158
158
  - `config.json`
159
159
  - `sessions\`
160
160
  - `skills\`
161
+ - project skills are stored per workspace under `.codemini\skills\`
161
162
  - `input-history.json`
162
163
 
163
164
  ## 9. Skills
@@ -168,13 +169,19 @@ List installed skills:
168
169
  codemini skill list
169
170
  ```
170
171
 
171
- Install a local skill:
172
+ Install a local skill into the current project:
172
173
 
173
174
  ```powershell
174
175
  codemini skill install C:\path\to\skill-folder
175
176
  ```
176
177
 
177
- Rebuild the local registry:
178
+ Install a local skill globally:
179
+
180
+ ```powershell
181
+ codemini skill install --scope=global C:\path\to\skill-folder
182
+ ```
183
+
184
+ Rebuild the global registry:
178
185
 
179
186
  ```powershell
180
187
  codemini skill reindex
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codemini-cli",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
5
5
  "keywords": [
6
6
  "cli",
@@ -51,7 +51,6 @@
51
51
  "cli-truncate": "^6.0.0",
52
52
  "duck-duck-scrape": "^2.2.7",
53
53
  "ink": "^7.0.0",
54
- "playwright": "^1.54.2",
55
54
  "react": "^19.2.5",
56
55
  "strip-ansi": "^7.2.0",
57
56
  "web-tree-sitter": "^0.26.8"
package/src/cli.js CHANGED
@@ -17,7 +17,7 @@ Usage:
17
17
  codemini run --pipeline <task> [--model <name>]
18
18
  codemini config set|get|list <key> [value]
19
19
  codemini doctor
20
- codemini skill list|install|enable|disable|inspect|reindex
20
+ codemini skill list|install|enable|disable|inspect|reindex [--scope=project|global]
21
21
  codemini --version
22
22
  codemini --help`);
23
23
  }
@@ -3,7 +3,9 @@ import path from 'node:path';
3
3
  import os from 'node:os';
4
4
  import { spawn } from 'node:child_process';
5
5
  import { copyRecursive } from '../core/fs-utils.js';
6
- import { getSkillsDir } from '../core/paths.js';
6
+ import { loadConfig, saveConfig } from '../core/config-store.js';
7
+ import { loadCommandsAndSkills } from '../core/command-loader.js';
8
+ import { getProjectSkillsDir, getSkillsDir } from '../core/paths.js';
7
9
  import {
8
10
  computeFileSha256,
9
11
  readSkillRegistry,
@@ -11,17 +13,85 @@ import {
11
13
  writeSkillRegistry
12
14
  } from '../core/skill-registry.js';
13
15
 
14
- async function listSkillEntries() {
15
- const registry = await readSkillRegistry();
16
- const byName = new Map((registry.skills || []).map((s) => [s.name, s]));
17
- await fs.mkdir(getSkillsDir(), { recursive: true });
18
- const entries = await fs.readdir(getSkillsDir(), { withFileTypes: true });
19
- const names = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort();
20
- return names.map((name) => byName.get(name) || { name, version: 'unknown', enabled: true });
16
+ function parseScopeArgs(args = [], { defaultScope = 'project', allowAll = false } = {}) {
17
+ let scope = defaultScope;
18
+ const rest = [];
19
+ for (let index = 0; index < args.length; index += 1) {
20
+ const arg = String(args[index] || '');
21
+ if (arg === '--global') {
22
+ scope = 'global';
23
+ continue;
24
+ }
25
+ if (arg === '--project') {
26
+ scope = 'project';
27
+ continue;
28
+ }
29
+ if (arg === '--scope') {
30
+ const next = String(args[index + 1] || '').toLowerCase();
31
+ if (['project', 'global', ...(allowAll ? ['all', 'builtin'] : [])].includes(next)) {
32
+ scope = next;
33
+ index += 1;
34
+ continue;
35
+ }
36
+ }
37
+ if (arg.startsWith('--scope=')) {
38
+ const value = arg.slice('--scope='.length).toLowerCase();
39
+ if (['project', 'global', ...(allowAll ? ['all', 'builtin'] : [])].includes(value)) {
40
+ scope = value;
41
+ continue;
42
+ }
43
+ }
44
+ rest.push(arg);
45
+ }
46
+ return { scope, rest };
47
+ }
48
+
49
+ function baseDirForScope(scope, cwd = process.cwd()) {
50
+ return scope === 'global' ? getSkillsDir() : getProjectSkillsDir(cwd);
51
+ }
52
+
53
+ function scopeFromSource(source = '') {
54
+ if (source === 'bundled-skill') return 'builtin';
55
+ if (source === 'project-skill') return 'project';
56
+ if (source === 'global-skill' || source === 'registry-skill') return 'global';
57
+ return source || 'unknown';
58
+ }
59
+
60
+ async function setSkillEnabledConfig(name, enabled) {
61
+ const config = await loadConfig();
62
+ config.skills = config.skills || {};
63
+ config.skills.enabled = config.skills.enabled || {};
64
+ config.skills.enabled[name] = enabled;
65
+ await saveConfig(config);
21
66
  }
22
67
 
23
- async function readSkillMeta(name) {
24
- const dir = path.join(getSkillsDir(), name);
68
+ async function listSkillEntries({ scope = 'all', cwd = process.cwd() } = {}) {
69
+ const commands = await loadCommandsAndSkills(cwd);
70
+ const config = await loadConfig();
71
+ const entries = [];
72
+ for (const command of commands.values()) {
73
+ if (command.metadata?.type !== 'skill') continue;
74
+ const itemScope = scopeFromSource(command.source);
75
+ if (scope !== 'all' && itemScope !== scope) continue;
76
+ entries.push({
77
+ name: command.name,
78
+ version: command.metadata?.version || '0.0.0',
79
+ description: command.metadata?.description || '',
80
+ scope: itemScope,
81
+ path: command.path,
82
+ enabled: itemScope === 'builtin' ? true : config.skills?.enabled?.[command.name] !== false
83
+ });
84
+ }
85
+ return entries.sort((a, b) => `${a.scope}:${a.name}`.localeCompare(`${b.scope}:${b.name}`));
86
+ }
87
+
88
+ async function readSkillMeta(name, { scope = 'all', cwd = process.cwd() } = {}) {
89
+ const entries = await listSkillEntries({ scope, cwd });
90
+ const found = entries.find((item) => item.name === name);
91
+ if (!found) {
92
+ return { exists: false, path: '', preview: '', manifest: null };
93
+ }
94
+ const dir = path.dirname(found.path);
25
95
  const manifestPath = path.join(dir, 'manifest.json');
26
96
  let manifest = null;
27
97
  try {
@@ -30,13 +100,13 @@ async function readSkillMeta(name) {
30
100
  manifest = null;
31
101
  }
32
102
  const entryFile = manifest?.entry || 'SKILL.md';
33
- const skillPath = path.join(dir, entryFile);
103
+ const skillPath = found.path || path.join(dir, entryFile);
34
104
  try {
35
105
  const content = await fs.readFile(skillPath, 'utf8');
36
106
  const firstLines = content.split('\n').slice(0, 20).join('\n');
37
- return { exists: true, path: skillPath, preview: firstLines, manifest };
107
+ return { exists: true, path: skillPath, preview: firstLines, manifest, scope: found.scope };
38
108
  } catch {
39
- return { exists: false, path: skillPath, preview: '', manifest };
109
+ return { exists: false, path: skillPath, preview: '', manifest, scope: found.scope };
40
110
  }
41
111
  }
42
112
 
@@ -104,11 +174,15 @@ async function resolveSkillSourceDir(sourcePath) {
104
174
  throw new Error('skill install supports <skill-dir>, <SKILL.md>, or <skill.tgz>');
105
175
  }
106
176
 
107
- async function installSkill(sourcePath) {
177
+ async function installSkill(sourcePath, { scope = 'project', cwd = process.cwd() } = {}) {
108
178
  const resolved = await resolveSkillSourceDir(sourcePath);
109
179
  const manifest = await readManifestSafe(resolved.dir);
110
180
  const folderName = manifest?.name || path.basename(resolved.dir);
111
- const targetDir = path.join(getSkillsDir(), folderName);
181
+ const bundled = (await listSkillEntries({ scope: 'builtin', cwd })).find((item) => item.name === folderName);
182
+ if (bundled) {
183
+ throw new Error(`cannot install over builtin skill: ${folderName}`);
184
+ }
185
+ const targetDir = path.join(baseDirForScope(scope, cwd), folderName);
112
186
  await fs.rm(targetDir, { recursive: true, force: true });
113
187
  await copyRecursive(resolved.dir, targetDir);
114
188
 
@@ -117,16 +191,19 @@ async function installSkill(sourcePath) {
117
191
  await fs.access(entryPath);
118
192
 
119
193
  const hash = await computeFileSha256(entryPath);
120
- await upsertSkillRegistryEntry(undefined, {
121
- name: folderName,
122
- version: manifest?.version || '0.0.0',
123
- description: manifest?.description || '',
124
- enabled: true,
125
- source: sourcePath,
126
- entryFile,
127
- sha256: hash,
128
- installedAt: new Date().toISOString()
129
- });
194
+ if (scope === 'global') {
195
+ await upsertSkillRegistryEntry(undefined, {
196
+ name: folderName,
197
+ version: manifest?.version || '0.0.0',
198
+ description: manifest?.description || '',
199
+ enabled: true,
200
+ source: sourcePath,
201
+ entryFile,
202
+ sha256: hash,
203
+ installedAt: new Date().toISOString()
204
+ });
205
+ }
206
+ await setSkillEnabledConfig(folderName, true);
130
207
 
131
208
  if (resolved.cleanupDir) {
132
209
  await fs.rm(resolved.cleanupDir, { recursive: true, force: true });
@@ -135,19 +212,28 @@ async function installSkill(sourcePath) {
135
212
  return folderName;
136
213
  }
137
214
 
138
- async function setEnabled(name, enabled) {
215
+ async function setEnabled(name, enabled, { cwd = process.cwd() } = {}) {
216
+ const entries = await listSkillEntries({ scope: 'all', cwd });
217
+ const found = entries.find((item) => item.name === name);
218
+ if (!found) {
219
+ throw new Error(`skill not found: ${name}`);
220
+ }
221
+ if (found.scope === 'builtin') {
222
+ throw new Error(`builtin skill cannot be ${enabled ? 'enabled' : 'disabled'}: ${name}`);
223
+ }
224
+ await setSkillEnabledConfig(name, enabled);
139
225
  const registry = await readSkillRegistry();
140
226
  const idx = registry.skills.findIndex((s) => s.name === name);
141
- if (idx === -1) {
142
- throw new Error(`skill not found: ${name}`);
227
+ if (idx !== -1) {
228
+ registry.skills[idx].enabled = enabled;
229
+ await writeSkillRegistry(undefined, registry);
143
230
  }
144
- registry.skills[idx].enabled = enabled;
145
- await writeSkillRegistry(undefined, registry);
146
231
  }
147
232
 
148
- async function reindexSkills() {
149
- await fs.mkdir(getSkillsDir(), { recursive: true });
150
- const entries = await fs.readdir(getSkillsDir(), { withFileTypes: true });
233
+ async function reindexSkills({ scope = 'global', cwd = process.cwd() } = {}) {
234
+ const baseDir = baseDirForScope(scope, cwd);
235
+ await fs.mkdir(baseDir, { recursive: true });
236
+ const entries = await fs.readdir(baseDir, { withFileTypes: true });
151
237
  const registry = await readSkillRegistry();
152
238
  const byName = new Map((registry.skills || []).map((s) => [s.name, s]));
153
239
  const rebuilt = [];
@@ -155,7 +241,7 @@ async function reindexSkills() {
155
241
  for (const entry of entries) {
156
242
  if (!entry.isDirectory()) continue;
157
243
  const name = entry.name;
158
- const dir = path.join(getSkillsDir(), name);
244
+ const dir = path.join(baseDir, name);
159
245
  const manifest = await readManifestSafe(dir);
160
246
  const entryFile = manifest?.entry || 'SKILL.md';
161
247
  const entryPath = path.join(dir, entryFile);
@@ -178,22 +264,24 @@ async function reindexSkills() {
178
264
  });
179
265
  }
180
266
 
181
- await writeSkillRegistry(undefined, {
182
- version: 1,
183
- skills: rebuilt
184
- });
267
+ if (scope === 'global') {
268
+ await writeSkillRegistry(undefined, {
269
+ version: 1,
270
+ skills: rebuilt
271
+ });
272
+ }
185
273
 
186
274
  return rebuilt.length;
187
275
  }
188
276
 
189
277
  function usage() {
190
278
  console.log(`Usage:
191
- codemini skill list
192
- codemini skill install <path>
279
+ codemini skill list [--scope=all|project|global|builtin]
280
+ codemini skill install [--scope=project|global] <path>
193
281
  codemini skill enable <name>
194
282
  codemini skill disable <name>
195
- codemini skill inspect <name>
196
- codemini skill reindex`);
283
+ codemini skill inspect [--scope=all|project|global|builtin] <name>
284
+ codemini skill reindex [--scope=project|global]`);
197
285
  }
198
286
 
199
287
  export async function handleSkill(args) {
@@ -204,26 +292,27 @@ export async function handleSkill(args) {
204
292
  }
205
293
 
206
294
  if (sub === 'list') {
207
- const entries = await listSkillEntries();
295
+ const { scope } = parseScopeArgs(rest, { defaultScope: 'all', allowAll: true });
296
+ const entries = await listSkillEntries({ scope });
208
297
  if (entries.length === 0) {
209
298
  console.log('No installed skills');
210
299
  return;
211
300
  }
212
301
  for (const item of entries) {
213
- const state = item.enabled !== false ? 'enabled' : 'disabled';
214
- console.log(`${item.name}@${item.version || '0.0.0'} (${state})`);
302
+ const state = item.scope === 'builtin' ? 'builtin/default' : (item.enabled !== false ? 'enabled' : 'disabled');
303
+ console.log(`${item.name}@${item.version || '0.0.0'} [${item.scope}] (${state})`);
215
304
  }
216
305
  return;
217
306
  }
218
307
 
219
308
  if (sub === 'install') {
220
- const sourcePath = rest[0];
309
+ const { scope, rest: positional } = parseScopeArgs(rest, { defaultScope: 'project' });
310
+ const sourcePath = positional[0];
221
311
  if (!sourcePath) {
222
312
  throw new Error('skill install requires <path>');
223
313
  }
224
- const installedName = await installSkill(sourcePath);
225
- await setEnabled(installedName, true);
226
- console.log(`Installed skill: ${installedName}`);
314
+ const installedName = await installSkill(sourcePath, { scope });
315
+ console.log(`Installed skill: ${installedName} (${scope})`);
227
316
  return;
228
317
  }
229
318
 
@@ -238,25 +327,28 @@ export async function handleSkill(args) {
238
327
  }
239
328
 
240
329
  if (sub === 'inspect') {
241
- const name = rest[0];
330
+ const { scope, rest: positional } = parseScopeArgs(rest, { defaultScope: 'all', allowAll: true });
331
+ const name = positional[0];
242
332
  if (!name) {
243
333
  throw new Error('skill inspect requires <name>');
244
334
  }
245
- const meta = await readSkillMeta(name);
335
+ const meta = await readSkillMeta(name, { scope });
246
336
  if (!meta.exists) {
247
337
  throw new Error(`skill not found: ${name}`);
248
338
  }
249
339
  if (meta.manifest) {
250
340
  console.log(`Manifest: ${JSON.stringify(meta.manifest, null, 2)}\n`);
251
341
  }
342
+ console.log(`Scope: ${meta.scope}\n`);
252
343
  console.log(`Path: ${meta.path}\n`);
253
344
  console.log(meta.preview);
254
345
  return;
255
346
  }
256
347
 
257
348
  if (sub === 'reindex') {
258
- const count = await reindexSkills();
259
- console.log(`Reindexed skills: ${count}`);
349
+ const { scope } = parseScopeArgs(rest, { defaultScope: 'global' });
350
+ const count = await reindexSkills({ scope });
351
+ console.log(`Reindexed skills: ${count} (${scope})`);
260
352
  return;
261
353
  }
262
354