@crouton-kit/crouter 0.3.11 → 0.3.13
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/bin/crtrd +2 -0
- package/dist/builtin-personas/design/base.md +9 -0
- package/dist/builtin-personas/design/orchestrator.md +10 -0
- package/dist/builtin-personas/developer/base.md +9 -0
- package/dist/builtin-personas/developer/orchestrator.md +12 -0
- package/dist/builtin-personas/explore/base.md +9 -0
- package/dist/builtin-personas/explore/orchestrator.md +9 -0
- package/dist/builtin-personas/general/base.md +5 -0
- package/dist/builtin-personas/general/orchestrator.md +7 -0
- package/dist/builtin-personas/orchestration-kernel.md +71 -0
- package/dist/builtin-personas/plan/base.md +7 -0
- package/dist/builtin-personas/plan/orchestrator.md +12 -0
- package/dist/builtin-personas/review/base.md +7 -0
- package/dist/builtin-personas/review/orchestrator.md +9 -0
- package/dist/builtin-personas/runtime-base.md +39 -0
- package/dist/builtin-personas/spec/base.md +7 -0
- package/dist/builtin-personas/spec/orchestrator.md +10 -0
- package/dist/builtin-skills/skills/design/SKILL.md +51 -0
- package/dist/builtin-skills/skills/development/SKILL.md +109 -0
- package/dist/builtin-skills/skills/planning/SKILL.md +59 -0
- package/dist/builtin-skills/skills/spec/SKILL.md +83 -0
- package/dist/cli.js +14 -6
- package/dist/commands/{mode.d.ts → attention.d.ts} +1 -1
- package/dist/commands/attention.js +152 -0
- package/dist/commands/canvas.d.ts +2 -0
- package/dist/commands/canvas.js +35 -0
- package/dist/commands/daemon.d.ts +2 -0
- package/dist/commands/daemon.js +111 -0
- package/dist/commands/dashboard.d.ts +2 -0
- package/dist/commands/dashboard.js +65 -0
- package/dist/commands/human/prompts.d.ts +5 -0
- package/dist/commands/human/prompts.js +269 -0
- package/dist/commands/human/queue.d.ts +3 -0
- package/dist/commands/human/queue.js +133 -0
- package/dist/commands/human/shared.d.ts +43 -0
- package/dist/commands/human/shared.js +107 -0
- package/dist/commands/human.js +10 -454
- package/dist/commands/node.d.ts +2 -0
- package/dist/commands/node.js +407 -0
- package/dist/commands/pkg/market-inspect.d.ts +1 -0
- package/dist/commands/pkg/market-inspect.js +157 -0
- package/dist/commands/pkg/market-manage.d.ts +1 -0
- package/dist/commands/pkg/market-manage.js +316 -0
- package/dist/commands/pkg/market.d.ts +1 -0
- package/dist/commands/pkg/market.js +16 -0
- package/dist/commands/pkg/plugin-inspect.d.ts +1 -0
- package/dist/commands/pkg/plugin-inspect.js +142 -0
- package/dist/commands/pkg/plugin-manage.d.ts +1 -0
- package/dist/commands/pkg/plugin-manage.js +294 -0
- package/dist/commands/pkg/plugin.d.ts +1 -0
- package/dist/commands/pkg/plugin.js +16 -0
- package/dist/commands/pkg/shared.d.ts +5 -0
- package/dist/commands/pkg/shared.js +61 -0
- package/dist/commands/pkg.js +3 -1004
- package/dist/commands/push.d.ts +3 -0
- package/dist/commands/push.js +159 -0
- package/dist/commands/revive.d.ts +2 -0
- package/dist/commands/revive.js +64 -0
- package/dist/commands/skill/author.d.ts +3 -0
- package/dist/commands/skill/author.js +147 -0
- package/dist/commands/skill/find.d.ts +4 -0
- package/dist/commands/skill/find.js +254 -0
- package/dist/commands/skill/read.d.ts +1 -0
- package/dist/commands/skill/read.js +89 -0
- package/dist/commands/skill/shared.d.ts +19 -0
- package/dist/commands/skill/shared.js +207 -0
- package/dist/commands/skill/state.d.ts +3 -0
- package/dist/commands/skill/state.js +69 -0
- package/dist/commands/skill.js +6 -691
- package/dist/commands/sys/config.d.ts +1 -0
- package/dist/commands/sys/config.js +186 -0
- package/dist/commands/sys/doctor.d.ts +1 -0
- package/dist/commands/sys/doctor.js +369 -0
- package/dist/commands/sys/shared.d.ts +3 -0
- package/dist/commands/sys/shared.js +24 -0
- package/dist/commands/sys/update.d.ts +2 -0
- package/dist/commands/sys/update.js +114 -0
- package/dist/commands/sys.js +4 -694
- package/dist/core/__tests__/argv-parser.test.js +19 -1
- package/dist/core/__tests__/canvas-inbox-watcher.test.js +100 -0
- package/dist/core/__tests__/canvas.test.js +154 -0
- package/dist/core/__tests__/reset.test.js +105 -0
- package/dist/core/canvas/attention.d.ts +24 -0
- package/dist/core/canvas/attention.js +94 -0
- package/dist/core/canvas/canvas.d.ts +40 -0
- package/dist/core/canvas/canvas.js +210 -0
- package/dist/core/canvas/db.d.ts +7 -0
- package/dist/core/canvas/db.js +61 -0
- package/dist/core/canvas/index.d.ts +4 -0
- package/dist/core/canvas/index.js +6 -0
- package/dist/core/canvas/paths.d.ts +16 -0
- package/dist/core/canvas/paths.js +62 -0
- package/dist/core/canvas/render.d.ts +30 -0
- package/dist/core/canvas/render.js +186 -0
- package/dist/core/canvas/types.d.ts +87 -0
- package/dist/core/canvas/types.js +8 -0
- package/dist/core/command.d.ts +5 -0
- package/dist/core/command.js +35 -10
- package/dist/core/feed/feed.d.ts +43 -0
- package/dist/core/feed/feed.js +116 -0
- package/dist/core/feed/inbox.d.ts +50 -0
- package/dist/core/feed/inbox.js +124 -0
- package/dist/core/help.js +5 -3
- package/dist/core/io.d.ts +15 -1
- package/dist/core/io.js +56 -6
- package/dist/core/personas/index.d.ts +12 -0
- package/dist/core/personas/index.js +10 -0
- package/dist/core/personas/loader.d.ts +44 -0
- package/dist/core/personas/loader.js +157 -0
- package/dist/core/personas/resolve.d.ts +36 -0
- package/dist/core/personas/resolve.js +110 -0
- package/dist/core/render.d.ts +11 -0
- package/dist/core/render.js +126 -0
- package/dist/core/resolver.d.ts +10 -0
- package/dist/core/resolver.js +109 -1
- package/dist/core/runtime/front-door.d.ts +10 -0
- package/dist/core/runtime/front-door.js +97 -0
- package/dist/core/runtime/kickoff.d.ts +23 -0
- package/dist/core/runtime/kickoff.js +134 -0
- package/dist/core/runtime/launch.d.ts +34 -0
- package/dist/core/runtime/launch.js +85 -0
- package/dist/core/runtime/nodes.d.ts +38 -0
- package/dist/core/runtime/nodes.js +95 -0
- package/dist/core/runtime/presence.d.ts +55 -0
- package/dist/core/runtime/presence.js +198 -0
- package/dist/core/runtime/promote.d.ts +30 -0
- package/dist/core/runtime/promote.js +105 -0
- package/dist/core/runtime/reset.d.ts +13 -0
- package/dist/core/runtime/reset.js +97 -0
- package/dist/core/runtime/revive.d.ts +26 -0
- package/dist/core/runtime/revive.js +87 -0
- package/dist/core/runtime/roadmap.d.ts +12 -0
- package/dist/core/runtime/roadmap.js +52 -0
- package/dist/core/runtime/spawn.d.ts +31 -0
- package/dist/core/runtime/spawn.js +123 -0
- package/dist/core/runtime/stop-guard.d.ts +18 -0
- package/dist/core/runtime/stop-guard.js +33 -0
- package/dist/core/runtime/tmux.d.ts +107 -0
- package/dist/core/runtime/tmux.js +244 -0
- package/dist/core/spawn.d.ts +17 -197
- package/dist/core/spawn.js +16 -539
- package/dist/daemon/crtrd-cli.js +4 -0
- package/dist/daemon/crtrd.d.ts +20 -0
- package/dist/daemon/crtrd.js +200 -0
- package/dist/daemon/manage.d.ts +17 -0
- package/dist/daemon/manage.js +57 -0
- package/dist/pi-extensions/canvas-inbox-watcher.d.ts +16 -0
- package/dist/pi-extensions/canvas-inbox-watcher.js +229 -0
- package/dist/pi-extensions/canvas-nav.d.ts +32 -0
- package/dist/pi-extensions/canvas-nav.js +536 -0
- package/dist/pi-extensions/canvas-stophook.d.ts +17 -0
- package/dist/pi-extensions/canvas-stophook.js +396 -0
- package/package.json +6 -5
- package/dist/commands/agent.d.ts +0 -6
- package/dist/commands/agent.js +0 -585
- package/dist/commands/debug.d.ts +0 -3
- package/dist/commands/debug.js +0 -192
- package/dist/commands/job.d.ts +0 -11
- package/dist/commands/job.js +0 -384
- package/dist/commands/mode.js +0 -231
- package/dist/commands/plan.d.ts +0 -4
- package/dist/commands/plan.js +0 -322
- package/dist/commands/spec.d.ts +0 -3
- package/dist/commands/spec.js +0 -299
- package/dist/core/__tests__/flow-leaves.test.js +0 -248
- package/dist/core/__tests__/job.test.js +0 -310
- package/dist/core/__tests__/jobs.test.js +0 -98
- package/dist/core/__tests__/spawn.test.js +0 -138
- package/dist/core/__tests__/subagents.test.d.ts +0 -1
- package/dist/core/__tests__/subagents.test.js +0 -75
- package/dist/core/jobs.d.ts +0 -107
- package/dist/core/jobs.js +0 -565
- package/dist/core/subagents.d.ts +0 -18
- package/dist/core/subagents.js +0 -163
- package/dist/prompts/agent.d.ts +0 -27
- package/dist/prompts/agent.js +0 -184
- package/dist/prompts/debug.d.ts +0 -8
- package/dist/prompts/debug.js +0 -44
- /package/dist/core/__tests__/{flow-leaves.test.d.ts → canvas-inbox-watcher.test.d.ts} +0 -0
- /package/dist/core/__tests__/{job.test.d.ts → canvas.test.d.ts} +0 -0
- /package/dist/core/__tests__/{jobs.test.d.ts → reset.test.d.ts} +0 -0
- /package/dist/{core/__tests__/spawn.test.d.ts → daemon/crtrd-cli.d.ts} +0 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { defineLeaf } from '../../core/command.js';
|
|
2
|
+
import { CrtrError } from '../../core/errors.js';
|
|
3
|
+
import { resolveSkill, resolveCategory, buildCategoryIndex, } from '../../core/resolver.js';
|
|
4
|
+
import { resolveScopeArg } from '../../core/scope.js';
|
|
5
|
+
import { parseFrontmatter } from '../../core/frontmatter.js';
|
|
6
|
+
import { readText } from '../../core/fs-utils.js';
|
|
7
|
+
import { appendNeighbors } from './shared.js';
|
|
8
|
+
export const readLeaf = defineLeaf({
|
|
9
|
+
name: 'read',
|
|
10
|
+
help: {
|
|
11
|
+
name: 'skill read',
|
|
12
|
+
summary: 'load SKILL.md body and resolution metadata for a named skill',
|
|
13
|
+
params: [
|
|
14
|
+
{ kind: 'positional', name: 'name', required: true, constraint: 'Skill identifier. Forms: <name>, <plugin>/<name>, <scope>/<name>, <scope>/<plugin>/<name>.' },
|
|
15
|
+
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Narrows resolution when name is ambiguous.' },
|
|
16
|
+
{ kind: 'flag', name: 'plugin', type: 'string', required: false, constraint: 'Narrows resolution to a specific plugin.' },
|
|
17
|
+
{ kind: 'flag', name: 'frontmatter', type: 'bool', required: false, constraint: 'When present, includes YAML frontmatter in the output content.' },
|
|
18
|
+
{ kind: 'flag', name: 'no-body', type: 'bool', required: false, constraint: 'When present, omits the body — returns resolution metadata only. Use to confirm a skill exists or locate it without loading SKILL.md.' },
|
|
19
|
+
],
|
|
20
|
+
output: [
|
|
21
|
+
{ name: 'name', type: 'string', required: true, constraint: 'Resolved skill name, or category id when the name resolved to a group.' },
|
|
22
|
+
{ name: 'kind', type: 'string', required: false, constraint: '"category" when the name resolved to a group rather than a single skill.' },
|
|
23
|
+
{ name: 'count', type: 'integer', required: false, constraint: 'Number of skills in the category. Present when kind is "category".' },
|
|
24
|
+
{ name: 'plugin', type: 'string', required: false, constraint: 'Plugin the skill belongs to. Omitted for category reads.' },
|
|
25
|
+
{ name: 'scope', type: 'string', required: false, constraint: 'Scope the skill was resolved from. Omitted for category reads.' },
|
|
26
|
+
{ name: 'path', type: 'string', required: false, constraint: 'Absolute path to SKILL.md. Omitted for category reads.' },
|
|
27
|
+
{ name: 'content', type: 'string', required: false, constraint: 'SKILL.md body or category index. Omitted when --no-body is set.' },
|
|
28
|
+
{ name: 'follow_up', type: 'string', required: false, constraint: 'Hints at variant flags or next commands. Omitted when --no-body is set.' },
|
|
29
|
+
],
|
|
30
|
+
outputKind: 'object',
|
|
31
|
+
effects: ['None. Read-only.'],
|
|
32
|
+
},
|
|
33
|
+
run: async (input) => {
|
|
34
|
+
const nameRaw = input['name'];
|
|
35
|
+
const scopeStr = input['scope'];
|
|
36
|
+
const pluginFilter = input['plugin'];
|
|
37
|
+
const includeFrontmatter = input['frontmatter'];
|
|
38
|
+
const noBody = input['noBody'];
|
|
39
|
+
const resolveOpts = {};
|
|
40
|
+
if (scopeStr !== undefined) {
|
|
41
|
+
const resolved = resolveScopeArg(scopeStr);
|
|
42
|
+
if (resolved !== 'all')
|
|
43
|
+
resolveOpts.scope = resolved;
|
|
44
|
+
}
|
|
45
|
+
if (pluginFilter !== undefined)
|
|
46
|
+
resolveOpts.pluginFilter = pluginFilter;
|
|
47
|
+
let skillObj;
|
|
48
|
+
let notFoundErr;
|
|
49
|
+
try {
|
|
50
|
+
skillObj = resolveSkill(nameRaw, resolveOpts);
|
|
51
|
+
}
|
|
52
|
+
catch (e) {
|
|
53
|
+
if (e instanceof CrtrError && e.code === 'not_found') {
|
|
54
|
+
notFoundErr = e;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
throw e;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
if (skillObj === undefined) {
|
|
61
|
+
const cat = resolveCategory(nameRaw, resolveOpts);
|
|
62
|
+
if (cat !== null) {
|
|
63
|
+
if (noBody)
|
|
64
|
+
return { name: cat.id, kind: 'category', count: cat.skills.length };
|
|
65
|
+
return {
|
|
66
|
+
name: cat.id,
|
|
67
|
+
kind: 'category',
|
|
68
|
+
count: cat.skills.length,
|
|
69
|
+
content: buildCategoryIndex(cat),
|
|
70
|
+
follow_up: 'Read a listed skill with `crtr skill read <id>`.',
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
throw notFoundErr;
|
|
74
|
+
}
|
|
75
|
+
const out = {
|
|
76
|
+
name: skillObj.name,
|
|
77
|
+
plugin: skillObj.plugin,
|
|
78
|
+
scope: skillObj.scope,
|
|
79
|
+
path: skillObj.path,
|
|
80
|
+
};
|
|
81
|
+
if (noBody)
|
|
82
|
+
return out;
|
|
83
|
+
const rawContent = readText(skillObj.path);
|
|
84
|
+
const rawBody = includeFrontmatter ? rawContent : parseFrontmatter(rawContent).body;
|
|
85
|
+
out['content'] = appendNeighbors(skillObj, rawBody);
|
|
86
|
+
out['follow_up'] = 'Add --no-body to skip the body and return path/scope/plugin only. Add --frontmatter to include YAML frontmatter in content.';
|
|
87
|
+
return out;
|
|
88
|
+
},
|
|
89
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { Skill, Scope } from '../../types.js';
|
|
2
|
+
export declare function formatNeighborQualifier(s: Skill): string;
|
|
3
|
+
export declare function formatNeighborKeywords(s: Skill): string;
|
|
4
|
+
export declare function buildNeighborsSection(skill: Skill): string | null;
|
|
5
|
+
export declare function appendNeighbors(skill: Skill, body: string): string;
|
|
6
|
+
export declare function resolveWriteScope(scopeStr: string | undefined): Scope;
|
|
7
|
+
export declare const VALID_TYPES: readonly ["playbook", "primer", "reference", "runbook", "freeform"];
|
|
8
|
+
type CatalogSource = {
|
|
9
|
+
plugin: string;
|
|
10
|
+
roots: string[];
|
|
11
|
+
};
|
|
12
|
+
/** The skill subtree's live state: the loaded-skills catalog as a self-named
|
|
13
|
+
* `<skills count="N">` element. The tag carries the label and the count is an
|
|
14
|
+
* attribute, so the body is the grouped tree alone — no "Loaded skills (N)"
|
|
15
|
+
* header to duplicate. Returns null (block omitted) when discovery fails or
|
|
16
|
+
* nothing is loaded. */
|
|
17
|
+
export declare function buildSkillCatalog(): string | null;
|
|
18
|
+
export declare function renderCatalogSection(label: string, sources: CatalogSource[], descriptions: Map<string, string>, out: string[]): void;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { stateBlock } from '../../core/help.js';
|
|
2
|
+
import { usage } from '../../core/errors.js';
|
|
3
|
+
import { SCOPE_SKILL_PLUGIN } from '../../types.js';
|
|
4
|
+
import { listSkillSiblings, listSkillChildren, listAllSkills, listAllPlugins, } from '../../core/resolver.js';
|
|
5
|
+
import { resolveScopeArg, projectScopeRoot } from '../../core/scope.js';
|
|
6
|
+
// ---------------------------------------------------------------------------
|
|
7
|
+
// Neighbors section (ported from old impl)
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
export function formatNeighborQualifier(s) {
|
|
10
|
+
return s.plugin === SCOPE_SKILL_PLUGIN
|
|
11
|
+
? `${s.scope}/${s.name}`
|
|
12
|
+
: `${s.plugin}/${s.name}`;
|
|
13
|
+
}
|
|
14
|
+
export function formatNeighborKeywords(s) {
|
|
15
|
+
const kw = s.frontmatter.keywords;
|
|
16
|
+
if (!kw || kw.length === 0)
|
|
17
|
+
return '';
|
|
18
|
+
return ` — [${kw.join(', ')}]`;
|
|
19
|
+
}
|
|
20
|
+
export function buildNeighborsSection(skill) {
|
|
21
|
+
const siblings = listSkillSiblings(skill);
|
|
22
|
+
const children = listSkillChildren(skill);
|
|
23
|
+
if (siblings.length === 0 && children.length === 0)
|
|
24
|
+
return null;
|
|
25
|
+
const lines = [
|
|
26
|
+
'## Neighbors',
|
|
27
|
+
'*Auto-discovered from filesystem. Run `crtr skill read <name>` for full description + body.*',
|
|
28
|
+
'',
|
|
29
|
+
];
|
|
30
|
+
if (siblings.length > 0) {
|
|
31
|
+
lines.push('**Siblings:**');
|
|
32
|
+
for (const s of siblings) {
|
|
33
|
+
lines.push(`- \`${formatNeighborQualifier(s)}\`${formatNeighborKeywords(s)}`);
|
|
34
|
+
}
|
|
35
|
+
if (children.length > 0)
|
|
36
|
+
lines.push('');
|
|
37
|
+
}
|
|
38
|
+
if (children.length > 0) {
|
|
39
|
+
lines.push('**Nested:**');
|
|
40
|
+
for (const s of children) {
|
|
41
|
+
lines.push(`- \`${formatNeighborQualifier(s)}\`${formatNeighborKeywords(s)}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return lines.join('\n');
|
|
45
|
+
}
|
|
46
|
+
export function appendNeighbors(skill, body) {
|
|
47
|
+
const section = buildNeighborsSection(skill);
|
|
48
|
+
if (section === null)
|
|
49
|
+
return body;
|
|
50
|
+
const sep = body.endsWith('\n') ? '\n' : '\n\n';
|
|
51
|
+
return body + sep + `<neighbors>\n${section}\n</neighbors>\n`;
|
|
52
|
+
}
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
// Resolve scope for enable/disable/scaffold
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
export function resolveWriteScope(scopeStr) {
|
|
57
|
+
if (scopeStr !== undefined) {
|
|
58
|
+
const resolved = resolveScopeArg(scopeStr);
|
|
59
|
+
if (resolved === 'all') {
|
|
60
|
+
throw usage('scope must be user or project, not all');
|
|
61
|
+
}
|
|
62
|
+
return resolved;
|
|
63
|
+
}
|
|
64
|
+
return projectScopeRoot() !== null ? 'project' : 'user';
|
|
65
|
+
}
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Valid skill types (used by author sub-branch)
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
export const VALID_TYPES = ['playbook', 'primer', 'reference', 'runbook', 'freeform'];
|
|
70
|
+
const CATALOG_T = 5;
|
|
71
|
+
/** The skill subtree's live state: the loaded-skills catalog as a self-named
|
|
72
|
+
* `<skills count="N">` element. The tag carries the label and the count is an
|
|
73
|
+
* attribute, so the body is the grouped tree alone — no "Loaded skills (N)"
|
|
74
|
+
* header to duplicate. Returns null (block omitted) when discovery fails or
|
|
75
|
+
* nothing is loaded. */
|
|
76
|
+
export function buildSkillCatalog() {
|
|
77
|
+
let skills;
|
|
78
|
+
try {
|
|
79
|
+
skills = listAllSkills().filter((s) => s.enabled);
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
if (skills.length === 0)
|
|
85
|
+
return null;
|
|
86
|
+
const pluginDescriptions = new Map();
|
|
87
|
+
for (const p of listAllPlugins()) {
|
|
88
|
+
if (p.manifest.description)
|
|
89
|
+
pluginDescriptions.set(p.name, p.manifest.description);
|
|
90
|
+
}
|
|
91
|
+
const bySource = new Map();
|
|
92
|
+
for (const s of skills) {
|
|
93
|
+
const key = `${s.scope}\t${s.plugin}`;
|
|
94
|
+
const arr = bySource.get(key);
|
|
95
|
+
if (arr)
|
|
96
|
+
arr.push(s);
|
|
97
|
+
else
|
|
98
|
+
bySource.set(key, [s]);
|
|
99
|
+
}
|
|
100
|
+
const projectSources = [];
|
|
101
|
+
const userSources = [];
|
|
102
|
+
for (const [key, group] of bySource) {
|
|
103
|
+
const [scope, plugin] = key.split('\t');
|
|
104
|
+
const names = group.map((g) => g.name);
|
|
105
|
+
const roots = names
|
|
106
|
+
.filter((n) => !names.some((m) => m !== n && n.startsWith(m + '/')))
|
|
107
|
+
.sort();
|
|
108
|
+
if (roots.length === 0)
|
|
109
|
+
continue;
|
|
110
|
+
(scope === 'project' ? projectSources : userSources).push({ plugin, roots });
|
|
111
|
+
}
|
|
112
|
+
const body = [];
|
|
113
|
+
renderCatalogSection('Project', projectSources, pluginDescriptions, body);
|
|
114
|
+
renderCatalogSection('User', userSources, pluginDescriptions, body);
|
|
115
|
+
// renderCatalogSection leads each section with a blank separator; drop the
|
|
116
|
+
// leading one so the element body starts on its first real line.
|
|
117
|
+
while (body.length > 0 && body[0] === '')
|
|
118
|
+
body.shift();
|
|
119
|
+
body.push('');
|
|
120
|
+
body.push("Groups shown as `name/ N skills` are collapsed. Read the group to get its menu before assuming a skill is or isn't there: `crtr skill read <group>` (or `crtr skill find list --plugin <group>`). Search across everything with `crtr skill find search <topic>`.");
|
|
121
|
+
return stateBlock('skills', { count: skills.length }, body.join('\n'));
|
|
122
|
+
}
|
|
123
|
+
export function renderCatalogSection(label, sources, descriptions, out) {
|
|
124
|
+
if (sources.length === 0)
|
|
125
|
+
return;
|
|
126
|
+
const count = sources.reduce((n, s) => n + s.roots.length, 0);
|
|
127
|
+
out.push('');
|
|
128
|
+
out.push(`${label} (${count})`);
|
|
129
|
+
const sentinel = sources.filter((s) => s.plugin === SCOPE_SKILL_PLUGIN);
|
|
130
|
+
const named = sources
|
|
131
|
+
.filter((s) => s.plugin !== SCOPE_SKILL_PLUGIN)
|
|
132
|
+
.sort((a, b) => a.plugin.localeCompare(b.plugin));
|
|
133
|
+
for (const s of sentinel) {
|
|
134
|
+
if (s.roots.length > CATALOG_T) {
|
|
135
|
+
out.push(` (scope skills) ${s.roots.length} skills`);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
for (const n of s.roots)
|
|
139
|
+
out.push(` ${n}`);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (named.length === 0)
|
|
143
|
+
return;
|
|
144
|
+
const classified = named.map((s) => {
|
|
145
|
+
const subcats = new Map();
|
|
146
|
+
const bare = [];
|
|
147
|
+
for (const n of s.roots) {
|
|
148
|
+
const slash = n.indexOf('/');
|
|
149
|
+
if (slash === -1) {
|
|
150
|
+
bare.push(n);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
const sub = n.slice(0, slash);
|
|
154
|
+
const rest = n.slice(slash + 1);
|
|
155
|
+
const arr = subcats.get(sub);
|
|
156
|
+
if (arr)
|
|
157
|
+
arr.push(rest);
|
|
158
|
+
else
|
|
159
|
+
subcats.set(sub, [rest]);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { plugin: s.plugin, roots: s.roots, subcats, bare };
|
|
163
|
+
});
|
|
164
|
+
const descSuffix = (plugin) => {
|
|
165
|
+
const d = descriptions.get(plugin);
|
|
166
|
+
if (!d)
|
|
167
|
+
return '';
|
|
168
|
+
return ` — ${d.length > 80 ? d.slice(0, 77) + '…' : d}`;
|
|
169
|
+
};
|
|
170
|
+
// inlineW aligns the count column for collapsed + inline-enumerated plugins
|
|
171
|
+
// (nested plugins render their own header line, don't use this width)
|
|
172
|
+
const inlineW = classified
|
|
173
|
+
.filter((p) => {
|
|
174
|
+
const direct = p.subcats.size + p.bare.length;
|
|
175
|
+
return direct > CATALOG_T || p.subcats.size < 2;
|
|
176
|
+
})
|
|
177
|
+
.reduce((m, p) => Math.max(m, p.plugin.length + 1), 0);
|
|
178
|
+
for (const p of classified) {
|
|
179
|
+
const direct = p.subcats.size + p.bare.length;
|
|
180
|
+
if (direct > CATALOG_T) {
|
|
181
|
+
out.push(` ${(p.plugin + '/').padEnd(inlineW)} ${p.roots.length} skills${descSuffix(p.plugin)}`);
|
|
182
|
+
continue;
|
|
183
|
+
}
|
|
184
|
+
if (p.subcats.size >= 2) {
|
|
185
|
+
out.push(` ${p.plugin}/`);
|
|
186
|
+
if (p.bare.length > 0) {
|
|
187
|
+
out.push(` ${[...p.bare].sort().join(', ')}`);
|
|
188
|
+
}
|
|
189
|
+
const subKeys = [...p.subcats.keys()].sort();
|
|
190
|
+
const subW = subKeys
|
|
191
|
+
.map((k) => `${k}/`)
|
|
192
|
+
.reduce((m, l) => (l.length > m ? l.length : m), 0);
|
|
193
|
+
for (const subKey of subKeys) {
|
|
194
|
+
const children = p.subcats.get(subKey).sort();
|
|
195
|
+
if (children.length > CATALOG_T) {
|
|
196
|
+
out.push(` ${(subKey + '/').padEnd(subW)} ${children.length} skills`);
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
out.push(` ${(subKey + '/').padEnd(subW)} ${children.join(', ')}`);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
else {
|
|
204
|
+
out.push(` ${(p.plugin + '/').padEnd(inlineW)} ${[...p.roots].sort().join(', ')}`);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { defineLeaf, defineBranch } from '../../core/command.js';
|
|
2
|
+
import { skillConfigKey } from '../../types.js';
|
|
3
|
+
import { resolveSkill } from '../../core/resolver.js';
|
|
4
|
+
import { requireScopeRoot } from '../../core/scope.js';
|
|
5
|
+
import { updateConfig, ensureScopeInitialized } from '../../core/config.js';
|
|
6
|
+
import { resolveWriteScope } from './shared.js';
|
|
7
|
+
async function toggleSkill(input, enabled) {
|
|
8
|
+
const nameRaw = input['name'];
|
|
9
|
+
const scopeStr = input['scope'];
|
|
10
|
+
const scope = resolveWriteScope(scopeStr);
|
|
11
|
+
const skillObj = resolveSkill(nameRaw);
|
|
12
|
+
const key = skillConfigKey(skillObj.plugin, skillObj.name);
|
|
13
|
+
const scopeRootPath = requireScopeRoot(scope);
|
|
14
|
+
ensureScopeInitialized(scope, scopeRootPath);
|
|
15
|
+
updateConfig(scope, (cfg) => {
|
|
16
|
+
cfg.skills[key] = { enabled };
|
|
17
|
+
});
|
|
18
|
+
return { name: skillObj.name, scope, enabled };
|
|
19
|
+
}
|
|
20
|
+
export const stateEnable = defineLeaf({
|
|
21
|
+
name: 'enable',
|
|
22
|
+
help: {
|
|
23
|
+
name: 'skill state enable',
|
|
24
|
+
summary: 'enable a skill in the given scope',
|
|
25
|
+
params: [
|
|
26
|
+
{ kind: 'positional', name: 'name', required: true, constraint: 'Skill identifier. Same forms as skill read.' },
|
|
27
|
+
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Default: project if available, else user.' },
|
|
28
|
+
],
|
|
29
|
+
output: [
|
|
30
|
+
{ name: 'name', type: 'string', required: true, constraint: 'Resolved skill name.' },
|
|
31
|
+
{ name: 'scope', type: 'string', required: true, constraint: 'Scope where the enable was applied.' },
|
|
32
|
+
{ name: 'enabled', type: 'boolean', required: true, constraint: 'Always true.' },
|
|
33
|
+
],
|
|
34
|
+
outputKind: 'object',
|
|
35
|
+
effects: ['Writes the skill enable flag to config.json in the target scope.'],
|
|
36
|
+
},
|
|
37
|
+
run: async (input) => toggleSkill(input, true),
|
|
38
|
+
});
|
|
39
|
+
export const stateDisable = defineLeaf({
|
|
40
|
+
name: 'disable',
|
|
41
|
+
help: {
|
|
42
|
+
name: 'skill state disable',
|
|
43
|
+
summary: 'disable a skill in the given scope, hiding it from list and agent discovery',
|
|
44
|
+
params: [
|
|
45
|
+
{ kind: 'positional', name: 'name', required: true, constraint: 'Skill identifier. Same forms as skill read.' },
|
|
46
|
+
{ kind: 'flag', name: 'scope', type: 'enum', choices: ['user', 'project'], required: false, constraint: 'Default: project if available, else user.' },
|
|
47
|
+
],
|
|
48
|
+
output: [
|
|
49
|
+
{ name: 'name', type: 'string', required: true, constraint: 'Resolved skill name.' },
|
|
50
|
+
{ name: 'scope', type: 'string', required: true, constraint: 'Scope where the disable was applied.' },
|
|
51
|
+
{ name: 'enabled', type: 'boolean', required: true, constraint: 'Always false.' },
|
|
52
|
+
],
|
|
53
|
+
outputKind: 'object',
|
|
54
|
+
effects: ['Writes the skill disable flag to config.json in the target scope.'],
|
|
55
|
+
},
|
|
56
|
+
run: async (input) => toggleSkill(input, false),
|
|
57
|
+
});
|
|
58
|
+
export const stateBranch = defineBranch({
|
|
59
|
+
name: 'state',
|
|
60
|
+
help: {
|
|
61
|
+
name: 'skill state',
|
|
62
|
+
summary: 'enable or disable skills',
|
|
63
|
+
children: [
|
|
64
|
+
{ name: 'enable', desc: 'enable a skill', useWhen: 'making a previously disabled skill available again' },
|
|
65
|
+
{ name: 'disable', desc: 'disable a skill', useWhen: 'hiding a skill from list and agent discovery without removing it' },
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
children: [stateEnable, stateDisable],
|
|
69
|
+
});
|