@elmundi/ship-cli 0.14.1 → 0.15.3

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.
Files changed (39) hide show
  1. package/README.md +17 -16
  2. package/bin/shipctl.mjs +4 -80
  3. package/lib/commands/feedback.mjs +1 -1
  4. package/lib/commands/help.mjs +47 -131
  5. package/lib/commands/init.mjs +17 -250
  6. package/lib/commands/knowledge.mjs +25 -328
  7. package/lib/commands/preflight.mjs +213 -0
  8. package/lib/commands/run.mjs +277 -119
  9. package/lib/commands/trigger.mjs +95 -10
  10. package/lib/config/schema.mjs +68 -11
  11. package/lib/http.mjs +0 -2
  12. package/lib/runtime/routines.mjs +34 -0
  13. package/lib/templates.mjs +2 -2
  14. package/lib/verify/checks/agents-on-disk.mjs +5 -28
  15. package/lib/verify/registry.mjs +7 -8
  16. package/package.json +1 -1
  17. package/lib/artifacts/fs-index.mjs +0 -230
  18. package/lib/cache/store.mjs +0 -422
  19. package/lib/commands/bootstrap.mjs +0 -4
  20. package/lib/commands/callback.mjs +0 -742
  21. package/lib/commands/docs.mjs +0 -90
  22. package/lib/commands/kickoff.mjs +0 -192
  23. package/lib/commands/lanes.mjs +0 -566
  24. package/lib/commands/manifest-catalog.mjs +0 -251
  25. package/lib/commands/migrate.mjs +0 -204
  26. package/lib/commands/new.mjs +0 -452
  27. package/lib/commands/patterns.mjs +0 -160
  28. package/lib/commands/process.mjs +0 -388
  29. package/lib/commands/search.mjs +0 -43
  30. package/lib/commands/sync.mjs +0 -824
  31. package/lib/config/migrate.mjs +0 -223
  32. package/lib/find-ship-root.mjs +0 -75
  33. package/lib/process/specialist-prompt-contract.mjs +0 -171
  34. package/lib/state/lockfile.mjs +0 -180
  35. package/lib/vendor/run-agent.workflow.yml +0 -254
  36. package/lib/verify/checks/artifacts-up-to-date.mjs +0 -78
  37. package/lib/verify/checks/cache-integrity.mjs +0 -51
  38. package/lib/verify/checks/gitignore-cache.mjs +0 -51
  39. package/lib/verify/checks/rules-markers.mjs +0 -135
