@dsalazardev/ai-hub-cli 0.1.0 → 0.2.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/dist/catalog.d.ts +76 -0
- package/dist/catalog.js +321 -0
- package/dist/cli/context.js +5 -0
- package/dist/cli/doctor.js +91 -9
- package/dist/cli/items.d.ts +1 -1
- package/dist/cli/items.js +1 -1
- package/dist/cli/maintenance.js +93 -21
- package/dist/cli/memory.js +5 -0
- package/dist/cli/onboarding.d.ts +20 -1
- package/dist/cli/onboarding.js +334 -52
- package/dist/cli/run.js +5 -3
- package/dist/concurrency.d.ts +5 -0
- package/dist/concurrency.js +23 -0
- package/dist/engine/gates.js +53 -15
- package/dist/manifest/manifest.d.ts +17 -0
- package/dist/manifest/manifest.js +19 -1
- package/dist/presets/materialize.d.ts +9 -1
- package/dist/presets/materialize.js +102 -11
- package/dist/sync/render.js +5 -1
- package/dist/sync/sync.js +20 -20
- package/manifest.json +10 -1
- package/package.json +2 -1
- package/presets/angular-express/preset.yaml +2 -4
- package/presets/base/preset.yaml +1 -3
- package/presets/generic-ts/preset.yaml +1 -3
- package/presets/python/preset.yaml +1 -3
- package/rules/skill-quality/skill-behavior-rubric.md +63 -0
- package/rules/skill-quality/skill-harden-rubric.md +102 -0
- package/rules/skill-quality/skill-migration-brief.md +200 -0
- package/rules/skill-quality/skill-rubric.md +40 -0
- package/schema/manifest.schema.json +12 -0
package/dist/cli/maintenance.js
CHANGED
|
@@ -5,15 +5,25 @@ import { createBackup, listBackups, purgeGenerated, restoreBackup } from '../bac
|
|
|
5
5
|
import { readAiHubRc } from '../paths.js';
|
|
6
6
|
import { loadManifestContext, writeInventory } from '../manifest/manifest.js';
|
|
7
7
|
import { findPresetAsset } from '../presets/resolve.js';
|
|
8
|
-
import { ENGINE_AGENTS, readScaffold, SCAFFOLD_RELATIVE } from '../presets/materialize.js';
|
|
8
|
+
import { ENGINE_AGENTS, materializeAgent, materializeSkill, readScaffold, SCAFFOLD_RELATIVE, } from '../presets/materialize.js';
|
|
9
9
|
import { ENGINE_SKILLS_DIR } from '../engine/engine.js';
|
|
10
|
+
import { catalogSeal, hasFullDimension } from '../catalog.js';
|
|
10
11
|
import { readLock } from '../sync/lock.js';
|
|
11
12
|
import { runSync } from '../sync/sync.js';
|
|
12
|
-
import { cliVersion, writeManifest } from './onboarding.js';
|
|
13
|
+
import { cliVersion, cmdInstall, writeManifest } from './onboarding.js';
|
|
13
14
|
import { splitItem } from './items.js';
|
|
14
15
|
async function exists(file) {
|
|
15
16
|
return fs.access(file).then(() => true, () => false);
|
|
16
17
|
}
|
|
18
|
+
function markCustom(manifest, dimension) {
|
|
19
|
+
manifest[dimension].selection = 'custom';
|
|
20
|
+
if (hasFullDimension(manifest)) {
|
|
21
|
+
manifest.catalog = catalogSeal(manifest);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
delete manifest.catalog;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
17
27
|
export async function cmdList(args, ctx) {
|
|
18
28
|
let context;
|
|
19
29
|
try {
|
|
@@ -24,11 +34,18 @@ export async function cmdList(args, ctx) {
|
|
|
24
34
|
return 1;
|
|
25
35
|
}
|
|
26
36
|
const { manifest, engine, preset } = context;
|
|
37
|
+
const mode = manifest.skills.selection ?? 'preset';
|
|
27
38
|
const data = {
|
|
28
39
|
project: manifest.project,
|
|
29
40
|
preset: { id: preset.id, version: preset.version, chain: preset.chain },
|
|
30
41
|
targets: manifest.targets,
|
|
42
|
+
selection: {
|
|
43
|
+
skills: manifest.skills.selection ?? 'preset',
|
|
44
|
+
agents: manifest.agents.selection ?? 'preset',
|
|
45
|
+
rules: manifest.rules.selection ?? 'preset',
|
|
46
|
+
},
|
|
31
47
|
commands: engine.commands.map((command) => ({ id: command.id, lane: command.lane, role: command.role })),
|
|
48
|
+
nativeSkills: engine.commands.map((command) => command.skill),
|
|
32
49
|
skills: manifest.skills.publish,
|
|
33
50
|
agents: manifest.agents.publish,
|
|
34
51
|
rules: manifest.rules.publish,
|
|
@@ -40,10 +57,11 @@ export async function cmdList(args, ctx) {
|
|
|
40
57
|
ctx.io.out(`project: ${manifest.project.id} (${manifest.project.name})`);
|
|
41
58
|
ctx.io.out(`preset: ${preset.id}@${preset.version} [${preset.chain.join(' -> ')}]`);
|
|
42
59
|
ctx.io.out(`targets: ${manifest.targets.join(', ')}`);
|
|
60
|
+
ctx.io.out(`selection: skills=${data.selection.skills} agents=${data.selection.agents} rules=${data.selection.rules}`);
|
|
43
61
|
ctx.io.out(`commands (${data.commands.length}): ${data.commands.map((command) => command.id).join(', ')}`);
|
|
44
|
-
ctx.io.out(`skills
|
|
62
|
+
ctx.io.out(`skills: 17 nativas + ${data.skills.length} de proyecto (${mode})`);
|
|
45
63
|
ctx.io.out(`agents (${data.agents.length}): ${data.agents.join(', ')}`);
|
|
46
|
-
ctx.io.out(`rules
|
|
64
|
+
ctx.io.out(`rules: ${data.rules.length + 1} universales (${data.rules.length} materializadas + preflight)`);
|
|
47
65
|
return 0;
|
|
48
66
|
}
|
|
49
67
|
export async function cmdAdd(args, ctx) {
|
|
@@ -52,6 +70,10 @@ export async function cmdAdd(args, ctx) {
|
|
|
52
70
|
ctx.io.err('uso: ai-hub add <skill|agent> <id>');
|
|
53
71
|
return 2;
|
|
54
72
|
}
|
|
73
|
+
if (parsed.kind === 'rule') {
|
|
74
|
+
ctx.io.err('las reglas universales se seleccionan con --exclude rule:<id> en init; no se añaden con add');
|
|
75
|
+
return 2;
|
|
76
|
+
}
|
|
55
77
|
let context;
|
|
56
78
|
try {
|
|
57
79
|
context = await loadManifestContext(ctx.root);
|
|
@@ -64,8 +86,17 @@ export async function cmdAdd(args, ctx) {
|
|
|
64
86
|
const id = parsed.id;
|
|
65
87
|
const engineSkill = context.engine.commands.some((command) => command.skill === id);
|
|
66
88
|
const catalogSkill = await exists(path.join(ENGINE_SKILLS_DIR, id, 'SKILL.md'));
|
|
89
|
+
const catalogAgent = await exists(path.join(ENGINE_AGENTS, `${id}.md`));
|
|
67
90
|
const kind = parsed.kind ??
|
|
68
|
-
(preset.skills.includes(id) || engineSkill || catalogSkill
|
|
91
|
+
(preset.skills.includes(id) || engineSkill || catalogSkill
|
|
92
|
+
? 'skill'
|
|
93
|
+
: catalogAgent || preset.agents.includes(id)
|
|
94
|
+
? 'agent'
|
|
95
|
+
: null);
|
|
96
|
+
if (kind === null) {
|
|
97
|
+
ctx.io.err(`no existe "${id}" en el preset ${preset.id} ni en el catálogo del motor`);
|
|
98
|
+
return 1;
|
|
99
|
+
}
|
|
69
100
|
if (kind === 'skill') {
|
|
70
101
|
if (engineSkill) {
|
|
71
102
|
ctx.io.out(`"${id}" ya forma parte de las 17 skills del motor`);
|
|
@@ -84,29 +115,25 @@ export async function cmdAdd(args, ctx) {
|
|
|
84
115
|
ctx.io.out(JSON.stringify({ dryRun: true, action: 'add', kind, id, destination }, null, 2));
|
|
85
116
|
return 0;
|
|
86
117
|
}
|
|
87
|
-
|
|
88
|
-
await fs.cp(source, destination, { recursive: true });
|
|
89
|
-
}
|
|
118
|
+
await materializeSkill(ctx.root, id, source, fromPreset ? `@preset/${preset.id}/skills/${id}` : `@engine/skills/shared/${id}`);
|
|
90
119
|
manifest.skills.publish = [...new Set([...manifest.skills.publish, id])];
|
|
91
120
|
}
|
|
92
121
|
else {
|
|
93
|
-
if (!preset.agents.includes(id)) {
|
|
94
|
-
ctx.io.err(`el agente "${id}" no pertenece al preset ${preset.id}`);
|
|
95
|
-
return 1;
|
|
96
|
-
}
|
|
97
122
|
const fromPreset = await findPresetAsset(preset.chain, `agents/${id}.md`);
|
|
98
123
|
const source = fromPreset ?? path.join(ENGINE_AGENTS, `${id}.md`);
|
|
124
|
+
if (!(await exists(source))) {
|
|
125
|
+
ctx.io.err(`no existe el agente "${id}" en el catálogo ${ENGINE_AGENTS}`);
|
|
126
|
+
return 1;
|
|
127
|
+
}
|
|
99
128
|
const destination = path.join(ctx.root, manifest.agents.source, `${id}.md`);
|
|
100
129
|
if (args.flags['dry-run']) {
|
|
101
130
|
ctx.io.out(JSON.stringify({ dryRun: true, action: 'add', kind, id, destination }, null, 2));
|
|
102
131
|
return 0;
|
|
103
132
|
}
|
|
104
|
-
|
|
105
|
-
await fs.mkdir(path.dirname(destination), { recursive: true });
|
|
106
|
-
await fs.copyFile(source, destination);
|
|
107
|
-
}
|
|
133
|
+
await materializeAgent(ctx.root, id, source, fromPreset ? `@preset/${preset.id}/agents/${id}.md` : `@engine/agents/${id}.md`);
|
|
108
134
|
manifest.agents.publish = [...new Set([...manifest.agents.publish, id])];
|
|
109
135
|
}
|
|
136
|
+
markCustom(manifest, kind === 'agent' ? 'agents' : 'skills');
|
|
110
137
|
await writeManifest(ctx.root, manifest);
|
|
111
138
|
const sync = await runSync(ctx.root, { check: false });
|
|
112
139
|
await writeInventory(ctx.root);
|
|
@@ -116,7 +143,7 @@ export async function cmdAdd(args, ctx) {
|
|
|
116
143
|
export async function cmdUninstall(args, ctx) {
|
|
117
144
|
const parsed = splitItem(args);
|
|
118
145
|
if (!parsed.id) {
|
|
119
|
-
ctx.io.err('uso: ai-hub uninstall <skill|agent> <id>');
|
|
146
|
+
ctx.io.err('uso: ai-hub uninstall <skill|agent|rule> <id>');
|
|
120
147
|
return 2;
|
|
121
148
|
}
|
|
122
149
|
let context;
|
|
@@ -129,11 +156,22 @@ export async function cmdUninstall(args, ctx) {
|
|
|
129
156
|
}
|
|
130
157
|
const { manifest } = context;
|
|
131
158
|
const id = parsed.id;
|
|
132
|
-
const kind = parsed.kind ??
|
|
159
|
+
const kind = parsed.kind ??
|
|
160
|
+
(manifest.skills.publish.includes(id)
|
|
161
|
+
? 'skill'
|
|
162
|
+
: manifest.agents.publish.includes(id)
|
|
163
|
+
? 'agent'
|
|
164
|
+
: manifest.rules.publish.includes(id) || id === 'skill-quality'
|
|
165
|
+
? 'rule'
|
|
166
|
+
: 'agent');
|
|
133
167
|
if (kind === 'skill' && context.engine.commands.some((command) => command.skill === id)) {
|
|
134
168
|
ctx.io.err(`"${id}" es una skill del motor y no se puede desinstalar`);
|
|
135
169
|
return 1;
|
|
136
170
|
}
|
|
171
|
+
if (kind === 'rule' && (id === 'core' || id === 'preflight')) {
|
|
172
|
+
ctx.io.err(`la regla base "${id}" no se puede desinstalar`);
|
|
173
|
+
return 1;
|
|
174
|
+
}
|
|
137
175
|
if (args.flags['dry-run']) {
|
|
138
176
|
ctx.io.out(JSON.stringify({ dryRun: true, action: 'uninstall', kind, id }, null, 2));
|
|
139
177
|
return 0;
|
|
@@ -142,10 +180,22 @@ export async function cmdUninstall(args, ctx) {
|
|
|
142
180
|
if (kind === 'skill') {
|
|
143
181
|
manifest.skills.publish = manifest.skills.publish.filter((skill) => skill !== id);
|
|
144
182
|
await removeOwned(ctx.root, path.join(manifest.skills.source, id), true);
|
|
183
|
+
markCustom(manifest, 'skills');
|
|
145
184
|
}
|
|
146
|
-
else {
|
|
185
|
+
else if (kind === 'agent') {
|
|
147
186
|
manifest.agents.publish = manifest.agents.publish.filter((agent) => agent !== id);
|
|
148
187
|
await removeOwned(ctx.root, path.join(manifest.agents.source, `${id}.md`), false);
|
|
188
|
+
markCustom(manifest, 'agents');
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
const removed = id === 'skill-quality'
|
|
192
|
+
? manifest.rules.publish.filter((rule) => rule.startsWith('skill-quality/'))
|
|
193
|
+
: manifest.rules.publish.filter((rule) => rule === id);
|
|
194
|
+
manifest.rules.publish = manifest.rules.publish.filter((rule) => !removed.includes(rule));
|
|
195
|
+
for (const rule of removed) {
|
|
196
|
+
await removeOwned(ctx.root, path.join(manifest.rules.source, `${rule}.md`), false);
|
|
197
|
+
}
|
|
198
|
+
markCustom(manifest, 'rules');
|
|
149
199
|
}
|
|
150
200
|
await writeManifest(ctx.root, manifest);
|
|
151
201
|
const sync = await runSync(ctx.root, { check: false });
|
|
@@ -159,9 +209,10 @@ async function removeOwned(root, relative, directory) {
|
|
|
159
209
|
return;
|
|
160
210
|
}
|
|
161
211
|
const record = await readScaffold(root);
|
|
212
|
+
const relativePosix = relative.split(path.sep).join('/');
|
|
162
213
|
if (directory) {
|
|
163
214
|
const files = record.files;
|
|
164
|
-
const prefix = `${
|
|
215
|
+
const prefix = `${relativePosix}/`;
|
|
165
216
|
const owned = Object.entries(files).filter(([name]) => name.startsWith(prefix));
|
|
166
217
|
if (owned.length === 0) {
|
|
167
218
|
const marker = await fs.readFile(path.join(absolute, 'SKILL.md'), 'utf8').catch(() => '');
|
|
@@ -173,7 +224,8 @@ async function removeOwned(root, relative, directory) {
|
|
|
173
224
|
return;
|
|
174
225
|
}
|
|
175
226
|
const text = await fs.readFile(absolute, 'utf8').catch(() => null);
|
|
176
|
-
|
|
227
|
+
const owned = record.files[relativePosix] !== undefined;
|
|
228
|
+
if (text === null || owned || text.includes('GENERADO por ai-hub')) {
|
|
177
229
|
await fs.rm(absolute, { force: true });
|
|
178
230
|
}
|
|
179
231
|
}
|
|
@@ -256,6 +308,26 @@ export async function cmdUpgrade(args, ctx) {
|
|
|
256
308
|
return 1;
|
|
257
309
|
}
|
|
258
310
|
}
|
|
311
|
+
let context;
|
|
312
|
+
try {
|
|
313
|
+
context = await loadManifestContext(ctx.root);
|
|
314
|
+
}
|
|
315
|
+
catch (error) {
|
|
316
|
+
ctx.io.err(error.message);
|
|
317
|
+
return 1;
|
|
318
|
+
}
|
|
319
|
+
const refreshFull = Boolean(args.flags.full) ||
|
|
320
|
+
(hasFullDimension(context.manifest) && !args.flags['preset-only']);
|
|
321
|
+
if (refreshFull) {
|
|
322
|
+
const code = await cmdInstall({
|
|
323
|
+
positionals: [],
|
|
324
|
+
flags: args.flags.full ? { full: true } : {},
|
|
325
|
+
}, ctx);
|
|
326
|
+
if (code === 0) {
|
|
327
|
+
ctx.io.out('upgrade: dimensiones full actualizadas y sello refrescado');
|
|
328
|
+
}
|
|
329
|
+
return code;
|
|
330
|
+
}
|
|
259
331
|
const result = await runSync(ctx.root, { check: false });
|
|
260
332
|
ctx.io.out(`upgrade: ${result.written.length} archivos regenerados`);
|
|
261
333
|
return 0;
|
package/dist/cli/memory.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { appendLedger, queryLedger, readLedger, resumeLedger, } from '../harness/memory/ledger.js';
|
|
2
|
+
import { isEngineCheckout } from '../source-checkout.js';
|
|
2
3
|
import { flagList, flagString } from './args.js';
|
|
3
4
|
function numberFlag(args, name) {
|
|
4
5
|
const raw = flagString(args, name);
|
|
@@ -9,6 +10,10 @@ function numberFlag(args, name) {
|
|
|
9
10
|
return Number.isFinite(value) ? value : null;
|
|
10
11
|
}
|
|
11
12
|
export async function cmdMemory(args, ctx) {
|
|
13
|
+
if (!(await isEngineCheckout(ctx.root))) {
|
|
14
|
+
ctx.io.out('memory: no aplica en instalación');
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
12
17
|
const sub = args.positionals[0] ?? 'list';
|
|
13
18
|
if (sub === 'add') {
|
|
14
19
|
const sessionId = flagString(args, 'session') ?? process.env.AI_HUB_SESSION ?? 'local';
|
package/dist/cli/onboarding.d.ts
CHANGED
|
@@ -1,11 +1,30 @@
|
|
|
1
1
|
import { type ProjectManifest } from '../manifest/manifest.js';
|
|
2
|
+
import { type SelectionMode } from '../catalog.js';
|
|
2
3
|
import type { CliContext, ParsedArgs } from './types.js';
|
|
3
4
|
export declare function cliVersion(): Promise<string>;
|
|
4
|
-
export interface
|
|
5
|
+
export interface AssetPlan {
|
|
6
|
+
selection: SelectionMode;
|
|
7
|
+
selections: {
|
|
8
|
+
skills: SelectionMode;
|
|
9
|
+
agents: SelectionMode;
|
|
10
|
+
rules: SelectionMode;
|
|
11
|
+
};
|
|
12
|
+
skills: string[];
|
|
13
|
+
agents: string[];
|
|
14
|
+
rules: string[];
|
|
15
|
+
destinations: number;
|
|
16
|
+
files: number;
|
|
17
|
+
bytes: number;
|
|
18
|
+
}
|
|
19
|
+
export interface Plan extends AssetPlan {
|
|
5
20
|
command: 'init';
|
|
6
21
|
project: string;
|
|
7
22
|
preset: string;
|
|
8
23
|
targets: string[];
|
|
24
|
+
skillCount: number;
|
|
25
|
+
agentCount: number;
|
|
26
|
+
ruleCount: number;
|
|
27
|
+
mcp: boolean;
|
|
9
28
|
migrate: boolean;
|
|
10
29
|
write: string[];
|
|
11
30
|
hash: string;
|