youmd 0.8.7 → 0.8.11
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/commands/agent.d.ts +4 -0
- package/dist/commands/agent.d.ts.map +1 -1
- package/dist/commands/agent.js +4 -0
- package/dist/commands/agent.js.map +1 -1
- package/dist/commands/machine.d.ts.map +1 -1
- package/dist/commands/machine.js +145 -2
- package/dist/commands/machine.js.map +1 -1
- package/dist/commands/project.d.ts.map +1 -1
- package/dist/commands/project.js +31 -0
- package/dist/commands/project.js.map +1 -1
- package/dist/commands/skill.d.ts.map +1 -1
- package/dist/commands/skill.js +679 -1
- package/dist/commands/skill.js.map +1 -1
- package/dist/commands/stack.d.ts.map +1 -1
- package/dist/commands/stack.js +97 -1
- package/dist/commands/stack.js.map +1 -1
- package/dist/commands/sync.d.ts.map +1 -1
- package/dist/commands/sync.js +84 -3
- package/dist/commands/sync.js.map +1 -1
- package/dist/index.js +10 -2
- package/dist/index.js.map +1 -1
- package/dist/lib/api.d.ts +179 -0
- package/dist/lib/api.d.ts.map +1 -1
- package/dist/lib/api.js +41 -4
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/machine-bootstrap-prompt.d.ts.map +1 -1
- package/dist/lib/machine-bootstrap-prompt.js +26 -4
- package/dist/lib/machine-bootstrap-prompt.js.map +1 -1
- package/dist/lib/machine-verify.d.ts +27 -0
- package/dist/lib/machine-verify.d.ts.map +1 -1
- package/dist/lib/machine-verify.js +84 -0
- package/dist/lib/machine-verify.js.map +1 -1
- package/dist/lib/realtime-sync.d.ts +26 -0
- package/dist/lib/realtime-sync.d.ts.map +1 -1
- package/dist/lib/realtime-sync.js +44 -1
- package/dist/lib/realtime-sync.js.map +1 -1
- package/dist/mcp/registry.d.ts +2 -1
- package/dist/mcp/registry.d.ts.map +1 -1
- package/dist/mcp/registry.js +63 -1
- package/dist/mcp/registry.js.map +1 -1
- package/package.json +1 -1
- package/scripts/local-agent-stack-inventory.mjs +1150 -0
- package/scripts/skillstack-sync/README.md +11 -3
package/dist/commands/skill.js
CHANGED
|
@@ -48,6 +48,7 @@ const path = __importStar(require("path"));
|
|
|
48
48
|
const os = __importStar(require("os"));
|
|
49
49
|
const readline = __importStar(require("readline"));
|
|
50
50
|
const yaml = __importStar(require("js-yaml"));
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
51
52
|
const chalk_1 = __importDefault(require("chalk"));
|
|
52
53
|
const skill_catalog_1 = require("../lib/skill-catalog");
|
|
53
54
|
const skills_1 = require("../lib/skills");
|
|
@@ -110,6 +111,300 @@ function truncateAtWord(text, max) {
|
|
|
110
111
|
const lastSpace = truncated.lastIndexOf(" ");
|
|
111
112
|
return (lastSpace > max * 0.5 ? truncated.slice(0, lastSpace) : truncated) + "...";
|
|
112
113
|
}
|
|
114
|
+
function sourcePrefix(source) {
|
|
115
|
+
const idx = source.indexOf(":");
|
|
116
|
+
return idx > 0 ? source.slice(0, idx) : "path";
|
|
117
|
+
}
|
|
118
|
+
function parseFlagArgs(args) {
|
|
119
|
+
const positional = [];
|
|
120
|
+
const flags = {};
|
|
121
|
+
for (let i = 0; i < args.length; i += 1) {
|
|
122
|
+
const arg = args[i];
|
|
123
|
+
if (!arg.startsWith("--")) {
|
|
124
|
+
positional.push(arg);
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const eq = arg.indexOf("=");
|
|
128
|
+
if (eq > 0) {
|
|
129
|
+
flags[arg.slice(2, eq)] = arg.slice(eq + 1);
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
|
+
const key = arg.slice(2);
|
|
133
|
+
const next = args[i + 1];
|
|
134
|
+
if (next && !next.startsWith("--")) {
|
|
135
|
+
flags[key] = next;
|
|
136
|
+
i += 1;
|
|
137
|
+
}
|
|
138
|
+
else {
|
|
139
|
+
flags[key] = true;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return { positional, flags };
|
|
143
|
+
}
|
|
144
|
+
function flagString(flags, key) {
|
|
145
|
+
const value = flags[key];
|
|
146
|
+
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
147
|
+
}
|
|
148
|
+
function resolveInventoryScript() {
|
|
149
|
+
const candidates = [
|
|
150
|
+
process.env.YOUMD_AGENT_STACK_INVENTORY_SCRIPT,
|
|
151
|
+
path.join(os.homedir(), ".agent-shared", "claude-skills", "agent-stack-inventory", "scripts", "local-agent-stack-inventory.mjs"),
|
|
152
|
+
path.join(os.homedir(), ".youmd", "skills", "agent-stack-inventory", "scripts", "local-agent-stack-inventory.mjs"),
|
|
153
|
+
path.resolve(__dirname, "../../scripts/local-agent-stack-inventory.mjs"),
|
|
154
|
+
path.resolve(__dirname, "../../../scripts/local-agent-stack-inventory.mjs"),
|
|
155
|
+
].filter(Boolean);
|
|
156
|
+
for (const candidate of candidates) {
|
|
157
|
+
if (fs.existsSync(candidate))
|
|
158
|
+
return candidate;
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
function readInventorySnapshot(file) {
|
|
163
|
+
return JSON.parse(fs.readFileSync(file, "utf-8"));
|
|
164
|
+
}
|
|
165
|
+
function inventoryNames(snapshot) {
|
|
166
|
+
return new Set((snapshot.uniqueSkills || []).map((skill) => skill.name).filter(Boolean));
|
|
167
|
+
}
|
|
168
|
+
function sortInventoryJsonPaths(files) {
|
|
169
|
+
return files.sort((a, b) => {
|
|
170
|
+
const delta = fs.statSync(b).mtimeMs - fs.statSync(a).mtimeMs;
|
|
171
|
+
if (delta !== 0)
|
|
172
|
+
return delta;
|
|
173
|
+
return path.basename(b).localeCompare(path.basename(a));
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
function findLatestInventoryJson(outDir, sinceMs = 0) {
|
|
177
|
+
if (!fs.existsSync(outDir))
|
|
178
|
+
return null;
|
|
179
|
+
const files = sortInventoryJsonPaths(fs.readdirSync(outDir)
|
|
180
|
+
.filter((file) => /^local-agent-stack-inventory-.*\.json$/.test(file))
|
|
181
|
+
.map((file) => path.join(outDir, file))
|
|
182
|
+
.filter((file) => {
|
|
183
|
+
try {
|
|
184
|
+
return fs.statSync(file).mtimeMs >= sinceMs - 1000;
|
|
185
|
+
}
|
|
186
|
+
catch {
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}));
|
|
190
|
+
return files[0] || null;
|
|
191
|
+
}
|
|
192
|
+
function renderInventoryTotals(totals) {
|
|
193
|
+
const rows = [
|
|
194
|
+
["unique skill names", totals.uniqueSkillNames],
|
|
195
|
+
["real SKILL.md files", totals.uniqueRealSkillFiles],
|
|
196
|
+
["direct host exposures", totals.directExposureSkillRecords],
|
|
197
|
+
["You.md catalog skills", totals.youmdCatalogSkills],
|
|
198
|
+
["missing from You.md catalog", totals.missingFromYoumdCatalog],
|
|
199
|
+
["DRY review cases", totals.duplicateNameDifferentRealpaths],
|
|
200
|
+
["healthy mirror clusters", totals.sameRealpathMirrors],
|
|
201
|
+
["project/context signals", totals.projectSignals],
|
|
202
|
+
];
|
|
203
|
+
const maxLabel = Math.max(...rows.map(([label]) => label.length));
|
|
204
|
+
for (const [label, value] of rows) {
|
|
205
|
+
if (typeof value !== "number")
|
|
206
|
+
continue;
|
|
207
|
+
console.log(` ${DIM(label.padEnd(maxLabel + 2))}${chalk_1.default.cyan(value.toLocaleString())}`);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
function renderInventoryPhaseTimings(snapshot) {
|
|
211
|
+
const timings = (snapshot.phaseTimings || [])
|
|
212
|
+
.filter((phase) => typeof phase.durationMs === "number")
|
|
213
|
+
.sort((a, b) => (b.durationMs || 0) - (a.durationMs || 0))
|
|
214
|
+
.slice(0, 4);
|
|
215
|
+
if (timings.length === 0)
|
|
216
|
+
return;
|
|
217
|
+
console.log(` ${DIM("slowest phases")}`);
|
|
218
|
+
for (const phase of timings) {
|
|
219
|
+
const seconds = Math.max(0, Math.round((phase.durationMs || 0) / 1000));
|
|
220
|
+
const label = phase.label || phase.phase || "unknown phase";
|
|
221
|
+
const status = phase.failed ? ACCENT(" failed") : "";
|
|
222
|
+
console.log(` ${DIM(label.padEnd(50))}${chalk_1.default.cyan(`${seconds}s`)}${status}`);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function runInventoryScript(scriptArgs, spinner) {
|
|
226
|
+
return new Promise((resolve, reject) => {
|
|
227
|
+
const child = (0, child_process_1.spawn)(process.execPath, scriptArgs, {
|
|
228
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
229
|
+
});
|
|
230
|
+
const stdout = [];
|
|
231
|
+
const stderr = [];
|
|
232
|
+
let progressBuffer = "";
|
|
233
|
+
child.stdout.on("data", (chunk) => {
|
|
234
|
+
stdout.push(chunk);
|
|
235
|
+
});
|
|
236
|
+
child.stderr.on("data", (chunk) => {
|
|
237
|
+
const text = chunk.toString("utf-8");
|
|
238
|
+
stderr.push(chunk);
|
|
239
|
+
progressBuffer += text;
|
|
240
|
+
const lines = progressBuffer.split(/\r?\n/);
|
|
241
|
+
progressBuffer = lines.pop() || "";
|
|
242
|
+
for (const line of lines) {
|
|
243
|
+
const trimmed = line.trim();
|
|
244
|
+
if (!trimmed.startsWith("youmd-inventory-progress "))
|
|
245
|
+
continue;
|
|
246
|
+
try {
|
|
247
|
+
const event = JSON.parse(trimmed.slice("youmd-inventory-progress ".length));
|
|
248
|
+
if (event.label && event.event === "start") {
|
|
249
|
+
spinner.update(event.label);
|
|
250
|
+
}
|
|
251
|
+
else if (event.label && event.event === "failed") {
|
|
252
|
+
spinner.update(`${event.label} failed`);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Non-JSON stderr is collected for failure output below.
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
});
|
|
260
|
+
child.on("error", reject);
|
|
261
|
+
child.on("close", (code) => {
|
|
262
|
+
if (code === 0) {
|
|
263
|
+
resolve(Buffer.concat(stdout).toString("utf-8"));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const detail = Buffer.concat(stderr).toString("utf-8").trim();
|
|
267
|
+
reject(new Error(detail || `inventory exited with code ${code}`));
|
|
268
|
+
});
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
function compactDate(value) {
|
|
272
|
+
if (typeof value === "string" && value)
|
|
273
|
+
return value.replace(/\.\d{3}Z$/, "Z");
|
|
274
|
+
if (typeof value === "number" && Number.isFinite(value))
|
|
275
|
+
return new Date(value).toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
276
|
+
return "unknown";
|
|
277
|
+
}
|
|
278
|
+
function inventoryTotalsFromSynced(row) {
|
|
279
|
+
return {
|
|
280
|
+
uniqueSkillNames: row.uniqueSkillNames,
|
|
281
|
+
uniqueRealSkillFiles: row.uniqueRealSkillFiles,
|
|
282
|
+
directExposureSkillRecords: row.directExposureSkillRecords,
|
|
283
|
+
canonicalSkillFiles: row.canonicalSkillFiles,
|
|
284
|
+
youmdCatalogSkills: row.youmdCatalogSkills,
|
|
285
|
+
missingFromYoumdCatalog: row.missingFromYoumdCatalog,
|
|
286
|
+
duplicateNameDifferentRealpaths: row.duplicateNameDifferentRealpaths,
|
|
287
|
+
sameRealpathMirrors: row.sameRealpathMirrors,
|
|
288
|
+
projectSignals: row.projectSignals,
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
function inventoryDelta(localValue, remoteValue) {
|
|
292
|
+
if (typeof localValue !== "number" || typeof remoteValue !== "number")
|
|
293
|
+
return DIM("?");
|
|
294
|
+
const delta = remoteValue - localValue;
|
|
295
|
+
if (delta === 0)
|
|
296
|
+
return chalk_1.default.green("0");
|
|
297
|
+
return delta > 0 ? chalk_1.default.green(`+${delta}`) : ACCENT(String(delta));
|
|
298
|
+
}
|
|
299
|
+
function renderInventoryDrift(drift) {
|
|
300
|
+
if (!drift?.baseline)
|
|
301
|
+
return;
|
|
302
|
+
console.log("");
|
|
303
|
+
console.log(` ${DIM("drift baseline")} ${chalk_1.default.cyan(drift.baseline.hostName)}${DIM(` · ${compactDate(drift.baseline.generatedAt)} · ${drift.baseline.counts.uniqueSkillNames} skills`)}`);
|
|
304
|
+
console.log(` ${DIM("drift summary ")} ` +
|
|
305
|
+
`${chalk_1.default.cyan(String(drift.summary.machineCount))} machines · ` +
|
|
306
|
+
`${drift.summary.driftCount > 0 ? ACCENT(String(drift.summary.driftCount)) : chalk_1.default.green("0")} drift · ` +
|
|
307
|
+
`${drift.summary.staleCount > 0 ? ACCENT(String(drift.summary.staleCount)) : chalk_1.default.green("0")} stale · ` +
|
|
308
|
+
`${drift.summary.unsafeCount > 0 ? ACCENT(String(drift.summary.unsafeCount)) : chalk_1.default.green("0")} unsafe`);
|
|
309
|
+
const issueRows = drift.machines.filter((machine) => machine.status !== "baseline" && machine.issues.length > 0).slice(0, 4);
|
|
310
|
+
for (const machine of issueRows) {
|
|
311
|
+
console.log(` ${ACCENT(machine.status.padEnd(8))} ${chalk_1.default.cyan(machine.hostName)} ${DIM(machine.issues.slice(0, 2).join("; "))}`);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
function inventoryHostName(snapshot) {
|
|
315
|
+
if (typeof snapshot.host === "string" && snapshot.host.trim())
|
|
316
|
+
return snapshot.host.trim();
|
|
317
|
+
if (snapshot.host && typeof snapshot.host === "object" && snapshot.host.hostname) {
|
|
318
|
+
return snapshot.host.hostname;
|
|
319
|
+
}
|
|
320
|
+
return os.hostname();
|
|
321
|
+
}
|
|
322
|
+
function inventorySnapshotLabel(snapshot, fallback) {
|
|
323
|
+
const host = inventoryHostName(snapshot);
|
|
324
|
+
return host || fallback;
|
|
325
|
+
}
|
|
326
|
+
function safeInventoryCount(value) {
|
|
327
|
+
return Number.isFinite(value) ? Math.max(0, Math.trunc(value)) : 0;
|
|
328
|
+
}
|
|
329
|
+
function inventorySampleNames(rows) {
|
|
330
|
+
if (!Array.isArray(rows))
|
|
331
|
+
return [];
|
|
332
|
+
return rows
|
|
333
|
+
.flatMap((row) => {
|
|
334
|
+
if (typeof row === "string")
|
|
335
|
+
return [row];
|
|
336
|
+
if (row?.name)
|
|
337
|
+
return [row.name];
|
|
338
|
+
return [];
|
|
339
|
+
})
|
|
340
|
+
.map((name) => name.trim())
|
|
341
|
+
.filter(Boolean)
|
|
342
|
+
.slice(0, 24);
|
|
343
|
+
}
|
|
344
|
+
function buildAgentStackInventorySyncPayload(snapshot, jsonPath, htmlPath, syncRepo = true) {
|
|
345
|
+
const totals = snapshot.totals || {};
|
|
346
|
+
return {
|
|
347
|
+
inventorySchemaVersion: snapshot.inventorySchemaVersion || "local-agent-stack-inventory/v1",
|
|
348
|
+
generatedAt: snapshot.generatedAt || new Date().toISOString(),
|
|
349
|
+
hostName: inventoryHostName(snapshot),
|
|
350
|
+
platform: `${os.platform()} ${os.release()}`,
|
|
351
|
+
rootDir: snapshot.roots?.workspace || snapshot.repoRoot || process.cwd(),
|
|
352
|
+
secretValuesExposed: false,
|
|
353
|
+
reportJsonPath: jsonPath,
|
|
354
|
+
reportHtmlPath: htmlPath,
|
|
355
|
+
source: "youmd-cli",
|
|
356
|
+
agentName: "youmd skill inventory",
|
|
357
|
+
totals: {
|
|
358
|
+
uniqueSkillNames: safeInventoryCount(totals.uniqueSkillNames),
|
|
359
|
+
uniqueRealSkillFiles: safeInventoryCount(totals.uniqueRealSkillFiles),
|
|
360
|
+
directExposureSkillRecords: safeInventoryCount(totals.directExposureSkillRecords),
|
|
361
|
+
canonicalSkillFiles: safeInventoryCount(totals.canonicalSkillFiles),
|
|
362
|
+
youmdCatalogSkills: safeInventoryCount(totals.youmdCatalogSkills),
|
|
363
|
+
missingFromYoumdCatalog: safeInventoryCount(totals.missingFromYoumdCatalog),
|
|
364
|
+
duplicateNameDifferentRealpaths: safeInventoryCount(totals.duplicateNameDifferentRealpaths),
|
|
365
|
+
sameRealpathMirrors: safeInventoryCount(totals.sameRealpathMirrors),
|
|
366
|
+
projectSignals: safeInventoryCount(totals.projectSignals),
|
|
367
|
+
},
|
|
368
|
+
ownershipRollup: snapshot.ownershipRollup || {},
|
|
369
|
+
syncPolicyRollup: snapshot.syncPolicyRollup || {},
|
|
370
|
+
provenanceRollup: snapshot.provenanceRollup || {},
|
|
371
|
+
missingCatalogSamples: inventorySampleNames(snapshot.missingFromCatalog),
|
|
372
|
+
duplicateNameSamples: inventorySampleNames(snapshot.dryAudit?.duplicateNameDifferentRealpaths),
|
|
373
|
+
mirrorSamples: inventorySampleNames(snapshot.dryAudit?.sameRealpathMirrors),
|
|
374
|
+
syncRepo,
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function buildSkillSyncActivityMetadata(synced, errors) {
|
|
378
|
+
const catalog = (0, skill_catalog_1.readSkillCatalog)();
|
|
379
|
+
const entries = [];
|
|
380
|
+
for (const name of synced) {
|
|
381
|
+
const entry = (0, skill_catalog_1.findSkill)(catalog, name);
|
|
382
|
+
if (entry)
|
|
383
|
+
entries.push(entry);
|
|
384
|
+
}
|
|
385
|
+
const sharedSkills = entries
|
|
386
|
+
.filter((entry) => entry.source.startsWith("shared:"))
|
|
387
|
+
.map((entry) => entry.name)
|
|
388
|
+
.sort();
|
|
389
|
+
const sourcePrefixes = {};
|
|
390
|
+
const scopes = {};
|
|
391
|
+
for (const entry of entries) {
|
|
392
|
+
const prefix = sourcePrefix(entry.source);
|
|
393
|
+
sourcePrefixes[prefix] = (sourcePrefixes[prefix] ?? 0) + 1;
|
|
394
|
+
scopes[entry.scope] = (scopes[entry.scope] ?? 0) + 1;
|
|
395
|
+
}
|
|
396
|
+
return {
|
|
397
|
+
syncedCount: synced.length,
|
|
398
|
+
errorCount: errors.length,
|
|
399
|
+
skills: synced.slice(0, 20),
|
|
400
|
+
sharedSkillCount: sharedSkills.length,
|
|
401
|
+
sharedSkills: sharedSkills.slice(0, 20),
|
|
402
|
+
sourcePrefixes,
|
|
403
|
+
scopes,
|
|
404
|
+
canonicalSharedRoot: "~/.agent-shared/claude-skills",
|
|
405
|
+
secretValuesExposed: false,
|
|
406
|
+
};
|
|
407
|
+
}
|
|
113
408
|
// ─── Subcommands ──────────────────────────────────────────────────────
|
|
114
409
|
const RECOMMENDED_SKILLS = new Set([
|
|
115
410
|
"youstack-start",
|
|
@@ -428,6 +723,37 @@ async function syncSkillsCmd() {
|
|
|
428
723
|
console.log(` ${ACCENT("\u2717")} ${DIM(err)}`);
|
|
429
724
|
}
|
|
430
725
|
}
|
|
726
|
+
if ((0, config_1.isAuthenticated)()) {
|
|
727
|
+
try {
|
|
728
|
+
const metadata = buildSkillSyncActivityMetadata(result.synced, result.errors);
|
|
729
|
+
const sharedSkillCount = metadata.sharedSkillCount;
|
|
730
|
+
await (0, api_1.recordBrainActivity)({
|
|
731
|
+
activityId: `skill-sync:${os.hostname()}`,
|
|
732
|
+
source: "skill-sync",
|
|
733
|
+
channel: "skills",
|
|
734
|
+
kind: result.errors.length > 0 ? "warn" : sharedSkillCount > 0 ? "shared-skill-sync" : "synced",
|
|
735
|
+
status: result.errors.length > 0 ? "warn" : "ok",
|
|
736
|
+
title: sharedSkillCount > 0
|
|
737
|
+
? `${result.synced.length} skills synced · ${sharedSkillCount} shared`
|
|
738
|
+
: `${result.synced.length} skills synced`,
|
|
739
|
+
detail: result.errors.length > 0
|
|
740
|
+
? `${result.errors.length} sync warning${result.errors.length === 1 ? "" : "s"}`
|
|
741
|
+
: sharedSkillCount > 0
|
|
742
|
+
? `shared skills propagated from ~/.agent-shared: ${metadata.sharedSkills.slice(0, 5).join(", ")}`
|
|
743
|
+
: "installed skills re-rendered against the latest identity bundle",
|
|
744
|
+
sourceHost: os.hostname(),
|
|
745
|
+
sourceAgent: "youmd CLI",
|
|
746
|
+
sourceRuntime: process.version,
|
|
747
|
+
entityType: sharedSkillCount > 0 ? "sharedSkillSync" : "skillSync",
|
|
748
|
+
entityId: os.hostname(),
|
|
749
|
+
metadata,
|
|
750
|
+
});
|
|
751
|
+
}
|
|
752
|
+
catch (err) {
|
|
753
|
+
if (process.env.DEBUG)
|
|
754
|
+
console.error(`[skill sync] brain activity failed: ${err}`);
|
|
755
|
+
}
|
|
756
|
+
}
|
|
431
757
|
console.log("");
|
|
432
758
|
}
|
|
433
759
|
function addSkillCmd(args) {
|
|
@@ -708,6 +1034,7 @@ async function improveCmd() {
|
|
|
708
1034
|
const notInstalled = catalog.skills.filter((s) => !s.installed);
|
|
709
1035
|
// ─── Live outcome insights (L10) ──────────────────────────────────
|
|
710
1036
|
let liveInsights = null;
|
|
1037
|
+
const lowPerformers = [];
|
|
711
1038
|
if ((0, config_1.isAuthenticated)()) {
|
|
712
1039
|
const insightSpinner = new render_1.BrailleSpinner("fetching outcome telemetry");
|
|
713
1040
|
insightSpinner.start();
|
|
@@ -751,7 +1078,6 @@ async function improveCmd() {
|
|
|
751
1078
|
`last used`;
|
|
752
1079
|
console.log(DIM(headerStr));
|
|
753
1080
|
console.log(DIM(" " + "-".repeat(headerStr.length - 2)));
|
|
754
|
-
const lowPerformers = [];
|
|
755
1081
|
for (const s of liveInsights) {
|
|
756
1082
|
const ratePct = Math.round(s.successRate * 100);
|
|
757
1083
|
const rateStr = `${ratePct}%`.padStart(4);
|
|
@@ -907,6 +1233,349 @@ async function improveCmd() {
|
|
|
907
1233
|
}
|
|
908
1234
|
console.log("");
|
|
909
1235
|
}
|
|
1236
|
+
if ((0, config_1.isAuthenticated)()) {
|
|
1237
|
+
try {
|
|
1238
|
+
await (0, api_1.recordBrainActivity)({
|
|
1239
|
+
activityId: `skill-improve:${os.hostname()}`,
|
|
1240
|
+
source: "skill-improve",
|
|
1241
|
+
channel: "skills",
|
|
1242
|
+
kind: proposals.length > 0 || lowPerformers.length > 0 ? "proposed" : "checked",
|
|
1243
|
+
status: proposals.length > 0 || lowPerformers.length > 0 ? "warn" : "ok",
|
|
1244
|
+
title: proposals.length > 0 || lowPerformers.length > 0
|
|
1245
|
+
? `skill improvement analysis found ${proposals.length + lowPerformers.length} signal${proposals.length + lowPerformers.length === 1 ? "" : "s"}`
|
|
1246
|
+
: "skill improvement analysis clean",
|
|
1247
|
+
detail: proposals.length > 0
|
|
1248
|
+
? proposals.slice(0, 3).join(" · ")
|
|
1249
|
+
: lowPerformers.length > 0
|
|
1250
|
+
? `low performers: ${lowPerformers.map((s) => s.skill).slice(0, 5).join(", ")}`
|
|
1251
|
+
: "no immediate skill improvements suggested",
|
|
1252
|
+
entityType: "skillImprovementAnalysis",
|
|
1253
|
+
entityId: os.hostname(),
|
|
1254
|
+
sourceHost: os.hostname(),
|
|
1255
|
+
sourceAgent: "youmd CLI",
|
|
1256
|
+
sourceRuntime: process.version,
|
|
1257
|
+
metadata: {
|
|
1258
|
+
installedCount: installed.length,
|
|
1259
|
+
notInstalledCount: notInstalled.length,
|
|
1260
|
+
localUseCount: totalUses,
|
|
1261
|
+
identityFieldCount: allFields.size,
|
|
1262
|
+
missingIdentityFields: missing.slice(0, 20),
|
|
1263
|
+
proposalCount: proposals.length,
|
|
1264
|
+
proposals: proposals.slice(0, 12),
|
|
1265
|
+
lowPerformerCount: lowPerformers.length,
|
|
1266
|
+
lowPerformers: lowPerformers.slice(0, 12).map((skill) => ({
|
|
1267
|
+
skill: skill.skill,
|
|
1268
|
+
uses: skill.uses,
|
|
1269
|
+
successRate: skill.successRate,
|
|
1270
|
+
failure: skill.failure,
|
|
1271
|
+
partial: skill.partial,
|
|
1272
|
+
})),
|
|
1273
|
+
liveInsightCount: liveInsights?.length ?? 0,
|
|
1274
|
+
fleetLineCount: fleetLines.length,
|
|
1275
|
+
secretValuesExposed: false,
|
|
1276
|
+
},
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
catch (err) {
|
|
1280
|
+
if (process.env.DEBUG)
|
|
1281
|
+
console.error(`[skill improve] brain activity failed: ${err}`);
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
}
|
|
1285
|
+
function inventoryDiffCmd(args) {
|
|
1286
|
+
const { positional, flags } = parseFlagArgs(args);
|
|
1287
|
+
const leftPath = flagString(flags, "left") || positional[0];
|
|
1288
|
+
const rightPath = flagString(flags, "right") || positional[1];
|
|
1289
|
+
if (!leftPath || !rightPath) {
|
|
1290
|
+
console.log("");
|
|
1291
|
+
console.log(chalk_1.default.yellow(" usage: youmd skill inventory diff --left mac-mini.json --right laptop.json"));
|
|
1292
|
+
console.log(DIM(" or: youmd skill inventory diff mac-mini.json laptop.json"));
|
|
1293
|
+
console.log("");
|
|
1294
|
+
return;
|
|
1295
|
+
}
|
|
1296
|
+
const left = readInventorySnapshot(path.resolve(leftPath));
|
|
1297
|
+
const right = readInventorySnapshot(path.resolve(rightPath));
|
|
1298
|
+
const leftNames = inventoryNames(left);
|
|
1299
|
+
const rightNames = inventoryNames(right);
|
|
1300
|
+
const onlyLeft = [...leftNames].filter((name) => !rightNames.has(name)).sort();
|
|
1301
|
+
const onlyRight = [...rightNames].filter((name) => !leftNames.has(name)).sort();
|
|
1302
|
+
console.log("");
|
|
1303
|
+
console.log(" " + chalk_1.default.bold("skill inventory diff"));
|
|
1304
|
+
console.log("");
|
|
1305
|
+
console.log(` ${DIM("left ")}${chalk_1.default.cyan(inventorySnapshotLabel(left, path.basename(leftPath)))}${DIM(left.generatedAt ? ` · ${left.generatedAt}` : "")}`);
|
|
1306
|
+
console.log(` ${DIM("right")}${chalk_1.default.cyan(" " + inventorySnapshotLabel(right, path.basename(rightPath)))}${DIM(right.generatedAt ? ` · ${right.generatedAt}` : "")}`);
|
|
1307
|
+
console.log("");
|
|
1308
|
+
const totalRows = [
|
|
1309
|
+
["unique skill names", left.totals?.uniqueSkillNames, right.totals?.uniqueSkillNames],
|
|
1310
|
+
["real SKILL.md files", left.totals?.uniqueRealSkillFiles, right.totals?.uniqueRealSkillFiles],
|
|
1311
|
+
["direct host exposures", left.totals?.directExposureSkillRecords, right.totals?.directExposureSkillRecords],
|
|
1312
|
+
["You.md catalog skills", left.totals?.youmdCatalogSkills, right.totals?.youmdCatalogSkills],
|
|
1313
|
+
["missing from catalog", left.totals?.missingFromYoumdCatalog, right.totals?.missingFromYoumdCatalog],
|
|
1314
|
+
["DRY review cases", left.totals?.duplicateNameDifferentRealpaths, right.totals?.duplicateNameDifferentRealpaths],
|
|
1315
|
+
["mirror clusters", left.totals?.sameRealpathMirrors, right.totals?.sameRealpathMirrors],
|
|
1316
|
+
];
|
|
1317
|
+
const maxLabel = Math.max(...totalRows.map(([label]) => label.length));
|
|
1318
|
+
for (const [label, leftValue, rightValue] of totalRows) {
|
|
1319
|
+
const delta = typeof leftValue === "number" && typeof rightValue === "number"
|
|
1320
|
+
? rightValue - leftValue
|
|
1321
|
+
: undefined;
|
|
1322
|
+
const deltaText = typeof delta === "number"
|
|
1323
|
+
? delta === 0
|
|
1324
|
+
? DIM(" 0")
|
|
1325
|
+
: delta > 0
|
|
1326
|
+
? chalk_1.default.green(` +${delta}`)
|
|
1327
|
+
: ACCENT(` ${delta}`)
|
|
1328
|
+
: "";
|
|
1329
|
+
console.log(` ${DIM(label.padEnd(maxLabel + 2))}` +
|
|
1330
|
+
`${String(leftValue ?? "?").padStart(6)} ${DIM("->")} ${String(rightValue ?? "?").padStart(6)}${deltaText}`);
|
|
1331
|
+
}
|
|
1332
|
+
console.log("");
|
|
1333
|
+
console.log(` ${DIM("skills only in left: ")}${onlyLeft.length}`);
|
|
1334
|
+
for (const name of onlyLeft.slice(0, 20))
|
|
1335
|
+
console.log(` ${ACCENT("-")} ${name}`);
|
|
1336
|
+
if (onlyLeft.length > 20)
|
|
1337
|
+
console.log(DIM(` ... ${onlyLeft.length - 20} more`));
|
|
1338
|
+
console.log("");
|
|
1339
|
+
console.log(` ${DIM("skills only in right: ")}${onlyRight.length}`);
|
|
1340
|
+
for (const name of onlyRight.slice(0, 20))
|
|
1341
|
+
console.log(` ${chalk_1.default.green("+")} ${name}`);
|
|
1342
|
+
if (onlyRight.length > 20)
|
|
1343
|
+
console.log(DIM(` ... ${onlyRight.length - 20} more`));
|
|
1344
|
+
console.log("");
|
|
1345
|
+
}
|
|
1346
|
+
async function inventoryStatusCmd(args) {
|
|
1347
|
+
const { flags } = parseFlagArgs(args);
|
|
1348
|
+
const outDir = path.resolve(flagString(flags, "out-dir") || path.join(os.homedir(), ".youmd", "agent-stack-inventory"));
|
|
1349
|
+
const limitRaw = flagString(flags, "limit");
|
|
1350
|
+
const limit = limitRaw && Number.isFinite(Number(limitRaw))
|
|
1351
|
+
? Math.max(1, Math.min(50, Math.trunc(Number(limitRaw))))
|
|
1352
|
+
: 12;
|
|
1353
|
+
const jsonOutput = flags.json === true;
|
|
1354
|
+
const latestPath = findLatestInventoryJson(outDir);
|
|
1355
|
+
const local = latestPath ? readInventorySnapshot(latestPath) : null;
|
|
1356
|
+
const localTotals = local?.totals || {};
|
|
1357
|
+
if (!(0, config_1.isAuthenticated)()) {
|
|
1358
|
+
if (jsonOutput) {
|
|
1359
|
+
console.log(JSON.stringify({
|
|
1360
|
+
success: false,
|
|
1361
|
+
error: "not_authenticated",
|
|
1362
|
+
repair: "youmd login",
|
|
1363
|
+
secretValuesExposed: false,
|
|
1364
|
+
}, null, 2));
|
|
1365
|
+
return;
|
|
1366
|
+
}
|
|
1367
|
+
console.log("");
|
|
1368
|
+
console.log(chalk_1.default.yellow(" not authenticated. run: ") + chalk_1.default.cyan("youmd login"));
|
|
1369
|
+
console.log(DIM(" then: ") + chalk_1.default.cyan("youmd skill inventory status"));
|
|
1370
|
+
console.log("");
|
|
1371
|
+
return;
|
|
1372
|
+
}
|
|
1373
|
+
const [res, driftRes] = await Promise.all([
|
|
1374
|
+
(0, api_1.getAgentStackInventories)({ limit }),
|
|
1375
|
+
(0, api_1.getAgentStackInventoryDrift)({ limit }),
|
|
1376
|
+
]);
|
|
1377
|
+
if (!res.ok) {
|
|
1378
|
+
const message = (0, api_1.apiErrorMessage)(res.data) || `HTTP ${res.status}`;
|
|
1379
|
+
if (jsonOutput) {
|
|
1380
|
+
console.log(JSON.stringify({
|
|
1381
|
+
success: false,
|
|
1382
|
+
error: message,
|
|
1383
|
+
secretValuesExposed: false,
|
|
1384
|
+
}, null, 2));
|
|
1385
|
+
return;
|
|
1386
|
+
}
|
|
1387
|
+
console.log("");
|
|
1388
|
+
console.log(chalk_1.default.yellow(` inventory status unavailable: ${message}`));
|
|
1389
|
+
console.log("");
|
|
1390
|
+
return;
|
|
1391
|
+
}
|
|
1392
|
+
const inventories = res.data?.inventories ?? [];
|
|
1393
|
+
const drift = driftRes.ok ? driftRes.data : null;
|
|
1394
|
+
if (jsonOutput) {
|
|
1395
|
+
console.log(JSON.stringify({
|
|
1396
|
+
success: true,
|
|
1397
|
+
local: local
|
|
1398
|
+
? {
|
|
1399
|
+
path: latestPath,
|
|
1400
|
+
hostName: inventoryHostName(local),
|
|
1401
|
+
generatedAt: local.generatedAt,
|
|
1402
|
+
rootDir: local.roots?.workspace || local.repoRoot,
|
|
1403
|
+
totals: localTotals,
|
|
1404
|
+
}
|
|
1405
|
+
: null,
|
|
1406
|
+
inventories,
|
|
1407
|
+
drift,
|
|
1408
|
+
secretValuesExposed: false,
|
|
1409
|
+
}, null, 2));
|
|
1410
|
+
return;
|
|
1411
|
+
}
|
|
1412
|
+
console.log("");
|
|
1413
|
+
console.log(" " + chalk_1.default.bold("skill inventory status"));
|
|
1414
|
+
console.log("");
|
|
1415
|
+
if (local && latestPath) {
|
|
1416
|
+
console.log(` ${DIM("local latest")} ${chalk_1.default.cyan(inventoryHostName(local))}${DIM(local.generatedAt ? ` · ${compactDate(local.generatedAt)}` : "")}`);
|
|
1417
|
+
console.log(` ${DIM("local json ")} ${chalk_1.default.cyan(latestPath)}`);
|
|
1418
|
+
if (localTotals) {
|
|
1419
|
+
console.log(` ${DIM("local counts")} ` +
|
|
1420
|
+
`${chalk_1.default.cyan(String(localTotals.uniqueSkillNames ?? "?"))} skills · ` +
|
|
1421
|
+
`${chalk_1.default.cyan(String(localTotals.uniqueRealSkillFiles ?? "?"))} files · ` +
|
|
1422
|
+
`${ACCENT(String(localTotals.missingFromYoumdCatalog ?? 0))} catalog gaps · ` +
|
|
1423
|
+
`${ACCENT(String(localTotals.duplicateNameDifferentRealpaths ?? 0))} DRY reviews`);
|
|
1424
|
+
}
|
|
1425
|
+
}
|
|
1426
|
+
else {
|
|
1427
|
+
console.log(chalk_1.default.yellow(" local latest: missing"));
|
|
1428
|
+
console.log(DIM(" repair: ") + chalk_1.default.cyan(`youmd skill inventory --out-dir ${outDir} --sync`));
|
|
1429
|
+
}
|
|
1430
|
+
console.log("");
|
|
1431
|
+
if (inventories.length === 0) {
|
|
1432
|
+
console.log(chalk_1.default.yellow(" no synced machine inventories found yet."));
|
|
1433
|
+
}
|
|
1434
|
+
else {
|
|
1435
|
+
const rows = inventories.map((row) => {
|
|
1436
|
+
const totals = inventoryTotalsFromSynced(row);
|
|
1437
|
+
const localHost = local ? inventoryHostName(local) : "";
|
|
1438
|
+
const sameHost = localHost && row.hostName === localHost;
|
|
1439
|
+
return {
|
|
1440
|
+
row,
|
|
1441
|
+
totals,
|
|
1442
|
+
marker: sameHost ? "*" : " ",
|
|
1443
|
+
};
|
|
1444
|
+
});
|
|
1445
|
+
console.log(` ${DIM("synced machines")} ${chalk_1.default.cyan(String(inventories.length))}${DIM(` · limit ${limit}`)}`);
|
|
1446
|
+
console.log("");
|
|
1447
|
+
console.log(` ${DIM(" host".padEnd(26))}` +
|
|
1448
|
+
`${DIM("updated".padEnd(22))}` +
|
|
1449
|
+
`${DIM("skills".padStart(8))}` +
|
|
1450
|
+
`${DIM(" files".padStart(8))}` +
|
|
1451
|
+
`${DIM(" gaps".padStart(8))}` +
|
|
1452
|
+
`${DIM(" dry".padStart(7))}` +
|
|
1453
|
+
`${DIM(" Δskills".padStart(10))}`);
|
|
1454
|
+
for (const { row, totals, marker } of rows) {
|
|
1455
|
+
const host = `${marker} ${row.hostName}`.slice(0, 25).padEnd(26);
|
|
1456
|
+
const updated = compactDate(row.updatedAt).slice(0, 21).padEnd(22);
|
|
1457
|
+
console.log(` ${chalk_1.default.cyan(host)}` +
|
|
1458
|
+
`${DIM(updated)}` +
|
|
1459
|
+
`${String(totals.uniqueSkillNames ?? "?").padStart(8)}` +
|
|
1460
|
+
`${String(totals.uniqueRealSkillFiles ?? "?").padStart(8)}` +
|
|
1461
|
+
`${String(totals.missingFromYoumdCatalog ?? "?").padStart(8)}` +
|
|
1462
|
+
`${String(totals.duplicateNameDifferentRealpaths ?? "?").padStart(7)}` +
|
|
1463
|
+
`${inventoryDelta(localTotals.uniqueSkillNames, totals.uniqueSkillNames).padStart(10)}`);
|
|
1464
|
+
if (row.rootDir)
|
|
1465
|
+
console.log(` ${DIM(" root")} ${DIM(row.rootDir)}`);
|
|
1466
|
+
}
|
|
1467
|
+
console.log("");
|
|
1468
|
+
console.log(DIM(" * matches this machine's local latest host name"));
|
|
1469
|
+
}
|
|
1470
|
+
renderInventoryDrift(drift);
|
|
1471
|
+
console.log("");
|
|
1472
|
+
console.log(DIM(" refresh: ") + chalk_1.default.cyan(`youmd skill inventory --out-dir ${outDir} --sync`));
|
|
1473
|
+
console.log(DIM(" verify: ") + chalk_1.default.cyan("youmd machine verify --write-report --sync-report"));
|
|
1474
|
+
console.log(DIM(" exact diff needs two local JSON files: ") + chalk_1.default.cyan("youmd skill inventory diff macbook.json mac-mini.json"));
|
|
1475
|
+
console.log("");
|
|
1476
|
+
}
|
|
1477
|
+
async function inventoryCmd(args) {
|
|
1478
|
+
if (args[0] === "diff") {
|
|
1479
|
+
inventoryDiffCmd(args.slice(1));
|
|
1480
|
+
return;
|
|
1481
|
+
}
|
|
1482
|
+
if (args[0] === "status" || args[0] === "remote" || args[0] === "machines") {
|
|
1483
|
+
await inventoryStatusCmd(args.slice(1));
|
|
1484
|
+
return;
|
|
1485
|
+
}
|
|
1486
|
+
const { flags } = parseFlagArgs(args);
|
|
1487
|
+
const script = resolveInventoryScript();
|
|
1488
|
+
if (!script) {
|
|
1489
|
+
console.log("");
|
|
1490
|
+
console.log(chalk_1.default.yellow(" agent-stack-inventory script not found on this machine."));
|
|
1491
|
+
console.log(DIM(" expected one of:"));
|
|
1492
|
+
console.log(DIM(" ~/.agent-shared/claude-skills/agent-stack-inventory/scripts/local-agent-stack-inventory.mjs"));
|
|
1493
|
+
console.log(DIM(" ~/.youmd/skills/agent-stack-inventory/scripts/local-agent-stack-inventory.mjs"));
|
|
1494
|
+
console.log("");
|
|
1495
|
+
console.log(DIM(" run ") + chalk_1.default.cyan("youmd skill install agent-stack-inventory") + DIM(" or sync your shared skills, then retry."));
|
|
1496
|
+
console.log("");
|
|
1497
|
+
return;
|
|
1498
|
+
}
|
|
1499
|
+
const outDir = path.resolve(flagString(flags, "out-dir") || process.cwd());
|
|
1500
|
+
const workspace = flagString(flags, "workspace");
|
|
1501
|
+
const date = flagString(flags, "date");
|
|
1502
|
+
const shouldSync = flags.sync === true;
|
|
1503
|
+
const shouldSyncRepo = flags["no-repo-sync"] !== true;
|
|
1504
|
+
const startedAt = Date.now();
|
|
1505
|
+
const scriptArgs = [script, "--out-dir", outDir, "--progress"];
|
|
1506
|
+
if (workspace)
|
|
1507
|
+
scriptArgs.push("--workspace", path.resolve(workspace));
|
|
1508
|
+
if (date)
|
|
1509
|
+
scriptArgs.push("--date", date);
|
|
1510
|
+
console.log("");
|
|
1511
|
+
const spinner = new render_1.BrailleSpinner("mapping the local skill mesh");
|
|
1512
|
+
spinner.start();
|
|
1513
|
+
let output = "";
|
|
1514
|
+
try {
|
|
1515
|
+
output = await runInventoryScript(scriptArgs, spinner);
|
|
1516
|
+
}
|
|
1517
|
+
catch (err) {
|
|
1518
|
+
spinner.fail("inventory failed");
|
|
1519
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1520
|
+
console.log(DIM(` ${message}`));
|
|
1521
|
+
console.log("");
|
|
1522
|
+
return;
|
|
1523
|
+
}
|
|
1524
|
+
const latest = findLatestInventoryJson(outDir, startedAt);
|
|
1525
|
+
const parsed = latest ? readInventorySnapshot(latest) : null;
|
|
1526
|
+
spinner.stop("inventory generated");
|
|
1527
|
+
console.log("");
|
|
1528
|
+
if (parsed?.totals) {
|
|
1529
|
+
renderInventoryTotals(parsed.totals);
|
|
1530
|
+
renderInventoryPhaseTimings(parsed);
|
|
1531
|
+
console.log("");
|
|
1532
|
+
}
|
|
1533
|
+
let htmlPath = latest ? latest.replace(/\.json$/, ".html") : undefined;
|
|
1534
|
+
let jsonPath = latest || undefined;
|
|
1535
|
+
try {
|
|
1536
|
+
const scriptResult = JSON.parse(output);
|
|
1537
|
+
htmlPath = scriptResult.html || htmlPath;
|
|
1538
|
+
jsonPath = scriptResult.json || jsonPath;
|
|
1539
|
+
if (scriptResult.html)
|
|
1540
|
+
console.log(` ${DIM("html")} ${chalk_1.default.cyan(scriptResult.html)}`);
|
|
1541
|
+
if (scriptResult.json)
|
|
1542
|
+
console.log(` ${DIM("json")} ${chalk_1.default.cyan(scriptResult.json)}`);
|
|
1543
|
+
}
|
|
1544
|
+
catch {
|
|
1545
|
+
if (latest) {
|
|
1546
|
+
console.log(` ${DIM("json")} ${chalk_1.default.cyan(latest)}`);
|
|
1547
|
+
console.log(` ${DIM("html")} ${chalk_1.default.cyan(latest.replace(/\.json$/, ".html"))}`);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
if (shouldSync && parsed) {
|
|
1551
|
+
if (!(0, config_1.isAuthenticated)()) {
|
|
1552
|
+
console.log(DIM(" sync skipped: run `youmd login` or set YOUMD_API_KEY to persist this machine inventory."));
|
|
1553
|
+
}
|
|
1554
|
+
else {
|
|
1555
|
+
try {
|
|
1556
|
+
const res = await (0, api_1.syncAgentStackInventory)(buildAgentStackInventorySyncPayload(parsed, jsonPath, htmlPath, shouldSyncRepo));
|
|
1557
|
+
if (res.ok && res.data?.success) {
|
|
1558
|
+
const action = res.data.created ? "created" : "updated";
|
|
1559
|
+
console.log(` ${DIM("sync")} ${chalk_1.default.green(action)} You.md agent stack inventory`);
|
|
1560
|
+
if (res.data.repoSync?.attempted) {
|
|
1561
|
+
const label = res.data.repoSync.ok ? chalk_1.default.green("synced repo snapshot") : ACCENT("repo snapshot skipped");
|
|
1562
|
+
console.log(` ${DIM("repo")} ${label}${res.data.repoSync.error ? DIM(`: ${res.data.repoSync.error}`) : ""}`);
|
|
1563
|
+
}
|
|
1564
|
+
else if (!shouldSyncRepo) {
|
|
1565
|
+
console.log(` ${DIM("repo")} ${DIM("snapshot sync disabled by --no-repo-sync")}`);
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
else {
|
|
1569
|
+
console.log(` ${DIM("sync skipped:")} ${(0, api_1.apiErrorMessage)(res.data) || `HTTP ${res.status}`}`);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
catch (err) {
|
|
1573
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1574
|
+
console.log(` ${DIM("sync skipped:")} ${message}`);
|
|
1575
|
+
}
|
|
1576
|
+
}
|
|
1577
|
+
}
|
|
1578
|
+
console.log("");
|
|
910
1579
|
}
|
|
911
1580
|
function searchCmd(args) {
|
|
912
1581
|
const query = args.join(" ");
|
|
@@ -1216,6 +1885,10 @@ async function skillCommand(subcommand, ...args) {
|
|
|
1216
1885
|
case "improve":
|
|
1217
1886
|
await improveCmd();
|
|
1218
1887
|
break;
|
|
1888
|
+
case "inventory":
|
|
1889
|
+
case "audit":
|
|
1890
|
+
await inventoryCmd(args);
|
|
1891
|
+
break;
|
|
1219
1892
|
case "metrics":
|
|
1220
1893
|
case "stats":
|
|
1221
1894
|
metricsCmd();
|
|
@@ -1283,6 +1956,11 @@ async function skillCommand(subcommand, ...args) {
|
|
|
1283
1956
|
console.log(` ${chalk_1.default.cyan("link <agent>".padEnd(28))} ${DIM("link to claude | cursor | codex")}`);
|
|
1284
1957
|
console.log(` ${chalk_1.default.cyan("init-project [--mode auto|additive|zero-touch|scaffold]".padEnd(28))} ${DIM("bootstrap AGENTS/CLAUDE + project-context/ + .you/ + links")}`);
|
|
1285
1958
|
console.log(` ${chalk_1.default.cyan("improve".padEnd(28))} ${DIM("review metrics, find gaps, propose changes")}`);
|
|
1959
|
+
console.log(` ${chalk_1.default.cyan("inventory [--out-dir dir]".padEnd(28))} ${DIM("map local/global skills, mirrors, catalog gaps, and DRY risks")}`);
|
|
1960
|
+
console.log(` ${chalk_1.default.cyan("inventory --sync".padEnd(28))} ${DIM("persist safe inventory metadata to Convex + repo snapshot")}`);
|
|
1961
|
+
console.log(` ${chalk_1.default.cyan("inventory --no-repo-sync".padEnd(28))} ${DIM("skip GitHub snapshot push when syncing inventory")}`);
|
|
1962
|
+
console.log(` ${chalk_1.default.cyan("inventory status".padEnd(28))} ${DIM("show synced machine inventory summaries and count drift")}`);
|
|
1963
|
+
console.log(` ${chalk_1.default.cyan("inventory diff <a> <b>".padEnd(28))} ${DIM("compare two machine inventory JSON snapshots")}`);
|
|
1286
1964
|
console.log(` ${chalk_1.default.cyan("metrics".padEnd(28))} ${DIM("usage stats and effectiveness scores")}`);
|
|
1287
1965
|
console.log(` ${chalk_1.default.cyan("search <query>".padEnd(28))} ${DIM("search skills by name or description")}`);
|
|
1288
1966
|
console.log(` ${chalk_1.default.cyan("browse".padEnd(28))} ${DIM("browse the public skill registry")}`);
|