claudenv 1.3.0 → 1.3.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/README.md +80 -2
- package/bin/cli.js +296 -0
- package/package.json +1 -1
- package/scaffold/.claude/skills/dynamic-workflows/SKILL.md +105 -0
- package/scaffold/global/.claude/commands/add-source.md +31 -0
- package/scaffold/global/.claude/commands/claudenv.md +3 -1
- package/scaffold/global/.claude/commands/harness.md +29 -0
- package/scaffold/global/.claude/skills/claudenv/scaffold/.claude/skills/dynamic-workflows/SKILL.md +105 -0
- package/scaffold/global/.claude/skills/harness/SKILL.md +148 -0
- package/scaffold/global/.claude/skills/source-connector/SKILL.md +128 -0
- package/src/bundled-catalog.js +165 -0
- package/src/capabilities.js +164 -0
- package/src/doctor.js +57 -0
- package/src/installer.js +4 -0
- package/src/kimi.js +43 -0
- package/src/loop.js +27 -1
- package/src/memory-context.js +37 -2
- package/src/memory-paths.js +61 -0
- package/src/skills-registry.js +447 -0
- package/src/sources.js +78 -0
- package/src/workspaces.js +129 -0
- package/templates/connector-mssql.ejs +62 -0
- package/templates/connector-rest.ejs +59 -0
package/README.md
CHANGED
|
@@ -2,9 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/claudenv)
|
|
4
4
|
|
|
5
|
-
Set up [Claude Code](https://docs.anthropic.com/en/docs/claude-code) in any project with one command. claudenv analyzes your codebase and generates everything Claude needs to work effectively — documentation, rules, hooks, MCP servers, slash commands, **
|
|
5
|
+
Set up [Claude Code](https://docs.anthropic.com/en/docs/claude-code) in any project with one command. claudenv analyzes your codebase and generates everything Claude needs to work effectively — documentation, rules, hooks, MCP servers, slash commands, **cross-session memory, and a self-extending harness that equips Claude with new skills on demand (1.3.2).**
|
|
6
6
|
|
|
7
|
-
> **New in 1.3.
|
|
7
|
+
> **New in 1.3.2 — self-extending harness.** Claude can now introspect what claudenv gives it (`claudenv capabilities`), find what's missing for the task, and equip it from the [awesome-claude-skills](https://github.com/ComposioHQ/awesome-claude-skills) registry (`claudenv skills search|add`) — plus first-class **kimi-webbridge** browser automation. New `/harness` command + skill drive it autonomously. See [CHANGELOG.md](./CHANGELOG.md).
|
|
8
|
+
>
|
|
9
|
+
> **1.3.0** — split memory layout, `vibe-decisions` skill, CLI: `memory`, `decisions`, `canon`, `doctor`. Companion Python package `claudenv-memory` on PyPI (alpha).
|
|
8
10
|
|
|
9
11
|
## Quick Start
|
|
10
12
|
|
|
@@ -214,6 +216,72 @@ claudenv doctor # OK/WARN/FAIL check
|
|
|
214
216
|
|
|
215
217
|
After each iteration, `claudenv loop` regenerates `INDEX.md` so the next iteration's briefing reflects what was just decided.
|
|
216
218
|
|
|
219
|
+
## Self-extending harness (1.3.2)
|
|
220
|
+
|
|
221
|
+
claudenv is Claude's *harness* — a CLI plus skills, connectors, memory, and a
|
|
222
|
+
browser bridge. The `harness` skill makes Claude **self-aware** of it and lets it
|
|
223
|
+
**extend itself**: understand what it already has, find what's missing for the
|
|
224
|
+
task, and equip it — autonomously.
|
|
225
|
+
|
|
226
|
+
```bash
|
|
227
|
+
claudenv capabilities # self-introspection: skills, memory, workspace,
|
|
228
|
+
# connectors, kimi-webbridge, MCP, registry
|
|
229
|
+
claudenv skills search "pdf" # find a skill (curated + awesome-claude-skills)
|
|
230
|
+
claudenv skills add docx # install it into ~/.claude/skills/
|
|
231
|
+
claudenv skills add kimi-webbridge # bootstrap browser automation
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
In Claude Code, `/harness` (or just describe a task that needs a capability you
|
|
235
|
+
lack) runs the whole flow: introspect → gap-analysis → discover → equip →
|
|
236
|
+
configure connectors/MCP/memory → bootstrap the browser.
|
|
237
|
+
|
|
238
|
+
### Skills registry
|
|
239
|
+
|
|
240
|
+
`claudenv skills` discovers skills from
|
|
241
|
+
[awesome-claude-skills](https://github.com/ComposioHQ/awesome-claude-skills). The
|
|
242
|
+
registry is a heterogeneous markdown README, so each entry is resolved by **install
|
|
243
|
+
class**:
|
|
244
|
+
|
|
245
|
+
| Class | Example | Behavior |
|
|
246
|
+
|---|---|---|
|
|
247
|
+
| `repo-path` | `github.com/anthropics/skills/tree/main/skills/docx` | fetch raw `SKILL.md` |
|
|
248
|
+
| `in-repo` | a `./folder/` link in the README | fetch from the awesome repo |
|
|
249
|
+
| `repo-root` | `github.com/user/skill` | best-effort probe, else guide |
|
|
250
|
+
| `bootstrap` | kimi-webbridge | run an install command (e.g. `install.sh`) |
|
|
251
|
+
| `guide` | a vendor dashboard / Composio connector | open the link to set up |
|
|
252
|
+
|
|
253
|
+
A curated **bundled catalog** ships with claudenv, so `search`/`add` work
|
|
254
|
+
**offline**; `claudenv skills refresh` parses and caches the live registry.
|
|
255
|
+
|
|
256
|
+
### Trust & safety
|
|
257
|
+
|
|
258
|
+
A fetched `SKILL.md` is auto-loaded, model-facing instruction text — a
|
|
259
|
+
prompt-injection surface. So:
|
|
260
|
+
|
|
261
|
+
- **Curated (★)** entries have a vetted source URL + install class (the bytes are
|
|
262
|
+
fetched live, not content-pinned) → the only entries allowed to auto-equip,
|
|
263
|
+
including inside `claudenv loop`.
|
|
264
|
+
- **Live** entries (parsed from the README or a raw URL) are gated in code:
|
|
265
|
+
`skills add` writes nothing without `--yes`. `claudenv loop` never passes it.
|
|
266
|
+
|
|
267
|
+
`skills add` only ever writes under `~/.claude/skills/<slug>/`, never overwrites
|
|
268
|
+
without `--force`, validates the body, and **never executes** fetched content.
|
|
269
|
+
Bootstrap installers are printed first and run only with `--yes`.
|
|
270
|
+
|
|
271
|
+
### Browser automation (kimi-webbridge)
|
|
272
|
+
|
|
273
|
+
`claudenv skills add kimi-webbridge` makes browser automation work even if it
|
|
274
|
+
isn't installed yet — it detects an existing daemon and starts it, or surfaces the
|
|
275
|
+
official bootstrap. `claudenv capabilities` and `claudenv doctor` report its
|
|
276
|
+
health. Once up, Claude drives your real browser (with your login sessions) via
|
|
277
|
+
the `kimi-webbridge` skill.
|
|
278
|
+
|
|
279
|
+
## Dynamic workflows
|
|
280
|
+
|
|
281
|
+
`/claudenv` installs a `dynamic-workflows` skill that teaches Claude when to reach for the built-in **Workflow** tool — deterministic multi-agent orchestration (fan-out, pipelines, adversarial-verify, multi-file migrations) — and when to stay single-threaded. It triggers on wide, decomposable work (reviewing/migrating across many files, research over many sources, generate-N-then-judge) and stays out of the way for single-file or sequential edits.
|
|
282
|
+
|
|
283
|
+
`claudenv loop` drives the same capability: alongside the no-pause loop-mode fragment, it injects a guarded **Parallel decomposition** directive into the execution prompt that tells the loop to run a dynamic workflow when the picked plan item splits into 3+ independent sub-tasks (and to stay single-threaded otherwise). Fan-out is hard-capped (multi-agent work costs ~15× the tokens and runs under the per-iteration budget); if the Workflow runtime is unavailable it degrades to parallel `Agent` calls. The directive lives in the user prompt because the Workflow tool's opt-in keys on the user message — a system-prompt hint alone won't trigger it.
|
|
284
|
+
|
|
217
285
|
### Python companion: `claudenv-memory`
|
|
218
286
|
|
|
219
287
|
Building your own agent on Claude Agent SDK and want the same memory layout?
|
|
@@ -311,6 +379,16 @@ claudenv memory init|index|show|edit Manage ~/.claudenv/memories/ (1.3.0+)
|
|
|
311
379
|
claudenv decisions list|show|search Logged vibe-decisions (1.3.0+)
|
|
312
380
|
claudenv canon add|list|search|prune Personal canon (1.3.0+)
|
|
313
381
|
claudenv doctor Health check (1.3.0+)
|
|
382
|
+
|
|
383
|
+
claudenv workspace add|list|use|show Isolated per-company memory spaces (1.3.1+)
|
|
384
|
+
claudenv source list|show Data-source connectors of active workspace (1.3.1+)
|
|
385
|
+
|
|
386
|
+
claudenv capabilities [--json] [-d <dir>] Self-introspection map (1.3.2+)
|
|
387
|
+
claudenv skills search <query> Find skills (curated + awesome-claude-skills) (1.3.2+)
|
|
388
|
+
claudenv skills list Skills installed under ~/.claude/skills/ (1.3.2+)
|
|
389
|
+
claudenv skills info <name> Trust level, install class, source (1.3.2+)
|
|
390
|
+
claudenv skills add <name> [--force] Install a skill (--yes to run bootstraps) (1.3.2+)
|
|
391
|
+
claudenv skills refresh Refetch the live registry into cache (1.3.2+)
|
|
314
392
|
```
|
|
315
393
|
|
|
316
394
|
## Run Without Installing
|
package/bin/cli.js
CHANGED
|
@@ -469,6 +469,302 @@ canonCmd
|
|
|
469
469
|
}
|
|
470
470
|
});
|
|
471
471
|
|
|
472
|
+
// =============================================
|
|
473
|
+
// Workspaces (isolated per-company/context memory)
|
|
474
|
+
// =============================================
|
|
475
|
+
const wsCmd = program.command('workspace').description('Isolated memory spaces (~/.claudenv/workspaces/)');
|
|
476
|
+
|
|
477
|
+
wsCmd
|
|
478
|
+
.command('add')
|
|
479
|
+
.argument('<id>', 'Workspace id (slug: a-z0-9-_)')
|
|
480
|
+
.option('--name <name>', 'Human-readable name')
|
|
481
|
+
.option('--desc <description>', 'Description')
|
|
482
|
+
.option('--path <path...>', 'Project path(s) bound to this workspace')
|
|
483
|
+
.action(async (id, opts) => {
|
|
484
|
+
const { createWorkspace } = await import('../src/workspaces.js');
|
|
485
|
+
try {
|
|
486
|
+
await createWorkspace(id, { name: opts.name, description: opts.desc, paths: opts.path });
|
|
487
|
+
console.log(` + workspace "${id}" created`);
|
|
488
|
+
} catch (err) {
|
|
489
|
+
console.error(err.message);
|
|
490
|
+
process.exit(2);
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
wsCmd
|
|
495
|
+
.command('list')
|
|
496
|
+
.action(async () => {
|
|
497
|
+
const { listWorkspaces } = await import('../src/workspaces.js');
|
|
498
|
+
const list = await listWorkspaces();
|
|
499
|
+
if (list.length === 0) {
|
|
500
|
+
console.log(' No workspaces yet — create one with `claudenv workspace add <id>`');
|
|
501
|
+
return;
|
|
502
|
+
}
|
|
503
|
+
for (const w of list) {
|
|
504
|
+
console.log(` ${w.active ? '*' : ' '} ${w.id}${w.name && w.name !== w.id ? ` — ${w.name}` : ''}`);
|
|
505
|
+
}
|
|
506
|
+
});
|
|
507
|
+
|
|
508
|
+
wsCmd
|
|
509
|
+
.command('use')
|
|
510
|
+
.argument('<id>', 'Workspace id to activate')
|
|
511
|
+
.action(async (id) => {
|
|
512
|
+
const { useWorkspace } = await import('../src/workspaces.js');
|
|
513
|
+
try {
|
|
514
|
+
await useWorkspace(id);
|
|
515
|
+
console.log(` active workspace → ${id}`);
|
|
516
|
+
} catch (err) {
|
|
517
|
+
console.error(err.message);
|
|
518
|
+
process.exit(2);
|
|
519
|
+
}
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
wsCmd
|
|
523
|
+
.command('show')
|
|
524
|
+
.action(async () => {
|
|
525
|
+
const { showActive } = await import('../src/workspaces.js');
|
|
526
|
+
const w = await showActive();
|
|
527
|
+
if (!w) {
|
|
528
|
+
console.log(' No active workspace (set CLAUDENV_WORKSPACE or run `claudenv workspace use <id>`)');
|
|
529
|
+
return;
|
|
530
|
+
}
|
|
531
|
+
console.log(` active: ${w.id} (${w.source})`);
|
|
532
|
+
if (w.name && w.name !== w.id) console.log(` name: ${w.name}`);
|
|
533
|
+
if (w.description) console.log(` desc: ${w.description}`);
|
|
534
|
+
if (w.paths && w.paths.length) console.log(` paths: ${w.paths.join(', ')}`);
|
|
535
|
+
});
|
|
536
|
+
|
|
537
|
+
// =============================================
|
|
538
|
+
// Sources (connectors in the active workspace)
|
|
539
|
+
// =============================================
|
|
540
|
+
const srcCmd = program.command('source').description('Data-source connectors in the active workspace');
|
|
541
|
+
|
|
542
|
+
srcCmd
|
|
543
|
+
.command('list')
|
|
544
|
+
.action(async () => {
|
|
545
|
+
const { listConnectors } = await import('../src/sources.js');
|
|
546
|
+
const { workspace, connectors } = await listConnectors();
|
|
547
|
+
if (!workspace) {
|
|
548
|
+
console.log(' No active workspace — set one first (`claudenv workspace use <id>`)');
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
if (connectors.length === 0) {
|
|
552
|
+
console.log(` [${workspace}] no connectors yet — add one via /add-source in Claude Code`);
|
|
553
|
+
return;
|
|
554
|
+
}
|
|
555
|
+
console.log(` [${workspace}]`);
|
|
556
|
+
for (const c of connectors) {
|
|
557
|
+
console.log(` ${c.name} (${c.type}, ${c.status})${c.host ? ` ${c.host}` : ''}`);
|
|
558
|
+
}
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
srcCmd
|
|
562
|
+
.command('show')
|
|
563
|
+
.argument('<name>', 'Connector name')
|
|
564
|
+
.action(async (name) => {
|
|
565
|
+
const { showConnector } = await import('../src/sources.js');
|
|
566
|
+
const text = await showConnector(name);
|
|
567
|
+
if (!text) {
|
|
568
|
+
console.error(` Connector "${name}" not found in active workspace`);
|
|
569
|
+
process.exit(2);
|
|
570
|
+
}
|
|
571
|
+
console.log(text);
|
|
572
|
+
});
|
|
573
|
+
|
|
574
|
+
// =============================================
|
|
575
|
+
// Skills (discover & install from awesome-claude-skills + curated catalog)
|
|
576
|
+
// =============================================
|
|
577
|
+
const skillsCmd = program
|
|
578
|
+
.command('skills')
|
|
579
|
+
.description('Discover & install Claude skills (awesome-claude-skills + curated catalog)');
|
|
580
|
+
|
|
581
|
+
skillsCmd
|
|
582
|
+
.command('search')
|
|
583
|
+
.description('Find skills matching a need (curated catalog + cached live registry)')
|
|
584
|
+
.argument('[query...]', 'What capability you need (free text)')
|
|
585
|
+
.option('--refresh', 'Refetch the live registry before searching')
|
|
586
|
+
.option('--limit <n>', 'Max results', '15')
|
|
587
|
+
.action(async (queryParts, opts) => {
|
|
588
|
+
const { loadCatalog, searchCatalog } = await import('../src/skills-registry.js');
|
|
589
|
+
const query = (queryParts || []).join(' ');
|
|
590
|
+
const catalog = await loadCatalog({ refresh: opts.refresh });
|
|
591
|
+
const hits = searchCatalog(catalog, query).slice(0, parseInt(opts.limit, 10) || 15);
|
|
592
|
+
if (hits.length === 0) {
|
|
593
|
+
console.log(' No matching skills. Try `claudenv skills search --refresh <query>`.');
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
596
|
+
console.log();
|
|
597
|
+
for (const e of hits) {
|
|
598
|
+
const tag = e.curated ? '★' : ' ';
|
|
599
|
+
console.log(` ${tag} ${e.slug} [${e.install}]${e.category ? ` ${e.category}` : ''}`);
|
|
600
|
+
if (e.description) console.log(` ${e.description}`);
|
|
601
|
+
}
|
|
602
|
+
console.log(`\n ★ = curated (vetted, safe to auto-install). Install: claudenv skills add <slug>`);
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
skillsCmd
|
|
606
|
+
.command('list')
|
|
607
|
+
.description('List skills installed under ~/.claude/skills/')
|
|
608
|
+
.action(async () => {
|
|
609
|
+
const { listInstalledSkills } = await import('../src/skills-registry.js');
|
|
610
|
+
const installed = await listInstalledSkills();
|
|
611
|
+
if (installed.length === 0) {
|
|
612
|
+
console.log(' No skills installed yet — `claudenv skills search <task>`');
|
|
613
|
+
return;
|
|
614
|
+
}
|
|
615
|
+
console.log();
|
|
616
|
+
for (const s of installed) {
|
|
617
|
+
console.log(` ${s.slug}${s.hasSkillMd ? '' : ' (no SKILL.md)'}`);
|
|
618
|
+
if (s.description) console.log(` ${s.description.split('\n')[0].slice(0, 100)}`);
|
|
619
|
+
}
|
|
620
|
+
});
|
|
621
|
+
|
|
622
|
+
skillsCmd
|
|
623
|
+
.command('info')
|
|
624
|
+
.description('Show trust level, install class, and source for a skill')
|
|
625
|
+
.argument('<name>', 'Skill slug or name')
|
|
626
|
+
.option('--refresh', 'Refetch the live registry first')
|
|
627
|
+
.action(async (name, opts) => {
|
|
628
|
+
const { loadCatalog, findEntry, resolveSkillSource } = await import('../src/skills-registry.js');
|
|
629
|
+
const catalog = await loadCatalog({ refresh: opts.refresh });
|
|
630
|
+
const e = findEntry(catalog, name);
|
|
631
|
+
if (!e) {
|
|
632
|
+
console.error(` No catalog entry for "${name}". Try \`claudenv skills search ${name}\`.`);
|
|
633
|
+
process.exit(1);
|
|
634
|
+
}
|
|
635
|
+
const src = resolveSkillSource(e);
|
|
636
|
+
console.log(`\n ${e.name} (${e.slug})`);
|
|
637
|
+
if (e.category) console.log(` category: ${e.category}`);
|
|
638
|
+
console.log(` trust: ${e.curated ? 'curated (vetted)' : 'live (confirm before installing)'}`);
|
|
639
|
+
console.log(` install: ${e.install} → ${src.kind}`);
|
|
640
|
+
if (src.kind === 'bootstrap') console.log(` command: ${src.command}`);
|
|
641
|
+
console.log(` url: ${e.url}`);
|
|
642
|
+
if (e.description) console.log(`\n ${e.description}`);
|
|
643
|
+
console.log();
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
skillsCmd
|
|
647
|
+
.command('add')
|
|
648
|
+
.description('Install a skill into ~/.claude/skills/ (curated = safe; live = confirm)')
|
|
649
|
+
.argument('<name>', 'Skill slug or name to install')
|
|
650
|
+
.option('-f, --force', 'Overwrite an already-installed skill')
|
|
651
|
+
.option('--refresh', 'Refetch the live registry first')
|
|
652
|
+
.option('-y, --yes', 'Confirm a live (non-curated) install and run bootstrap installers')
|
|
653
|
+
.action(async (name, opts) => {
|
|
654
|
+
const { loadCatalog, findEntry, makeEntryFromUrl, classifyUrl, installSkill } = await import('../src/skills-registry.js');
|
|
655
|
+
const catalog = await loadCatalog({ refresh: opts.refresh });
|
|
656
|
+
let entry = findEntry(catalog, name);
|
|
657
|
+
// Allow `skills add <github-url-or-relative-path>` (always treated as live).
|
|
658
|
+
if (!entry && (/^https?:\/\//i.test(name) || /^\.?\//.test(name)) && classifyUrl(name) !== 'guide') {
|
|
659
|
+
entry = makeEntryFromUrl(name);
|
|
660
|
+
console.log(` resolving URL as a live skill (${entry.install}): ${name}`);
|
|
661
|
+
}
|
|
662
|
+
if (!entry) {
|
|
663
|
+
console.error(` No catalog entry for "${name}". Try \`claudenv skills search ${name}\`.`);
|
|
664
|
+
process.exit(1);
|
|
665
|
+
}
|
|
666
|
+
if (entry.curated !== true) {
|
|
667
|
+
console.log(` ⚠ "${entry.slug}" is a LIVE (non-curated) entry. A SKILL.md is auto-loaded,`);
|
|
668
|
+
console.log(` model-facing instructions — review ${entry.url} before relying on it.`);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
const result = await installSkill(entry, { force: opts.force, confirmLive: !!opts.yes });
|
|
672
|
+
|
|
673
|
+
switch (result.action) {
|
|
674
|
+
case 'installed':
|
|
675
|
+
console.log(` + installed ${result.slug} → ${result.path}`);
|
|
676
|
+
console.log(` source: ${result.source}`);
|
|
677
|
+
console.log(` note: only SKILL.md was fetched; for scripts/templates see ${entry.url}`);
|
|
678
|
+
break;
|
|
679
|
+
case 'exists':
|
|
680
|
+
console.log(` ~ ${result.slug} already installed (${result.path}) — use --force to overwrite`);
|
|
681
|
+
break;
|
|
682
|
+
case 'needs-confirm':
|
|
683
|
+
console.log(` ⛔ "${result.slug}" is live (untrusted) — not installed.`);
|
|
684
|
+
console.log(` Review ${result.url}, then re-run with --yes to confirm.`);
|
|
685
|
+
break;
|
|
686
|
+
case 'bootstrap':
|
|
687
|
+
await handleBootstrap(result, opts);
|
|
688
|
+
break;
|
|
689
|
+
case 'guide':
|
|
690
|
+
console.log(` i ${result.slug} can't be auto-copied: ${result.reason}`);
|
|
691
|
+
console.log(` open: ${result.url}`);
|
|
692
|
+
break;
|
|
693
|
+
case 'invalid':
|
|
694
|
+
console.error(` ! ${result.slug} fetch rejected: ${result.reason}`);
|
|
695
|
+
console.error(` source: ${result.url}`);
|
|
696
|
+
process.exit(2);
|
|
697
|
+
break;
|
|
698
|
+
default:
|
|
699
|
+
console.error(` ! unexpected result: ${JSON.stringify(result)}`);
|
|
700
|
+
process.exit(2);
|
|
701
|
+
}
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
skillsCmd
|
|
705
|
+
.command('refresh')
|
|
706
|
+
.description('Refetch the awesome-claude-skills registry into the local cache')
|
|
707
|
+
.action(async () => {
|
|
708
|
+
const { refreshCatalog } = await import('../src/skills-registry.js');
|
|
709
|
+
const { skillsRegistryCachePath } = await import('../src/memory-paths.js');
|
|
710
|
+
try {
|
|
711
|
+
const entries = await refreshCatalog();
|
|
712
|
+
console.log(` cached ${entries.length} registry entries → ${skillsRegistryCachePath()}`);
|
|
713
|
+
} catch (err) {
|
|
714
|
+
console.error(` Could not refresh registry: ${err.message}`);
|
|
715
|
+
process.exit(2);
|
|
716
|
+
}
|
|
717
|
+
});
|
|
718
|
+
|
|
719
|
+
async function handleBootstrap(result, opts) {
|
|
720
|
+
const { existsSync } = await import('node:fs');
|
|
721
|
+
const { homedir } = await import('node:os');
|
|
722
|
+
const { execSync } = await import('node:child_process');
|
|
723
|
+
const bin = join(homedir(), '.kimi-webbridge', 'bin', 'kimi-webbridge');
|
|
724
|
+
|
|
725
|
+
if (result.slug === 'kimi-webbridge' && existsSync(bin)) {
|
|
726
|
+
console.log(' kimi-webbridge already installed — ensuring the daemon is running...');
|
|
727
|
+
try {
|
|
728
|
+
execSync(`"${bin}" start`, { stdio: 'inherit' });
|
|
729
|
+
} catch { /* start is idempotent; ignore */ }
|
|
730
|
+
return;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
console.log(`\n ${result.slug} installs via a shell command (not a copyable SKILL.md):\n`);
|
|
734
|
+
console.log(` ${result.command}\n`);
|
|
735
|
+
if (!opts.yes) {
|
|
736
|
+
console.log(' Review it, then re-run with --yes to execute, or run it yourself.');
|
|
737
|
+
return;
|
|
738
|
+
}
|
|
739
|
+
console.log(' Running (--yes given)...\n');
|
|
740
|
+
try {
|
|
741
|
+
execSync(result.command, { stdio: 'inherit', shell: '/bin/bash' });
|
|
742
|
+
} catch (err) {
|
|
743
|
+
console.error(` Bootstrap failed: ${err.message}`);
|
|
744
|
+
process.exit(2);
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
// --- capabilities (self-introspection: "connect to claudenv, understand it") ---
|
|
749
|
+
program
|
|
750
|
+
.command('capabilities')
|
|
751
|
+
.alias('caps')
|
|
752
|
+
.description('Print a map of everything this claudenv install offers')
|
|
753
|
+
.option('--json', 'Emit the raw capability map as JSON')
|
|
754
|
+
.option('-d, --dir <path>', 'Project directory')
|
|
755
|
+
.action(async (opts) => {
|
|
756
|
+
const { buildCapabilityMap, formatCapabilities } = await import('../src/capabilities.js');
|
|
757
|
+
const map = await buildCapabilityMap({
|
|
758
|
+
cwd: opts.dir ? resolve(opts.dir) : process.cwd(),
|
|
759
|
+
version: pkgJson.version,
|
|
760
|
+
});
|
|
761
|
+
if (opts.json) {
|
|
762
|
+
console.log(JSON.stringify(map, null, 2));
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
console.log('\n' + formatCapabilities(map) + '\n');
|
|
766
|
+
});
|
|
767
|
+
|
|
472
768
|
// --- doctor ---
|
|
473
769
|
program
|
|
474
770
|
.command('doctor')
|
package/package.json
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dynamic-workflows
|
|
3
|
+
description: |
|
|
4
|
+
Trigger when a task naturally decomposes into many independent sub-tasks
|
|
5
|
+
that benefit from deterministic multi-agent orchestration: reviewing or
|
|
6
|
+
migrating across many files, research over many sources, multi-dimension
|
|
7
|
+
audits, generate-N-then-judge, or "find all X" with verification. Use the
|
|
8
|
+
Workflow tool (agent()/parallel()/pipeline()). Also triggers on the word
|
|
9
|
+
"workflow"/"workflows" or an explicit ask to fan out / orchestrate
|
|
10
|
+
subagents. Do NOT trigger for single-file edits, strictly sequential work,
|
|
11
|
+
or anything one or two Agent calls already cover.
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
# Dynamic workflows
|
|
15
|
+
|
|
16
|
+
Deterministic multi-agent orchestration via the built-in **Workflow** tool —
|
|
17
|
+
a JavaScript script that fans work out across subagents (`agent()`),
|
|
18
|
+
pipelines it (`pipeline()`), or barriers on it (`parallel()`). This skill
|
|
19
|
+
decides *when* orchestration earns its cost and gives the minimal scaffold to
|
|
20
|
+
launch one. The Workflow tool's own description carries the full API — lean on
|
|
21
|
+
it; don't reproduce it here.
|
|
22
|
+
|
|
23
|
+
## Mode detection (FIRST step every time)
|
|
24
|
+
|
|
25
|
+
Check the system prompt for the marker `Dynamic-workflows mode (loop)`.
|
|
26
|
+
|
|
27
|
+
- **Marker present** → LOOP mode. You are inside `claudenv loop`. Orchestrate
|
|
28
|
+
without pausing, but only for plan items that genuinely split into
|
|
29
|
+
independent sub-tasks, and keep fan-out narrow (see Cost discipline). The
|
|
30
|
+
goal is law — never pause to ask "запустить workflow?".
|
|
31
|
+
- **Marker absent** → INTERACTIVE mode. Propose a one-line orchestration plan
|
|
32
|
+
(how many agents, what each does, pipeline vs parallel) and wait for the
|
|
33
|
+
user before launching.
|
|
34
|
+
|
|
35
|
+
## When to orchestrate (triggers)
|
|
36
|
+
|
|
37
|
+
Reach for a workflow when the work is *wide* — many items, each handled the
|
|
38
|
+
same way, with little cross-talk:
|
|
39
|
+
|
|
40
|
+
- **Review / audit** a diff across several dimensions (bugs, perf, security,
|
|
41
|
+
style), then adversarially verify each finding.
|
|
42
|
+
- **Migrate / edit a pattern** across many files (worktree isolation per
|
|
43
|
+
agent when they mutate in parallel).
|
|
44
|
+
- **Research** a question over many sources, then synthesize.
|
|
45
|
+
- **Generate N independent attempts** (different angles), judge, synthesize
|
|
46
|
+
from the winner.
|
|
47
|
+
- **"Find all X"** with a verification pass and loop-until-dry.
|
|
48
|
+
|
|
49
|
+
## When NOT to orchestrate (anti-triggers)
|
|
50
|
+
|
|
51
|
+
Stay single-threaded — a workflow is pure overhead here:
|
|
52
|
+
|
|
53
|
+
- A single file, or a change that must happen in strict sequence.
|
|
54
|
+
- Trivial edits, renames, formatting.
|
|
55
|
+
- Anything one or two plain `Agent` calls already cover — prefer those.
|
|
56
|
+
|
|
57
|
+
**Cost discipline.** Multi-agent fan-out costs roughly an order of magnitude
|
|
58
|
+
more tokens than single-threaded work (~15×; see `claudenv-update-plan.md`).
|
|
59
|
+
Single-threaded is the default. Orchestrate only when the width is real, and
|
|
60
|
+
cap the number of concurrent agents to what the task needs.
|
|
61
|
+
|
|
62
|
+
## How to launch (minimal scaffold)
|
|
63
|
+
|
|
64
|
+
Every script starts with a pure-literal `meta`, then uses `pipeline()` as the
|
|
65
|
+
default and `parallel()` only as a barrier when you genuinely need all
|
|
66
|
+
prior-stage results at once. Pattern — review each dimension, verify each
|
|
67
|
+
finding as soon as it lands:
|
|
68
|
+
|
|
69
|
+
```js
|
|
70
|
+
export const meta = {
|
|
71
|
+
name: 'review-diff',
|
|
72
|
+
description: 'Review the diff across dimensions and verify each finding',
|
|
73
|
+
phases: [{ title: 'Review' }, { title: 'Verify' }],
|
|
74
|
+
}
|
|
75
|
+
const DIMENSIONS = [
|
|
76
|
+
{ key: 'bugs', prompt: 'Find correctness bugs in the diff…' },
|
|
77
|
+
{ key: 'perf', prompt: 'Find performance regressions in the diff…' },
|
|
78
|
+
]
|
|
79
|
+
const results = await pipeline(
|
|
80
|
+
DIMENSIONS,
|
|
81
|
+
d => agent(d.prompt, { label: `review:${d.key}`, phase: 'Review', schema: FINDINGS }),
|
|
82
|
+
review => parallel(review.findings.map(f => () =>
|
|
83
|
+
agent(`Adversarially verify, default to refuted if unsure: ${f.title}`,
|
|
84
|
+
{ label: `verify:${f.file}`, phase: 'Verify', schema: VERDICT })
|
|
85
|
+
.then(v => ({ ...f, verdict: v }))))
|
|
86
|
+
)
|
|
87
|
+
const confirmed = results.flat().filter(Boolean).filter(f => f.verdict?.isReal)
|
|
88
|
+
return { confirmed }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Useful patterns (compose as the task needs): **adversarial-verify** (N
|
|
92
|
+
skeptics per finding, kill on majority-refute), **loop-until-dry** (keep
|
|
93
|
+
finding until K empty rounds), **judge panel** (N attempts → score →
|
|
94
|
+
synthesize). Scale the agent count to the ask — a few for "quick check",
|
|
95
|
+
more for "thorough audit".
|
|
96
|
+
|
|
97
|
+
## Notes
|
|
98
|
+
|
|
99
|
+
- `agent(prompt, { schema })` returns the validated object; without a schema
|
|
100
|
+
it returns the agent's final text.
|
|
101
|
+
- Use `isolation: 'worktree'` only when agents mutate files in parallel and
|
|
102
|
+
would otherwise conflict — it is expensive.
|
|
103
|
+
- In LOOP mode, if the Workflow tool's background completion does not resolve
|
|
104
|
+
under headless `claude -p`, fall back to plain parallel `Agent` calls for
|
|
105
|
+
the same fan-out — same spirit, no dependency on the background runtime.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Добавить коннектор к источнику данных (SQL/REST/вики/Redash) с изоляцией по workspace
|
|
3
|
+
allowed-tools: Bash, Read, Write, Edit, Skill
|
|
4
|
+
argument-hint: [<описание источника>]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /add-source - подключить источник данных
|
|
8
|
+
|
|
9
|
+
Запускает skill `source-connector`: опрашивает тебя, генерирует рабочий коннектор,
|
|
10
|
+
кладёт секреты в `.env.local`, кэширует знание о коннекторе (параметры + provenance)
|
|
11
|
+
в память активного workspace.
|
|
12
|
+
|
|
13
|
+
## Routing
|
|
14
|
+
|
|
15
|
+
1. Активируй skill `source-connector`.
|
|
16
|
+
2. Если в `$ARGUMENTS` есть описание источника (тип/хост/система) - передай его как
|
|
17
|
+
стартовый контекст, остальное доспроси.
|
|
18
|
+
3. Если аргументов нет - начни с вопроса, к какому источнику подключаемся.
|
|
19
|
+
|
|
20
|
+
## Перед стартом
|
|
21
|
+
|
|
22
|
+
- Определи активный workspace (env `CLAUDENV_WORKSPACE` -> файл
|
|
23
|
+
`~/.claudenv/active-workspace`). Нет активного - предложи выбрать/создать,
|
|
24
|
+
не пиши в глобальную память.
|
|
25
|
+
- Напомни инвариант: секреты идут только в `.env.local` (gitignored), в память -
|
|
26
|
+
только имена переменных.
|
|
27
|
+
|
|
28
|
+
## Когда НЕ создавать коннектор
|
|
29
|
+
|
|
30
|
+
Если для источника есть готовый MCP-сервер - предложи путь через `/setup-mcp`
|
|
31
|
+
(`.mcp.json`), коннектор нужен только для источников без MCP.
|
|
@@ -96,7 +96,7 @@ Copy the following scaffold files from the global skill directory into this proj
|
|
|
96
96
|
|
|
97
97
|
```bash
|
|
98
98
|
# Create directories
|
|
99
|
-
mkdir -p .claude/commands .claude/skills/doc-generator/scripts .claude/skills/doc-generator/templates .claude/agents
|
|
99
|
+
mkdir -p .claude/commands .claude/skills/doc-generator/scripts .claude/skills/doc-generator/templates .claude/skills/dynamic-workflows .claude/agents
|
|
100
100
|
|
|
101
101
|
# Copy project-level scaffold from global skill
|
|
102
102
|
cp ~/.claude/skills/claudenv/scaffold/.claude/commands/init-docs.md .claude/commands/init-docs.md
|
|
@@ -108,6 +108,7 @@ cp ~/.claude/skills/claudenv/scaffold/.claude/skills/doc-generator/templates/det
|
|
|
108
108
|
cp ~/.claude/skills/claudenv/scaffold/.claude/skills/doc-generator/templates/mcp-servers.md .claude/skills/doc-generator/templates/mcp-servers.md
|
|
109
109
|
cp ~/.claude/skills/claudenv/scaffold/.claude/commands/setup-mcp.md .claude/commands/setup-mcp.md
|
|
110
110
|
cp ~/.claude/skills/claudenv/scaffold/.claude/commands/improve.md .claude/commands/improve.md
|
|
111
|
+
cp ~/.claude/skills/claudenv/scaffold/.claude/skills/dynamic-workflows/SKILL.md .claude/skills/dynamic-workflows/SKILL.md
|
|
111
112
|
cp ~/.claude/skills/claudenv/scaffold/.claude/agents/doc-analyzer.md .claude/agents/doc-analyzer.md
|
|
112
113
|
chmod +x .claude/skills/doc-generator/scripts/validate.sh
|
|
113
114
|
```
|
|
@@ -179,6 +180,7 @@ Created:
|
|
|
179
180
|
+ .claude/commands/setup-mcp.md
|
|
180
181
|
+ .claude/commands/improve.md
|
|
181
182
|
+ .claude/skills/doc-generator/
|
|
183
|
+
+ .claude/skills/dynamic-workflows/
|
|
182
184
|
+ .claude/agents/doc-analyzer.md
|
|
183
185
|
+ .mcp.json (if MCP servers were configured)
|
|
184
186
|
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Подобрать и установить оптимальный harness для текущей задачи — интроспекция claudenv, поиск/установка скиллов, коннекторы, MCP, браузер, память
|
|
3
|
+
allowed-tools: Bash, Read, Write, Edit, WebFetch, Skill
|
|
4
|
+
argument-hint: [<задача или нужная возможность>]
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# /harness — самонастройка под задачу
|
|
8
|
+
|
|
9
|
+
Запускает skill `harness`: ты понимаешь, что уже даёт claudenv, чего не хватает
|
|
10
|
+
под задачу, и автономно дооснащаешься — ставишь скиллы из awesome-claude-skills,
|
|
11
|
+
настраиваешь коннекторы/MCP, поднимаешь браузер kimi-webbridge, управляешь памятью.
|
|
12
|
+
|
|
13
|
+
## Routing
|
|
14
|
+
|
|
15
|
+
1. Активируй skill `harness`.
|
|
16
|
+
2. Сначала интроспекция: `claudenv capabilities` (или `npx claudenv capabilities`).
|
|
17
|
+
3. Если в `$ARGUMENTS` есть задача/возможность — используй как цель gap-анализа и
|
|
18
|
+
поиска (`claudenv skills search "<...>"`). Если аргументов нет — спроси, подо
|
|
19
|
+
что настраиваемся, и предложи варианты из каталога.
|
|
20
|
+
|
|
21
|
+
## Инварианты
|
|
22
|
+
|
|
23
|
+
- **Trust boundary:** курируемые (★) скиллы ставим сразу; live (не-курируемые) —
|
|
24
|
+
только после подтверждения пользователя (SKILL.md = авто-загружаемые инструкции,
|
|
25
|
+
это поверхность для prompt-injection).
|
|
26
|
+
- Секреты — только в `.env.local`. Знание о коннекторе — в активный workspace.
|
|
27
|
+
- Bootstrap-установщики (`curl | bash`, напр. kimi-webbridge) — сначала показать
|
|
28
|
+
команду, выполнять с явным согласием.
|
|
29
|
+
- Не ставь скиллы «про запас» — только то, что реально двигает задачу.
|