@skaile/workspaces 0.14.0 → 0.15.1
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/CHANGELOG.md +111 -0
- package/dist/asset-manager/index.js +2 -2
- package/dist/asset-manager/installer.js +1 -1
- package/dist/asset-manager/src/index.d.ts.map +1 -1
- package/dist/asset-manager/src/installer.d.ts.map +1 -1
- package/dist/base-assets/connectors/flow/run-flow.js +1 -1
- package/dist/{chunk-OIFGKFZY.js → chunk-67TJEQJE.js} +2 -2
- package/dist/{chunk-OIFGKFZY.js.map → chunk-67TJEQJE.js.map} +1 -1
- package/dist/{chunk-7R4WLTZW.js → chunk-DEZVZSBN.js} +11 -16
- package/dist/chunk-DEZVZSBN.js.map +1 -0
- package/dist/{chunk-4GEVGRWB.js → chunk-ERCOCLW5.js} +9 -11
- package/dist/chunk-ERCOCLW5.js.map +1 -0
- package/dist/{chunk-42OQF7UU.js → chunk-FNCYNUGS.js} +304 -227
- package/dist/chunk-FNCYNUGS.js.map +1 -0
- package/dist/{chunk-DN5476SV.js → chunk-HJV7MHG5.js} +17 -10
- package/dist/chunk-HJV7MHG5.js.map +1 -0
- package/dist/{chunk-VAJB2UJ5.js → chunk-IY4X7PZN.js} +13 -13
- package/dist/chunk-IY4X7PZN.js.map +1 -0
- package/dist/cli/index.js +807 -601
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/src/commands/asset-cmd.d.ts +4 -1
- package/dist/cli/src/commands/asset-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/library-cmd.d.ts +6 -4
- package/dist/cli/src/commands/library-cmd.d.ts.map +1 -1
- package/dist/cli/src/commands/manage.d.ts +31 -20
- package/dist/cli/src/commands/manage.d.ts.map +1 -1
- package/dist/cli/src/commands/npx.d.ts.map +1 -1
- package/dist/cli/src/commands/project.d.ts +2 -2
- package/dist/cli/src/commands/source-manifest.d.ts +19 -0
- package/dist/cli/src/commands/source-manifest.d.ts.map +1 -0
- package/dist/cli/src/commands/source.d.ts +6 -2
- package/dist/cli/src/commands/source.d.ts.map +1 -1
- package/dist/cli/src/commands/store.d.ts.map +1 -1
- package/dist/cli/src/open-library.d.ts +18 -18
- package/dist/cli/src/open-library.d.ts.map +1 -1
- package/dist/library/index.js +1 -1
- package/dist/library/src/config.d.ts +12 -3
- package/dist/library/src/config.d.ts.map +1 -1
- package/dist/library/src/index.d.ts +7 -8
- package/dist/library/src/index.d.ts.map +1 -1
- package/dist/library/src/library.d.ts +24 -77
- package/dist/library/src/library.d.ts.map +1 -1
- package/dist/library/src/local/db.d.ts +3 -2
- package/dist/library/src/local/db.d.ts.map +1 -1
- package/dist/library/src/local/{user-library-manager.d.ts → library-manager.d.ts} +15 -16
- package/dist/library/src/local/library-manager.d.ts.map +1 -0
- package/dist/library/src/local/library.d.ts +27 -23
- package/dist/library/src/local/library.d.ts.map +1 -1
- package/dist/library/src/local/local-catalog-source.d.ts +5 -5
- package/dist/library/src/local/local-catalog-source.d.ts.map +1 -1
- package/dist/library/src/local/sidecar-paths.d.ts +3 -3
- package/dist/library/src/local/store-paths.d.ts +42 -0
- package/dist/library/src/local/store-paths.d.ts.map +1 -0
- package/dist/library/src/preset/apply.d.ts +2 -2
- package/dist/library/src/preset/apply.d.ts.map +1 -1
- package/dist/library/src/preset/placeholders.d.ts +3 -3
- package/dist/library/src/preset/placeholders.d.ts.map +1 -1
- package/dist/library/src/preset/resolve-item.d.ts +3 -3
- package/dist/library/src/preset/resolve-item.d.ts.map +1 -1
- package/dist/library/src/sync/driver.d.ts +6 -6
- package/dist/library/src/sync/driver.d.ts.map +1 -1
- package/dist/library/src/sync/git-driver.d.ts +6 -6
- package/dist/library/src/sync/git-driver.d.ts.map +1 -1
- package/dist/library/src/sync/local-driver.d.ts +6 -6
- package/dist/library/src/sync/local-driver.d.ts.map +1 -1
- package/dist/library/src/user-library.d.ts +12 -17
- package/dist/library/src/user-library.d.ts.map +1 -1
- package/dist/{open-library-S6FK4N4S.js → open-library-T6RXQJTQ.js} +4 -4
- package/dist/{open-library-S6FK4N4S.js.map → open-library-T6RXQJTQ.js.map} +1 -1
- package/dist/runner/index.js +1 -1
- package/dist/runner/src/composition/resolve.d.ts +3 -3
- package/dist/runner/src/composition/resolve.d.ts.map +1 -1
- package/dist/sdk/asset-manager.js +2 -2
- package/dist/sdk/index.js +1 -1
- package/dist/sdk/runner.js +1 -1
- package/dist/tui/index.js +1 -1
- package/dist/types/src/install-manifest.d.ts +1 -1
- package/package.json +1 -1
- package/dist/chunk-42OQF7UU.js.map +0 -1
- package/dist/chunk-4GEVGRWB.js.map +0 -1
- package/dist/chunk-7R4WLTZW.js.map +0 -1
- package/dist/chunk-DN5476SV.js.map +0 -1
- package/dist/chunk-VAJB2UJ5.js.map +0 -1
- package/dist/cli/src/commands/library-status.d.ts +0 -19
- package/dist/cli/src/commands/library-status.d.ts.map +0 -1
- package/dist/cli/src/commands/source-sidecar.d.ts +0 -6
- package/dist/cli/src/commands/source-sidecar.d.ts.map +0 -1
- package/dist/library/src/local/user-library-manager.d.ts.map +0 -1
- package/dist/library/src/sync/store-driver.d.ts +0 -21
- package/dist/library/src/sync/store-driver.d.ts.map +0 -1
package/dist/cli/index.js
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { openCatalogSource, openLibrary, createFullRegistry,
|
|
2
|
+
import { openCatalogSource, openLibrary, createFullRegistry, openLibraryManager } from '../chunk-IY4X7PZN.js';
|
|
3
3
|
import { logErr, S, logOk, colorRef, logInfo, logWarn, kindColorPad, kindColor, formatRelativeTime } from '../chunk-4NDWKA64.js';
|
|
4
4
|
import { getStoreConfig, storeFetch, saveStoreTokens, clearStoreTokens, isStoreAuthenticated } from '../chunk-6DEGWPAR.js';
|
|
5
5
|
import { AI_RESOURCES } from '../chunk-2M3XTMOL.js';
|
|
6
6
|
import { LocalSecretsProvider } from '../chunk-JDX54X4Y.js';
|
|
7
|
-
import { resolveLibraryDir } from '../chunk-
|
|
7
|
+
import { resolveLibraryDir, skaileHomeDir } from '../chunk-FNCYNUGS.js';
|
|
8
8
|
import '../chunk-R7FOF242.js';
|
|
9
9
|
import '../chunk-GKIA2PU5.js';
|
|
10
10
|
import '../chunk-OKRUTSG7.js';
|
|
11
|
-
import { runFlow, resumeFlow } from '../chunk-
|
|
11
|
+
import { runFlow, resumeFlow } from '../chunk-67TJEQJE.js';
|
|
12
12
|
import '../chunk-GCJXPUHG.js';
|
|
13
13
|
import { validateFlowVersions, parseSkillFrontmatter } from '../chunk-IPUYL6TD.js';
|
|
14
|
-
import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-
|
|
14
|
+
import { runAgentChat, loadSessionById, loadSession, listSessions, setCurrentSession, deleteSession, clearSession, loadAgentManifest, compileComposition, MarkdownStreamer, resolveMixin } from '../chunk-HJV7MHG5.js';
|
|
15
15
|
import { buildClaudePluginFiles } from '../chunk-G7O7WDXX.js';
|
|
16
16
|
import '../chunk-X5YPJV4N.js';
|
|
17
17
|
import '../chunk-O7SG5PC2.js';
|
|
@@ -32,8 +32,8 @@ import '../chunk-QAVZOJCV.js';
|
|
|
32
32
|
import { loadAllFlows } from '../chunk-ICS76R4T.js';
|
|
33
33
|
import '../chunk-GZWJGNNN.js';
|
|
34
34
|
import '../chunk-FVTV7M76.js';
|
|
35
|
-
import { AssetManager } from '../chunk-
|
|
36
|
-
import '../chunk-
|
|
35
|
+
import { AssetManager } from '../chunk-DEZVZSBN.js';
|
|
36
|
+
import '../chunk-ERCOCLW5.js';
|
|
37
37
|
import { readLock, resolveSettings, globalSettingsPath, projectSettingsPath, loadSettings, saveSettings, portableSpawn, portableSpawnSync } from '../chunk-4RUVG5GX.js';
|
|
38
38
|
import '../chunk-JKNWJ64A.js';
|
|
39
39
|
import { SUPPORTED_DRIVER_TARGETS } from '../chunk-O4JH3KUE.js';
|
|
@@ -53,15 +53,15 @@ import '../chunk-LV2HPH3C.js';
|
|
|
53
53
|
import '../chunk-NSBPE2FW.js';
|
|
54
54
|
import * as fs10 from 'fs';
|
|
55
55
|
import fs10__default, { readFileSync, existsSync, writeFileSync, statSync } from 'fs';
|
|
56
|
-
import * as
|
|
57
|
-
import
|
|
56
|
+
import * as path15 from 'path';
|
|
57
|
+
import path15__default, { resolve, join } from 'path';
|
|
58
58
|
import { fileURLToPath } from 'url';
|
|
59
59
|
import { Help, Command, Option } from 'commander';
|
|
60
60
|
import * as p5 from '@clack/prompts';
|
|
61
|
-
import
|
|
61
|
+
import pc5 from 'picocolors';
|
|
62
62
|
import * as readline from 'readline';
|
|
63
63
|
import { createInterface } from 'readline';
|
|
64
|
-
import { execSync } from 'child_process';
|
|
64
|
+
import { execSync, spawnSync } from 'child_process';
|
|
65
65
|
import { homedir } from 'os';
|
|
66
66
|
import fs5 from 'fs/promises';
|
|
67
67
|
import { parse as parse$1, stringify } from 'smol-toml';
|
|
@@ -80,7 +80,7 @@ function makeAssetCommand() {
|
|
|
80
80
|
logErr(`Source path does not exist: ${srcPath}`);
|
|
81
81
|
process.exit(1);
|
|
82
82
|
}
|
|
83
|
-
const { manager, close } = await
|
|
83
|
+
const { manager, close } = await openLibraryManager();
|
|
84
84
|
try {
|
|
85
85
|
let target;
|
|
86
86
|
if (opts.to) {
|
|
@@ -103,10 +103,10 @@ function makeAssetCommand() {
|
|
|
103
103
|
);
|
|
104
104
|
process.exit(1);
|
|
105
105
|
}
|
|
106
|
-
const basename2 =
|
|
107
|
-
const destDir = target.structure === "domain" && opts.domain ?
|
|
106
|
+
const basename2 = path15.basename(srcPath);
|
|
107
|
+
const destDir = target.structure === "domain" && opts.domain ? path15.join(target.path, opts.domain) : target.path;
|
|
108
108
|
fs10.mkdirSync(destDir, { recursive: true });
|
|
109
|
-
const destPath =
|
|
109
|
+
const destPath = path15.join(destDir, basename2);
|
|
110
110
|
if (fs10.existsSync(destPath)) {
|
|
111
111
|
logErr(`Destination already exists: ${destPath}`);
|
|
112
112
|
process.exit(1);
|
|
@@ -119,13 +119,13 @@ function makeAssetCommand() {
|
|
|
119
119
|
fs10.renameSync(srcPath, destPath);
|
|
120
120
|
break;
|
|
121
121
|
case "link":
|
|
122
|
-
fs10.symlinkSync(
|
|
122
|
+
fs10.symlinkSync(path15.resolve(srcPath), destPath);
|
|
123
123
|
break;
|
|
124
124
|
}
|
|
125
125
|
if (target.backend === "git" && opts.commit) {
|
|
126
|
-
const { spawnSync } = await import('child_process');
|
|
127
|
-
|
|
128
|
-
|
|
126
|
+
const { spawnSync: spawnSync2 } = await import('child_process');
|
|
127
|
+
spawnSync2("git", ["add", "-A"], { cwd: target.path, stdio: "ignore" });
|
|
128
|
+
spawnSync2("git", ["commit", "-m", `skaile: migrate ${basename2}`], {
|
|
129
129
|
cwd: target.path,
|
|
130
130
|
stdio: "ignore"
|
|
131
131
|
});
|
|
@@ -149,7 +149,7 @@ function makeAssetCommand() {
|
|
|
149
149
|
logErr(`Invalid --mode "${opts.mode}". Expected: copy | link.`);
|
|
150
150
|
process.exit(1);
|
|
151
151
|
}
|
|
152
|
-
const { manager, library, close } = await
|
|
152
|
+
const { manager, library, close } = await openLibraryManager();
|
|
153
153
|
try {
|
|
154
154
|
const def = await library.getAssetDef(ref);
|
|
155
155
|
if (!def) {
|
|
@@ -157,7 +157,7 @@ function makeAssetCommand() {
|
|
|
157
157
|
process.exit(1);
|
|
158
158
|
}
|
|
159
159
|
const target = opts.target ?? process.cwd();
|
|
160
|
-
const lib = def.
|
|
160
|
+
const lib = def.libraryId ? (await manager.listLibraries()).find((l) => l.id === def.libraryId) : void 0;
|
|
161
161
|
if (!lib) {
|
|
162
162
|
logErr(`Asset has no associated library; install path unknown.`);
|
|
163
163
|
process.exit(1);
|
|
@@ -167,17 +167,17 @@ function makeAssetCommand() {
|
|
|
167
167
|
}
|
|
168
168
|
const manifestPath = def.manifest.path;
|
|
169
169
|
const candidateSubpath = typeof manifestPath === "string" && manifestPath.length > 0 ? manifestPath : def.name;
|
|
170
|
-
const srcPath =
|
|
170
|
+
const srcPath = path15.join(lib.path, candidateSubpath);
|
|
171
171
|
if (!fs10.existsSync(srcPath)) {
|
|
172
172
|
logErr(`Asset path not found: ${srcPath}`);
|
|
173
173
|
process.exit(1);
|
|
174
174
|
}
|
|
175
|
-
const destPath =
|
|
176
|
-
fs10.mkdirSync(
|
|
175
|
+
const destPath = path15.join(target, candidateSubpath);
|
|
176
|
+
fs10.mkdirSync(path15.dirname(destPath), { recursive: true });
|
|
177
177
|
if (opts.mode === "copy") {
|
|
178
178
|
fs10.cpSync(srcPath, destPath, { recursive: true });
|
|
179
179
|
} else {
|
|
180
|
-
fs10.symlinkSync(
|
|
180
|
+
fs10.symlinkSync(path15.resolve(srcPath), destPath);
|
|
181
181
|
}
|
|
182
182
|
logOk(`Installed ${ref} \u2192 ${destPath} (${opts.mode})`);
|
|
183
183
|
} finally {
|
|
@@ -186,12 +186,12 @@ function makeAssetCommand() {
|
|
|
186
186
|
}
|
|
187
187
|
);
|
|
188
188
|
cmd.command("list").description("List asset definitions across libraries").option("--library <name>", "Filter by library").option("--domain <name>", "Filter by domain").option("--kind <kind>", "Filter by kind").option("--json", "Output as JSON").action(async (opts) => {
|
|
189
|
-
const { manager, library, close } = await
|
|
189
|
+
const { manager, library, close } = await openLibraryManager();
|
|
190
190
|
try {
|
|
191
191
|
let defs = await library.listAssetDefs(opts.kind ? { kind: opts.kind } : void 0);
|
|
192
192
|
if (opts.library) {
|
|
193
193
|
const lib = await manager.requireLibrary(opts.library);
|
|
194
|
-
defs = defs.filter((d) => d.
|
|
194
|
+
defs = defs.filter((d) => d.libraryId === lib.id);
|
|
195
195
|
}
|
|
196
196
|
if (opts.json) return console.log(JSON.stringify(defs, null, 2));
|
|
197
197
|
if (defs.length === 0) {
|
|
@@ -209,7 +209,7 @@ function makeAssetCommand() {
|
|
|
209
209
|
}
|
|
210
210
|
});
|
|
211
211
|
cmd.command("show <ref>").description("Show details for an asset by canonical ref").action(async (ref) => {
|
|
212
|
-
const { library, close } = await
|
|
212
|
+
const { library, close } = await openLibraryManager();
|
|
213
213
|
try {
|
|
214
214
|
const def = await library.getAssetDef(ref);
|
|
215
215
|
if (!def) {
|
|
@@ -221,10 +221,6 @@ function makeAssetCommand() {
|
|
|
221
221
|
close();
|
|
222
222
|
}
|
|
223
223
|
});
|
|
224
|
-
cmd.command("publish <ref>").description("Publish to the store (STUB)").option("--scope <scope>", "team | public", "team").action(async (_ref, _opts) => {
|
|
225
|
-
logErr("`skaile asset publish` is not yet wired (store write path stub).");
|
|
226
|
-
process.exit(2);
|
|
227
|
-
});
|
|
228
224
|
return cmd;
|
|
229
225
|
}
|
|
230
226
|
var VALID_STRATEGIES = ["keychain", "passphrase", "machine"];
|
|
@@ -275,7 +271,7 @@ function makeSearchCommand() {
|
|
|
275
271
|
const showLocal = !opts.store;
|
|
276
272
|
const showStore = !opts.local;
|
|
277
273
|
if (showLocal) {
|
|
278
|
-
const am2 = new AssetManager({ projectDir:
|
|
274
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
279
275
|
const entries = am2.search(query, kind);
|
|
280
276
|
if (entries.length === 0) {
|
|
281
277
|
if (!showStore) {
|
|
@@ -341,7 +337,7 @@ function makeSearchCommand() {
|
|
|
341
337
|
function makeAddCommand() {
|
|
342
338
|
return new Command("add").description("Add to skaile.yaml + deploy").argument("<ref>", "Asset reference (kind:name[@repo])").option("--project-dir <path>", "Project directory", process.cwd()).option("--global", "Deploy to global dir (~/.claude)").option("--target <agent>", "Driver target", "claude-code").action((ref, opts) => {
|
|
343
339
|
const am2 = new AssetManager({
|
|
344
|
-
projectDir:
|
|
340
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
345
341
|
global: opts.global,
|
|
346
342
|
driverTarget: opts.target
|
|
347
343
|
});
|
|
@@ -362,7 +358,7 @@ function makeAddCommand() {
|
|
|
362
358
|
function makeRemoveCommand() {
|
|
363
359
|
return new Command("remove").description("Remove from skaile.yaml + undeploy").argument("<ref>", "Asset reference (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).option("--global", "Remove from global dir").option("--target <agent>", "Driver target", "claude-code").action((ref, opts) => {
|
|
364
360
|
const am2 = new AssetManager({
|
|
365
|
-
projectDir:
|
|
361
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
366
362
|
global: opts.global,
|
|
367
363
|
driverTarget: opts.target
|
|
368
364
|
});
|
|
@@ -376,7 +372,7 @@ function makeListCommand() {
|
|
|
376
372
|
new Option("--target <agent>", "Driver target").default("claude-code").choices(SUPPORTED_DRIVER_TARGETS)
|
|
377
373
|
).action((kind, opts) => {
|
|
378
374
|
const am2 = new AssetManager({
|
|
379
|
-
projectDir:
|
|
375
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
380
376
|
global: opts.global,
|
|
381
377
|
driverTarget: opts.target
|
|
382
378
|
});
|
|
@@ -400,7 +396,7 @@ function makeListCommand() {
|
|
|
400
396
|
}
|
|
401
397
|
function makeInfoCommand() {
|
|
402
398
|
return new Command("info").description("Show asset metadata, deps, deploy status").argument("<ref>", "Asset reference (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
403
|
-
const am2 = new AssetManager({ projectDir:
|
|
399
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
404
400
|
const entry = am2.info(ref);
|
|
405
401
|
if (!entry) {
|
|
406
402
|
logErr(
|
|
@@ -433,9 +429,9 @@ function makeCreateCommand() {
|
|
|
433
429
|
new Option("-k, --kind <kind>", "Asset kind").default("skill").choices([...ASSET_KINDS])
|
|
434
430
|
).option("-d, --dir <dir>", "Target directory (default: cwd)").action((name, opts) => {
|
|
435
431
|
const am2 = new AssetManager({ projectDir: process.cwd() });
|
|
436
|
-
const destDir = opts.dir ?
|
|
432
|
+
const destDir = opts.dir ? path15__default.resolve(opts.dir) : process.cwd();
|
|
437
433
|
const result = am2.create(name, opts.kind, destDir);
|
|
438
|
-
if (result.ok) logOk(`Created ${kindColor(opts.kind)}: ${
|
|
434
|
+
if (result.ok) logOk(`Created ${kindColor(opts.kind)}: ${pc5.dim(result.path)}`);
|
|
439
435
|
else {
|
|
440
436
|
logErr(result.path);
|
|
441
437
|
process.exit(1);
|
|
@@ -444,7 +440,7 @@ function makeCreateCommand() {
|
|
|
444
440
|
}
|
|
445
441
|
function makeDoctorCommand() {
|
|
446
442
|
return new Command("doctor").description("Scan for missing dependency chains").argument("[name]", "Check a specific asset (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).action((name, opts) => {
|
|
447
|
-
const am2 = new AssetManager({ projectDir:
|
|
443
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
448
444
|
const issues = am2.doctor();
|
|
449
445
|
if (issues.length === 0) {
|
|
450
446
|
logOk("No missing dependencies.");
|
|
@@ -470,11 +466,11 @@ function makeCatalogCommand() {
|
|
|
470
466
|
const catalog = new Command("catalog").description("Catalog source health and configuration");
|
|
471
467
|
catalog.command("test").description("Probe the configured catalog source and report status").option("--project-dir <path>", "Project directory (for project-level config overlay)").option("--url <url>", "Override the catalog URL for this probe").option("--force", "Probe the network even in air-gapped mode (cache_ttl: 0)").action(async (opts) => {
|
|
472
468
|
const { resolveConfig, isLocalCatalogUrl, trpcGetUrl } = await import('../library/index.js');
|
|
473
|
-
const projectDir = opts.projectDir ?
|
|
469
|
+
const projectDir = opts.projectDir ? path15__default.resolve(opts.projectDir) : void 0;
|
|
474
470
|
const cfg = resolveConfig({ projectDir });
|
|
475
471
|
const baseUrl = opts.url ?? cfg.catalog.url;
|
|
476
472
|
if (isLocalCatalogUrl(baseUrl)) {
|
|
477
|
-
const { resolveCatalogSource } = await import('../open-library-
|
|
473
|
+
const { resolveCatalogSource } = await import('../open-library-T6RXQJTQ.js');
|
|
478
474
|
let resolved;
|
|
479
475
|
try {
|
|
480
476
|
resolved = await resolveCatalogSource({ projectDir: opts.projectDir });
|
|
@@ -563,7 +559,7 @@ function makeCatalogCommand() {
|
|
|
563
559
|
});
|
|
564
560
|
catalog.command("info").description("Print the resolved catalog configuration (url, framing, cacheTtlMs)").option("--project-dir <path>", "Project directory (for project-level config overlay)").action(async (opts) => {
|
|
565
561
|
const { resolveConfig } = await import('../library/index.js');
|
|
566
|
-
const projectDir = opts.projectDir ?
|
|
562
|
+
const projectDir = opts.projectDir ? path15__default.resolve(opts.projectDir) : void 0;
|
|
567
563
|
const cfg = resolveConfig({ projectDir });
|
|
568
564
|
console.log(`url: ${cfg.catalog.url}`);
|
|
569
565
|
console.log(`framing: ${cfg.catalog.framing}`);
|
|
@@ -620,7 +616,7 @@ function findSkills(domain) {
|
|
|
620
616
|
if (!fs10__default.existsSync(AI_RESOURCES)) return skills;
|
|
621
617
|
const domains = fs10__default.readdirSync(AI_RESOURCES, { withFileTypes: true }).filter((d) => d.isDirectory()).filter((d) => true);
|
|
622
618
|
for (const d of domains) {
|
|
623
|
-
const skillsDir =
|
|
619
|
+
const skillsDir = path15__default.join(AI_RESOURCES, d.name, "skills");
|
|
624
620
|
if (!fs10__default.existsSync(skillsDir)) continue;
|
|
625
621
|
walkSkills(skillsDir, d.name, skills);
|
|
626
622
|
}
|
|
@@ -628,9 +624,9 @@ function findSkills(domain) {
|
|
|
628
624
|
}
|
|
629
625
|
function walkSkills(dir, domain, out) {
|
|
630
626
|
for (const entry of fs10__default.readdirSync(dir, { withFileTypes: true })) {
|
|
631
|
-
const full =
|
|
627
|
+
const full = path15__default.join(dir, entry.name);
|
|
632
628
|
if (entry.isDirectory()) {
|
|
633
|
-
const skillMd =
|
|
629
|
+
const skillMd = path15__default.join(full, "SKILL.md");
|
|
634
630
|
if (fs10__default.existsSync(skillMd)) {
|
|
635
631
|
out.push({ name: entry.name, path: full, skillPath: skillMd, domain });
|
|
636
632
|
} else {
|
|
@@ -656,7 +652,7 @@ function resolveFlowIds() {
|
|
|
656
652
|
}
|
|
657
653
|
}
|
|
658
654
|
function resolveSessionIds(projectDir) {
|
|
659
|
-
const sessionsDir =
|
|
655
|
+
const sessionsDir = path15__default.join(projectDir, ".skaile", "sessions");
|
|
660
656
|
if (!fs10__default.existsSync(sessionsDir)) return [];
|
|
661
657
|
try {
|
|
662
658
|
return fs10__default.readdirSync(sessionsDir).filter((f) => f.endsWith(".json")).map((f) => f.slice(0, -".json".length));
|
|
@@ -717,7 +713,7 @@ var API_KEY_PREFIX = "apiKey.";
|
|
|
717
713
|
function makeConfigCommand() {
|
|
718
714
|
const cmd = new Command("config").description("Manage personal config (.skaile/settings.json)");
|
|
719
715
|
cmd.command("show").description("Show effective config with sources").option("--project-dir <path>", "Project directory").action(async (opts) => {
|
|
720
|
-
const projectDir = opts.projectDir ?
|
|
716
|
+
const projectDir = opts.projectDir ? path15__default.resolve(opts.projectDir) : process.cwd();
|
|
721
717
|
const config = await resolveSettings(projectDir);
|
|
722
718
|
const wsDefaults = {};
|
|
723
719
|
try {
|
|
@@ -774,13 +770,13 @@ function makeConfigCommand() {
|
|
|
774
770
|
console.log();
|
|
775
771
|
});
|
|
776
772
|
cmd.command("get <key>").description("Get an effective config value").option("--project-dir <path>", "Project directory").action(async (key, opts) => {
|
|
777
|
-
const projectDir = opts.projectDir ?
|
|
773
|
+
const projectDir = opts.projectDir ? path15__default.resolve(opts.projectDir) : process.cwd();
|
|
778
774
|
const config = await resolveSettings(projectDir);
|
|
779
775
|
const value = config[key];
|
|
780
776
|
console.log(value ?? S.dim("(not set)"));
|
|
781
777
|
});
|
|
782
778
|
cmd.command("set <key> <value>").description("Set a personal config value (driver, model, provider, apiKey.<provider>)").option("--project-dir <path>", "Project directory").option("--global", "Write to ~/.skaile/settings.json instead of project-local").action(async (key, value, opts) => {
|
|
783
|
-
const projectDir = opts.projectDir ?
|
|
779
|
+
const projectDir = opts.projectDir ? path15__default.resolve(opts.projectDir) : process.cwd();
|
|
784
780
|
const isApiKey = key.startsWith(API_KEY_PREFIX);
|
|
785
781
|
if (!PERSONAL_KEYS.has(key) && !isApiKey) {
|
|
786
782
|
logErr(
|
|
@@ -796,11 +792,11 @@ function makeConfigCommand() {
|
|
|
796
792
|
const provider = key.slice(API_KEY_PREFIX.length);
|
|
797
793
|
existing.apiKeys = { ...existing.apiKeys, [provider]: value };
|
|
798
794
|
await saveSettings(existing, filePath);
|
|
799
|
-
logOk(`Set ${
|
|
795
|
+
logOk(`Set ${pc5.bold(`apiKeys.${provider}`)} = ****${value.slice(-4)}`);
|
|
800
796
|
} else {
|
|
801
797
|
existing[key] = value;
|
|
802
798
|
await saveSettings(existing, filePath);
|
|
803
|
-
logOk(`Set ${
|
|
799
|
+
logOk(`Set ${pc5.bold(key)} = ${value}`);
|
|
804
800
|
}
|
|
805
801
|
console.log(S.dim(` \u2192 ${filePath}`));
|
|
806
802
|
});
|
|
@@ -897,11 +893,11 @@ function makeConnectCommand() {
|
|
|
897
893
|
process.exitCode = 1;
|
|
898
894
|
return;
|
|
899
895
|
}
|
|
900
|
-
await new Promise((
|
|
896
|
+
await new Promise((resolve4) => {
|
|
901
897
|
client.onMessage((event) => {
|
|
902
898
|
console.log(formatEvent(event));
|
|
903
899
|
if (event.type === "finished" || event.type === "debug_result") {
|
|
904
|
-
|
|
900
|
+
resolve4();
|
|
905
901
|
}
|
|
906
902
|
});
|
|
907
903
|
client.send(parsed);
|
|
@@ -945,12 +941,12 @@ function makeConnectCommand() {
|
|
|
945
941
|
client.send(cmd);
|
|
946
942
|
rl.prompt();
|
|
947
943
|
});
|
|
948
|
-
await new Promise((
|
|
949
|
-
rl.on("close",
|
|
944
|
+
await new Promise((resolve4) => {
|
|
945
|
+
rl.on("close", resolve4);
|
|
950
946
|
client.onDisconnect(() => {
|
|
951
947
|
logErr("Server disconnected");
|
|
952
948
|
rl.close();
|
|
953
|
-
|
|
949
|
+
resolve4();
|
|
954
950
|
});
|
|
955
951
|
});
|
|
956
952
|
await client.disconnect();
|
|
@@ -1016,15 +1012,15 @@ function parseOpArgs(args) {
|
|
|
1016
1012
|
async function promptText(label, defaultVal) {
|
|
1017
1013
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1018
1014
|
const hint = defaultVal ? ` [${defaultVal}]` : "";
|
|
1019
|
-
return new Promise((
|
|
1015
|
+
return new Promise((resolve4) => {
|
|
1020
1016
|
rl.question(` ${label}${hint}: `, (answer) => {
|
|
1021
1017
|
rl.close();
|
|
1022
|
-
|
|
1018
|
+
resolve4(answer.trim() || defaultVal || "");
|
|
1023
1019
|
});
|
|
1024
1020
|
});
|
|
1025
1021
|
}
|
|
1026
1022
|
async function promptSecret(label) {
|
|
1027
|
-
return new Promise((
|
|
1023
|
+
return new Promise((resolve4) => {
|
|
1028
1024
|
process.stdout.write(` ${label} (hidden): `);
|
|
1029
1025
|
const stdin = process.stdin;
|
|
1030
1026
|
stdin.setRawMode?.(true);
|
|
@@ -1037,7 +1033,7 @@ async function promptSecret(label) {
|
|
|
1037
1033
|
stdin.pause();
|
|
1038
1034
|
stdin.removeListener("data", onData);
|
|
1039
1035
|
process.stdout.write("\n");
|
|
1040
|
-
|
|
1036
|
+
resolve4(value);
|
|
1041
1037
|
} else if (char === "") {
|
|
1042
1038
|
process.exit(1);
|
|
1043
1039
|
} else if (char === "\x7F") {
|
|
@@ -1087,8 +1083,8 @@ function makeConnectorCommand() {
|
|
|
1087
1083
|
async (driver, opts) => {
|
|
1088
1084
|
const { installNpmPackages } = await import('../connectors/index.js');
|
|
1089
1085
|
const { scanDirectory, resolveBaseAssetsRoot, WorkspaceYamlEditor } = await import('../core/index.js');
|
|
1090
|
-
const projectDir =
|
|
1091
|
-
const yamlPath =
|
|
1086
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1087
|
+
const yamlPath = path15__default.join(projectDir, "skaile.yaml");
|
|
1092
1088
|
if (!existsSync(yamlPath)) {
|
|
1093
1089
|
logErr("No skaile.yaml found in current directory. Run 'skaile init' first.");
|
|
1094
1090
|
process.exit(1);
|
|
@@ -1151,8 +1147,8 @@ ${installResult.output}`);
|
|
|
1151
1147
|
);
|
|
1152
1148
|
cmd.command("remove <id>").option("--project-dir <path>", "Workspace directory", process.cwd()).description("Remove a connector from skaile.yaml by its instance ID").action(async (id, opts) => {
|
|
1153
1149
|
const { WorkspaceYamlEditor } = await import('../core/index.js');
|
|
1154
|
-
const projectDir =
|
|
1155
|
-
const yamlPath =
|
|
1150
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1151
|
+
const yamlPath = path15__default.join(projectDir, "skaile.yaml");
|
|
1156
1152
|
if (!existsSync(yamlPath)) {
|
|
1157
1153
|
logErr("No skaile.yaml found in current directory.");
|
|
1158
1154
|
process.exit(1);
|
|
@@ -1169,7 +1165,7 @@ ${installResult.output}`);
|
|
|
1169
1165
|
cmd.command("status [id]").option("--project-dir <path>", "Workspace directory", process.cwd()).description("Connect and report health status for one or all connectors").action(async (id, opts) => {
|
|
1170
1166
|
const res = await import('../connectors/index.js');
|
|
1171
1167
|
res.registerBuiltinConnectors();
|
|
1172
|
-
const projectDir =
|
|
1168
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1173
1169
|
const declarations = res.loadConnectorDeclarations(projectDir);
|
|
1174
1170
|
if (declarations.length === 0) {
|
|
1175
1171
|
console.log("No connectors configured in skaile.yaml.");
|
|
@@ -1193,7 +1189,7 @@ ${installResult.output}`);
|
|
|
1193
1189
|
await manager.disconnectAll();
|
|
1194
1190
|
});
|
|
1195
1191
|
cmd.command("list").description("List all configured connectors").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (opts) => {
|
|
1196
|
-
const projectDir =
|
|
1192
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1197
1193
|
const manager = await connectConnectors(projectDir);
|
|
1198
1194
|
try {
|
|
1199
1195
|
console.log(JSON.stringify(manager.listConnectors(), null, 2));
|
|
@@ -1202,7 +1198,7 @@ ${installResult.output}`);
|
|
|
1202
1198
|
}
|
|
1203
1199
|
});
|
|
1204
1200
|
cmd.command("sync [id]").description("Re-sync a filesystem-face connector or all of them (git pull, S3 refresh, etc.)").option("--all", "Sync all connectors").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (id, opts) => {
|
|
1205
|
-
const projectDir =
|
|
1201
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1206
1202
|
const manager = await connectConnectors(projectDir);
|
|
1207
1203
|
try {
|
|
1208
1204
|
if (opts.all) {
|
|
@@ -1226,7 +1222,7 @@ ${installResult.output}`);
|
|
|
1226
1222
|
}
|
|
1227
1223
|
});
|
|
1228
1224
|
cmd.command("ops <id>").description("List available operations for a connector").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (id, opts) => {
|
|
1229
|
-
const projectDir =
|
|
1225
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1230
1226
|
const manager = await connectConnectors(projectDir);
|
|
1231
1227
|
try {
|
|
1232
1228
|
if (!manager.has(id)) {
|
|
@@ -1241,7 +1237,7 @@ ${installResult.output}`);
|
|
|
1241
1237
|
}
|
|
1242
1238
|
});
|
|
1243
1239
|
cmd.command("read <id> <resource-path>").description("Read content from a connector").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (id, resourcePath, opts) => {
|
|
1244
|
-
const projectDir =
|
|
1240
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1245
1241
|
const manager = await connectConnectors(projectDir);
|
|
1246
1242
|
try {
|
|
1247
1243
|
const content = await manager.read(id, resourcePath);
|
|
@@ -1251,7 +1247,7 @@ ${installResult.output}`);
|
|
|
1251
1247
|
}
|
|
1252
1248
|
});
|
|
1253
1249
|
cmd.command("write <id> <resource-path> <content>").description("Write content to a connector at a path").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (id, resourcePath, content, opts) => {
|
|
1254
|
-
const projectDir =
|
|
1250
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1255
1251
|
const manager = await connectConnectors(projectDir);
|
|
1256
1252
|
try {
|
|
1257
1253
|
await manager.write(id, resourcePath, { data: content });
|
|
@@ -1261,7 +1257,7 @@ ${installResult.output}`);
|
|
|
1261
1257
|
}
|
|
1262
1258
|
});
|
|
1263
1259
|
cmd.command("entries <id> [resource-path]").description("List entries in a connector (files, keys, rows, etc)").option("--project-dir <path>", "Workspace directory", process.cwd()).option("--recursive", "Include nested entries").option("--limit <n>", "Maximum entries to return", parseInt).action(async (id, resourcePath, opts) => {
|
|
1264
|
-
const projectDir =
|
|
1260
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1265
1261
|
const manager = await connectConnectors(projectDir);
|
|
1266
1262
|
try {
|
|
1267
1263
|
const entries = await manager.list(id, resourcePath, {
|
|
@@ -1274,7 +1270,7 @@ ${installResult.output}`);
|
|
|
1274
1270
|
}
|
|
1275
1271
|
});
|
|
1276
1272
|
cmd.command("search <id> <query>").description("Search within a connector for matching content").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (id, query, opts) => {
|
|
1277
|
-
const projectDir =
|
|
1273
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1278
1274
|
const manager = await connectConnectors(projectDir);
|
|
1279
1275
|
try {
|
|
1280
1276
|
const results = await manager.search(id, query);
|
|
@@ -1284,7 +1280,7 @@ ${installResult.output}`);
|
|
|
1284
1280
|
}
|
|
1285
1281
|
});
|
|
1286
1282
|
cmd.command("delete <id> <resource-path>").description("Delete an entry from a connector").option("--project-dir <path>", "Workspace directory", process.cwd()).action(async (id, resourcePath, opts) => {
|
|
1287
|
-
const projectDir =
|
|
1283
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1288
1284
|
const manager = await connectConnectors(projectDir);
|
|
1289
1285
|
try {
|
|
1290
1286
|
const ok = await manager.delete(id, resourcePath);
|
|
@@ -1294,7 +1290,7 @@ ${installResult.output}`);
|
|
|
1294
1290
|
}
|
|
1295
1291
|
});
|
|
1296
1292
|
cmd.command("run <id> <op> [args...]").description("Run a custom adapter operation (e.g. git log, sqlite query)").option("--project-dir <path>", "Workspace directory", process.cwd()).allowUnknownOption().action(async (id, op, args, opts) => {
|
|
1297
|
-
const projectDir =
|
|
1293
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
1298
1294
|
const manager = await connectConnectors(projectDir);
|
|
1299
1295
|
try {
|
|
1300
1296
|
if (!manager.has(id)) {
|
|
@@ -1329,17 +1325,17 @@ async function runDebugQuery(query, parentOpts, args) {
|
|
|
1329
1325
|
process.exitCode = 1;
|
|
1330
1326
|
return;
|
|
1331
1327
|
}
|
|
1332
|
-
await new Promise((
|
|
1328
|
+
await new Promise((resolve4) => {
|
|
1333
1329
|
const timeout = setTimeout(() => {
|
|
1334
1330
|
logErr("Timeout: no debug_result received within 5s");
|
|
1335
1331
|
process.exitCode = 1;
|
|
1336
|
-
|
|
1332
|
+
resolve4();
|
|
1337
1333
|
}, 5e3);
|
|
1338
1334
|
client.onMessage((event) => {
|
|
1339
1335
|
if (event.type !== "debug_result") return;
|
|
1340
1336
|
clearTimeout(timeout);
|
|
1341
1337
|
printDebugResult(query, event.data);
|
|
1342
|
-
|
|
1338
|
+
resolve4();
|
|
1343
1339
|
});
|
|
1344
1340
|
client.send({ type: "debug", query, args });
|
|
1345
1341
|
});
|
|
@@ -1385,7 +1381,7 @@ function makeDebugCommand() {
|
|
|
1385
1381
|
}
|
|
1386
1382
|
function makeDiffCommand() {
|
|
1387
1383
|
return new Command("diff").description("Show changes between deployed and source versions").argument("<ref>", "Asset reference (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
1388
|
-
const am2 = new AssetManager({ projectDir:
|
|
1384
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
1389
1385
|
const result = am2.diff(ref);
|
|
1390
1386
|
if (result === null) {
|
|
1391
1387
|
logInfo("No differences (or asset not found).");
|
|
@@ -1438,7 +1434,7 @@ function makeFlowCommand() {
|
|
|
1438
1434
|
function makeHistoryCommand() {
|
|
1439
1435
|
const cmd = new Command("history").description("Show recent add/remove/update actions");
|
|
1440
1436
|
cmd.argument("[limit]", "Number of entries to show", "20").option("--project-dir <path>", "Project directory", process.cwd()).action((limitStr, opts) => {
|
|
1441
|
-
const am2 = new AssetManager({ projectDir:
|
|
1437
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
1442
1438
|
const limit = parseInt(limitStr, 10) || 20;
|
|
1443
1439
|
const entries = am2.history(limit);
|
|
1444
1440
|
if (entries.length === 0) {
|
|
@@ -1458,18 +1454,21 @@ function makeHistoryCommand() {
|
|
|
1458
1454
|
console.log();
|
|
1459
1455
|
});
|
|
1460
1456
|
cmd.command("clear").description("Clear action history").option("--project-dir <path>", "Project directory", process.cwd()).action((opts) => {
|
|
1461
|
-
const am2 = new AssetManager({ projectDir:
|
|
1457
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
1462
1458
|
am2.clearHistory();
|
|
1463
1459
|
logOk("History cleared.");
|
|
1464
1460
|
});
|
|
1465
1461
|
return cmd;
|
|
1466
1462
|
}
|
|
1463
|
+
var NOT_WIRED_MSG_PUBLISH = "`skaile library publish` is scaffolded but not yet wired. Server endpoint `POST /sources/<id>/manifest` lands in a follow-up PR.";
|
|
1464
|
+
function notWired(verb, msg) {
|
|
1465
|
+
logErr(`${msg} (verb: library ${verb})`);
|
|
1466
|
+
process.exit(2);
|
|
1467
|
+
}
|
|
1467
1468
|
function makeLibraryCommand() {
|
|
1468
|
-
const cmd = new Command("library").description(
|
|
1469
|
-
"Manage user-facing libraries (local / git / store)"
|
|
1470
|
-
);
|
|
1469
|
+
const cmd = new Command("library").description("Manage user authoring libraries (local / git)");
|
|
1471
1470
|
cmd.command("list").description("List all registered libraries").option("--json", "Output as JSON").action(async (opts) => {
|
|
1472
|
-
const { manager, close } = await
|
|
1471
|
+
const { manager, close } = await openLibraryManager();
|
|
1473
1472
|
try {
|
|
1474
1473
|
const ls = await manager.listLibraries();
|
|
1475
1474
|
if (opts.json) return console.log(JSON.stringify(ls, null, 2));
|
|
@@ -1481,9 +1480,9 @@ function makeLibraryCommand() {
|
|
|
1481
1480
|
console.log(S.heading(" Libraries"));
|
|
1482
1481
|
console.log(` ${S.rule(80)}`);
|
|
1483
1482
|
for (const l of ls) {
|
|
1484
|
-
const def = l.isDefault ?
|
|
1483
|
+
const def = l.isDefault ? pc5.green("\u25B8 ") : " ";
|
|
1485
1484
|
console.log(
|
|
1486
|
-
`${def}${S.cmd(l.name.padEnd(16))} ${
|
|
1485
|
+
`${def}${S.cmd(l.name.padEnd(16))} ${pc5.dim(l.backend.padEnd(7))} ${pc5.dim(l.ownership.padEnd(12))} ${pc5.dim(l.path)}`
|
|
1487
1486
|
);
|
|
1488
1487
|
}
|
|
1489
1488
|
console.log(`
|
|
@@ -1493,41 +1492,15 @@ function makeLibraryCommand() {
|
|
|
1493
1492
|
close();
|
|
1494
1493
|
}
|
|
1495
1494
|
});
|
|
1496
|
-
cmd.command("status [name]").description("Show
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
const results = await Promise.all(
|
|
1501
|
-
targets.map(async (l) => ({
|
|
1502
|
-
library: l.name,
|
|
1503
|
-
status: await manager.driverFor(l.backend).status(l)
|
|
1504
|
-
}))
|
|
1505
|
-
);
|
|
1506
|
-
if (opts.json) return console.log(JSON.stringify(results, null, 2));
|
|
1507
|
-
for (const { library, status: status4 } of results) {
|
|
1508
|
-
console.log(`
|
|
1509
|
-
${S.heading(library)}`);
|
|
1510
|
-
console.log(` reachable: ${status4.reachable ? S.ok("yes") : S.err("no")}`);
|
|
1511
|
-
if (status4.remoteChanges) {
|
|
1512
|
-
console.log(
|
|
1513
|
-
` behind/ahead: ${status4.remoteChanges.behind}/${status4.remoteChanges.ahead}`
|
|
1514
|
-
);
|
|
1515
|
-
}
|
|
1516
|
-
const lc = status4.localChanges;
|
|
1517
|
-
if (lc.added.length + lc.modified.length + lc.deleted.length > 0) {
|
|
1518
|
-
console.log(
|
|
1519
|
-
` local changes: +${lc.added.length} ~${lc.modified.length} -${lc.deleted.length}`
|
|
1520
|
-
);
|
|
1521
|
-
}
|
|
1522
|
-
for (const note of status4.notes) console.log(` ${pc6.dim("note:")} ${note}`);
|
|
1523
|
-
}
|
|
1524
|
-
console.log();
|
|
1525
|
-
} finally {
|
|
1526
|
-
close();
|
|
1495
|
+
cmd.command("status [name]").description("Show overall Library health or per-library sync status").option("--project-dir <path>", "Project directory", process.cwd()).option("--json", "Output as JSON").action(async (name, opts) => {
|
|
1496
|
+
if (name) {
|
|
1497
|
+
await printPerLibraryStatus(name, opts.json ?? false);
|
|
1498
|
+
return;
|
|
1527
1499
|
}
|
|
1500
|
+
await printOverallStatus(opts.projectDir, opts.json ?? false);
|
|
1528
1501
|
});
|
|
1529
1502
|
cmd.command("show <name>").description("Detailed info about a library").action(async (name) => {
|
|
1530
|
-
const { manager, close } = await
|
|
1503
|
+
const { manager, close } = await openLibraryManager();
|
|
1531
1504
|
try {
|
|
1532
1505
|
const l = await manager.requireLibrary(name);
|
|
1533
1506
|
console.log();
|
|
@@ -1546,12 +1519,12 @@ function makeLibraryCommand() {
|
|
|
1546
1519
|
close();
|
|
1547
1520
|
}
|
|
1548
1521
|
});
|
|
1549
|
-
cmd.command("init <name>").description("Create a new local library at ~/.skaile/libraries/<name>").option("--git <url>", "Initialize as a git library tracking <url>").option("--
|
|
1522
|
+
cmd.command("init <name>").description("Create a new local library at ~/.skaile/libraries/<name>").option("--git <url>", "Initialize as a git library tracking <url>").option("--owner", "Mark as owner (default for local)").option("--contributor", "Mark as contributor").option("--reader", "Mark as reader").action(
|
|
1550
1523
|
async (name, opts) => {
|
|
1551
1524
|
const libDir = resolveLibraryDir();
|
|
1552
|
-
const libPath =
|
|
1525
|
+
const libPath = path15.join(libDir, name);
|
|
1553
1526
|
const ownership = opts.contributor ? "contributor" : opts.reader ? "reader" : "owner";
|
|
1554
|
-
const { manager, close } = await
|
|
1527
|
+
const { manager, close } = await openLibraryManager();
|
|
1555
1528
|
try {
|
|
1556
1529
|
if (opts.git) {
|
|
1557
1530
|
fs10.mkdirSync(libPath, { recursive: true });
|
|
@@ -1562,14 +1535,6 @@ function makeLibraryCommand() {
|
|
|
1562
1535
|
backendConfig: { url: opts.git, branch: "main", authHint: "ssh" },
|
|
1563
1536
|
ownership
|
|
1564
1537
|
});
|
|
1565
|
-
} else if (opts.store) {
|
|
1566
|
-
await manager.addLibrary({
|
|
1567
|
-
name,
|
|
1568
|
-
path: libPath,
|
|
1569
|
-
backend: "store",
|
|
1570
|
-
backendConfig: { url: opts.store, publisher: "", tokenRef: "" },
|
|
1571
|
-
ownership
|
|
1572
|
-
});
|
|
1573
1538
|
} else {
|
|
1574
1539
|
fs10.mkdirSync(libPath, { recursive: true });
|
|
1575
1540
|
await manager.addLibrary({
|
|
@@ -1586,45 +1551,65 @@ function makeLibraryCommand() {
|
|
|
1586
1551
|
}
|
|
1587
1552
|
}
|
|
1588
1553
|
);
|
|
1589
|
-
cmd.command("
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
const
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
logErr(`git clone failed for ${url}`);
|
|
1554
|
+
cmd.command("link <name> <git-url>").description("Attach a github remote to a local library (makes it store-publishable)").option("--branch <name>", "Default branch", "main").action(async (name, url, opts) => {
|
|
1555
|
+
const { manager, close } = await openLibraryManager();
|
|
1556
|
+
try {
|
|
1557
|
+
const lib = await manager.requireLibrary(name);
|
|
1558
|
+
if (lib.backend === "git") {
|
|
1559
|
+
logErr(
|
|
1560
|
+
`Library "${name}" is already git-backed. Use \`skaile library set ${name} backendConfig.url=${url}\` to change its remote.`
|
|
1561
|
+
);
|
|
1598
1562
|
process.exit(1);
|
|
1599
1563
|
}
|
|
1600
|
-
|
|
1601
|
-
|
|
1564
|
+
await manager.updateLibrary(name, {
|
|
1565
|
+
backend: "git",
|
|
1566
|
+
backendConfig: { url, branch: opts.branch, authHint: "ssh" }
|
|
1567
|
+
});
|
|
1568
|
+
logOk(`Library "${name}" linked to ${url}.`);
|
|
1569
|
+
} finally {
|
|
1570
|
+
close();
|
|
1571
|
+
}
|
|
1572
|
+
});
|
|
1573
|
+
cmd.command("create <kind> <name>").description("Scaffold a new asset inside the default (or named) library").addOption(
|
|
1574
|
+
new Option("--kind <kind>", "Asset kind override").choices([...ASSET_KINDS])
|
|
1575
|
+
).option("--library <name>", "Target library (default: the default-marked one)").option("--dir <dir>", "Target directory inside the library (default: library root)").action(
|
|
1576
|
+
async (kind, name, opts) => {
|
|
1577
|
+
if (!ASSET_KINDS.includes(kind)) {
|
|
1578
|
+
logErr(`Unknown asset kind "${kind}". Expected: ${ASSET_KINDS.join(", ")}.`);
|
|
1579
|
+
process.exit(1);
|
|
1580
|
+
}
|
|
1581
|
+
const { manager, close } = await openLibraryManager();
|
|
1602
1582
|
try {
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1583
|
+
let target;
|
|
1584
|
+
if (opts.library) {
|
|
1585
|
+
target = await manager.requireLibrary(opts.library);
|
|
1586
|
+
} else {
|
|
1587
|
+
const all = await manager.listLibraries();
|
|
1588
|
+
const def = all.find((l) => l.isDefault);
|
|
1589
|
+
if (!def) {
|
|
1590
|
+
logErr(
|
|
1591
|
+
"No default library set. Either run `skaile library default <name>` first or pass --library."
|
|
1592
|
+
);
|
|
1593
|
+
process.exit(1);
|
|
1594
|
+
}
|
|
1595
|
+
target = def;
|
|
1596
|
+
}
|
|
1597
|
+
const am2 = new AssetManager({ projectDir: process.cwd() });
|
|
1598
|
+
const destDir = opts.dir ? path15.join(target.path, opts.dir) : target.path;
|
|
1599
|
+
const res = am2.create(name, opts.kind ?? kind, destDir);
|
|
1600
|
+
if (res.ok) {
|
|
1601
|
+
logOk(`Created ${opts.kind ?? kind}: ${pc5.dim(res.path)}`);
|
|
1602
|
+
} else {
|
|
1603
|
+
logErr(res.path);
|
|
1604
|
+
process.exit(1);
|
|
1619
1605
|
}
|
|
1620
|
-
logOk(`Library "${derived}" added (${ownership}).`);
|
|
1621
1606
|
} finally {
|
|
1622
1607
|
close();
|
|
1623
1608
|
}
|
|
1624
1609
|
}
|
|
1625
1610
|
);
|
|
1626
1611
|
cmd.command("default <name>").description("Set <name> as the default library").action(async (name) => {
|
|
1627
|
-
const { manager, close } = await
|
|
1612
|
+
const { manager, close } = await openLibraryManager();
|
|
1628
1613
|
try {
|
|
1629
1614
|
const updated = await manager.setDefault(name);
|
|
1630
1615
|
logOk(`Default \u2192 ${updated.name}`);
|
|
@@ -1633,11 +1618,11 @@ function makeLibraryCommand() {
|
|
|
1633
1618
|
}
|
|
1634
1619
|
});
|
|
1635
1620
|
cmd.command("rename <old> <new>").description("Rename a library (also moves the directory)").action(async (oldName, newName) => {
|
|
1636
|
-
const { manager, close } = await
|
|
1621
|
+
const { manager, close } = await openLibraryManager();
|
|
1637
1622
|
try {
|
|
1638
1623
|
const lib = await manager.requireLibrary(oldName);
|
|
1639
|
-
const parent =
|
|
1640
|
-
const newPath =
|
|
1624
|
+
const parent = path15.dirname(lib.path);
|
|
1625
|
+
const newPath = path15.join(parent, newName);
|
|
1641
1626
|
if (fs10.existsSync(newPath)) {
|
|
1642
1627
|
logErr(`Target directory already exists: ${newPath}`);
|
|
1643
1628
|
process.exit(1);
|
|
@@ -1652,7 +1637,7 @@ function makeLibraryCommand() {
|
|
|
1652
1637
|
cmd.command("set <name> <field=value>").description("Update a single library field (e.g. ownership=contributor)").action(async (name, expr) => {
|
|
1653
1638
|
const [field, ...rest] = expr.split("=");
|
|
1654
1639
|
const value = rest.join("=");
|
|
1655
|
-
const { manager, close } = await
|
|
1640
|
+
const { manager, close } = await openLibraryManager();
|
|
1656
1641
|
try {
|
|
1657
1642
|
const lib = await manager.requireLibrary(name);
|
|
1658
1643
|
const patch = {};
|
|
@@ -1673,7 +1658,7 @@ function makeLibraryCommand() {
|
|
|
1673
1658
|
}
|
|
1674
1659
|
});
|
|
1675
1660
|
cmd.command("remove <name>").description("Unregister a library (use --purge to also delete files)").option("--purge", "Also delete the library directory from disk", false).option("-y, --yes", "Skip confirmation", false).action(async (name, opts) => {
|
|
1676
|
-
const { manager, close } = await
|
|
1661
|
+
const { manager, close } = await openLibraryManager();
|
|
1677
1662
|
try {
|
|
1678
1663
|
if (!opts.yes) {
|
|
1679
1664
|
const confirmed = await p5.confirm({
|
|
@@ -1692,7 +1677,7 @@ function makeLibraryCommand() {
|
|
|
1692
1677
|
}
|
|
1693
1678
|
});
|
|
1694
1679
|
cmd.command("sync <name>").description("Pull then push (as backend permits)").action(async (name) => {
|
|
1695
|
-
const { manager, close } = await
|
|
1680
|
+
const { manager, close } = await openLibraryManager();
|
|
1696
1681
|
try {
|
|
1697
1682
|
const lib = await manager.requireLibrary(name);
|
|
1698
1683
|
const drv = manager.driverFor(lib.backend);
|
|
@@ -1709,7 +1694,7 @@ function makeLibraryCommand() {
|
|
|
1709
1694
|
}
|
|
1710
1695
|
});
|
|
1711
1696
|
cmd.command("pull <name>").description("Pull from remote").action(async (name) => {
|
|
1712
|
-
const { manager, close } = await
|
|
1697
|
+
const { manager, close } = await openLibraryManager();
|
|
1713
1698
|
try {
|
|
1714
1699
|
const lib = await manager.requireLibrary(name);
|
|
1715
1700
|
const res = await manager.driverFor(lib.backend).pull(lib);
|
|
@@ -1723,7 +1708,7 @@ function makeLibraryCommand() {
|
|
|
1723
1708
|
}
|
|
1724
1709
|
});
|
|
1725
1710
|
cmd.command("push <name>").description("Push local commits (owner only)").option("--force", "Force-with-lease push", false).action(async (name, opts) => {
|
|
1726
|
-
const { manager, close } = await
|
|
1711
|
+
const { manager, close } = await openLibraryManager();
|
|
1727
1712
|
try {
|
|
1728
1713
|
const lib = await manager.requireLibrary(name);
|
|
1729
1714
|
const res = await manager.driverFor(lib.backend).push(lib, { force: opts.force });
|
|
@@ -1736,7 +1721,7 @@ function makeLibraryCommand() {
|
|
|
1736
1721
|
}
|
|
1737
1722
|
});
|
|
1738
1723
|
cmd.command("propose <name>").description("Open a pull request on the upstream repo (contributor only)").option("--title <title>", "PR title").option("--body <body>", "PR body").option("--branch <name>", "Branch name (default: skaile/<timestamp>)").action(async (name, opts) => {
|
|
1739
|
-
const { manager, close } = await
|
|
1724
|
+
const { manager, close } = await openLibraryManager();
|
|
1740
1725
|
try {
|
|
1741
1726
|
const lib = await manager.requireLibrary(name);
|
|
1742
1727
|
const res = await manager.driverFor(lib.backend).propose(lib, opts);
|
|
@@ -1749,16 +1734,16 @@ function makeLibraryCommand() {
|
|
|
1749
1734
|
}
|
|
1750
1735
|
});
|
|
1751
1736
|
cmd.command("commit <name>").description("Git-commit local changes inside the library (git only)").option("-m, --message <msg>", "Commit message", "skaile: update assets").action(async (name, opts) => {
|
|
1752
|
-
const { manager, close } = await
|
|
1737
|
+
const { manager, close } = await openLibraryManager();
|
|
1753
1738
|
try {
|
|
1754
1739
|
const lib = await manager.requireLibrary(name);
|
|
1755
1740
|
if (lib.backend !== "git") {
|
|
1756
1741
|
logErr("`library commit` only applies to git-backed libraries.");
|
|
1757
1742
|
process.exit(1);
|
|
1758
1743
|
}
|
|
1759
|
-
const { spawnSync } = await import('child_process');
|
|
1760
|
-
|
|
1761
|
-
const r =
|
|
1744
|
+
const { spawnSync: spawnSync2 } = await import('child_process');
|
|
1745
|
+
spawnSync2("git", ["add", "-A"], { cwd: lib.path, stdio: "inherit" });
|
|
1746
|
+
const r = spawnSync2("git", ["commit", "-m", opts.message], {
|
|
1762
1747
|
cwd: lib.path,
|
|
1763
1748
|
stdio: "inherit"
|
|
1764
1749
|
});
|
|
@@ -1773,126 +1758,151 @@ function makeLibraryCommand() {
|
|
|
1773
1758
|
});
|
|
1774
1759
|
cmd.command("git <name>").description("Pass-through: run arbitrary git commands inside the library").allowUnknownOption().action(async (name, _, command) => {
|
|
1775
1760
|
const args = command.args.slice(1);
|
|
1776
|
-
const { manager, close } = await
|
|
1761
|
+
const { manager, close } = await openLibraryManager();
|
|
1777
1762
|
try {
|
|
1778
1763
|
const lib = await manager.requireLibrary(name);
|
|
1779
|
-
const { spawnSync } = await import('child_process');
|
|
1780
|
-
const r =
|
|
1764
|
+
const { spawnSync: spawnSync2 } = await import('child_process');
|
|
1765
|
+
const r = spawnSync2("git", args, { cwd: lib.path, stdio: "inherit" });
|
|
1781
1766
|
process.exit(r.status ?? 0);
|
|
1782
1767
|
} finally {
|
|
1783
1768
|
close();
|
|
1784
1769
|
}
|
|
1785
1770
|
});
|
|
1771
|
+
cmd.command("publish <ref>").description("Push + register manifest atomically (needs git backend)").action((_ref) => notWired("publish", NOT_WIRED_MSG_PUBLISH));
|
|
1772
|
+
cmd.command("register <ref>").description("Register manifest in store without pushing (bytes already on github)").action((_ref) => notWired("register", NOT_WIRED_MSG_PUBLISH));
|
|
1773
|
+
cmd.command("yank <ref>").description("Retract a catalog entry").option("--version <v>", "Version to yank").action((_ref) => notWired("yank", NOT_WIRED_MSG_PUBLISH));
|
|
1786
1774
|
return cmd;
|
|
1787
1775
|
}
|
|
1788
|
-
function
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
const provider = new LocalSecretsProvider2();
|
|
1799
|
-
secretsStrategy = provider.getKeyStrategy();
|
|
1800
|
-
} catch {
|
|
1801
|
-
secretsStrategy = "unavailable";
|
|
1802
|
-
}
|
|
1803
|
-
const projectDir = path16__default.resolve(opts.projectDir);
|
|
1804
|
-
const hasWorkspaceConfig = existsSync(path16__default.join(projectDir, "skaile.yaml"));
|
|
1805
|
-
const hasLockFile = existsSync(path16__default.join(projectDir, "skaile.lock.yaml"));
|
|
1806
|
-
let subscriptions = [];
|
|
1807
|
-
if (hasWorkspaceConfig) {
|
|
1808
|
-
try {
|
|
1809
|
-
subscriptions = await library.listSubscriptions(projectDir);
|
|
1810
|
-
} catch {
|
|
1811
|
-
}
|
|
1812
|
-
}
|
|
1813
|
-
const registry = createFullRegistry();
|
|
1814
|
-
const registeredKinds = registry.getAllKinds();
|
|
1815
|
-
if (opts.json) {
|
|
1816
|
-
console.log(
|
|
1817
|
-
JSON.stringify(
|
|
1818
|
-
{
|
|
1819
|
-
sources: sources.length,
|
|
1820
|
-
assetDefinitions: defs.length,
|
|
1821
|
-
instances: instances.length,
|
|
1822
|
-
subscriptions: subscriptions.length,
|
|
1823
|
-
secretsStrategy,
|
|
1824
|
-
registeredKinds,
|
|
1825
|
-
workspace: {
|
|
1826
|
-
dir: projectDir,
|
|
1827
|
-
hasConfig: hasWorkspaceConfig,
|
|
1828
|
-
hasLock: hasLockFile
|
|
1829
|
-
}
|
|
1830
|
-
},
|
|
1831
|
-
null,
|
|
1832
|
-
2
|
|
1833
|
-
)
|
|
1834
|
-
);
|
|
1835
|
-
return;
|
|
1836
|
-
}
|
|
1837
|
-
console.log();
|
|
1838
|
-
console.log(S.heading(" Library Status"));
|
|
1839
|
-
console.log(` ${S.rule(50)}`);
|
|
1776
|
+
async function printPerLibraryStatus(name, asJson) {
|
|
1777
|
+
const { manager, close } = await openLibraryManager();
|
|
1778
|
+
try {
|
|
1779
|
+
const lib = await manager.requireLibrary(name);
|
|
1780
|
+
const status4 = await manager.driverFor(lib.backend).status(lib);
|
|
1781
|
+
if (asJson) return console.log(JSON.stringify({ library: lib.name, status: status4 }, null, 2));
|
|
1782
|
+
console.log(`
|
|
1783
|
+
${S.heading(lib.name)}`);
|
|
1784
|
+
console.log(` reachable: ${status4.reachable ? S.ok("yes") : S.err("no")}`);
|
|
1785
|
+
if (status4.remoteChanges) {
|
|
1840
1786
|
console.log(
|
|
1841
|
-
`
|
|
1787
|
+
` behind/ahead: ${status4.remoteChanges.behind}/${status4.remoteChanges.ahead}`
|
|
1842
1788
|
);
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
console.log(
|
|
1847
|
-
` ${S.dim(s.id.slice(0, 8))} ${s.name} ${S.dim(`(${s.type})`)} ${S.dim(s.ownership)} ${S.dim(synced)}${defaultMark}`
|
|
1848
|
-
);
|
|
1849
|
-
}
|
|
1850
|
-
if (sources.length > 3) {
|
|
1851
|
-
console.log(` ${S.dim(`...and ${sources.length - 3} more`)}`);
|
|
1852
|
-
}
|
|
1853
|
-
const byKind = /* @__PURE__ */ new Map();
|
|
1854
|
-
for (const d of defs) {
|
|
1855
|
-
byKind.set(d.kind, (byKind.get(d.kind) ?? 0) + 1);
|
|
1856
|
-
}
|
|
1789
|
+
}
|
|
1790
|
+
const lc = status4.localChanges;
|
|
1791
|
+
if (lc.added.length + lc.modified.length + lc.deleted.length > 0) {
|
|
1857
1792
|
console.log(
|
|
1858
|
-
`
|
|
1793
|
+
` local changes: +${lc.added.length} ~${lc.modified.length} -${lc.deleted.length}`
|
|
1859
1794
|
);
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1795
|
+
}
|
|
1796
|
+
for (const note of status4.notes) console.log(` ${pc5.dim("note:")} ${note}`);
|
|
1797
|
+
console.log();
|
|
1798
|
+
} finally {
|
|
1799
|
+
close();
|
|
1800
|
+
}
|
|
1801
|
+
}
|
|
1802
|
+
async function printOverallStatus(projectDir, asJson) {
|
|
1803
|
+
const library = await openLibrary();
|
|
1804
|
+
try {
|
|
1805
|
+
const libs = await library.listSources();
|
|
1806
|
+
const defs = await library.listAssetDefs();
|
|
1807
|
+
const instances = await library.listInstances();
|
|
1808
|
+
let secretsStrategy = "unknown";
|
|
1809
|
+
try {
|
|
1810
|
+
const { LocalSecretsProvider: LocalSecretsProvider2 } = await import('../secrets/index.js');
|
|
1811
|
+
const provider = new LocalSecretsProvider2();
|
|
1812
|
+
secretsStrategy = provider.getKeyStrategy();
|
|
1813
|
+
} catch {
|
|
1814
|
+
secretsStrategy = "unavailable";
|
|
1815
|
+
}
|
|
1816
|
+
const resolvedProjectDir = path15.resolve(projectDir);
|
|
1817
|
+
const hasWorkspaceConfig = existsSync(path15.join(resolvedProjectDir, "skaile.yaml"));
|
|
1818
|
+
const hasLockFile = existsSync(path15.join(resolvedProjectDir, "skaile.lock.yaml"));
|
|
1819
|
+
let subscriptions = [];
|
|
1820
|
+
if (hasWorkspaceConfig) {
|
|
1821
|
+
try {
|
|
1822
|
+
subscriptions = await library.listSubscriptions(resolvedProjectDir);
|
|
1823
|
+
} catch {
|
|
1863
1824
|
}
|
|
1825
|
+
}
|
|
1826
|
+
const registry = createFullRegistry();
|
|
1827
|
+
const registeredKinds = registry.getAllKinds();
|
|
1828
|
+
if (asJson) {
|
|
1864
1829
|
console.log(
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1830
|
+
JSON.stringify(
|
|
1831
|
+
{
|
|
1832
|
+
libraries: libs.length,
|
|
1833
|
+
assetDefinitions: defs.length,
|
|
1834
|
+
instances: instances.length,
|
|
1835
|
+
subscriptions: subscriptions.length,
|
|
1836
|
+
secretsStrategy,
|
|
1837
|
+
registeredKinds,
|
|
1838
|
+
workspace: {
|
|
1839
|
+
dir: resolvedProjectDir,
|
|
1840
|
+
hasConfig: hasWorkspaceConfig,
|
|
1841
|
+
hasLock: hasLockFile
|
|
1842
|
+
}
|
|
1843
|
+
},
|
|
1844
|
+
null,
|
|
1845
|
+
2
|
|
1846
|
+
)
|
|
1880
1847
|
);
|
|
1848
|
+
return;
|
|
1849
|
+
}
|
|
1850
|
+
console.log();
|
|
1851
|
+
console.log(S.heading(" Library Status"));
|
|
1852
|
+
console.log(` ${S.rule(50)}`);
|
|
1853
|
+
console.log(
|
|
1854
|
+
` ${S.label("Libraries")} ${libs.length === 0 ? S.dim("none") : pc5.green(String(libs.length))}`
|
|
1855
|
+
);
|
|
1856
|
+
for (const l of libs.slice(0, 3)) {
|
|
1857
|
+
const synced = l.lastSyncAt ? `synced ${formatRelativeTime(l.lastSyncAt)}` : "never synced";
|
|
1858
|
+
const defaultMark = l.isDefault ? pc5.green(" *") : "";
|
|
1881
1859
|
console.log(
|
|
1882
|
-
`
|
|
1860
|
+
` ${S.dim(l.id.slice(0, 8))} ${l.name} ${S.dim(`(${l.backend})`)} ${S.dim(l.ownership)} ${S.dim(synced)}${defaultMark}`
|
|
1883
1861
|
);
|
|
1884
|
-
console.log();
|
|
1885
|
-
if (sources.length === 0) {
|
|
1886
|
-
logInfo("Get started: `skaile library init personal` or `skaile library add <git-url>`");
|
|
1887
|
-
} else if (defs.length === 0) {
|
|
1888
|
-
logInfo("Run `skaile library sync <name>` to discover assets.");
|
|
1889
|
-
} else if (instances.length === 0) {
|
|
1890
|
-
logInfo("Run `skaile asset install <ref>` to install an asset into a workspace.");
|
|
1891
|
-
}
|
|
1892
|
-
} finally {
|
|
1893
|
-
library.close();
|
|
1894
1862
|
}
|
|
1895
|
-
|
|
1863
|
+
if (libs.length > 3) {
|
|
1864
|
+
console.log(` ${S.dim(`...and ${libs.length - 3} more`)}`);
|
|
1865
|
+
}
|
|
1866
|
+
const byKind = /* @__PURE__ */ new Map();
|
|
1867
|
+
for (const d of defs) byKind.set(d.kind, (byKind.get(d.kind) ?? 0) + 1);
|
|
1868
|
+
console.log(
|
|
1869
|
+
` ${S.label("Cached assets")} ${defs.length === 0 ? S.dim("none") : pc5.green(String(defs.length))}`
|
|
1870
|
+
);
|
|
1871
|
+
if (byKind.size > 0) {
|
|
1872
|
+
const parts = [...byKind.entries()].sort((a, b) => b[1] - a[1]).map(([k, v]) => `${v} ${k}${v > 1 ? "s" : ""}`);
|
|
1873
|
+
console.log(` ${S.dim(parts.join(", "))}`);
|
|
1874
|
+
}
|
|
1875
|
+
console.log(
|
|
1876
|
+
` ${S.label("Registered kinds")} ${pc5.green(String(registeredKinds.length))}`
|
|
1877
|
+
);
|
|
1878
|
+
console.log(` ${S.dim(registeredKinds.join(", "))}`);
|
|
1879
|
+
console.log(
|
|
1880
|
+
` ${S.label("Instances")} ${instances.length === 0 ? S.dim("none") : pc5.green(String(instances.length))}`
|
|
1881
|
+
);
|
|
1882
|
+
console.log(
|
|
1883
|
+
` ${S.label("Subscriptions")} ${subscriptions.length === 0 ? S.dim("none (this workspace)") : pc5.green(String(subscriptions.length))}`
|
|
1884
|
+
);
|
|
1885
|
+
const strategyColor = secretsStrategy === "unavailable" ? S.dim : pc5.green;
|
|
1886
|
+
console.log(` ${S.label("Secrets strategy")} ${strategyColor(secretsStrategy)}`);
|
|
1887
|
+
console.log(` ${S.rule(50)}`);
|
|
1888
|
+
console.log(` ${S.label("Workspace")} ${resolvedProjectDir}`);
|
|
1889
|
+
console.log(
|
|
1890
|
+
` ${S.label("skaile.yaml")} ${hasWorkspaceConfig ? pc5.green("present") : S.dim("not found")}`
|
|
1891
|
+
);
|
|
1892
|
+
console.log(
|
|
1893
|
+
` ${S.label("skaile.lock.yaml")} ${hasLockFile ? pc5.green("present") : S.dim("not found")}`
|
|
1894
|
+
);
|
|
1895
|
+
console.log();
|
|
1896
|
+
if (libs.length === 0) {
|
|
1897
|
+
logInfo("Get started: `skaile library init personal` or `skaile source add <git-url>`");
|
|
1898
|
+
} else if (defs.length === 0) {
|
|
1899
|
+
logInfo("Run `skaile library sync <name>` to discover assets.");
|
|
1900
|
+
} else if (instances.length === 0) {
|
|
1901
|
+
logInfo("Run `skaile asset install <ref>` to install an asset into a workspace.");
|
|
1902
|
+
}
|
|
1903
|
+
} finally {
|
|
1904
|
+
library.close();
|
|
1905
|
+
}
|
|
1896
1906
|
}
|
|
1897
1907
|
function parseContainers(raw) {
|
|
1898
1908
|
return raw.split("\n").map((line) => line.trim()).filter(Boolean).map((line) => {
|
|
@@ -1901,11 +1911,11 @@ function parseContainers(raw) {
|
|
|
1901
1911
|
});
|
|
1902
1912
|
}
|
|
1903
1913
|
function colorLine(line) {
|
|
1904
|
-
if (/error|Error|ERROR|failed|Failed/.test(line)) return
|
|
1905
|
-
if (/connector|MCP|tool/.test(line)) return
|
|
1906
|
-
if (/rebuilt|registered|ready/.test(line)) return
|
|
1907
|
-
if (/\[driver\]/.test(line)) return
|
|
1908
|
-
if (/\[serve\]/.test(line)) return
|
|
1914
|
+
if (/error|Error|ERROR|failed|Failed/.test(line)) return pc5.red(line);
|
|
1915
|
+
if (/connector|MCP|tool/.test(line)) return pc5.yellow(line);
|
|
1916
|
+
if (/rebuilt|registered|ready/.test(line)) return pc5.green(line);
|
|
1917
|
+
if (/\[driver\]/.test(line)) return pc5.cyan(line);
|
|
1918
|
+
if (/\[serve\]/.test(line)) return pc5.dim(line);
|
|
1909
1919
|
return line;
|
|
1910
1920
|
}
|
|
1911
1921
|
function dockerAvailable() {
|
|
@@ -2012,42 +2022,43 @@ function makeLogsCommand() {
|
|
|
2012
2022
|
}
|
|
2013
2023
|
var am;
|
|
2014
2024
|
var state = {
|
|
2015
|
-
tab: "
|
|
2025
|
+
tab: "assets",
|
|
2016
2026
|
cursor: 0,
|
|
2017
2027
|
assetRows: [],
|
|
2028
|
+
sourceRows: [],
|
|
2018
2029
|
libraryRows: [],
|
|
2019
2030
|
visibleRows: [],
|
|
2020
2031
|
pendingAdds: /* @__PURE__ */ new Set(),
|
|
2021
2032
|
pendingRemoves: /* @__PURE__ */ new Set(),
|
|
2022
|
-
|
|
2033
|
+
collapsedSources: /* @__PURE__ */ new Set(),
|
|
2023
2034
|
collapsedDomains: /* @__PURE__ */ new Set(),
|
|
2024
2035
|
message: "",
|
|
2025
2036
|
cols: 80,
|
|
2026
2037
|
awaitingExitConfirm: false
|
|
2027
2038
|
};
|
|
2028
|
-
function buildVisibleRows(rows,
|
|
2039
|
+
function buildVisibleRows(rows, collapsedSources, collapsedDomains) {
|
|
2029
2040
|
return rows.filter((row) => {
|
|
2030
|
-
if (row.type === "
|
|
2031
|
-
if (row.type === "header") return !
|
|
2032
|
-
const
|
|
2041
|
+
if (row.type === "source-header") return true;
|
|
2042
|
+
if (row.type === "header") return !collapsedSources.has(row.source);
|
|
2043
|
+
const source = row.entry?.repository ?? "other";
|
|
2033
2044
|
const domain = row.entry?.domain ?? "other";
|
|
2034
|
-
return !
|
|
2045
|
+
return !collapsedSources.has(source) && !collapsedDomains.has(`${source}:${domain}`);
|
|
2035
2046
|
});
|
|
2036
2047
|
}
|
|
2037
|
-
function domainAssetRefs(rows,
|
|
2038
|
-
return rows.filter((r) => isAsset(r) && inDomain(r,
|
|
2048
|
+
function domainAssetRefs(rows, source, domain) {
|
|
2049
|
+
return rows.filter((r) => isAsset(r) && inDomain(r, source, domain)).map((r) => assetRefOf(r));
|
|
2039
2050
|
}
|
|
2040
|
-
function
|
|
2041
|
-
return rows.filter((r) => isAsset(r) &&
|
|
2051
|
+
function sourceAssetRefs(rows, source) {
|
|
2052
|
+
return rows.filter((r) => isAsset(r) && inSource(r, source)).map((r) => assetRefOf(r));
|
|
2042
2053
|
}
|
|
2043
2054
|
function isAsset(r) {
|
|
2044
2055
|
return r.type === "asset";
|
|
2045
2056
|
}
|
|
2046
|
-
function
|
|
2047
|
-
return (r.entry?.repository ?? "other") ===
|
|
2057
|
+
function inSource(r, source) {
|
|
2058
|
+
return (r.entry?.repository ?? "other") === source;
|
|
2048
2059
|
}
|
|
2049
|
-
function inDomain(r,
|
|
2050
|
-
return
|
|
2060
|
+
function inDomain(r, source, domain) {
|
|
2061
|
+
return inSource(r, source) && (r.entry?.domain ?? "other") === domain;
|
|
2051
2062
|
}
|
|
2052
2063
|
function assetRefOf(r) {
|
|
2053
2064
|
if (!isAsset(r) || !r.entry) return null;
|
|
@@ -2071,17 +2082,17 @@ function setSelection(ref, action) {
|
|
|
2071
2082
|
state.pendingRemoves.delete(ref);
|
|
2072
2083
|
return;
|
|
2073
2084
|
}
|
|
2074
|
-
resolved = row.
|
|
2085
|
+
resolved = row.installed ? "remove" : "add";
|
|
2075
2086
|
} else {
|
|
2076
2087
|
resolved = action;
|
|
2077
2088
|
}
|
|
2078
2089
|
if (resolved === "add") {
|
|
2079
2090
|
state.pendingRemoves.delete(ref);
|
|
2080
|
-
if (row.
|
|
2091
|
+
if (row.installed) return;
|
|
2081
2092
|
state.pendingAdds.add(ref);
|
|
2082
2093
|
} else {
|
|
2083
2094
|
state.pendingAdds.delete(ref);
|
|
2084
|
-
if (!row.
|
|
2095
|
+
if (!row.installed) return;
|
|
2085
2096
|
state.pendingRemoves.add(ref);
|
|
2086
2097
|
}
|
|
2087
2098
|
}
|
|
@@ -2089,51 +2100,53 @@ function bulkSelection(refs, action) {
|
|
|
2089
2100
|
for (const ref of refs) setSelection(ref, action);
|
|
2090
2101
|
}
|
|
2091
2102
|
function actOnHeader(row, action) {
|
|
2092
|
-
if (row.type === "
|
|
2093
|
-
bulkSelection(
|
|
2103
|
+
if (row.type === "source-header") {
|
|
2104
|
+
bulkSelection(sourceAssetRefs(state.assetRows, row.source), action);
|
|
2094
2105
|
return true;
|
|
2095
2106
|
}
|
|
2096
2107
|
if (row.type === "header") {
|
|
2097
|
-
bulkSelection(domainAssetRefs(state.assetRows, row.
|
|
2108
|
+
bulkSelection(domainAssetRefs(state.assetRows, row.source, row.domain), action);
|
|
2098
2109
|
return true;
|
|
2099
2110
|
}
|
|
2100
2111
|
return false;
|
|
2101
2112
|
}
|
|
2102
2113
|
function loadAssets() {
|
|
2103
2114
|
const entries = am.search();
|
|
2104
|
-
const
|
|
2105
|
-
const
|
|
2115
|
+
const installed = new Set(am.listDeployed().map((e) => `${e.kind}:${e.name}`));
|
|
2116
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
2106
2117
|
for (const e of entries) {
|
|
2107
|
-
const
|
|
2118
|
+
const source = e.repository ?? "other";
|
|
2108
2119
|
const domain = e.domain ?? "other";
|
|
2109
|
-
if (!
|
|
2110
|
-
const domainMap =
|
|
2120
|
+
if (!bySource.has(source)) bySource.set(source, /* @__PURE__ */ new Map());
|
|
2121
|
+
const domainMap = bySource.get(source);
|
|
2111
2122
|
if (!domainMap.has(domain)) domainMap.set(domain, []);
|
|
2112
2123
|
domainMap.get(domain).push(e);
|
|
2113
2124
|
}
|
|
2114
2125
|
const rows = [];
|
|
2115
|
-
for (const [
|
|
2116
|
-
|
|
2126
|
+
for (const [source, domainMap] of [...bySource.entries()].sort(
|
|
2127
|
+
(a, b) => a[0].localeCompare(b[0])
|
|
2128
|
+
)) {
|
|
2129
|
+
rows.push({ type: "source-header", source });
|
|
2117
2130
|
for (const [domain, items] of [...domainMap.entries()].sort(
|
|
2118
2131
|
(a, b) => a[0].localeCompare(b[0])
|
|
2119
2132
|
)) {
|
|
2120
|
-
rows.push({ type: "header",
|
|
2133
|
+
rows.push({ type: "header", source, domain });
|
|
2121
2134
|
items.sort(
|
|
2122
2135
|
(a, b) => a.kind !== b.kind ? a.kind.localeCompare(b.kind) : a.name.localeCompare(b.name)
|
|
2123
2136
|
);
|
|
2124
2137
|
for (const entry of items) {
|
|
2125
2138
|
const ref = `${entry.kind}:${entry.name}`;
|
|
2126
|
-
rows.push({ type: "asset", entry,
|
|
2139
|
+
rows.push({ type: "asset", entry, installed: installed.has(ref) });
|
|
2127
2140
|
}
|
|
2128
2141
|
}
|
|
2129
2142
|
}
|
|
2130
2143
|
state.assetRows = rows;
|
|
2131
|
-
const
|
|
2132
|
-
for (const
|
|
2133
|
-
if (!
|
|
2144
|
+
const newSources = new Set(rows.filter((r) => r.type === "source-header").map((r) => r.source));
|
|
2145
|
+
for (const s of state.collapsedSources) {
|
|
2146
|
+
if (!newSources.has(s)) state.collapsedSources.delete(s);
|
|
2134
2147
|
}
|
|
2135
2148
|
const newDomainKeys = new Set(
|
|
2136
|
-
rows.filter((r) => r.type === "header").map((r) => `${r.
|
|
2149
|
+
rows.filter((r) => r.type === "header").map((r) => `${r.source}:${r.domain}`)
|
|
2137
2150
|
);
|
|
2138
2151
|
for (const d of state.collapsedDomains) {
|
|
2139
2152
|
if (!newDomainKeys.has(d)) state.collapsedDomains.delete(d);
|
|
@@ -2141,19 +2154,30 @@ function loadAssets() {
|
|
|
2141
2154
|
for (const d of newDomainKeys) state.collapsedDomains.add(d);
|
|
2142
2155
|
rebuildVisible();
|
|
2143
2156
|
}
|
|
2144
|
-
async function
|
|
2157
|
+
async function loadSourcesAndLibraries() {
|
|
2145
2158
|
try {
|
|
2146
|
-
const {
|
|
2147
|
-
|
|
2159
|
+
const [{ openLibraryManager: openLibraryManager2 }, { skaileHomeDir: skaileHomeDir2 }] = await Promise.all([
|
|
2160
|
+
import('../open-library-T6RXQJTQ.js'),
|
|
2161
|
+
import('../library/index.js')
|
|
2162
|
+
]);
|
|
2163
|
+
const { manager, library, close } = await openLibraryManager2();
|
|
2148
2164
|
try {
|
|
2165
|
+
const sourcesDir2 = path15__default.join(skaileHomeDir2(), "sources");
|
|
2166
|
+
const isSourceRow2 = (l) => l.path.startsWith(sourcesDir2);
|
|
2149
2167
|
const libs = await manager.listLibraries();
|
|
2150
2168
|
const defs = await library.listAssetDefs();
|
|
2151
2169
|
const counts = /* @__PURE__ */ new Map();
|
|
2152
2170
|
for (const d of defs) {
|
|
2153
|
-
const id = d.
|
|
2171
|
+
const id = d.libraryId ?? "";
|
|
2154
2172
|
counts.set(id, (counts.get(id) ?? 0) + 1);
|
|
2155
2173
|
}
|
|
2156
|
-
state.
|
|
2174
|
+
state.sourceRows = libs.filter(isSourceRow2).map((l) => ({
|
|
2175
|
+
name: l.name,
|
|
2176
|
+
path: l.path,
|
|
2177
|
+
ownership: l.ownership,
|
|
2178
|
+
assetCount: counts.get(l.id) ?? 0
|
|
2179
|
+
}));
|
|
2180
|
+
state.libraryRows = libs.filter((l) => !isSourceRow2(l)).map((l) => ({
|
|
2157
2181
|
name: l.name,
|
|
2158
2182
|
backend: l.backend,
|
|
2159
2183
|
ownership: l.ownership,
|
|
@@ -2164,13 +2188,14 @@ async function loadLibraries() {
|
|
|
2164
2188
|
close();
|
|
2165
2189
|
}
|
|
2166
2190
|
} catch {
|
|
2191
|
+
state.sourceRows = [];
|
|
2167
2192
|
state.libraryRows = [];
|
|
2168
2193
|
}
|
|
2169
2194
|
}
|
|
2170
2195
|
function rebuildVisible() {
|
|
2171
2196
|
state.visibleRows = buildVisibleRows(
|
|
2172
2197
|
state.assetRows,
|
|
2173
|
-
state.
|
|
2198
|
+
state.collapsedSources,
|
|
2174
2199
|
state.collapsedDomains
|
|
2175
2200
|
);
|
|
2176
2201
|
clampCursor();
|
|
@@ -2185,19 +2210,11 @@ function moveCursor(delta) {
|
|
|
2185
2210
|
state.cursor = Math.max(0, Math.min(maxCursor(), state.cursor + delta));
|
|
2186
2211
|
}
|
|
2187
2212
|
function nextTab() {
|
|
2188
|
-
const order = ["
|
|
2213
|
+
const order = ["assets", "sources", "libraries", "pending", "sync"];
|
|
2189
2214
|
state.tab = order[(order.indexOf(state.tab) + 1) % order.length];
|
|
2190
2215
|
state.cursor = 0;
|
|
2191
2216
|
}
|
|
2192
2217
|
var TABS = {
|
|
2193
|
-
libraries: {
|
|
2194
|
-
label: "Libraries",
|
|
2195
|
-
rowCount: () => state.libraryRows.length,
|
|
2196
|
-
selectableCount: () => state.libraryRows.length,
|
|
2197
|
-
emptyMsg: " No libraries registered. Run `skaile library init <name>` or `skaile library add <git-url>` from the shell.",
|
|
2198
|
-
renderRow: (i, sel) => renderLibraryRow(state.libraryRows[i], sel),
|
|
2199
|
-
footer: " \u2191\u2193 navigate Tab switch q quit (use `skaile library \u2026` from the shell for CRUD)"
|
|
2200
|
-
},
|
|
2201
2218
|
assets: {
|
|
2202
2219
|
label: "Assets",
|
|
2203
2220
|
rowCount: () => state.visibleRows.length,
|
|
@@ -2205,6 +2222,22 @@ var TABS = {
|
|
|
2205
2222
|
renderRow: (i, sel) => renderAssetRow(state.visibleRows[i], sel),
|
|
2206
2223
|
footer: " \u2191\u2193/jk navigate \u2190/\u2192 collapse/expand [space/+/-] select [a/\u21B5] apply [i] info Tab switch [q/esc] quit"
|
|
2207
2224
|
},
|
|
2225
|
+
sources: {
|
|
2226
|
+
label: "Sources",
|
|
2227
|
+
rowCount: () => state.sourceRows.length,
|
|
2228
|
+
selectableCount: () => state.sourceRows.length,
|
|
2229
|
+
emptyMsg: " No sources registered. Run `skaile source add <git-url>` from the shell to track a github repo.",
|
|
2230
|
+
renderRow: (i, sel) => renderSourceRow(state.sourceRows[i], sel),
|
|
2231
|
+
footer: " \u2191\u2193 navigate Tab switch q quit (use `skaile source \u2026` from the shell for CRUD)"
|
|
2232
|
+
},
|
|
2233
|
+
libraries: {
|
|
2234
|
+
label: "Libraries",
|
|
2235
|
+
rowCount: () => state.libraryRows.length,
|
|
2236
|
+
selectableCount: () => state.libraryRows.length,
|
|
2237
|
+
emptyMsg: " No libraries registered. Run `skaile library init <name>` from the shell to author your own.",
|
|
2238
|
+
renderRow: (i, sel) => renderLibraryRow(state.libraryRows[i], sel),
|
|
2239
|
+
footer: " \u2191\u2193 navigate Tab switch q quit (use `skaile library \u2026` from the shell for CRUD)"
|
|
2240
|
+
},
|
|
2208
2241
|
pending: {
|
|
2209
2242
|
label: "Pending",
|
|
2210
2243
|
rowCount: () => 0,
|
|
@@ -2217,7 +2250,7 @@ var TABS = {
|
|
|
2217
2250
|
label: "Sync",
|
|
2218
2251
|
rowCount: () => 0,
|
|
2219
2252
|
selectableCount: () => 0,
|
|
2220
|
-
emptyMsg: " Press S to sync all libraries (rich rendering deferred to AF-LIB-TUI-RICH).",
|
|
2253
|
+
emptyMsg: " Press S to sync all sources + libraries (rich rendering deferred to AF-LIB-TUI-RICH).",
|
|
2221
2254
|
renderRow: () => "",
|
|
2222
2255
|
footer: " s sync all Tab switch q quit"
|
|
2223
2256
|
}
|
|
@@ -2234,8 +2267,8 @@ function showCursor() {
|
|
|
2234
2267
|
function renderTabs() {
|
|
2235
2268
|
return Object.entries(TABS).map(([id, spec]) => {
|
|
2236
2269
|
const text = `${spec.label} (${spec.selectableCount()})`;
|
|
2237
|
-
return id === state.tab ?
|
|
2238
|
-
}).join(
|
|
2270
|
+
return id === state.tab ? pc5.bgCyan(pc5.black(` ${text} `)) : pc5.dim(` ${text} `);
|
|
2271
|
+
}).join(pc5.dim("\u2502"));
|
|
2239
2272
|
}
|
|
2240
2273
|
function pendingCountsFor(match) {
|
|
2241
2274
|
let add = 0;
|
|
@@ -2251,63 +2284,67 @@ function pendingCountsFor(match) {
|
|
|
2251
2284
|
return [add, remove];
|
|
2252
2285
|
}
|
|
2253
2286
|
function groupStatus(refs) {
|
|
2254
|
-
if (refs.length === 0) return
|
|
2255
|
-
if (refs.some((r) => state.pendingAdds.has(r))) return
|
|
2256
|
-
if (refs.some((r) => state.pendingRemoves.has(r))) return
|
|
2257
|
-
const
|
|
2258
|
-
return
|
|
2287
|
+
if (refs.length === 0) return pc5.dim("\xB7 ");
|
|
2288
|
+
if (refs.some((r) => state.pendingAdds.has(r))) return pc5.blue("+ ");
|
|
2289
|
+
if (refs.some((r) => state.pendingRemoves.has(r))) return pc5.red("- ");
|
|
2290
|
+
const allInstalled = refs.every((r) => findAssetRow(r)?.installed);
|
|
2291
|
+
return allInstalled ? pc5.green("\u2713 ") : pc5.dim("\xB7 ");
|
|
2259
2292
|
}
|
|
2260
2293
|
function renderHeaderLine(opts) {
|
|
2261
2294
|
const indicator = opts.collapsed ? "\u25B6" : "\u25BC";
|
|
2262
2295
|
const total = state.assetRows.filter(opts.match).length;
|
|
2263
|
-
const installed = state.assetRows.filter((r) => opts.match(r) && r.
|
|
2296
|
+
const installed = state.assetRows.filter((r) => opts.match(r) && r.installed).length;
|
|
2264
2297
|
const [add, remove] = pendingCountsFor(opts.match);
|
|
2265
2298
|
let count = String(installed);
|
|
2266
|
-
if (add > 0) count +=
|
|
2267
|
-
if (remove > 0) count +=
|
|
2299
|
+
if (add > 0) count += pc5.green(`+${add}`);
|
|
2300
|
+
if (remove > 0) count += pc5.red(`-${remove}`);
|
|
2268
2301
|
count += `/${total}`;
|
|
2269
2302
|
const status4 = groupStatus(opts.refs);
|
|
2270
2303
|
const line = `${opts.indent}${status4}${indicator} ${opts.label} (${count} assets)`;
|
|
2271
|
-
return opts.selected ?
|
|
2304
|
+
return opts.selected ? pc5.bgWhite(pc5.black(`${line} `.padEnd(state.cols))) : line;
|
|
2272
2305
|
}
|
|
2273
2306
|
function renderAssetRow(row, selected) {
|
|
2274
|
-
if (row.type === "
|
|
2307
|
+
if (row.type === "source-header") {
|
|
2275
2308
|
return renderHeaderLine({
|
|
2276
2309
|
indent: " ",
|
|
2277
|
-
collapsed: state.
|
|
2278
|
-
match: (r) => isAsset(r) &&
|
|
2279
|
-
refs:
|
|
2280
|
-
label:
|
|
2310
|
+
collapsed: state.collapsedSources.has(row.source),
|
|
2311
|
+
match: (r) => isAsset(r) && inSource(r, row.source),
|
|
2312
|
+
refs: sourceAssetRefs(state.assetRows, row.source),
|
|
2313
|
+
label: pc5.bold(pc5.cyan(row.source)),
|
|
2281
2314
|
selected
|
|
2282
2315
|
});
|
|
2283
2316
|
}
|
|
2284
2317
|
if (row.type === "header") {
|
|
2285
2318
|
return renderHeaderLine({
|
|
2286
2319
|
indent: " ",
|
|
2287
|
-
collapsed: state.collapsedDomains.has(`${row.
|
|
2288
|
-
match: (r) => isAsset(r) && inDomain(r, row.
|
|
2289
|
-
refs: domainAssetRefs(state.assetRows, row.
|
|
2290
|
-
label:
|
|
2320
|
+
collapsed: state.collapsedDomains.has(`${row.source}:${row.domain}`),
|
|
2321
|
+
match: (r) => isAsset(r) && inDomain(r, row.source, row.domain),
|
|
2322
|
+
refs: domainAssetRefs(state.assetRows, row.source, row.domain),
|
|
2323
|
+
label: pc5.bold(row.domain),
|
|
2291
2324
|
selected
|
|
2292
2325
|
});
|
|
2293
2326
|
}
|
|
2294
2327
|
const e = row.entry;
|
|
2295
2328
|
const ref = assetRefOf(row);
|
|
2296
2329
|
let status4;
|
|
2297
|
-
if (state.pendingAdds.has(ref)) status4 =
|
|
2298
|
-
else if (state.pendingRemoves.has(ref)) status4 =
|
|
2299
|
-
else if (row.
|
|
2300
|
-
else status4 =
|
|
2330
|
+
if (state.pendingAdds.has(ref)) status4 = pc5.blue("+ ");
|
|
2331
|
+
else if (state.pendingRemoves.has(ref)) status4 = pc5.red("- ");
|
|
2332
|
+
else if (row.installed) status4 = pc5.green("\u2713 ");
|
|
2333
|
+
else status4 = pc5.dim("\xB7 ");
|
|
2301
2334
|
const kind = kindColorPad(e.kind, 8);
|
|
2302
2335
|
const name = e.name.padEnd(30);
|
|
2303
2336
|
const desc = e.description?.slice(0, state.cols - 52) ?? "";
|
|
2304
|
-
const line = ` ${status4} ${kind} ${name} ${
|
|
2305
|
-
return selected ?
|
|
2337
|
+
const line = ` ${status4} ${kind} ${name} ${pc5.dim(desc)}`;
|
|
2338
|
+
return selected ? pc5.inverse(line.padEnd(state.cols)) : line;
|
|
2339
|
+
}
|
|
2340
|
+
function renderSourceRow(row, selected) {
|
|
2341
|
+
const line = ` ${S.cmd(row.name.padEnd(20))} ${pc5.dim(row.ownership.padEnd(12))} ${pc5.dim(`${row.assetCount} assets`)} ${pc5.dim(row.path)}`;
|
|
2342
|
+
return selected ? pc5.inverse(line.padEnd(state.cols)) : line;
|
|
2306
2343
|
}
|
|
2307
2344
|
function renderLibraryRow(row, selected) {
|
|
2308
|
-
const mark = row.isDefault ?
|
|
2309
|
-
const line = `${mark} ${S.cmd(row.name.padEnd(16))} ${
|
|
2310
|
-
return selected ?
|
|
2345
|
+
const mark = row.isDefault ? pc5.green("\u25B8") : " ";
|
|
2346
|
+
const line = `${mark} ${S.cmd(row.name.padEnd(16))} ${pc5.dim(row.backend.padEnd(7))} ${pc5.dim(row.ownership.padEnd(12))} ${pc5.dim(`${row.assetCount} assets`)}`;
|
|
2347
|
+
return selected ? pc5.inverse(line.padEnd(state.cols)) : line;
|
|
2311
2348
|
}
|
|
2312
2349
|
function render() {
|
|
2313
2350
|
clearScreen();
|
|
@@ -2317,12 +2354,12 @@ function render() {
|
|
|
2317
2354
|
const overhead = 5 + pendingLines + msgLines;
|
|
2318
2355
|
const maxRows = Math.max(5, (process.stdout.rows || 30) - overhead);
|
|
2319
2356
|
console.log();
|
|
2320
|
-
console.log(` ${
|
|
2357
|
+
console.log(` ${pc5.bold("skaile manage")} ${renderTabs()}`);
|
|
2321
2358
|
console.log(` ${S.rule(state.cols - 4)}`);
|
|
2322
2359
|
const spec = TABS[state.tab];
|
|
2323
2360
|
const total = spec.rowCount();
|
|
2324
2361
|
if (total === 0 && spec.emptyMsg) {
|
|
2325
|
-
console.log(
|
|
2362
|
+
console.log(pc5.dim(spec.emptyMsg));
|
|
2326
2363
|
} else {
|
|
2327
2364
|
const start = Math.max(0, state.cursor - maxRows + 3);
|
|
2328
2365
|
const end = Math.min(total, start + maxRows);
|
|
@@ -2333,9 +2370,9 @@ function render() {
|
|
|
2333
2370
|
if (state.pendingAdds.size > 0 || state.pendingRemoves.size > 0) {
|
|
2334
2371
|
console.log();
|
|
2335
2372
|
if (state.pendingAdds.size > 0)
|
|
2336
|
-
console.log(` ${
|
|
2373
|
+
console.log(` ${pc5.blue(`+ ${state.pendingAdds.size} to add`)}`);
|
|
2337
2374
|
if (state.pendingRemoves.size > 0)
|
|
2338
|
-
console.log(` ${
|
|
2375
|
+
console.log(` ${pc5.red(`- ${state.pendingRemoves.size} to remove`)}`);
|
|
2339
2376
|
}
|
|
2340
2377
|
if (state.message) {
|
|
2341
2378
|
console.log();
|
|
@@ -2343,27 +2380,27 @@ function render() {
|
|
|
2343
2380
|
state.message = "";
|
|
2344
2381
|
}
|
|
2345
2382
|
console.log();
|
|
2346
|
-
console.log(
|
|
2383
|
+
console.log(pc5.dim(spec.footer));
|
|
2347
2384
|
}
|
|
2348
2385
|
function renderExitPrompt() {
|
|
2349
2386
|
clearScreen();
|
|
2350
2387
|
console.log();
|
|
2351
|
-
console.log(` ${
|
|
2388
|
+
console.log(` ${pc5.yellow("!")} ${pc5.bold("You have unapplied changes:")}`);
|
|
2352
2389
|
if (state.pendingAdds.size > 0)
|
|
2353
|
-
console.log(` ${
|
|
2390
|
+
console.log(` ${pc5.blue(`+ ${state.pendingAdds.size} to add`)}`);
|
|
2354
2391
|
if (state.pendingRemoves.size > 0)
|
|
2355
|
-
console.log(` ${
|
|
2392
|
+
console.log(` ${pc5.red(`- ${state.pendingRemoves.size} to remove`)}`);
|
|
2356
2393
|
console.log();
|
|
2357
2394
|
console.log(
|
|
2358
|
-
` Apply before exiting? ${
|
|
2395
|
+
` Apply before exiting? ${pc5.bold("[y]")} apply ${pc5.bold("[n]")} discard ${pc5.bold("[c/Esc]")} cancel`
|
|
2359
2396
|
);
|
|
2360
2397
|
}
|
|
2361
2398
|
async function applyChanges() {
|
|
2362
2399
|
if (state.pendingAdds.size === 0 && state.pendingRemoves.size === 0) {
|
|
2363
|
-
state.message =
|
|
2400
|
+
state.message = pc5.dim("Nothing to apply.");
|
|
2364
2401
|
return;
|
|
2365
2402
|
}
|
|
2366
|
-
state.message =
|
|
2403
|
+
state.message = pc5.yellow("Applying changes...");
|
|
2367
2404
|
render();
|
|
2368
2405
|
let added = 0;
|
|
2369
2406
|
let removed = 0;
|
|
@@ -2375,25 +2412,30 @@ async function applyChanges() {
|
|
|
2375
2412
|
am.add(ref);
|
|
2376
2413
|
added++;
|
|
2377
2414
|
} catch (err) {
|
|
2378
|
-
state.message =
|
|
2415
|
+
state.message = pc5.red(`Failed to add ${ref}: ${err}`);
|
|
2379
2416
|
}
|
|
2380
2417
|
}
|
|
2381
2418
|
state.pendingAdds.clear();
|
|
2382
2419
|
state.pendingRemoves.clear();
|
|
2383
2420
|
loadAssets();
|
|
2384
2421
|
const parts = [];
|
|
2385
|
-
if (added > 0) parts.push(
|
|
2386
|
-
if (removed > 0) parts.push(
|
|
2422
|
+
if (added > 0) parts.push(pc5.green(`+${added} added`));
|
|
2423
|
+
if (removed > 0) parts.push(pc5.red(`-${removed} removed`));
|
|
2387
2424
|
if (parts.length) state.message = parts.join(" ");
|
|
2388
2425
|
}
|
|
2389
|
-
async function
|
|
2390
|
-
state.message =
|
|
2391
|
-
"
|
|
2426
|
+
async function syncAll() {
|
|
2427
|
+
state.message = pc5.yellow(
|
|
2428
|
+
"Sync is not yet driven from the manage TUI. Run `skaile source sync` (sources) or `skaile library sync <name>` (git-backed libraries) from the shell."
|
|
2429
|
+
);
|
|
2430
|
+
}
|
|
2431
|
+
function sourceShellHint(action) {
|
|
2432
|
+
state.message = pc5.yellow(
|
|
2433
|
+
`${action} is a shell command. Run \`skaile source \u2026\` from the shell.`
|
|
2392
2434
|
);
|
|
2393
2435
|
}
|
|
2394
|
-
function
|
|
2395
|
-
state.message =
|
|
2396
|
-
`${action} is a shell command
|
|
2436
|
+
function libraryShellHint(action) {
|
|
2437
|
+
state.message = pc5.yellow(
|
|
2438
|
+
`${action} is a shell command. Run \`skaile library \u2026\` from the shell.`
|
|
2397
2439
|
);
|
|
2398
2440
|
}
|
|
2399
2441
|
async function showInfo() {
|
|
@@ -2403,13 +2445,13 @@ async function showInfo() {
|
|
|
2403
2445
|
const e = row.entry;
|
|
2404
2446
|
clearScreen();
|
|
2405
2447
|
console.log();
|
|
2406
|
-
console.log(` ${kindColor(e.kind)} ${
|
|
2448
|
+
console.log(` ${kindColor(e.kind)} ${pc5.bold(e.name)}`);
|
|
2407
2449
|
console.log(` ${S.rule(50)}`);
|
|
2408
2450
|
if (e.version) console.log(` version: ${e.version}`);
|
|
2409
2451
|
if (e.description) console.log(` description: ${e.description}`);
|
|
2410
|
-
if (e.repository) console.log(`
|
|
2411
|
-
console.log(`
|
|
2412
|
-
console.log(`
|
|
2452
|
+
if (e.repository) console.log(` source: ${pc5.dim(e.repository)}`);
|
|
2453
|
+
console.log(` manifest: ${pc5.dim(e.source)}`);
|
|
2454
|
+
console.log(` installed: ${row.installed ? pc5.green("yes") : pc5.red("no")}`);
|
|
2413
2455
|
if (e.requires.length) {
|
|
2414
2456
|
console.log(` requires:`);
|
|
2415
2457
|
for (const d of e.requires) console.log(` ${kindColor(d.kind)}:${d.name}`);
|
|
@@ -2419,11 +2461,11 @@ async function showInfo() {
|
|
|
2419
2461
|
for (const d of e.dependencies) console.log(` ${d}`);
|
|
2420
2462
|
}
|
|
2421
2463
|
console.log();
|
|
2422
|
-
console.log(
|
|
2423
|
-
await new Promise((
|
|
2464
|
+
console.log(pc5.dim(" Press any key to return..."));
|
|
2465
|
+
await new Promise((resolve4) => {
|
|
2424
2466
|
const onData = () => {
|
|
2425
2467
|
process.stdin.removeListener("data", onData);
|
|
2426
|
-
|
|
2468
|
+
resolve4();
|
|
2427
2469
|
};
|
|
2428
2470
|
process.stdin.once("data", onData);
|
|
2429
2471
|
});
|
|
@@ -2431,11 +2473,11 @@ async function showInfo() {
|
|
|
2431
2473
|
function expandUnderCursor() {
|
|
2432
2474
|
const row = state.visibleRows[state.cursor];
|
|
2433
2475
|
if (!row) return;
|
|
2434
|
-
if (row.type === "
|
|
2435
|
-
state.
|
|
2476
|
+
if (row.type === "source-header" && state.collapsedSources.has(row.source)) {
|
|
2477
|
+
state.collapsedSources.delete(row.source);
|
|
2436
2478
|
rebuildVisible();
|
|
2437
2479
|
} else if (row.type === "header") {
|
|
2438
|
-
const dk = `${row.
|
|
2480
|
+
const dk = `${row.source}:${row.domain}`;
|
|
2439
2481
|
if (state.collapsedDomains.has(dk)) {
|
|
2440
2482
|
state.collapsedDomains.delete(dk);
|
|
2441
2483
|
rebuildVisible();
|
|
@@ -2445,15 +2487,15 @@ function expandUnderCursor() {
|
|
|
2445
2487
|
function collapseOrJumpUnderCursor() {
|
|
2446
2488
|
const row = state.visibleRows[state.cursor];
|
|
2447
2489
|
if (!row) return;
|
|
2448
|
-
if (row.type === "
|
|
2449
|
-
if (!state.
|
|
2450
|
-
state.
|
|
2490
|
+
if (row.type === "source-header") {
|
|
2491
|
+
if (!state.collapsedSources.has(row.source)) {
|
|
2492
|
+
state.collapsedSources.add(row.source);
|
|
2451
2493
|
rebuildVisible();
|
|
2452
2494
|
}
|
|
2453
2495
|
return;
|
|
2454
2496
|
}
|
|
2455
2497
|
if (row.type === "header") {
|
|
2456
|
-
const dk = `${row.
|
|
2498
|
+
const dk = `${row.source}:${row.domain}`;
|
|
2457
2499
|
if (!state.collapsedDomains.has(dk)) {
|
|
2458
2500
|
state.collapsedDomains.add(dk);
|
|
2459
2501
|
rebuildVisible();
|
|
@@ -2461,7 +2503,7 @@ function collapseOrJumpUnderCursor() {
|
|
|
2461
2503
|
const idx2 = findLastIndex(
|
|
2462
2504
|
state.visibleRows,
|
|
2463
2505
|
state.cursor,
|
|
2464
|
-
(r) => r.type === "
|
|
2506
|
+
(r) => r.type === "source-header"
|
|
2465
2507
|
);
|
|
2466
2508
|
if (idx2 >= 0) state.cursor = idx2;
|
|
2467
2509
|
}
|
|
@@ -2470,7 +2512,7 @@ function collapseOrJumpUnderCursor() {
|
|
|
2470
2512
|
const idx = findLastIndex(
|
|
2471
2513
|
state.visibleRows,
|
|
2472
2514
|
state.cursor,
|
|
2473
|
-
(r) => r.type === "header" || r.type === "
|
|
2515
|
+
(r) => r.type === "header" || r.type === "source-header"
|
|
2474
2516
|
);
|
|
2475
2517
|
if (idx >= 0) state.cursor = idx;
|
|
2476
2518
|
}
|
|
@@ -2481,6 +2523,7 @@ function findLastIndex(arr, endExclusive, pred) {
|
|
|
2481
2523
|
return -1;
|
|
2482
2524
|
}
|
|
2483
2525
|
var TAB_ASSETS = (t) => t === "assets";
|
|
2526
|
+
var TAB_SOURCES = (t) => t === "sources";
|
|
2484
2527
|
var TAB_LIBRARIES = (t) => t === "libraries";
|
|
2485
2528
|
var KEYMAP = [
|
|
2486
2529
|
// Quit (with pending check) — handled here, not via a modal subroutine, so
|
|
@@ -2503,7 +2546,7 @@ var KEYMAP = [
|
|
|
2503
2546
|
// Collapse / expand — assets tab only
|
|
2504
2547
|
{ keys: ["\x1B[C"], when: TAB_ASSETS, run: expandUnderCursor },
|
|
2505
2548
|
{ keys: ["\x1B[D"], when: TAB_ASSETS, run: collapseOrJumpUnderCursor },
|
|
2506
|
-
// + : assets → bulk-add or single-add toggle
|
|
2549
|
+
// + : assets → bulk-add or single-add toggle
|
|
2507
2550
|
{
|
|
2508
2551
|
keys: ["+"],
|
|
2509
2552
|
when: TAB_ASSETS,
|
|
@@ -2516,9 +2559,28 @@ var KEYMAP = [
|
|
|
2516
2559
|
}
|
|
2517
2560
|
}
|
|
2518
2561
|
},
|
|
2519
|
-
//
|
|
2520
|
-
{ keys: ["+", "n"], when:
|
|
2521
|
-
|
|
2562
|
+
// Sources tab: +/-/n/r/x map to shell hints in the minimal scaffold.
|
|
2563
|
+
{ keys: ["+", "n", "a"], when: TAB_SOURCES, run: () => sourceShellHint("Adding a source") },
|
|
2564
|
+
{ keys: ["-", "r", "x"], when: TAB_SOURCES, run: () => sourceShellHint("Removing a source") },
|
|
2565
|
+
{ keys: ["\r", "\n"], when: TAB_SOURCES, run: () => sourceShellHint("Source actions") },
|
|
2566
|
+
// Libraries tab: +/-/n/r/x/d/enter map to shell hints in the minimal scaffold.
|
|
2567
|
+
{
|
|
2568
|
+
keys: ["+", "n", "a"],
|
|
2569
|
+
when: TAB_LIBRARIES,
|
|
2570
|
+
run: () => libraryShellHint("Adding a library")
|
|
2571
|
+
},
|
|
2572
|
+
{
|
|
2573
|
+
keys: ["-", "r", "x"],
|
|
2574
|
+
when: TAB_LIBRARIES,
|
|
2575
|
+
run: () => libraryShellHint("Removing a library")
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
keys: ["d"],
|
|
2579
|
+
when: TAB_LIBRARIES,
|
|
2580
|
+
run: () => libraryShellHint("Setting the default library")
|
|
2581
|
+
},
|
|
2582
|
+
{ keys: ["\r", "\n"], when: TAB_LIBRARIES, run: () => libraryShellHint("Library actions") },
|
|
2583
|
+
// - : assets → bulk-remove or single-remove toggle
|
|
2522
2584
|
{
|
|
2523
2585
|
keys: ["-"],
|
|
2524
2586
|
when: TAB_ASSETS,
|
|
@@ -2531,8 +2593,6 @@ var KEYMAP = [
|
|
|
2531
2593
|
}
|
|
2532
2594
|
}
|
|
2533
2595
|
},
|
|
2534
|
-
{ keys: ["-", "r", "x"], when: TAB_LIBRARIES, run: () => shellHint("Removing a library") },
|
|
2535
|
-
{ keys: ["d"], when: TAB_LIBRARIES, run: () => shellHint("Setting the default library") },
|
|
2536
2596
|
// Space — auto-toggle (single asset or bulk header)
|
|
2537
2597
|
{
|
|
2538
2598
|
keys: [" "],
|
|
@@ -2546,14 +2606,12 @@ var KEYMAP = [
|
|
|
2546
2606
|
}
|
|
2547
2607
|
}
|
|
2548
2608
|
},
|
|
2549
|
-
// s — sync (any tab) — minimal scaffold delegates to shell.
|
|
2550
|
-
{ keys: ["s"], run:
|
|
2551
|
-
// a : assets → apply
|
|
2609
|
+
// s — sync all (any tab) — minimal scaffold delegates to shell.
|
|
2610
|
+
{ keys: ["s"], run: syncAll },
|
|
2611
|
+
// a : assets → apply
|
|
2552
2612
|
{ keys: ["a"], when: TAB_ASSETS, run: applyChanges },
|
|
2553
|
-
|
|
2554
|
-
// Enter — apply on assets tab; libraries → shell hint
|
|
2613
|
+
// Enter — apply on assets tab
|
|
2555
2614
|
{ keys: ["\r", "\n"], when: TAB_ASSETS, run: applyChanges },
|
|
2556
|
-
{ keys: ["\r", "\n"], when: TAB_LIBRARIES, run: () => shellHint("Library actions") },
|
|
2557
2615
|
// Info — assets only
|
|
2558
2616
|
{ keys: ["i"], when: TAB_ASSETS, run: showInfo }
|
|
2559
2617
|
];
|
|
@@ -2582,7 +2640,7 @@ async function handleExitConfirm(key) {
|
|
|
2582
2640
|
async function run(projectDir) {
|
|
2583
2641
|
am = new AssetManager({ projectDir });
|
|
2584
2642
|
loadAssets();
|
|
2585
|
-
await
|
|
2643
|
+
await loadSourcesAndLibraries();
|
|
2586
2644
|
hideCursor();
|
|
2587
2645
|
process.stdin.setRawMode(true);
|
|
2588
2646
|
process.stdin.resume();
|
|
@@ -2600,8 +2658,8 @@ async function run(projectDir) {
|
|
|
2600
2658
|
clearScreen();
|
|
2601
2659
|
}
|
|
2602
2660
|
function makeManageCommand() {
|
|
2603
|
-
return new Command("manage").description("Interactive TUI for managing
|
|
2604
|
-
await run(
|
|
2661
|
+
return new Command("manage").description("Interactive TUI for managing assets, sources, and libraries").option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
|
|
2662
|
+
await run(path15__default.resolve(opts.projectDir));
|
|
2605
2663
|
});
|
|
2606
2664
|
}
|
|
2607
2665
|
function makeMcpServerCommand() {
|
|
@@ -2642,7 +2700,7 @@ function makeNpxCommand() {
|
|
|
2642
2700
|
const npx = new Command("npx").description("Compatibility shim for npx skills syntax");
|
|
2643
2701
|
const skills = npx.command("skills").description("Skill installation from external repos");
|
|
2644
2702
|
skills.command("add <url>").description("Register a repo as a Library Source and install a skill from it").requiredOption("--skill <name>", "Skill name to install from the repo").option("--project-dir <path>", "Project directory", process.cwd()).option("--target <agent>", "Agent framework", "claude-code").option("--global", "Deploy globally").action(async (url, opts) => {
|
|
2645
|
-
const projectDir =
|
|
2703
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
2646
2704
|
const spinner5 = p5.spinner();
|
|
2647
2705
|
let sourcePath;
|
|
2648
2706
|
const isUrl = /^(https?:|git@|git:)/.test(url);
|
|
@@ -2653,14 +2711,14 @@ function makeNpxCommand() {
|
|
|
2653
2711
|
if (!existsSync(sourcePath)) {
|
|
2654
2712
|
execSync(`git clone ${url} ${sourcePath}`, { stdio: "ignore" });
|
|
2655
2713
|
}
|
|
2656
|
-
spinner5.stop(`Cloned to ${
|
|
2714
|
+
spinner5.stop(`Cloned to ${path15__default.basename(sourcePath)}`);
|
|
2657
2715
|
} catch (err) {
|
|
2658
2716
|
spinner5.stop("Clone failed");
|
|
2659
2717
|
logErr(err instanceof Error ? err.message : String(err));
|
|
2660
2718
|
process.exit(1);
|
|
2661
2719
|
}
|
|
2662
2720
|
} else {
|
|
2663
|
-
sourcePath =
|
|
2721
|
+
sourcePath = path15__default.resolve(url);
|
|
2664
2722
|
if (!existsSync(sourcePath)) {
|
|
2665
2723
|
logErr(`Path does not exist: ${sourcePath}`);
|
|
2666
2724
|
process.exit(1);
|
|
@@ -2672,9 +2730,10 @@ function makeNpxCommand() {
|
|
|
2672
2730
|
let source = existing.find((s) => s.path === sourcePath);
|
|
2673
2731
|
if (!source) {
|
|
2674
2732
|
source = await library.addSource({
|
|
2675
|
-
|
|
2676
|
-
name:
|
|
2677
|
-
path: sourcePath
|
|
2733
|
+
backend: "local",
|
|
2734
|
+
name: path15__default.basename(sourcePath),
|
|
2735
|
+
path: sourcePath,
|
|
2736
|
+
ownership: "owner"
|
|
2678
2737
|
});
|
|
2679
2738
|
}
|
|
2680
2739
|
spinner5.start("Syncing source");
|
|
@@ -2713,7 +2772,7 @@ function makeNpxCommand() {
|
|
|
2713
2772
|
}
|
|
2714
2773
|
function cloneDestination(url) {
|
|
2715
2774
|
const cleaned = url.replace(/\.git$/, "");
|
|
2716
|
-
const root =
|
|
2775
|
+
const root = path15__default.join(homedir(), ".skaile", "clones");
|
|
2717
2776
|
let host = "unknown";
|
|
2718
2777
|
let pathPart = cleaned;
|
|
2719
2778
|
const httpMatch = cleaned.match(/^https?:\/\/([^/]+)\/(.+)$/);
|
|
@@ -2729,14 +2788,14 @@ function cloneDestination(url) {
|
|
|
2729
2788
|
host = gitProtoMatch[1];
|
|
2730
2789
|
pathPart = gitProtoMatch[2];
|
|
2731
2790
|
}
|
|
2732
|
-
return
|
|
2791
|
+
return path15__default.join(root, host, pathPart);
|
|
2733
2792
|
}
|
|
2734
2793
|
function makeOutdatedCommand() {
|
|
2735
2794
|
return new Command("outdated").description("Check for assets behind their repo").argument("[name]", "Check a specific asset (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).addOption(
|
|
2736
2795
|
new Option("--target <agent>", "Driver target").default("claude-code").choices(SUPPORTED_DRIVER_TARGETS)
|
|
2737
2796
|
).action((name, opts) => {
|
|
2738
2797
|
const am2 = new AssetManager({
|
|
2739
|
-
projectDir:
|
|
2798
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
2740
2799
|
driverTarget: opts.target
|
|
2741
2800
|
});
|
|
2742
2801
|
const entries = am2.outdated();
|
|
@@ -2755,7 +2814,7 @@ function makeOutdatedCommand() {
|
|
|
2755
2814
|
console.log(` ${S.rule(nameW + 40)}`);
|
|
2756
2815
|
for (const e of filtered) {
|
|
2757
2816
|
console.log(
|
|
2758
|
-
` ${kindColorPad(e.kind)} ${e.name.padEnd(nameW)} ${S.dim(e.repository)} ${
|
|
2817
|
+
` ${kindColorPad(e.kind)} ${e.name.padEnd(nameW)} ${S.dim(e.repository)} ${pc5.yellow(`${e.behind} commit(s) behind`)}`
|
|
2759
2818
|
);
|
|
2760
2819
|
}
|
|
2761
2820
|
console.log();
|
|
@@ -2765,7 +2824,7 @@ function makeOutdatedCommand() {
|
|
|
2765
2824
|
function makePatchCommand() {
|
|
2766
2825
|
const cmd = new Command("patch").description("Patch workflow for skill improvement");
|
|
2767
2826
|
cmd.command("extract <ref>").alias("start").description("Extract asset for local editing").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
2768
|
-
const am2 = new AssetManager({ projectDir:
|
|
2827
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
2769
2828
|
try {
|
|
2770
2829
|
const dest = am2.patch(ref);
|
|
2771
2830
|
logOk(`Extracted to ${S.dim(dest)}`);
|
|
@@ -2776,7 +2835,7 @@ function makePatchCommand() {
|
|
|
2776
2835
|
}
|
|
2777
2836
|
});
|
|
2778
2837
|
cmd.command("commit <ref>").description("Generate .patch file from edits").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
2779
|
-
const am2 = new AssetManager({ projectDir:
|
|
2838
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
2780
2839
|
try {
|
|
2781
2840
|
const patchFile = am2.patchCommit(ref);
|
|
2782
2841
|
logOk(`Patch saved to ${S.dim(patchFile)}`);
|
|
@@ -2787,7 +2846,7 @@ function makePatchCommand() {
|
|
|
2787
2846
|
}
|
|
2788
2847
|
});
|
|
2789
2848
|
cmd.command("submit <ref>").description("Apply patch to repo clone and prepare for PR").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
2790
|
-
const am2 = new AssetManager({ projectDir:
|
|
2849
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
2791
2850
|
try {
|
|
2792
2851
|
am2.patchSubmit(ref);
|
|
2793
2852
|
logOk("Patch applied to repo clone and committed.");
|
|
@@ -2800,7 +2859,7 @@ function makePatchCommand() {
|
|
|
2800
2859
|
}
|
|
2801
2860
|
});
|
|
2802
2861
|
cmd.command("remove <ref>").description("Remove local patch after upstream merge").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
2803
|
-
const am2 = new AssetManager({ projectDir:
|
|
2862
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
2804
2863
|
am2.patchRemove(ref);
|
|
2805
2864
|
logOk(`Patch removed for ${ref}`);
|
|
2806
2865
|
});
|
|
@@ -2814,7 +2873,7 @@ var MCP_SERVER_ENTRY = {
|
|
|
2814
2873
|
env: {}
|
|
2815
2874
|
};
|
|
2816
2875
|
function pluginDir(projectDir) {
|
|
2817
|
-
return
|
|
2876
|
+
return path15__default.join(projectDir, ".claude", "plugins", "skaile");
|
|
2818
2877
|
}
|
|
2819
2878
|
async function readJsonStrict(p7) {
|
|
2820
2879
|
let raw;
|
|
@@ -2832,7 +2891,7 @@ async function readJsonStrict(p7) {
|
|
|
2832
2891
|
}
|
|
2833
2892
|
}
|
|
2834
2893
|
async function writeJson(p7, value) {
|
|
2835
|
-
await fs5.mkdir(
|
|
2894
|
+
await fs5.mkdir(path15__default.dirname(p7), { recursive: true });
|
|
2836
2895
|
await fs5.writeFile(p7, `${JSON.stringify(value, null, 2)}
|
|
2837
2896
|
`);
|
|
2838
2897
|
}
|
|
@@ -2840,8 +2899,8 @@ async function copyDir(src, dest) {
|
|
|
2840
2899
|
await fs5.mkdir(dest, { recursive: true });
|
|
2841
2900
|
const entries = await fs5.readdir(src, { withFileTypes: true });
|
|
2842
2901
|
for (const entry of entries) {
|
|
2843
|
-
const srcPath =
|
|
2844
|
-
const destPath =
|
|
2902
|
+
const srcPath = path15__default.join(src, entry.name);
|
|
2903
|
+
const destPath = path15__default.join(dest, entry.name);
|
|
2845
2904
|
if (entry.isDirectory()) await copyDir(srcPath, destPath);
|
|
2846
2905
|
else await fs5.copyFile(srcPath, destPath);
|
|
2847
2906
|
}
|
|
@@ -2850,14 +2909,14 @@ async function install(projectDir, opts) {
|
|
|
2850
2909
|
return opts?.full ? installFullPlugin(projectDir) : installMcp(projectDir);
|
|
2851
2910
|
}
|
|
2852
2911
|
async function installMcp(projectDir) {
|
|
2853
|
-
const mcpPath =
|
|
2912
|
+
const mcpPath = path15__default.join(projectDir, ".claude", "mcp.json");
|
|
2854
2913
|
const existing = await readJsonStrict(mcpPath) ?? {};
|
|
2855
2914
|
const mcpServers = { ...existing.mcpServers ?? {} };
|
|
2856
2915
|
const previous = mcpServers[SKAILE_MCP_NAME];
|
|
2857
2916
|
const matches = previous && JSON.stringify(previous) === JSON.stringify(MCP_SERVER_ENTRY);
|
|
2858
2917
|
let warning;
|
|
2859
2918
|
if (previous && !matches) {
|
|
2860
|
-
warning = `overwrote existing ${SKAILE_MCP_NAME} entry in ${
|
|
2919
|
+
warning = `overwrote existing ${SKAILE_MCP_NAME} entry in ${path15__default.relative(projectDir, mcpPath)}`;
|
|
2861
2920
|
}
|
|
2862
2921
|
mcpServers[SKAILE_MCP_NAME] = MCP_SERVER_ENTRY;
|
|
2863
2922
|
await writeJson(mcpPath, { ...existing, mcpServers });
|
|
@@ -2866,15 +2925,15 @@ async function installMcp(projectDir) {
|
|
|
2866
2925
|
async function installFullPlugin(projectDir) {
|
|
2867
2926
|
const dir = pluginDir(projectDir);
|
|
2868
2927
|
for (const [rel, content] of Object.entries(buildClaudePluginFiles())) {
|
|
2869
|
-
const dest =
|
|
2870
|
-
await fs5.mkdir(
|
|
2928
|
+
const dest = path15__default.join(dir, rel);
|
|
2929
|
+
await fs5.mkdir(path15__default.dirname(dest), { recursive: true });
|
|
2871
2930
|
await fs5.writeFile(dest, content);
|
|
2872
2931
|
}
|
|
2873
|
-
const projectSkillsDir =
|
|
2932
|
+
const projectSkillsDir = path15__default.join(projectDir, ".skaile", "skills");
|
|
2874
2933
|
if (existsSync(projectSkillsDir)) {
|
|
2875
|
-
await copyDir(projectSkillsDir,
|
|
2934
|
+
await copyDir(projectSkillsDir, path15__default.join(dir, "skills"));
|
|
2876
2935
|
}
|
|
2877
|
-
const settingsPath =
|
|
2936
|
+
const settingsPath = path15__default.join(projectDir, ".claude", "settings.json");
|
|
2878
2937
|
const settings = await readJsonStrict(settingsPath) ?? {};
|
|
2879
2938
|
const plugins = settings.plugins ?? [];
|
|
2880
2939
|
if (!plugins.includes(dir)) plugins.push(dir);
|
|
@@ -2882,8 +2941,8 @@ async function installFullPlugin(projectDir) {
|
|
|
2882
2941
|
return { changed: true, method: "plugin" };
|
|
2883
2942
|
}
|
|
2884
2943
|
async function uninstall(projectDir) {
|
|
2885
|
-
const settingsPath =
|
|
2886
|
-
const mcpPath =
|
|
2944
|
+
const settingsPath = path15__default.join(projectDir, ".claude", "settings.json");
|
|
2945
|
+
const mcpPath = path15__default.join(projectDir, ".claude", "mcp.json");
|
|
2887
2946
|
const dir = pluginDir(projectDir);
|
|
2888
2947
|
let changed = false;
|
|
2889
2948
|
const settings = await readJsonStrict(settingsPath);
|
|
@@ -2917,12 +2976,12 @@ async function uninstall(projectDir) {
|
|
|
2917
2976
|
async function detectInstall(projectDir) {
|
|
2918
2977
|
const dir = pluginDir(projectDir);
|
|
2919
2978
|
const settings = await readJsonStrict(
|
|
2920
|
-
|
|
2979
|
+
path15__default.join(projectDir, ".claude", "settings.json")
|
|
2921
2980
|
);
|
|
2922
|
-
if (existsSync(
|
|
2981
|
+
if (existsSync(path15__default.join(dir, ".claude-plugin", "plugin.json")) && settings?.plugins?.includes(dir)) {
|
|
2923
2982
|
return { method: "plugin", pluginPath: dir };
|
|
2924
2983
|
}
|
|
2925
|
-
const mcp = await readJsonStrict(
|
|
2984
|
+
const mcp = await readJsonStrict(path15__default.join(projectDir, ".claude", "mcp.json"));
|
|
2926
2985
|
if (mcp?.mcpServers && SKAILE_MCP_NAME in mcp.mcpServers) {
|
|
2927
2986
|
return { method: "mcp" };
|
|
2928
2987
|
}
|
|
@@ -2931,7 +2990,7 @@ async function detectInstall(projectDir) {
|
|
|
2931
2990
|
async function enable(projectDir) {
|
|
2932
2991
|
const detected = await detectInstall(projectDir);
|
|
2933
2992
|
if (!detected) throw new Error("plugin not installed for claude-code");
|
|
2934
|
-
const settingsPath =
|
|
2993
|
+
const settingsPath = path15__default.join(projectDir, ".claude", "settings.json");
|
|
2935
2994
|
const settings = await readJsonStrict(settingsPath) ?? {};
|
|
2936
2995
|
if (detected.method === "plugin") {
|
|
2937
2996
|
const disabled2 = settings.disabledPlugins ?? [];
|
|
@@ -2953,7 +3012,7 @@ async function enable(projectDir) {
|
|
|
2953
3012
|
async function disable(projectDir) {
|
|
2954
3013
|
const detected = await detectInstall(projectDir);
|
|
2955
3014
|
if (!detected) throw new Error("plugin not installed for claude-code");
|
|
2956
|
-
const settingsPath =
|
|
3015
|
+
const settingsPath = path15__default.join(projectDir, ".claude", "settings.json");
|
|
2957
3016
|
const settings = await readJsonStrict(settingsPath) ?? {};
|
|
2958
3017
|
if (detected.method === "plugin") {
|
|
2959
3018
|
const list2 = settings.disabledPlugins ?? [];
|
|
@@ -2973,12 +3032,12 @@ async function disable(projectDir) {
|
|
|
2973
3032
|
return { changed: true };
|
|
2974
3033
|
}
|
|
2975
3034
|
async function status(projectDir) {
|
|
2976
|
-
const settingsPath =
|
|
2977
|
-
const mcpPath =
|
|
3035
|
+
const settingsPath = path15__default.join(projectDir, ".claude", "settings.json");
|
|
3036
|
+
const mcpPath = path15__default.join(projectDir, ".claude", "mcp.json");
|
|
2978
3037
|
const dir = pluginDir(projectDir);
|
|
2979
3038
|
const settings = await readJsonStrict(settingsPath);
|
|
2980
3039
|
const mcp = await readJsonStrict(mcpPath);
|
|
2981
|
-
const pluginInstalled = !!(existsSync(
|
|
3040
|
+
const pluginInstalled = !!(existsSync(path15__default.join(dir, ".claude-plugin", "plugin.json")) && settings?.plugins?.includes(dir));
|
|
2982
3041
|
const mcpInstalled = !!(mcp?.mcpServers && SKAILE_MCP_NAME in mcp.mcpServers);
|
|
2983
3042
|
if (!pluginInstalled && !mcpInstalled) {
|
|
2984
3043
|
return {
|
|
@@ -2996,7 +3055,7 @@ async function status(projectDir) {
|
|
|
2996
3055
|
installed: "yes",
|
|
2997
3056
|
enabled: disabled2 ? "no" : "yes",
|
|
2998
3057
|
method: "plugin",
|
|
2999
|
-
location:
|
|
3058
|
+
location: path15__default.relative(projectDir, dir)
|
|
3000
3059
|
};
|
|
3001
3060
|
}
|
|
3002
3061
|
const disabled = settings?.disabledMcpjsonServers?.includes(SKAILE_MCP_NAME) ?? false;
|
|
@@ -3005,7 +3064,7 @@ async function status(projectDir) {
|
|
|
3005
3064
|
installed: "yes",
|
|
3006
3065
|
enabled: disabled ? "no" : "yes",
|
|
3007
3066
|
method: "mcp",
|
|
3008
|
-
location:
|
|
3067
|
+
location: path15__default.relative(projectDir, mcpPath)
|
|
3009
3068
|
};
|
|
3010
3069
|
}
|
|
3011
3070
|
var SKAILE_MCP_NAME2 = "skaile-workspace";
|
|
@@ -3032,7 +3091,7 @@ async function readConfig(p7) {
|
|
|
3032
3091
|
}
|
|
3033
3092
|
}
|
|
3034
3093
|
async function writeConfig(p7, value) {
|
|
3035
|
-
await fs5.mkdir(
|
|
3094
|
+
await fs5.mkdir(path15__default.dirname(p7), { recursive: true });
|
|
3036
3095
|
const cleaned = {};
|
|
3037
3096
|
for (const [k, v] of Object.entries(value)) {
|
|
3038
3097
|
if (v === void 0) continue;
|
|
@@ -3041,7 +3100,7 @@ async function writeConfig(p7, value) {
|
|
|
3041
3100
|
await fs5.writeFile(p7, stringify(cleaned));
|
|
3042
3101
|
}
|
|
3043
3102
|
function configPath(projectDir) {
|
|
3044
|
-
return
|
|
3103
|
+
return path15__default.join(projectDir, ".codex", "config.toml");
|
|
3045
3104
|
}
|
|
3046
3105
|
async function install2(projectDir) {
|
|
3047
3106
|
const p7 = configPath(projectDir);
|
|
@@ -3052,7 +3111,7 @@ async function install2(projectDir) {
|
|
|
3052
3111
|
const matches = previous && JSON.stringify(previous) === JSON.stringify(next);
|
|
3053
3112
|
let warning;
|
|
3054
3113
|
if (previous && !matches) {
|
|
3055
|
-
warning = `overwrote existing ${SKAILE_MCP_NAME2} entry in ${
|
|
3114
|
+
warning = `overwrote existing ${SKAILE_MCP_NAME2} entry in ${path15__default.relative(projectDir, p7)}`;
|
|
3056
3115
|
}
|
|
3057
3116
|
if (matches) return { changed: false, method: "toml" };
|
|
3058
3117
|
servers[SKAILE_MCP_NAME2] = next;
|
|
@@ -3093,12 +3152,12 @@ async function status2(projectDir) {
|
|
|
3093
3152
|
installed: "yes",
|
|
3094
3153
|
enabled: "n/a",
|
|
3095
3154
|
method: "mcp",
|
|
3096
|
-
location:
|
|
3155
|
+
location: path15__default.relative(projectDir, p7)
|
|
3097
3156
|
};
|
|
3098
3157
|
}
|
|
3099
3158
|
async function install3(projectDir) {
|
|
3100
|
-
const extDir =
|
|
3101
|
-
const extPath =
|
|
3159
|
+
const extDir = path15__default.join(projectDir, ".omp", "extensions");
|
|
3160
|
+
const extPath = path15__default.join(extDir, "skaile.ts");
|
|
3102
3161
|
await fs5.mkdir(extDir, { recursive: true });
|
|
3103
3162
|
const { WorkspacePlugin } = await import('../workspace-plugin/index.js');
|
|
3104
3163
|
const plugin = new WorkspacePlugin({ projectDir });
|
|
@@ -3113,7 +3172,7 @@ async function install3(projectDir) {
|
|
|
3113
3172
|
return { changed: true, method: "generated" };
|
|
3114
3173
|
}
|
|
3115
3174
|
async function uninstall3(projectDir) {
|
|
3116
|
-
const extPath =
|
|
3175
|
+
const extPath = path15__default.join(projectDir, ".omp", "extensions", "skaile.ts");
|
|
3117
3176
|
try {
|
|
3118
3177
|
await fs5.unlink(extPath);
|
|
3119
3178
|
return { changed: true };
|
|
@@ -3122,14 +3181,14 @@ async function uninstall3(projectDir) {
|
|
|
3122
3181
|
}
|
|
3123
3182
|
}
|
|
3124
3183
|
async function status3(projectDir) {
|
|
3125
|
-
const extPath =
|
|
3184
|
+
const extPath = path15__default.join(projectDir, ".omp", "extensions", "skaile.ts");
|
|
3126
3185
|
if (existsSync(extPath)) {
|
|
3127
3186
|
return {
|
|
3128
3187
|
backend: "omp",
|
|
3129
3188
|
installed: "yes",
|
|
3130
3189
|
enabled: "n/a",
|
|
3131
3190
|
method: "extension",
|
|
3132
|
-
location:
|
|
3191
|
+
location: path15__default.relative(projectDir, extPath)
|
|
3133
3192
|
};
|
|
3134
3193
|
}
|
|
3135
3194
|
return {
|
|
@@ -3150,7 +3209,7 @@ function resolveDriver(driver) {
|
|
|
3150
3209
|
throw new Error(`unknown driver: ${driver} (expected: claude-code | omp | codex | all)`);
|
|
3151
3210
|
}
|
|
3152
3211
|
async function runInstall(opts) {
|
|
3153
|
-
const projectDir =
|
|
3212
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3154
3213
|
const targets = resolveDriver(opts.driver);
|
|
3155
3214
|
let exitCode = 0;
|
|
3156
3215
|
if (targets.includes("claude-code")) {
|
|
@@ -3205,7 +3264,7 @@ async function runInstall(opts) {
|
|
|
3205
3264
|
return exitCode;
|
|
3206
3265
|
}
|
|
3207
3266
|
async function runUninstall(opts) {
|
|
3208
|
-
const projectDir =
|
|
3267
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3209
3268
|
const targets = resolveDriver(opts.driver);
|
|
3210
3269
|
let exitCode = 0;
|
|
3211
3270
|
if (targets.includes("claude-code")) {
|
|
@@ -3242,7 +3301,7 @@ async function runUninstall(opts) {
|
|
|
3242
3301
|
return exitCode;
|
|
3243
3302
|
}
|
|
3244
3303
|
async function runToggle(opts, action) {
|
|
3245
|
-
const projectDir =
|
|
3304
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3246
3305
|
const requestedAll = opts.driver === "all";
|
|
3247
3306
|
const targets = resolveDriver(opts.driver);
|
|
3248
3307
|
if (!requestedAll) {
|
|
@@ -3277,7 +3336,7 @@ async function runToggle(opts, action) {
|
|
|
3277
3336
|
return exitCode;
|
|
3278
3337
|
}
|
|
3279
3338
|
async function runStatus(opts) {
|
|
3280
|
-
const projectDir =
|
|
3339
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3281
3340
|
const targets = resolveDriver(opts.driver);
|
|
3282
3341
|
const rows = [];
|
|
3283
3342
|
let exitCode = 0;
|
|
@@ -3348,7 +3407,7 @@ async function runHook(event) {
|
|
|
3348
3407
|
await plugin.shutdown();
|
|
3349
3408
|
return;
|
|
3350
3409
|
}
|
|
3351
|
-
const store = new PluginStore(
|
|
3410
|
+
const store = new PluginStore(path15__default.join(projectDir, ".skaile", "plugin-state.json"));
|
|
3352
3411
|
await store.load();
|
|
3353
3412
|
const status4 = store.get("connector:status");
|
|
3354
3413
|
if (status4 && typeof status4 === "object" && Object.keys(status4).length > 0) {
|
|
@@ -3413,7 +3472,7 @@ function makePresetCommand() {
|
|
|
3413
3472
|
cmd.command("init [name]").description("Scaffold a new .preset.yaml file").option("--dir <path>", "Output directory", process.cwd()).action(async (name, opts) => {
|
|
3414
3473
|
const presetName = name ?? "my-preset";
|
|
3415
3474
|
const filename = `${presetName}.preset.yaml`;
|
|
3416
|
-
const outPath =
|
|
3475
|
+
const outPath = path15__default.join(path15__default.resolve(opts.dir), filename);
|
|
3417
3476
|
if (existsSync(outPath)) {
|
|
3418
3477
|
logErr(`File already exists: ${outPath}`);
|
|
3419
3478
|
logInfo("Choose a different name or remove the existing file.");
|
|
@@ -3427,7 +3486,7 @@ function makePresetCommand() {
|
|
|
3427
3486
|
logInfo(` skaile preset validate ${filename}`);
|
|
3428
3487
|
});
|
|
3429
3488
|
cmd.command("validate <path>").description("Validate a preset YAML file (schema + cycles + nesting depth)").action(async (filePath) => {
|
|
3430
|
-
const resolved =
|
|
3489
|
+
const resolved = path15__default.resolve(filePath);
|
|
3431
3490
|
if (!existsSync(resolved)) {
|
|
3432
3491
|
logErr(`File not found: ${resolved}`);
|
|
3433
3492
|
process.exit(1);
|
|
@@ -3453,7 +3512,7 @@ function makePresetCommand() {
|
|
|
3453
3512
|
logInfo(`Nested preset refs: ${nestedRefs.length} (depth validated at apply-time)`);
|
|
3454
3513
|
}
|
|
3455
3514
|
const { detectCycles } = await import('../discovery/index.js');
|
|
3456
|
-
const presetRef =
|
|
3515
|
+
const presetRef = path15__default.basename(resolved, ".preset.yaml");
|
|
3457
3516
|
const edges = [];
|
|
3458
3517
|
for (const item of preset.items) {
|
|
3459
3518
|
if (item.ref) {
|
|
@@ -3577,8 +3636,8 @@ function makeInstallCommand() {
|
|
|
3577
3636
|
const spinner6 = p5.spinner();
|
|
3578
3637
|
spinner6.start(`Installing ${ref}`);
|
|
3579
3638
|
try {
|
|
3580
|
-
const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-
|
|
3581
|
-
const catalog = await openCatalogSource2({ projectDir:
|
|
3639
|
+
const { openCatalogSource: openCatalogSource2, openLibrary: openLibrary2 } = await import('../open-library-T6RXQJTQ.js');
|
|
3640
|
+
const catalog = await openCatalogSource2({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
3582
3641
|
if (!supportsInstallManifest(catalog)) {
|
|
3583
3642
|
throw new Error(
|
|
3584
3643
|
"pointer-only install (skaile install <ref>) requires a tRPC-framed Catalog. The configured catalog uses REST framing, which does not serve install manifests. Set catalog.url to https://skaile.store, or catalog.framing: trpc, in ~/.skaile/config.yaml."
|
|
@@ -3608,7 +3667,7 @@ function makeInstallCommand() {
|
|
|
3608
3667
|
return;
|
|
3609
3668
|
}
|
|
3610
3669
|
const am2 = new AssetManager({
|
|
3611
|
-
projectDir:
|
|
3670
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
3612
3671
|
driverTarget: opts.target
|
|
3613
3672
|
});
|
|
3614
3673
|
const spinner5 = p5.spinner();
|
|
@@ -3647,7 +3706,7 @@ function makeInstallCommand() {
|
|
|
3647
3706
|
}
|
|
3648
3707
|
function makeCheckCommand() {
|
|
3649
3708
|
return new Command("check").description("Check for unmet requirements").option("--project-dir <path>", "Project directory", process.cwd()).action((opts) => {
|
|
3650
|
-
const am2 = new AssetManager({ projectDir:
|
|
3709
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
3651
3710
|
const issues = am2.doctor();
|
|
3652
3711
|
if (issues.length === 0) {
|
|
3653
3712
|
logOk("All dependencies satisfied.");
|
|
@@ -3664,10 +3723,10 @@ function makeCheckCommand() {
|
|
|
3664
3723
|
function makeCleanCommand() {
|
|
3665
3724
|
return new Command("clean").description("Remove all skaile-managed assets from the workspace").option("--all", "Full reset: also remove history, patches, repos, and lock file").option("--dry-run", "Show what would be removed without doing it").option("-y, --yes", "Skip confirmation prompt").option("--project-dir <path>", "Project directory", process.cwd()).option("--target <agent>", "Agent framework", "claude-code").action(async (opts) => {
|
|
3666
3725
|
const am2 = new AssetManager({
|
|
3667
|
-
projectDir:
|
|
3726
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
3668
3727
|
driverTarget: opts.target
|
|
3669
3728
|
});
|
|
3670
|
-
const lock = readLock(
|
|
3729
|
+
const lock = readLock(path15__default.resolve(opts.projectDir, "skaile.lock.yaml"));
|
|
3671
3730
|
if (!lock || Object.keys(lock.assets).length === 0) {
|
|
3672
3731
|
logInfo("Nothing to clean (no skaile-managed assets found).");
|
|
3673
3732
|
return;
|
|
@@ -3720,14 +3779,14 @@ function makeCleanCommand() {
|
|
|
3720
3779
|
}
|
|
3721
3780
|
function makeRebuildCommand() {
|
|
3722
3781
|
return new Command("rebuild").description("Re-snapshot inline-snapshot composition items and bump prompt version").argument("[agent]", "Agent name or path (defaults to current project agent)").option("--project-dir <dir>", "Project directory", process.cwd()).action(async (agentArg, opts) => {
|
|
3723
|
-
const projectDir =
|
|
3782
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3724
3783
|
let agentDir;
|
|
3725
3784
|
if (agentArg) {
|
|
3726
3785
|
if (agentArg.includes("/") || agentArg.includes("\\")) {
|
|
3727
|
-
agentDir =
|
|
3786
|
+
agentDir = path15__default.resolve(projectDir, agentArg);
|
|
3728
3787
|
} else {
|
|
3729
3788
|
const resolved = resolveAgentDir(projectDir);
|
|
3730
|
-
agentDir = resolved ??
|
|
3789
|
+
agentDir = resolved ?? path15__default.resolve(projectDir, agentArg);
|
|
3731
3790
|
}
|
|
3732
3791
|
} else {
|
|
3733
3792
|
const resolved = resolveAgentDir(projectDir);
|
|
@@ -3788,7 +3847,7 @@ function makeFlowEventHandler() {
|
|
|
3788
3847
|
if (n.status === "running") {
|
|
3789
3848
|
process.stdout.write(` ${S.dim("\u2192")} ${id}`);
|
|
3790
3849
|
} else if (n.status === "complete") {
|
|
3791
|
-
process.stdout.write(` ${
|
|
3850
|
+
process.stdout.write(` ${pc5.green("\u2713")}
|
|
3792
3851
|
`);
|
|
3793
3852
|
} else if (n.status === "skipped") {
|
|
3794
3853
|
process.stdout.write(` ${S.dim("\u21B7 skipped")}
|
|
@@ -3805,7 +3864,7 @@ function makeFlowEventHandler() {
|
|
|
3805
3864
|
}
|
|
3806
3865
|
function makeRunCommand() {
|
|
3807
3866
|
const cmd = new Command("run").description("Start a flow or run a single-shot text prompt").argument("[flow-id-or-text...]", "Flow ID or text prompt").option("--project-dir <path>", "Project directory", process.cwd()).option("--driver <name>", "Driver backend (claude-sdk, codex, omp)").option("--provider <name>", "LLM provider").option("--model <name>", "Model override").option("--label <label>", "Human-readable session label").option("--dry-run", "Print plan without executing").option("--skill <name>", "Skill name (for single-shot mode)").action(async (positional, opts) => {
|
|
3808
|
-
const projectDir =
|
|
3867
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3809
3868
|
const cliDriver = opts.driver;
|
|
3810
3869
|
const dryRun = opts.dryRun ?? false;
|
|
3811
3870
|
let flowId = positional[0];
|
|
@@ -3859,15 +3918,15 @@ function makeRunCommand() {
|
|
|
3859
3918
|
let flowPath;
|
|
3860
3919
|
let dir = projectDir;
|
|
3861
3920
|
for (let i = 0; i < 6; i++) {
|
|
3862
|
-
const candidate =
|
|
3921
|
+
const candidate = path15__default.join(dir, "ai-assets");
|
|
3863
3922
|
if (fs10__default.existsSync(candidate)) {
|
|
3864
3923
|
for (const domain of fs10__default.readdirSync(candidate)) {
|
|
3865
|
-
const p_ =
|
|
3924
|
+
const p_ = path15__default.join(candidate, domain, "flows", `${flowId}.flow.yaml`);
|
|
3866
3925
|
if (fs10__default.existsSync(p_)) {
|
|
3867
3926
|
flowPath = p_;
|
|
3868
3927
|
break;
|
|
3869
3928
|
}
|
|
3870
|
-
const legacy =
|
|
3929
|
+
const legacy = path15__default.join(candidate, domain, "flows", `${flowId}.json`);
|
|
3871
3930
|
if (fs10__default.existsSync(legacy)) {
|
|
3872
3931
|
flowPath = legacy;
|
|
3873
3932
|
break;
|
|
@@ -3875,16 +3934,16 @@ function makeRunCommand() {
|
|
|
3875
3934
|
}
|
|
3876
3935
|
break;
|
|
3877
3936
|
}
|
|
3878
|
-
dir =
|
|
3937
|
+
dir = path15__default.dirname(dir);
|
|
3879
3938
|
}
|
|
3880
3939
|
if (!flowPath) {
|
|
3881
3940
|
for (const domain of fs10__default.readdirSync(AI_RESOURCES)) {
|
|
3882
|
-
const p_ =
|
|
3941
|
+
const p_ = path15__default.join(AI_RESOURCES, domain, "flows", `${flowId}.flow.yaml`);
|
|
3883
3942
|
if (fs10__default.existsSync(p_)) {
|
|
3884
3943
|
flowPath = p_;
|
|
3885
3944
|
break;
|
|
3886
3945
|
}
|
|
3887
|
-
const legacy =
|
|
3946
|
+
const legacy = path15__default.join(AI_RESOURCES, domain, "flows", `${flowId}.json`);
|
|
3888
3947
|
if (fs10__default.existsSync(legacy)) {
|
|
3889
3948
|
flowPath = legacy;
|
|
3890
3949
|
break;
|
|
@@ -3918,7 +3977,7 @@ function makeRunCommand() {
|
|
|
3918
3977
|
}
|
|
3919
3978
|
function makeResumeCommand() {
|
|
3920
3979
|
const cmd = new Command("resume").description("Resume current session").option("--project-dir <path>", "Project directory", process.cwd()).option("--session <run-id>", "Target session by runId").option("--dry-run", "Print plan without executing").action(async (opts) => {
|
|
3921
|
-
const projectDir =
|
|
3980
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3922
3981
|
const sessionId = opts.session;
|
|
3923
3982
|
const dryRun = opts.dryRun ?? false;
|
|
3924
3983
|
const session = sessionId ? await loadSessionById(projectDir, sessionId) : await loadSession(projectDir);
|
|
@@ -3945,14 +4004,14 @@ function makeResumeCommand() {
|
|
|
3945
4004
|
}
|
|
3946
4005
|
function makeStatusCommand() {
|
|
3947
4006
|
return new Command("status").description("Show current session state").option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
|
|
3948
|
-
const projectDir =
|
|
4007
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3949
4008
|
const session = await loadSession(projectDir);
|
|
3950
4009
|
if (!session) {
|
|
3951
4010
|
logInfo("No active session.");
|
|
3952
4011
|
return;
|
|
3953
4012
|
}
|
|
3954
4013
|
const age = Math.round((Date.now() - new Date(session.updatedAt).getTime()) / 1e3);
|
|
3955
|
-
const statusColor = session.status === "complete" ?
|
|
4014
|
+
const statusColor = session.status === "complete" ? pc5.green : session.status === "failed" ? pc5.red : session.status === "running" ? pc5.cyan : pc5.yellow;
|
|
3956
4015
|
console.log();
|
|
3957
4016
|
console.log(` ${S.label("Flow:")} ${S.heading(session.flowId)}`);
|
|
3958
4017
|
console.log(` ${S.label("Run ID:")} ${session.runId.slice(0, 8)}`);
|
|
@@ -3970,7 +4029,7 @@ function makeStatusCommand() {
|
|
|
3970
4029
|
}
|
|
3971
4030
|
function makeClearCommand() {
|
|
3972
4031
|
return new Command("clear").description("Unset current session pointer").option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
|
|
3973
|
-
const projectDir =
|
|
4032
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
3974
4033
|
await clearSession(projectDir);
|
|
3975
4034
|
logOk("Session cleared.");
|
|
3976
4035
|
});
|
|
@@ -3980,7 +4039,7 @@ function makeReplCommand() {
|
|
|
3980
4039
|
const { findWorkspaceRoot } = await import('../core/index.js');
|
|
3981
4040
|
const { startRepl } = await import('../tui/index.js');
|
|
3982
4041
|
const os = await import('os');
|
|
3983
|
-
const userCwd =
|
|
4042
|
+
const userCwd = path15__default.resolve(opts.projectDir ?? process.cwd());
|
|
3984
4043
|
const workspaceRoot = findWorkspaceRoot(userCwd);
|
|
3985
4044
|
let projectDir;
|
|
3986
4045
|
let agentCwd;
|
|
@@ -4002,12 +4061,12 @@ function makeReplCommand() {
|
|
|
4002
4061
|
p5.cancel("Cancelled.");
|
|
4003
4062
|
process.exit(0);
|
|
4004
4063
|
}
|
|
4005
|
-
const tmpDir = fs10__default.mkdtempSync(
|
|
4064
|
+
const tmpDir = fs10__default.mkdtempSync(path15__default.join(os.tmpdir(), "skaile-repl-"));
|
|
4006
4065
|
projectDir = tmpDir;
|
|
4007
4066
|
agentCwd = userCwd;
|
|
4008
4067
|
logOk(`Temporary workspace: ${S.cmd(tmpDir)}`);
|
|
4009
4068
|
if (!driverOverride) {
|
|
4010
|
-
const claudeDir =
|
|
4069
|
+
const claudeDir = path15__default.join(os.homedir(), ".claude");
|
|
4011
4070
|
if (fs10__default.existsSync(claudeDir)) {
|
|
4012
4071
|
driverOverride = "claude-sdk";
|
|
4013
4072
|
logInfo(`Detected ${S.cmd("~/.claude")} \u2192 using ${S.cmd("claude-sdk")} backend`);
|
|
@@ -4055,7 +4114,7 @@ function makeServeCommand() {
|
|
|
4055
4114
|
async function runCompileTest(opts) {
|
|
4056
4115
|
const { logOk: logOk2, logErr: logErr2, logInfo: logInfo2, S: S2 } = await import('../helpers-I3SREIC3.js');
|
|
4057
4116
|
const { tmpdir } = await import('os');
|
|
4058
|
-
const { dirname: dirname3, join:
|
|
4117
|
+
const { dirname: dirname3, join: join5, resolve: resolve4 } = await import('path');
|
|
4059
4118
|
const { fileURLToPath: fileURLToPath2 } = await import('url');
|
|
4060
4119
|
const { unlinkSync, existsSync: existsSync11 } = await import('fs');
|
|
4061
4120
|
const { spawn } = await import('child_process');
|
|
@@ -4067,9 +4126,9 @@ async function runCompileTest(opts) {
|
|
|
4067
4126
|
process.exitCode = 2;
|
|
4068
4127
|
return;
|
|
4069
4128
|
}
|
|
4070
|
-
const outfile =
|
|
4129
|
+
const outfile = join5(tmpdir(), `skaile-compile-test-${Date.now()}`);
|
|
4071
4130
|
const __thisFile = fileURLToPath2(import.meta.url);
|
|
4072
|
-
const entryPoint =
|
|
4131
|
+
const entryPoint = resolve4(dirname3(__thisFile), "..", "index.ts");
|
|
4073
4132
|
logInfo2(`Building compiled binary...`);
|
|
4074
4133
|
logInfo2(`Entry: ${S2.dim(entryPoint)}`);
|
|
4075
4134
|
logInfo2(`Output: ${S2.dim(outfile)}`);
|
|
@@ -4145,7 +4204,7 @@ function formatEntries(entries, opts = {}) {
|
|
|
4145
4204
|
lines.push(...renderEntryLines(e));
|
|
4146
4205
|
}
|
|
4147
4206
|
if (opts.nextCursor) {
|
|
4148
|
-
lines.push(
|
|
4207
|
+
lines.push(pc5.dim(`... more available. Use --cursor ${opts.nextCursor} to page back.`));
|
|
4149
4208
|
}
|
|
4150
4209
|
return lines.join("\n");
|
|
4151
4210
|
}
|
|
@@ -4166,9 +4225,9 @@ function renderEntryLines(e) {
|
|
|
4166
4225
|
return lines;
|
|
4167
4226
|
}
|
|
4168
4227
|
function formatLine(e) {
|
|
4169
|
-
const ts =
|
|
4228
|
+
const ts = pc5.dim(`[${formatTime(e.timestamp)}]`);
|
|
4170
4229
|
const lvl = colorLevel(e.level);
|
|
4171
|
-
const src =
|
|
4230
|
+
const src = pc5.cyan(`[${formatSource(e.source)}]`);
|
|
4172
4231
|
return `${ts} ${lvl} ${src} ${e.message}`;
|
|
4173
4232
|
}
|
|
4174
4233
|
function formatTime(iso) {
|
|
@@ -4187,13 +4246,13 @@ function formatSource(src) {
|
|
|
4187
4246
|
function colorLevel(level) {
|
|
4188
4247
|
switch (level) {
|
|
4189
4248
|
case "error":
|
|
4190
|
-
return
|
|
4249
|
+
return pc5.red("ERROR");
|
|
4191
4250
|
case "warn":
|
|
4192
|
-
return
|
|
4251
|
+
return pc5.yellow("WARN ");
|
|
4193
4252
|
case "info":
|
|
4194
|
-
return
|
|
4253
|
+
return pc5.green("INFO ");
|
|
4195
4254
|
case "debug":
|
|
4196
|
-
return
|
|
4255
|
+
return pc5.dim("DEBUG");
|
|
4197
4256
|
default:
|
|
4198
4257
|
return String(level).toUpperCase();
|
|
4199
4258
|
}
|
|
@@ -4201,15 +4260,15 @@ function colorLevel(level) {
|
|
|
4201
4260
|
function formatData(data) {
|
|
4202
4261
|
const oneLine = JSON.stringify(data);
|
|
4203
4262
|
if (oneLine.length <= 120) {
|
|
4204
|
-
return [` ${
|
|
4263
|
+
return [` ${pc5.dim("> data:")} ${oneLine}`];
|
|
4205
4264
|
}
|
|
4206
4265
|
const pretty = JSON.stringify(data, null, 2);
|
|
4207
|
-
return [` ${
|
|
4266
|
+
return [` ${pc5.dim("> data:")}`, ...pretty.split("\n").map((l) => ` ${l}`)];
|
|
4208
4267
|
}
|
|
4209
4268
|
function formatError(err) {
|
|
4210
|
-
const head = ` ${
|
|
4269
|
+
const head = ` ${pc5.dim("> error:")} ${pc5.red(err.name)}: ${err.message}`;
|
|
4211
4270
|
if (!err.stack) return [head];
|
|
4212
|
-
const stackLines = err.stack.split("\n").filter((l) => l.trim().length > 0).map((l) => ` ${
|
|
4271
|
+
const stackLines = err.stack.split("\n").filter((l) => l.trim().length > 0).map((l) => ` ${pc5.dim(l)}`);
|
|
4213
4272
|
return [head, ...stackLines];
|
|
4214
4273
|
}
|
|
4215
4274
|
|
|
@@ -4372,19 +4431,19 @@ function buildTailSelect(q) {
|
|
|
4372
4431
|
return { sql, params };
|
|
4373
4432
|
}
|
|
4374
4433
|
function sleepUnlessAborted(ms, signal) {
|
|
4375
|
-
return new Promise((
|
|
4434
|
+
return new Promise((resolve4) => {
|
|
4376
4435
|
if (signal?.aborted) {
|
|
4377
|
-
|
|
4436
|
+
resolve4();
|
|
4378
4437
|
return;
|
|
4379
4438
|
}
|
|
4380
4439
|
const timer = setTimeout(() => {
|
|
4381
4440
|
signal?.removeEventListener("abort", onAbort);
|
|
4382
|
-
|
|
4441
|
+
resolve4();
|
|
4383
4442
|
}, ms);
|
|
4384
4443
|
const onAbort = () => {
|
|
4385
4444
|
clearTimeout(timer);
|
|
4386
4445
|
signal?.removeEventListener("abort", onAbort);
|
|
4387
|
-
|
|
4446
|
+
resolve4();
|
|
4388
4447
|
};
|
|
4389
4448
|
signal?.addEventListener("abort", onAbort, { once: true });
|
|
4390
4449
|
});
|
|
@@ -4461,7 +4520,7 @@ async function remoteTail(opts) {
|
|
|
4461
4520
|
]
|
|
4462
4521
|
});
|
|
4463
4522
|
const filter = makeFilter(opts.query);
|
|
4464
|
-
return new Promise((
|
|
4523
|
+
return new Promise((resolve4, reject) => {
|
|
4465
4524
|
let settled = false;
|
|
4466
4525
|
const finish = (err) => {
|
|
4467
4526
|
if (settled) return;
|
|
@@ -4472,11 +4531,11 @@ async function remoteTail(opts) {
|
|
|
4472
4531
|
}
|
|
4473
4532
|
opts.signal?.removeEventListener("abort", onAbort);
|
|
4474
4533
|
if (err) reject(err);
|
|
4475
|
-
else
|
|
4534
|
+
else resolve4();
|
|
4476
4535
|
};
|
|
4477
4536
|
const onAbort = () => finish();
|
|
4478
4537
|
if (opts.signal?.aborted) {
|
|
4479
|
-
|
|
4538
|
+
resolve4();
|
|
4480
4539
|
return;
|
|
4481
4540
|
}
|
|
4482
4541
|
opts.signal?.addEventListener("abort", onAbort, { once: true });
|
|
@@ -4530,7 +4589,7 @@ function resolveMode(opts) {
|
|
|
4530
4589
|
if (opts.forceLocal && opts.forceRemote) {
|
|
4531
4590
|
throw new Error("Cannot use both --local and --remote");
|
|
4532
4591
|
}
|
|
4533
|
-
const startDir =
|
|
4592
|
+
const startDir = path15__default.resolve(opts.projectDir ?? process.cwd());
|
|
4534
4593
|
const localDb = findLogsDb(startDir);
|
|
4535
4594
|
if (opts.forceLocal) {
|
|
4536
4595
|
if (!localDb) {
|
|
@@ -4563,14 +4622,14 @@ function resolveMode(opts) {
|
|
|
4563
4622
|
function findLogsDb(startDir) {
|
|
4564
4623
|
let dir = startDir;
|
|
4565
4624
|
for (let i = 0; i < MAX_WALK_LEVELS; i++) {
|
|
4566
|
-
const candidate =
|
|
4625
|
+
const candidate = path15__default.join(dir, ".skaile", "logs.db");
|
|
4567
4626
|
if (existsSync(candidate)) {
|
|
4568
4627
|
try {
|
|
4569
4628
|
if (statSync(candidate).isFile()) return candidate;
|
|
4570
4629
|
} catch {
|
|
4571
4630
|
}
|
|
4572
4631
|
}
|
|
4573
|
-
const parent =
|
|
4632
|
+
const parent = path15__default.dirname(dir);
|
|
4574
4633
|
if (parent === dir) return void 0;
|
|
4575
4634
|
dir = parent;
|
|
4576
4635
|
}
|
|
@@ -4596,7 +4655,7 @@ function readApiToken() {
|
|
|
4596
4655
|
}
|
|
4597
4656
|
function readGlobalSettings() {
|
|
4598
4657
|
try {
|
|
4599
|
-
const settingsPath =
|
|
4658
|
+
const settingsPath = path15__default.join(homedir(), ".skaile", "settings.json");
|
|
4600
4659
|
if (!existsSync(settingsPath)) return {};
|
|
4601
4660
|
return JSON.parse(readFileSync(settingsPath, "utf8"));
|
|
4602
4661
|
} catch {
|
|
@@ -4744,7 +4803,7 @@ function parsePositiveInt(value, flag) {
|
|
|
4744
4803
|
function makeSessionCommand() {
|
|
4745
4804
|
const cmd = new Command("session").description("Manage sessions");
|
|
4746
4805
|
cmd.command("list").description("List all sessions for a project").option("--project-dir <path>", "Project directory", process.cwd()).action(async (opts) => {
|
|
4747
|
-
const projectDir =
|
|
4806
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
4748
4807
|
const sessions = await listSessions(projectDir);
|
|
4749
4808
|
const current = await loadSession(projectDir);
|
|
4750
4809
|
if (sessions.length === 0) {
|
|
@@ -4760,10 +4819,10 @@ function makeSessionCommand() {
|
|
|
4760
4819
|
console.log(` ${S.dim(" ")} ${S.rule(75)}`);
|
|
4761
4820
|
for (const s of sessions) {
|
|
4762
4821
|
const isCurrent = current?.runId === s.runId;
|
|
4763
|
-
const marker = isCurrent ?
|
|
4822
|
+
const marker = isCurrent ? pc5.cyan(" \u25B6 ") : " ";
|
|
4764
4823
|
const age = Math.round((Date.now() - new Date(s.updatedAt).getTime()) / 6e4);
|
|
4765
4824
|
const label = (s.label ?? "").padEnd(20);
|
|
4766
|
-
const statusColor = s.status === "complete" ?
|
|
4825
|
+
const statusColor = s.status === "complete" ? pc5.green : s.status === "failed" ? pc5.red : s.status === "running" ? pc5.cyan : pc5.dim;
|
|
4767
4826
|
const status4 = statusColor((s.status ?? "").padEnd(12));
|
|
4768
4827
|
console.log(
|
|
4769
4828
|
`${marker} ${S.dim(s.runId.slice(0, 8))} ${s.flowId.padEnd(20)} ${label} ${status4} ${S.dim(`${age}m ago`)}`
|
|
@@ -4772,7 +4831,7 @@ function makeSessionCommand() {
|
|
|
4772
4831
|
console.log();
|
|
4773
4832
|
});
|
|
4774
4833
|
cmd.command("show <run-id>").description("Show a specific session").option("--project-dir <path>", "Project directory", process.cwd()).action(async (runId, opts) => {
|
|
4775
|
-
const projectDir =
|
|
4834
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
4776
4835
|
const session = await loadSessionById(projectDir, runId);
|
|
4777
4836
|
if (!session) {
|
|
4778
4837
|
logErr(`Session not found: ${runId}`);
|
|
@@ -4781,12 +4840,12 @@ function makeSessionCommand() {
|
|
|
4781
4840
|
console.log(JSON.stringify(session, null, 2));
|
|
4782
4841
|
});
|
|
4783
4842
|
cmd.command("switch <run-id>").description("Switch the current session").option("--project-dir <path>", "Project directory", process.cwd()).action(async (runId, opts) => {
|
|
4784
|
-
const projectDir =
|
|
4843
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
4785
4844
|
await setCurrentSession(projectDir, runId);
|
|
4786
4845
|
logOk(`Switched to session: ${S.dim(runId.slice(0, 8))}`);
|
|
4787
4846
|
});
|
|
4788
4847
|
cmd.command("delete <run-id>").description("Delete a session").option("--project-dir <path>", "Project directory", process.cwd()).action(async (runId, opts) => {
|
|
4789
|
-
const projectDir =
|
|
4848
|
+
const projectDir = path15__default.resolve(opts.projectDir);
|
|
4790
4849
|
await deleteSession(projectDir, runId);
|
|
4791
4850
|
logOk(`Deleted session: ${S.dim(runId.slice(0, 8))}`);
|
|
4792
4851
|
});
|
|
@@ -4795,11 +4854,11 @@ function makeSessionCommand() {
|
|
|
4795
4854
|
return cmd;
|
|
4796
4855
|
}
|
|
4797
4856
|
var SYNC_ICONS = {
|
|
4798
|
-
synced:
|
|
4799
|
-
outdated:
|
|
4800
|
-
local:
|
|
4801
|
-
error:
|
|
4802
|
-
unknown:
|
|
4857
|
+
synced: pc5.green("synced"),
|
|
4858
|
+
outdated: pc5.yellow("outdated"),
|
|
4859
|
+
local: pc5.cyan("local"),
|
|
4860
|
+
error: pc5.red("error"),
|
|
4861
|
+
unknown: pc5.dim("\u2014")
|
|
4803
4862
|
};
|
|
4804
4863
|
function showOverview(am2) {
|
|
4805
4864
|
const result = am2.overview();
|
|
@@ -4812,25 +4871,25 @@ function showOverview(am2) {
|
|
|
4812
4871
|
if (result.repos.length > 0) {
|
|
4813
4872
|
console.log();
|
|
4814
4873
|
for (const r of result.repos) {
|
|
4815
|
-
const icon = r.error ?
|
|
4816
|
-
const status4 = r.error ?
|
|
4817
|
-
console.log(` ${icon} ${S.cmd(r.name)} ${
|
|
4874
|
+
const icon = r.error ? pc5.red("!") : r.upToDate ? pc5.green("\u2713") : pc5.yellow("\u2193");
|
|
4875
|
+
const status4 = r.error ? pc5.red(r.error) : r.upToDate ? pc5.green("up to date") : pc5.yellow(`${r.behind} behind`);
|
|
4876
|
+
console.log(` ${icon} ${S.cmd(r.name)} ${pc5.dim(`[${r.kind}]`)} ${status4}`);
|
|
4818
4877
|
}
|
|
4819
4878
|
}
|
|
4820
4879
|
const nameW = Math.max(4, ...[...result.byDomain.values()].flat().map((e) => e.name.length));
|
|
4821
4880
|
const kindW = 8;
|
|
4822
4881
|
for (const [domain, entries] of result.byDomain) {
|
|
4823
4882
|
console.log();
|
|
4824
|
-
console.log(` ${S.heading(domain)} ${
|
|
4883
|
+
console.log(` ${S.heading(domain)} ${pc5.dim(`(${entries.length})`)}`);
|
|
4825
4884
|
console.log(` ${S.rule(nameW + kindW + 20)}`);
|
|
4826
4885
|
for (const e of entries) {
|
|
4827
4886
|
const sync = SYNC_ICONS[e.syncStatus] ?? SYNC_ICONS.unknown;
|
|
4828
|
-
const ver = e.version ?
|
|
4887
|
+
const ver = e.version ? pc5.dim(` v${e.version}`) : "";
|
|
4829
4888
|
console.log(` ${kindColorPad(e.kind, kindW)} ${S.cmd(e.name.padEnd(nameW))} ${sync}${ver}`);
|
|
4830
4889
|
}
|
|
4831
4890
|
}
|
|
4832
4891
|
console.log();
|
|
4833
|
-
console.log(` ${
|
|
4892
|
+
console.log(` ${pc5.dim(`${result.total} assets deployed`)}`);
|
|
4834
4893
|
console.log();
|
|
4835
4894
|
}
|
|
4836
4895
|
function showAsset(am2, kind, name) {
|
|
@@ -4849,7 +4908,7 @@ function showAsset(am2, kind, name) {
|
|
|
4849
4908
|
function makeShowCommand() {
|
|
4850
4909
|
return new Command("show").description("Show deployed assets overview, or print asset content").argument("[kind]", "Asset kind (skill, agent, prompt, flow, bundle, contract)").argument("[name]", "Asset name").option("--project-dir <path>", "Project directory", process.cwd()).option("--target <agent>", "Agent framework", "claude-code").action((kind, name, opts) => {
|
|
4851
4910
|
const am2 = new AssetManager({
|
|
4852
|
-
projectDir:
|
|
4911
|
+
projectDir: path15__default.resolve(opts.projectDir),
|
|
4853
4912
|
driverTarget: opts.target
|
|
4854
4913
|
});
|
|
4855
4914
|
if (!kind) {
|
|
@@ -4867,16 +4926,139 @@ function makeShowCommand() {
|
|
|
4867
4926
|
}
|
|
4868
4927
|
});
|
|
4869
4928
|
}
|
|
4870
|
-
|
|
4929
|
+
|
|
4930
|
+
// cli/src/commands/source-manifest.ts
|
|
4931
|
+
var NOT_WIRED_MSG = "`skaile source manifest *` is scaffolded but not yet wired. Server-side endpoint (`POST /sources/<id>/manifest`) lands in a follow-up PR.";
|
|
4932
|
+
function notWired2(verb) {
|
|
4933
|
+
logErr(`${NOT_WIRED_MSG} (verb: source manifest ${verb})`);
|
|
4934
|
+
process.exit(2);
|
|
4935
|
+
}
|
|
4936
|
+
function addSourceManifestCommands(src) {
|
|
4937
|
+
const manifest = src.command("manifest").description("Sidecar manifest lifecycle for a source");
|
|
4938
|
+
manifest.command("init <src>").description("Start a sidecar manifest for a source").action(() => notWired2("init"));
|
|
4939
|
+
manifest.command("edit <src>").description("Open the sidecar manifest in $EDITOR").action(() => notWired2("edit"));
|
|
4940
|
+
manifest.command("rebuild <src>").description("Re-derive the manifest after an upstream change").option("--merge", "Merge with existing entries instead of overwriting", false).action(() => notWired2("rebuild"));
|
|
4941
|
+
manifest.command("verify <src>").description("Verify hashes, deps, and schema").option("--strict", "Fail on advisory checks", false).action(() => notWired2("verify"));
|
|
4942
|
+
manifest.command("publish <src>").description("Register the manifest in the store (provenance=curator)").action(() => notWired2("publish"));
|
|
4943
|
+
manifest.command("propose <src>").description("Open a PR to upstream with the sidecar manifest").action(() => notWired2("propose"));
|
|
4944
|
+
manifest.command("yank <src>").description("Retract a published manifest from the remote store").option("--version <v>", "Version to yank").action(() => notWired2("yank"));
|
|
4945
|
+
}
|
|
4946
|
+
|
|
4947
|
+
// cli/src/commands/source.ts
|
|
4948
|
+
function sourcesDir() {
|
|
4949
|
+
return path15.join(skaileHomeDir(), "sources");
|
|
4950
|
+
}
|
|
4951
|
+
function deriveSlug(url) {
|
|
4952
|
+
return url.replace(/\.git$/, "").split(/[/:]/).pop() ?? "source";
|
|
4953
|
+
}
|
|
4954
|
+
function isSourceRow(lib) {
|
|
4955
|
+
return lib.path.startsWith(sourcesDir());
|
|
4956
|
+
}
|
|
4871
4957
|
function makeSourceCommand() {
|
|
4872
|
-
const cmd = new Command("source").description(
|
|
4873
|
-
|
|
4958
|
+
const cmd = new Command("source").description(
|
|
4959
|
+
"Manage github sources (third-party repos of AI assets)"
|
|
4960
|
+
);
|
|
4961
|
+
cmd.command("add <git-url>").description("Clone a github repo into ~/.skaile/sources/<slug> and register it").option("--name <slug>", "Override the derived slug").action(async (url, opts) => {
|
|
4962
|
+
const slug = opts.name ?? deriveSlug(url);
|
|
4963
|
+
const dest = path15.join(sourcesDir(), slug);
|
|
4964
|
+
const clone = spawnSync("git", ["clone", url, dest], { stdio: "inherit" });
|
|
4965
|
+
if (clone.status !== 0) {
|
|
4966
|
+
logErr(`git clone failed for ${url}`);
|
|
4967
|
+
process.exit(1);
|
|
4968
|
+
}
|
|
4969
|
+
const { manager, close } = await openLibraryManager();
|
|
4970
|
+
try {
|
|
4971
|
+
await manager.addLibrary({
|
|
4972
|
+
name: slug,
|
|
4973
|
+
path: dest,
|
|
4974
|
+
backend: "git",
|
|
4975
|
+
backendConfig: { url, branch: "main", authHint: "ssh" },
|
|
4976
|
+
ownership: "reader"
|
|
4977
|
+
});
|
|
4978
|
+
logOk(`Source "${slug}" added at ${dest}.`);
|
|
4979
|
+
} finally {
|
|
4980
|
+
close();
|
|
4981
|
+
}
|
|
4982
|
+
});
|
|
4983
|
+
cmd.command("list").description("List registered sources").option("--json", "Output as JSON").action(async (opts) => {
|
|
4984
|
+
const { manager, close } = await openLibraryManager();
|
|
4985
|
+
try {
|
|
4986
|
+
const all = await manager.listLibraries();
|
|
4987
|
+
const sources = all.filter(isSourceRow);
|
|
4988
|
+
if (opts.json) return console.log(JSON.stringify(sources, null, 2));
|
|
4989
|
+
if (sources.length === 0) {
|
|
4990
|
+
logInfo("No sources registered. Run `skaile source add <git-url>` to start.");
|
|
4991
|
+
return;
|
|
4992
|
+
}
|
|
4993
|
+
console.log();
|
|
4994
|
+
console.log(S.heading(" Sources"));
|
|
4995
|
+
for (const s of sources) {
|
|
4996
|
+
console.log(` ${S.cmd(s.name.padEnd(20))} ${S.dim(s.path)}`);
|
|
4997
|
+
}
|
|
4998
|
+
console.log(`
|
|
4999
|
+
${S.dim(`${sources.length} source(s)`)}
|
|
5000
|
+
`);
|
|
5001
|
+
} finally {
|
|
5002
|
+
close();
|
|
5003
|
+
}
|
|
5004
|
+
});
|
|
5005
|
+
cmd.command("show <name>").description("Show details for a source").action(async (name) => {
|
|
5006
|
+
const { manager, close } = await openLibraryManager();
|
|
5007
|
+
try {
|
|
5008
|
+
const s = await manager.requireLibrary(name);
|
|
5009
|
+
if (!isSourceRow(s)) {
|
|
5010
|
+
logErr(`"${name}" is a library, not a source \u2014 use \`skaile library show ${name}\`.`);
|
|
5011
|
+
process.exit(1);
|
|
5012
|
+
}
|
|
5013
|
+
console.log(JSON.stringify(s, null, 2));
|
|
5014
|
+
} finally {
|
|
5015
|
+
close();
|
|
5016
|
+
}
|
|
5017
|
+
});
|
|
5018
|
+
cmd.command("remove <name>").description("Unregister a source").option("--purge", "Also delete the clone directory from disk", false).action(async (name, opts) => {
|
|
5019
|
+
const { manager, close } = await openLibraryManager();
|
|
5020
|
+
try {
|
|
5021
|
+
const s = await manager.requireLibrary(name);
|
|
5022
|
+
if (!isSourceRow(s)) {
|
|
5023
|
+
logErr(`"${name}" is a library, not a source \u2014 use \`skaile library remove ${name}\`.`);
|
|
5024
|
+
process.exit(1);
|
|
5025
|
+
}
|
|
5026
|
+
await manager.removeLibrary(name, { purge: opts.purge });
|
|
5027
|
+
logOk(`Removed source "${name}".`);
|
|
5028
|
+
} finally {
|
|
5029
|
+
close();
|
|
5030
|
+
}
|
|
5031
|
+
});
|
|
5032
|
+
cmd.command("sync [name]").description("Re-fetch upstream changes for one or all sources").action(async (name) => {
|
|
5033
|
+
const { manager, close } = await openLibraryManager();
|
|
5034
|
+
try {
|
|
5035
|
+
const all = await manager.listLibraries();
|
|
5036
|
+
const targets = name ? all.filter((l) => l.name === name && isSourceRow(l)) : all.filter(isSourceRow);
|
|
5037
|
+
if (targets.length === 0) {
|
|
5038
|
+
logInfo(name ? `No source named "${name}".` : "No sources registered.");
|
|
5039
|
+
return;
|
|
5040
|
+
}
|
|
5041
|
+
for (const s of targets) {
|
|
5042
|
+
const res = await manager.driverFor(s.backend).pull(s);
|
|
5043
|
+
logOk(`${s.name}: pulled ${res.applied} change(s).`);
|
|
5044
|
+
}
|
|
5045
|
+
} finally {
|
|
5046
|
+
close();
|
|
5047
|
+
}
|
|
5048
|
+
});
|
|
5049
|
+
cmd.command("patch <ref>").description("Extract an asset for editing against the source").action((_ref) => {
|
|
5050
|
+
logErr(
|
|
5051
|
+
"`skaile source patch` is scaffolded but not yet wired. The patch flow lands in a follow-up PR."
|
|
5052
|
+
);
|
|
4874
5053
|
process.exit(2);
|
|
4875
5054
|
});
|
|
4876
|
-
cmd.command("
|
|
4877
|
-
logErr(
|
|
5055
|
+
cmd.command("propose <ref>").description("Open a PR with the patch against the source upstream").action((_ref) => {
|
|
5056
|
+
logErr(
|
|
5057
|
+
"`skaile source propose` is scaffolded but not yet wired. The propose flow lands in a follow-up PR."
|
|
5058
|
+
);
|
|
4878
5059
|
process.exit(2);
|
|
4879
5060
|
});
|
|
5061
|
+
addSourceManifestCommands(cmd);
|
|
4880
5062
|
return cmd;
|
|
4881
5063
|
}
|
|
4882
5064
|
function makeStoreCommand() {
|
|
@@ -4886,10 +5068,10 @@ function makeStoreCommand() {
|
|
|
4886
5068
|
if (opts.email) {
|
|
4887
5069
|
const readline2 = await import('readline');
|
|
4888
5070
|
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
4889
|
-
const password = await new Promise((
|
|
5071
|
+
const password = await new Promise((resolve4) => {
|
|
4890
5072
|
rl.question("Password: ", (answer) => {
|
|
4891
5073
|
rl.close();
|
|
4892
|
-
|
|
5074
|
+
resolve4(answer);
|
|
4893
5075
|
});
|
|
4894
5076
|
});
|
|
4895
5077
|
try {
|
|
@@ -4975,28 +5157,35 @@ function makeStoreCommand() {
|
|
|
4975
5157
|
process.exit(1);
|
|
4976
5158
|
}
|
|
4977
5159
|
});
|
|
4978
|
-
store.command("
|
|
4979
|
-
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
|
|
4984
|
-
|
|
4985
|
-
|
|
4986
|
-
|
|
4987
|
-
|
|
4988
|
-
|
|
4989
|
-
|
|
4990
|
-
|
|
4991
|
-
|
|
4992
|
-
|
|
4993
|
-
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5160
|
+
store.command("mode <auto|local|remote>").description("Set the catalog resolution mode (not yet wired)").action((_mode) => {
|
|
5161
|
+
logErr(
|
|
5162
|
+
"`skaile store mode` is scaffolded but not yet wired. Store config lives at ~/.skaile/store/config.yaml."
|
|
5163
|
+
);
|
|
5164
|
+
process.exit(2);
|
|
5165
|
+
});
|
|
5166
|
+
store.command("search <query>").description("Search the aggregated catalog (not yet wired)").action((_query) => {
|
|
5167
|
+
logErr("`skaile store search` is scaffolded but not yet wired.");
|
|
5168
|
+
process.exit(2);
|
|
5169
|
+
});
|
|
5170
|
+
store.command("show <ref>").description("Show details + provenance for a manifest (not yet wired)").action((_ref) => {
|
|
5171
|
+
logErr("`skaile store show` is scaffolded but not yet wired.");
|
|
5172
|
+
process.exit(2);
|
|
5173
|
+
});
|
|
5174
|
+
store.command("favourite <ref>").description("Pin a ref to your favourites (not yet wired)").action((_ref) => {
|
|
5175
|
+
logErr("`skaile store favourite` is scaffolded but not yet wired.");
|
|
5176
|
+
process.exit(2);
|
|
5177
|
+
});
|
|
5178
|
+
store.command("unfavourite <ref>").description("Remove a ref from your favourites (not yet wired)").action((_ref) => {
|
|
5179
|
+
logErr("`skaile store unfavourite` is scaffolded but not yet wired.");
|
|
5180
|
+
process.exit(2);
|
|
5181
|
+
});
|
|
5182
|
+
store.command("sync").description("Refresh the local cache from the remote store (not yet wired)").action(() => {
|
|
5183
|
+
logErr("`skaile store sync` is scaffolded but not yet wired.");
|
|
5184
|
+
process.exit(2);
|
|
5185
|
+
});
|
|
5186
|
+
store.command("push").description("Push the local store to a git remote (not yet wired)").option("--remote <name>", "Git remote name", "origin").action(() => {
|
|
5187
|
+
logErr("`skaile store push` is scaffolded but not yet wired.");
|
|
5188
|
+
process.exit(2);
|
|
5000
5189
|
});
|
|
5001
5190
|
store.command("yank <version>").description("Yank (soft-delete) a published version").option("--asset <ref>", "Asset reference (@publisher/slug)").action(async (version, opts) => {
|
|
5002
5191
|
const config = getStoreConfig();
|
|
@@ -5037,7 +5226,7 @@ function printTree(node, prefix = "", isLast = true) {
|
|
|
5037
5226
|
}
|
|
5038
5227
|
function makeTreeCommand() {
|
|
5039
5228
|
return new Command("tree").description("Show full dependency tree").option("--project-dir <path>", "Project directory", process.cwd()).action((opts) => {
|
|
5040
|
-
const am2 = new AssetManager({ projectDir:
|
|
5229
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
5041
5230
|
const root = am2.tree();
|
|
5042
5231
|
if (root.children.length === 0) {
|
|
5043
5232
|
logInfo("No dependencies in lock file. Run `skaile install` first.");
|
|
@@ -5055,7 +5244,7 @@ function makeUpdateCommand() {
|
|
|
5055
5244
|
try {
|
|
5056
5245
|
const catalogSpinner = p5.spinner();
|
|
5057
5246
|
catalogSpinner.start("Refreshing catalog cache");
|
|
5058
|
-
const remote = await openCatalogSource({ projectDir:
|
|
5247
|
+
const remote = await openCatalogSource({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
5059
5248
|
const result2 = await remote.refresh();
|
|
5060
5249
|
catalogSpinner.stop(
|
|
5061
5250
|
`Catalog refreshed: ${S.heading(String(result2.assetsCached))} assets cached`
|
|
@@ -5067,7 +5256,7 @@ function makeUpdateCommand() {
|
|
|
5067
5256
|
}
|
|
5068
5257
|
}
|
|
5069
5258
|
if (opts.catalogOnly) return;
|
|
5070
|
-
const am2 = new AssetManager({ projectDir:
|
|
5259
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
5071
5260
|
const spinner5 = p5.spinner();
|
|
5072
5261
|
spinner5.start("Re-deploying assets");
|
|
5073
5262
|
const result = await am2.install();
|
|
@@ -5113,7 +5302,7 @@ function findManifestCandidates(rootPath) {
|
|
|
5113
5302
|
return;
|
|
5114
5303
|
}
|
|
5115
5304
|
for (const entry of entries) {
|
|
5116
|
-
const fullPath =
|
|
5305
|
+
const fullPath = path15__default.join(dir, entry.name);
|
|
5117
5306
|
if (entry.isDirectory()) {
|
|
5118
5307
|
if (!SKIP_DIRS.has(entry.name)) walk(fullPath);
|
|
5119
5308
|
} else if (entry.isFile()) {
|
|
@@ -5135,7 +5324,7 @@ function parseMdFrontmatter(content) {
|
|
|
5135
5324
|
return parse(fmMatch[1]);
|
|
5136
5325
|
}
|
|
5137
5326
|
function validateManifests(rootPath) {
|
|
5138
|
-
const absRoot =
|
|
5327
|
+
const absRoot = path15__default.resolve(rootPath);
|
|
5139
5328
|
if (!fs10__default.existsSync(absRoot)) {
|
|
5140
5329
|
logErr(`Path does not exist: ${absRoot}`);
|
|
5141
5330
|
return { total: 0, errors: 1, warnings: 0 };
|
|
@@ -5156,14 +5345,14 @@ function validateManifests(rootPath) {
|
|
|
5156
5345
|
try {
|
|
5157
5346
|
parsed = c.filePath.endsWith(".json") ? JSON.parse(content) : parse(content);
|
|
5158
5347
|
} catch (e) {
|
|
5159
|
-
const relPath2 =
|
|
5348
|
+
const relPath2 = path15__default.relative(absRoot, c.filePath);
|
|
5160
5349
|
logErr(`${relPath2} \u2014 parse error: ${e instanceof Error ? e.message : String(e)}`);
|
|
5161
5350
|
errors++;
|
|
5162
5351
|
continue;
|
|
5163
5352
|
}
|
|
5164
5353
|
}
|
|
5165
5354
|
const provider = registry.getProvider(c.kind);
|
|
5166
|
-
const relPath =
|
|
5355
|
+
const relPath = path15__default.relative(absRoot, c.filePath);
|
|
5167
5356
|
if (!provider) {
|
|
5168
5357
|
logWarn(`${relPath} [${c.kind}] \u2014 kind "${c.kind}" is not registered, skipping validation`);
|
|
5169
5358
|
warnings++;
|
|
@@ -5185,20 +5374,20 @@ function collectAssetVersions() {
|
|
|
5185
5374
|
if (!fs10__default.existsSync(AI_RESOURCES)) return assets;
|
|
5186
5375
|
const domains = fs10__default.readdirSync(AI_RESOURCES, { withFileTypes: true }).filter((d) => d.isDirectory() && !EXCLUDED_DOMAINS.has(d.name));
|
|
5187
5376
|
for (const d of domains) {
|
|
5188
|
-
const domainDir =
|
|
5189
|
-
const skillsDir =
|
|
5377
|
+
const domainDir = path15__default.join(AI_RESOURCES, d.name);
|
|
5378
|
+
const skillsDir = path15__default.join(domainDir, "skills");
|
|
5190
5379
|
if (fs10__default.existsSync(skillsDir)) {
|
|
5191
5380
|
collectSkillVersions(skillsDir, d.name, assets);
|
|
5192
5381
|
}
|
|
5193
|
-
const agentsDir =
|
|
5382
|
+
const agentsDir = path15__default.join(domainDir, "agents");
|
|
5194
5383
|
if (fs10__default.existsSync(agentsDir)) {
|
|
5195
5384
|
collectAgentVersions(agentsDir, d.name, assets);
|
|
5196
5385
|
}
|
|
5197
|
-
const flowsDir =
|
|
5386
|
+
const flowsDir = path15__default.join(domainDir, "flows");
|
|
5198
5387
|
if (fs10__default.existsSync(flowsDir)) {
|
|
5199
5388
|
for (const f of fs10__default.readdirSync(flowsDir)) {
|
|
5200
5389
|
if (!f.endsWith(".flow.yaml")) continue;
|
|
5201
|
-
const fp =
|
|
5390
|
+
const fp = path15__default.join(flowsDir, f);
|
|
5202
5391
|
const content = fs10__default.readFileSync(fp, "utf-8");
|
|
5203
5392
|
const parsed = parse(content);
|
|
5204
5393
|
assets.push({
|
|
@@ -5210,11 +5399,11 @@ function collectAssetVersions() {
|
|
|
5210
5399
|
});
|
|
5211
5400
|
}
|
|
5212
5401
|
}
|
|
5213
|
-
const promptsDir =
|
|
5402
|
+
const promptsDir = path15__default.join(domainDir, "prompts");
|
|
5214
5403
|
if (fs10__default.existsSync(promptsDir)) {
|
|
5215
5404
|
for (const f of fs10__default.readdirSync(promptsDir)) {
|
|
5216
5405
|
if (!f.endsWith(".prompt.md")) continue;
|
|
5217
|
-
const fp =
|
|
5406
|
+
const fp = path15__default.join(promptsDir, f);
|
|
5218
5407
|
const content = fs10__default.readFileSync(fp, "utf-8");
|
|
5219
5408
|
const fm = parseSkillFrontmatter(content);
|
|
5220
5409
|
assets.push({
|
|
@@ -5232,8 +5421,8 @@ function collectAssetVersions() {
|
|
|
5232
5421
|
function collectSkillVersions(dir, domain, out) {
|
|
5233
5422
|
for (const entry of fs10__default.readdirSync(dir, { withFileTypes: true })) {
|
|
5234
5423
|
if (!entry.isDirectory()) continue;
|
|
5235
|
-
const full =
|
|
5236
|
-
const skillMd =
|
|
5424
|
+
const full = path15__default.join(dir, entry.name);
|
|
5425
|
+
const skillMd = path15__default.join(full, "SKILL.md");
|
|
5237
5426
|
if (fs10__default.existsSync(skillMd)) {
|
|
5238
5427
|
const content = fs10__default.readFileSync(skillMd, "utf-8");
|
|
5239
5428
|
const fm = parseSkillFrontmatter(content);
|
|
@@ -5252,7 +5441,7 @@ function collectSkillVersions(dir, domain, out) {
|
|
|
5252
5441
|
function collectAgentVersions(dir, domain, out) {
|
|
5253
5442
|
for (const entry of fs10__default.readdirSync(dir, { withFileTypes: true })) {
|
|
5254
5443
|
if (!entry.isDirectory()) continue;
|
|
5255
|
-
const agentYaml =
|
|
5444
|
+
const agentYaml = path15__default.join(dir, entry.name, "agent.yaml");
|
|
5256
5445
|
if (!fs10__default.existsSync(agentYaml)) continue;
|
|
5257
5446
|
const content = fs10__default.readFileSync(agentYaml, "utf-8");
|
|
5258
5447
|
const parsed = parse(content);
|
|
@@ -5283,7 +5472,7 @@ function makeValidateCommand() {
|
|
|
5283
5472
|
"Validate asset manifests, versions, and changelogs"
|
|
5284
5473
|
);
|
|
5285
5474
|
cmd.argument("[path]", "Path to asset repo or directory to validate", ".").action((targetPath) => {
|
|
5286
|
-
const absPath =
|
|
5475
|
+
const absPath = path15__default.resolve(targetPath);
|
|
5287
5476
|
logInfo(`Validating manifests in ${absPath}`);
|
|
5288
5477
|
const { total, errors } = validateManifests(absPath);
|
|
5289
5478
|
console.log();
|
|
@@ -5304,7 +5493,7 @@ function makeValidateCommand() {
|
|
|
5304
5493
|
encoding: "utf-8"
|
|
5305
5494
|
});
|
|
5306
5495
|
const changedFiles = new Set(
|
|
5307
|
-
diffOutput.trim().split("\n").filter(Boolean).map((f) =>
|
|
5496
|
+
diffOutput.trim().split("\n").filter(Boolean).map((f) => path15__default.resolve(AI_RESOURCES, f))
|
|
5308
5497
|
);
|
|
5309
5498
|
assets = assets.filter((a) => changedFiles.has(a.filePath));
|
|
5310
5499
|
} catch {
|
|
@@ -5326,7 +5515,7 @@ function makeValidateCommand() {
|
|
|
5326
5515
|
if (opts.changedOnly) {
|
|
5327
5516
|
for (const a of assets) {
|
|
5328
5517
|
if (!a.version) continue;
|
|
5329
|
-
const relPath =
|
|
5518
|
+
const relPath = path15__default.relative(AI_RESOURCES, a.filePath);
|
|
5330
5519
|
try {
|
|
5331
5520
|
const headContent = execSync(`git show HEAD:${relPath}`, {
|
|
5332
5521
|
cwd: AI_RESOURCES,
|
|
@@ -5406,7 +5595,7 @@ function makeValidateCommand() {
|
|
|
5406
5595
|
let errors = 0;
|
|
5407
5596
|
for (const domain of modifiedDomains) {
|
|
5408
5597
|
const changelogModified = modifiedFiles.some(
|
|
5409
|
-
(f) => f === `${domain}/CHANGELOG.md` || f ===
|
|
5598
|
+
(f) => f === `${domain}/CHANGELOG.md` || f === path15__default.join(domain, "CHANGELOG.md")
|
|
5410
5599
|
);
|
|
5411
5600
|
if (changelogModified) {
|
|
5412
5601
|
logOk(`${domain}/CHANGELOG.md updated`);
|
|
@@ -5488,7 +5677,7 @@ function makeVerifyCommand() {
|
|
|
5488
5677
|
}
|
|
5489
5678
|
function makeWhyCommand() {
|
|
5490
5679
|
return new Command("why").description("Show why an asset is installed (dependency chain)").argument("<ref>", "Asset reference (kind:name)").option("--project-dir <path>", "Project directory", process.cwd()).action((ref, opts) => {
|
|
5491
|
-
const am2 = new AssetManager({ projectDir:
|
|
5680
|
+
const am2 = new AssetManager({ projectDir: path15__default.resolve(opts.projectDir) });
|
|
5492
5681
|
const chain = am2.why(ref);
|
|
5493
5682
|
if (chain.length === 0) {
|
|
5494
5683
|
logErr(`${ref} not found in lock file. Run \`skaile install\` first.`);
|
|
@@ -5509,7 +5698,7 @@ function makeWhyCommand() {
|
|
|
5509
5698
|
var _defaultFormatHelp = Help.prototype.formatHelp;
|
|
5510
5699
|
var pkg = JSON.parse(
|
|
5511
5700
|
readFileSync(
|
|
5512
|
-
|
|
5701
|
+
path15__default.resolve(path15__default.dirname(fileURLToPath(import.meta.url)), "../../package.json"),
|
|
5513
5702
|
"utf-8"
|
|
5514
5703
|
)
|
|
5515
5704
|
);
|
|
@@ -5573,18 +5762,36 @@ Validation:
|
|
|
5573
5762
|
validate versions Check asset version fields and flow pins
|
|
5574
5763
|
validate changelog Check changelogs for modified domains
|
|
5575
5764
|
|
|
5576
|
-
|
|
5577
|
-
source add <
|
|
5578
|
-
source list
|
|
5579
|
-
source sync [
|
|
5580
|
-
source
|
|
5581
|
-
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
library
|
|
5765
|
+
Sources (github repos of AI assets):
|
|
5766
|
+
source add <git-url> Clone + register a github source
|
|
5767
|
+
source list / show / remove Source registry CRUD
|
|
5768
|
+
source sync [name] Re-fetch upstream changes
|
|
5769
|
+
source patch / propose Contribute back via PR
|
|
5770
|
+
source manifest * Sidecar manifest lifecycle
|
|
5771
|
+
|
|
5772
|
+
Libraries (your authoring workspaces):
|
|
5773
|
+
library init <name> Create a local library (use --git URL to attach a remote)
|
|
5774
|
+
library link <name> <url> Attach a github remote to a local library
|
|
5775
|
+
library create <kind> <name> Scaffold a new asset in a library
|
|
5776
|
+
library list / show / status Library inspection
|
|
5777
|
+
library default <name> Set the default library
|
|
5778
|
+
library rename / set / remove
|
|
5779
|
+
library commit / pull / push / propose / git Git ops (git backend only)
|
|
5780
|
+
library sync <name> Pull then push (as backend permits)
|
|
5781
|
+
library publish / register / yank <ref> Store publish flow (stubs)
|
|
5782
|
+
|
|
5783
|
+
Store (manifest catalog + auth):
|
|
5784
|
+
store login / logout / whoami / yank Auth + admin
|
|
5785
|
+
store mode <auto|local|remote> Resolution mode (stub)
|
|
5786
|
+
store search / show / favourite / unfavourite Catalog browsing (stub)
|
|
5787
|
+
store sync / push Local-store sync (stub)
|
|
5788
|
+
|
|
5789
|
+
Catalog (debug):
|
|
5790
|
+
catalog test / show Probe a catalog endpoint, print resolved config
|
|
5791
|
+
|
|
5792
|
+
Presets:
|
|
5585
5793
|
preset init [name] Scaffold a .preset.yaml
|
|
5586
5794
|
preset validate <path> Validate a preset file
|
|
5587
|
-
lib-status Show Library health summary
|
|
5588
5795
|
|
|
5589
5796
|
Advanced:
|
|
5590
5797
|
flow list|show Flow browsing
|
|
@@ -5613,24 +5820,24 @@ program.command("init [project-dir]").description("Initialize a project director
|
|
|
5613
5820
|
process.exitCode = 1;
|
|
5614
5821
|
return;
|
|
5615
5822
|
}
|
|
5616
|
-
const resolved =
|
|
5617
|
-
const projectName =
|
|
5823
|
+
const resolved = path15__default.resolve(projectDir ?? ".");
|
|
5824
|
+
const projectName = path15__default.basename(resolved);
|
|
5618
5825
|
const created = [];
|
|
5619
5826
|
if (!existsSync11(resolved)) {
|
|
5620
5827
|
mkdirSync3(resolved, { recursive: true });
|
|
5621
5828
|
created.push(".");
|
|
5622
5829
|
}
|
|
5623
|
-
const skaileDir =
|
|
5830
|
+
const skaileDir = path15__default.join(resolved, ".skaile");
|
|
5624
5831
|
if (!existsSync11(skaileDir)) {
|
|
5625
|
-
mkdirSync3(
|
|
5832
|
+
mkdirSync3(path15__default.join(skaileDir, "sessions"), { recursive: true });
|
|
5626
5833
|
created.push(".skaile/");
|
|
5627
5834
|
}
|
|
5628
|
-
const settingsPath =
|
|
5835
|
+
const settingsPath = path15__default.join(skaileDir, "settings.json");
|
|
5629
5836
|
if (!existsSync11(settingsPath)) {
|
|
5630
5837
|
writeFileSync2(settingsPath, "{}\n");
|
|
5631
5838
|
created.push(".skaile/settings.json");
|
|
5632
5839
|
}
|
|
5633
|
-
const wsConfigPath =
|
|
5840
|
+
const wsConfigPath = path15__default.join(resolved, "skaile.yaml");
|
|
5634
5841
|
if (!existsSync11(wsConfigPath)) {
|
|
5635
5842
|
writeFileSync2(
|
|
5636
5843
|
wsConfigPath,
|
|
@@ -5652,14 +5859,14 @@ program.command("init [project-dir]").description("Initialize a project director
|
|
|
5652
5859
|
created.push("skaile.yaml");
|
|
5653
5860
|
}
|
|
5654
5861
|
for (const dir of new Set(Object.values(DRIVER_TARGETS[backend].local))) {
|
|
5655
|
-
const full =
|
|
5862
|
+
const full = path15__default.join(resolved, dir);
|
|
5656
5863
|
if (!existsSync11(full)) {
|
|
5657
5864
|
mkdirSync3(full, { recursive: true });
|
|
5658
5865
|
created.push(`${dir}/`);
|
|
5659
5866
|
}
|
|
5660
5867
|
}
|
|
5661
5868
|
if (opts.git) {
|
|
5662
|
-
const gitignorePath =
|
|
5869
|
+
const gitignorePath = path15__default.join(resolved, ".gitignore");
|
|
5663
5870
|
const entries = ["node_modules/", ".skaile/sessions/", "*.log", ".env", ".env.local"];
|
|
5664
5871
|
const existing = existsSync11(gitignorePath) ? readFileSync5(gitignorePath, "utf-8") : "";
|
|
5665
5872
|
const have = new Set(existing.split("\n").map((l) => l.trim()));
|
|
@@ -5670,7 +5877,7 @@ program.command("init [project-dir]").description("Initialize a project director
|
|
|
5670
5877
|
`);
|
|
5671
5878
|
if (!existing) created.push(".gitignore");
|
|
5672
5879
|
}
|
|
5673
|
-
if (!existsSync11(
|
|
5880
|
+
if (!existsSync11(path15__default.join(resolved, ".git"))) {
|
|
5674
5881
|
try {
|
|
5675
5882
|
execSync3("git init", { cwd: resolved, stdio: "pipe" });
|
|
5676
5883
|
created.push(".git/");
|
|
@@ -5700,7 +5907,7 @@ program.command("init [project-dir]").description("Initialize a project director
|
|
|
5700
5907
|
logOk(`Initialized project at ${S.heading(resolved)}`);
|
|
5701
5908
|
console.log();
|
|
5702
5909
|
console.log(S.heading(" Next steps:"));
|
|
5703
|
-
const isCurrentDir = resolved ===
|
|
5910
|
+
const isCurrentDir = resolved === path15__default.resolve(".");
|
|
5704
5911
|
let step = 1;
|
|
5705
5912
|
if (!isCurrentDir) {
|
|
5706
5913
|
console.log(` ${S.dim(`${step}.`)} ${S.cmd(`cd ${projectDir}`)}`);
|
|
@@ -5761,7 +5968,6 @@ program.addCommand(makeSourceCommand());
|
|
|
5761
5968
|
program.addCommand(makeLibraryCommand());
|
|
5762
5969
|
program.addCommand(makeAssetCommand());
|
|
5763
5970
|
program.addCommand(makePresetCommand());
|
|
5764
|
-
program.addCommand(makeLibraryStatusCommand());
|
|
5765
5971
|
program.command("path").description("Show resolved paths").action(() => {
|
|
5766
5972
|
console.log();
|
|
5767
5973
|
console.log(` ${S.dim("ai-assets")} ${S.heading(AI_RESOURCES)}`);
|