@@ -1,251 +0,0 @@
1
- import path from "node:path";
2
- import { apiGet, apiPost, fetchArtifact } from "../http.mjs";
3
- import { resolveShipRepoRootForCatalog } from "../find-ship-root.mjs";
4
- import { findShipRoot } from "../config/io.mjs";
5
- import { writeCached, cachePath } from "../cache/store.mjs";
6
- import { searchCommand } from "./search.mjs";
7
- import { scanArtifacts, readArtifactFile, pluralFor } from "../artifacts/fs-index.mjs";
8
-
9
- /** @type {Record<string, { arrayKey: string; name: string; apiPath: string; fetchKind: "tool"|"collection" }>} */
10
- const RESOURCES = {
11
- tool: {
12
- arrayKey: "tools",
13
- name: "Tools",
14
- apiPath: "tools",
15
- fetchKind: "tool",
16
- },
17
- collection: {
18
- arrayKey: "collections",
19
- name: "Collections",
20
- apiPath: "collections",
21
- fetchKind: "collection",
22
- },
23
- };
24
-
25
- /**
26
- * @param {"tool"|"collection"} resource
27
- * @param {{ baseUrl: string; json: boolean }} ctx
28
- * @param {string[]} args
29
- */
30
- export async function resourceManifestCommand(resource, ctx, args) {
31
- const spec = RESOURCES[resource];
32
- if (!spec) throw new Error(`Unknown resource: ${resource}`);
33
-
34
- const [sub, ...rest] = args;
35
- if (!sub || sub === "help") {
36
- const plural = pluralFor(spec.fetchKind);
37
- console.log(`Usage:
38
- shipctl ${resource} list
39
- shipctl ${resource} show <id>
40
- shipctl ${resource} fetch <id> [--version V] [--print]
41
- shipctl ${resource} search <query> [--top-k N]
42
-
43
- With a local Ship tree (cwd or SHIP_REPO): scans artifacts/${plural}/<id>/ARTIFACT.md on disk.
44
- Otherwise: methodology API (GET /${spec.apiPath}, POST /fetch for fetch, POST /search for search).
45
-
46
- In a Ship workspace (.ship/config.yml), 'fetch' writes the artifact to
47
- .ship/cache/<kind>/<id>@<version>/ARTIFACT.md and prints a 'cached:' line. Pass
48
- --print to also echo the body on stdout.
49
-
50
- Plural alias: shipctl ${spec.apiPath} …
51
-
52
- Global flags: --base-url URL --json`);
53
- return;
54
- }
55
-
56
- if (sub === "search") {
57
- await searchCommand(ctx, rest);
58
- return;
59
- }
60
-
61
- const root = resolveShipRepoRootForCatalog();
62
- if (root) {
63
- await manifestFromDisk(resource, root, spec, ctx, sub, rest);
64
- } else {
65
- await manifestFromHosted(resource, spec, ctx, sub, rest);
66
- }
67
- }
68
-
69
- /**
70
- * Parse `fetch`-specific flags so the hosted catalog path can honour
71
- * `--print`, `--version` and `--cwd` without polluting the global flag
72
- * extractor. Unknown flags are silently preserved as positionals (no error),
73
- * matching the rest of the manifest-catalog command.
74
- * @param {string[]} rest
75
- */
76
- function parseFetchFlags(rest) {
77
- const out = { positional: /** @type {string[]} */ ([]), print: false, version: null, cwd: null };
78
- const copy = [...rest];
79
- while (copy.length) {
80
- const a = copy.shift();
81
- if (a === "--print") { out.print = true; continue; }
82
- if (a === "--version" && copy.length) { out.version = copy.shift(); continue; }
83
- if (a && a.startsWith("--version=")) { out.version = a.slice("--version=".length); continue; }
84
- if (a === "--cwd" && copy.length) { out.cwd = copy.shift(); continue; }
85
- if (a && a.startsWith("--cwd=")) { out.cwd = a.slice("--cwd=".length); continue; }
86
- out.positional.push(a);
87
- }
88
- return out;
89
- }
90
-
91
- /**
92
- * @param {"tool"|"collection"} resource
93
- */
94
- async function manifestFromHosted(resource, spec, ctx, sub, rest) {
95
- const base = ctx.baseUrl;
96
- if (sub === "list") {
97
- const data = await apiGet(base, `/${spec.apiPath}`);
98
- if (ctx.json) {
99
- console.log(JSON.stringify(data, null, 2));
100
- } else {
101
- const entries = /** @type {Array<{ id: string; title: string; path: string; tags?: string[] }>} */ (
102
- data[spec.arrayKey] || []
103
- );
104
- console.log(`${data.description || spec.name}\n`);
105
- for (const p of entries) {
106
- console.log(`- ${p.id}`);
107
- console.log(` ${p.title}`);
108
- console.log(` path: ${p.path} tags: ${(p.tags || []).join(", ")}\n`);
109
- }
110
- }
111
- return;
112
- }
113
- if (sub === "show") {
114
- const id = rest[0];
115
- if (!id) {
116
- console.error("show: id required.");
117
- process.exit(1);
118
- }
119
- const data = await apiGet(base, `/${spec.apiPath}/${encodeURIComponent(id)}`);
120
- if (ctx.json) {
121
- console.log(JSON.stringify(data, null, 2));
122
- } else {
123
- console.log(`# ${data.title} (${data.id})\n`);
124
- console.log(data.content);
125
- }
126
- return;
127
- }
128
- if (sub === "fetch") {
129
- const flags = parseFetchFlags(rest);
130
- const id = flags.positional[0];
131
- if (!id) {
132
- console.error("fetch: id required.");
133
- process.exit(1);
134
- }
135
-
136
- const shipRoot = findShipRoot(flags.cwd || process.cwd());
137
- const wantCache = !!shipRoot;
138
- const wantStdoutBody = flags.print || !wantCache || ctx.json;
139
-
140
- if (wantCache) {
141
- const { content, meta } = await fetchArtifact(
142
- base,
143
- spec.fetchKind,
144
- id,
145
- flags.version || undefined,
146
- );
147
- const version = meta.version || flags.version || "0.0.0";
148
- const writeRes = writeCached(shipRoot, spec.fetchKind, id, version, content, {
149
- content_sha256: meta.content_sha256,
150
- updated_at: meta.updated_at,
151
- channel: meta.channel,
152
- version,
153
- source_url: meta.source_url,
154
- });
155
- const rel = path.relative(shipRoot, writeRes.bodyPath) || cachePath(shipRoot, spec.fetchKind, id, version);
156
- const relDisplay = path.isAbsolute(rel) ? writeRes.bodyPath : rel;
157
- if (ctx.json) {
158
- console.log(
159
- JSON.stringify(
160
- {
161
- kind: spec.fetchKind,
162
- id,
163
- version,
164
- content_sha256: meta.content_sha256,
165
- cached_path: relDisplay,
166
- content: wantStdoutBody ? content : undefined,
167
- },
168
- null,
169
- 2,
170
- ),
171
- );
172
- } else {
173
- console.log(`cached: ${spec.fetchKind}/${id}@${version} \u2192 ${relDisplay}`);
174
- if (wantStdoutBody) {
175
- console.log(`# ${id}@${version}\n`);
176
- console.log(content);
177
- }
178
- }
179
- return;
180
- }
181
-
182
- const data = await apiPost(base, "/fetch", { kind: spec.fetchKind, id, ...(flags.version ? { version: flags.version } : {}) });
183
- if (ctx.json) {
184
- console.log(JSON.stringify(data, null, 2));
185
- } else {
186
- console.error(
187
- `note: not in a Ship workspace (no .ship/config.yml found); printing body only. Run 'shipctl config init' to enable caching.`,
188
- );
189
- console.log(`# ${data.title || data.id} (${data.id})\n`);
190
- console.log(data.content);
191
- }
192
- return;
193
- }
194
- console.error(`Unknown ${resource} subcommand: ${sub}`);
195
- process.exit(1);
196
- }
197
-
198
- /**
199
- * @param {"tool"|"collection"} resource
200
- */
201
- async function manifestFromDisk(resource, root, spec, ctx, sub, rest) {
202
- const entries = scanArtifacts(root, spec.fetchKind);
203
-
204
- if (sub === "list") {
205
- if (ctx.json) {
206
- const payload = {
207
- description: spec.name,
208
- version: 1,
209
- [spec.arrayKey]: entries,
210
- };
211
- console.log(JSON.stringify(payload, null, 2));
212
- } else {
213
- console.log(`${spec.name}\n`);
214
- for (const p of entries) {
215
- console.log(`- ${p.id}`);
216
- console.log(` ${p.title}`);
217
- console.log(` path: ${p.path} tags: ${(p.tags || []).join(", ")}\n`);
218
- }
219
- }
220
- return;
221
- }
222
-
223
- if (sub === "show" || sub === "fetch") {
224
- const id = rest[0];
225
- if (!id) {
226
- console.error(`${sub}: id required.`);
227
- process.exit(1);
228
- }
229
- const entry = entries.find((e) => e.id === id);
230
- if (!entry) {
231
- console.error(`Unknown id: ${id}`);
232
- process.exit(1);
233
- }
234
- const file = readArtifactFile(root, spec.fetchKind, id);
235
- if (!file) {
236
- console.error(`Missing file: ${entry.path}`);
237
- process.exit(1);
238
- }
239
- const content = file.content;
240
- if (ctx.json) {
241
- console.log(JSON.stringify({ ...entry, content }, null, 2));
242
- } else {
243
- console.log(`# ${entry.title} (${entry.id})\n`);
244
- console.log(content);
245
- }
246
- return;
247
- }
248
-
249
- console.error(`Unknown ${resource} subcommand: ${sub}`);
250
- process.exit(1);
251
- }
@@ -1,204 +0,0 @@
1
- /**
2
- * `shipctl migrate` — upgrade `.ship/config.yml` from v1 to v2 (RFC-0007).
3
- *
4
- * The command is conservative:
5
- * - Writes `.ship/config.yml.bak` before touching anything.
6
- * - Refuses to overwrite without `--yes` unless `--dry-run` is set.
7
- * - Emits a machine-readable summary under `--json` so adopters can
8
- * wire it into their own migration runbooks.
9
- *
10
- * The actual migration rules live in `cli/lib/config/migrate.mjs`; this
11
- * command is the I/O and UX layer.
12
- */
13
-
14
- import fs from "node:fs";
15
- import path from "node:path";
16
-
17
- import { readConfig, writeConfig } from "../config/io.mjs";
18
- import { validateConfig } from "../config/schema.mjs";
19
- import { migrateV1ToV2 } from "../config/migrate.mjs";
20
-
21
- const EXIT_OK = 0;
22
- const EXIT_USAGE = 2;
23
- const EXIT_NOOP = 0;
24
- const EXIT_VALIDATION = 1;
25
-
26
- function printHelp() {
27
- console.log(`shipctl migrate — upgrade .ship/config.yml to the current schema.
28
-
29
- USAGE
30
- shipctl migrate [--dry-run] [--yes] [--json] [--cwd <dir>]
31
-
32
- FLAGS
33
- --dry-run Print the proposed new config without writing to disk.
34
- --yes Skip the interactive confirmation and overwrite in place
35
- (a backup is always written to .ship/config.yml.bak first).
36
- --json Emit a structured summary (path, backup, warnings, stubs).
37
- --cwd <dir> Repo root (default: search upward for .ship/config.yml).
38
- --help Show this help.
39
-
40
- EXIT
41
- 0 migration applied (or already at the latest schema)
42
- 1 resulting config failed validation
43
- 2 argument / IO error
44
- `);
45
- }
46
-
47
- /**
48
- * @param {{json?: boolean, yes?: boolean, dryRun?: boolean}} ctx
49
- * @param {string[]} rest
50
- */
51
- export async function migrateCommand(ctx, rest) {
52
- const args = parseArgs(rest);
53
- if (args.help) {
54
- printHelp();
55
- process.exit(EXIT_OK);
56
- }
57
-
58
- const cwd = args.cwd || process.cwd();
59
- let read;
60
- try {
61
- read = readConfig(cwd);
62
- } catch (err) {
63
- die(EXIT_USAGE, err instanceof Error ? err.message : String(err));
64
- }
65
-
66
- const { config: src, filePath } = read;
67
-
68
- let result;
69
- try {
70
- result = migrateV1ToV2(src);
71
- } catch (err) {
72
- die(EXIT_USAGE, `migrate failed: ${err instanceof Error ? err.message : err}`);
73
- }
74
-
75
- if (!result.migrated) {
76
- const payload = {
77
- path: filePath,
78
- migrated: false,
79
- warnings: result.warnings,
80
- stub_lanes: [],
81
- };
82
- if (ctx.json || args.json) {
83
- console.log(JSON.stringify(payload, null, 2));
84
- } else {
85
- console.log(`${filePath}: already at the latest schema (no changes).`);
86
- }
87
- process.exit(EXIT_NOOP);
88
- }
89
-
90
- const validation = validateConfig(result.config);
91
- if (!validation.ok) {
92
- const msg = [
93
- "migrate produced an invalid v2 config:",
94
- ...validation.errors.map((e) => ` - ${e}`),
95
- ...result.warnings.map((w) => ` (warn) ${w}`),
96
- ].join("\n");
97
- die(EXIT_VALIDATION, msg);
98
- }
99
-
100
- const yes = ctx.yes || args.yes;
101
- const dryRun = ctx.dryRun || args.dryRun;
102
- const backupPath = `${filePath}.bak`;
103
-
104
- if (dryRun || !yes) {
105
- const summary = {
106
- path: filePath,
107
- migrated: true,
108
- backup: backupPath,
109
- dry_run: Boolean(dryRun),
110
- warnings: result.warnings,
111
- stub_lanes: result.stub_lanes,
112
- };
113
- if (ctx.json || args.json) {
114
- console.log(
115
- JSON.stringify(
116
- { ...summary, proposed_config: result.config },
117
- null,
118
- 2,
119
- ),
120
- );
121
- } else {
122
- console.log(`Proposed migration for ${filePath}:`);
123
- for (const w of result.warnings) console.log(` - ${w}`);
124
- if (result.stub_lanes.length) {
125
- console.log(` - stub lanes (fill before shipping): ${result.stub_lanes.join(", ")}`);
126
- }
127
- console.log("");
128
- console.log(serialiseForDisplay(result.config));
129
- console.log("");
130
- if (dryRun) {
131
- console.log("--dry-run: not writing to disk.");
132
- } else {
133
- console.log("Re-run with --yes to apply the migration (writes .bak first).");
134
- }
135
- }
136
- process.exit(EXIT_OK);
137
- }
138
-
139
- try {
140
- fs.copyFileSync(filePath, backupPath);
141
- writeConfig(filePath, result.config);
142
- } catch (err) {
143
- die(EXIT_USAGE, `migrate write failed: ${err instanceof Error ? err.message : err}`);
144
- }
145
-
146
- if (ctx.json || args.json) {
147
- console.log(
148
- JSON.stringify(
149
- {
150
- path: filePath,
151
- migrated: true,
152
- backup: backupPath,
153
- warnings: result.warnings,
154
- stub_lanes: result.stub_lanes,
155
- },
156
- null,
157
- 2,
158
- ),
159
- );
160
- } else {
161
- console.log(`Wrote ${filePath} (backup at ${backupPath}).`);
162
- for (const w of result.warnings) console.log(` - ${w}`);
163
- if (result.stub_lanes.length) {
164
- console.log(
165
- ` - stub lanes to finish: ${result.stub_lanes.join(", ")} — edit the file or rerun 'shipctl init'.`,
166
- );
167
- }
168
- }
169
- process.exit(EXIT_OK);
170
- }
171
-
172
- function parseArgs(rest) {
173
- const out = { dryRun: false, yes: false, json: false, help: false, cwd: null };
174
- const copy = [...rest];
175
- while (copy.length) {
176
- const a = copy.shift();
177
- if (a === "--help" || a === "-h") out.help = true;
178
- else if (a === "--dry-run") out.dryRun = true;
179
- else if (a === "--yes") out.yes = true;
180
- else if (a === "--json") out.json = true;
181
- else if (a === "--cwd" && copy[0] !== undefined) out.cwd = path.resolve(String(copy.shift()));
182
- else if (a && a.startsWith("--cwd=")) out.cwd = path.resolve(a.slice("--cwd=".length));
183
- else {
184
- console.error(`unknown argument: ${a}\nRun: shipctl migrate --help`);
185
- process.exit(EXIT_USAGE);
186
- }
187
- }
188
- return out;
189
- }
190
-
191
- /**
192
- * Render the config as YAML-ish for display. We deliberately don't
193
- * import the YAML module here — the real write path already
194
- * normalises, and `--dry-run` is human-scan territory. JSON is good
195
- * enough and shows every field unambiguously.
196
- */
197
- function serialiseForDisplay(config) {
198
- return JSON.stringify(config, null, 2);
199
- }
200
-
201
- function die(code, msg) {
202
- console.error(msg);
203
- process.exit(code);
204
- }