@elmundi/ship-cli 0.8.1 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/README.md +651 -25
  2. package/bin/shipctl.mjs +168 -0
  3. package/lib/adapters/_fs.mjs +165 -0
  4. package/lib/adapters/agents/index.mjs +26 -0
  5. package/lib/adapters/ci/azure-pipelines.mjs +23 -0
  6. package/lib/adapters/ci/buildkite.mjs +24 -0
  7. package/lib/adapters/ci/circleci.mjs +23 -0
  8. package/lib/adapters/ci/gh-actions.mjs +29 -0
  9. package/lib/adapters/ci/gitlab-ci.mjs +23 -0
  10. package/lib/adapters/ci/jenkins.mjs +23 -0
  11. package/lib/adapters/ci/manual.mjs +18 -0
  12. package/lib/adapters/index.mjs +122 -0
  13. package/lib/adapters/language/dart.mjs +23 -0
  14. package/lib/adapters/language/go.mjs +23 -0
  15. package/lib/adapters/language/java.mjs +27 -0
  16. package/lib/adapters/language/js.mjs +32 -0
  17. package/lib/adapters/language/kotlin.mjs +48 -0
  18. package/lib/adapters/language/py.mjs +34 -0
  19. package/lib/adapters/language/rust.mjs +23 -0
  20. package/lib/adapters/language/swift.mjs +37 -0
  21. package/lib/adapters/language/ts.mjs +35 -0
  22. package/lib/adapters/trackers/azure-boards.mjs +49 -0
  23. package/lib/adapters/trackers/clickup.mjs +43 -0
  24. package/lib/adapters/trackers/github-issues.mjs +52 -0
  25. package/lib/adapters/trackers/jira.mjs +72 -0
  26. package/lib/adapters/trackers/linear.mjs +62 -0
  27. package/lib/adapters/trackers/none.mjs +18 -0
  28. package/lib/adapters/trackers/spreadsheet.mjs +28 -0
  29. package/lib/artifacts/fs-index.mjs +230 -0
  30. package/lib/bootstrap/render.mjs +422 -0
  31. package/lib/cache/store.mjs +422 -0
  32. package/lib/commands/bootstrap.mjs +4 -0
  33. package/lib/commands/callback.mjs +742 -0
  34. package/lib/commands/config.mjs +257 -0
  35. package/lib/commands/docs.mjs +4 -4
  36. package/lib/commands/doctor.mjs +583 -0
  37. package/lib/commands/feedback.mjs +355 -0
  38. package/lib/commands/help.mjs +159 -24
  39. package/lib/commands/init.mjs +830 -158
  40. package/lib/commands/kickoff.mjs +192 -0
  41. package/lib/commands/knowledge.mjs +562 -0
  42. package/lib/commands/lanes.mjs +527 -0
  43. package/lib/commands/manifest-catalog.mjs +106 -42
  44. package/lib/commands/migrate.mjs +204 -0
  45. package/lib/commands/new.mjs +452 -0
  46. package/lib/commands/patterns.mjs +14 -48
  47. package/lib/commands/run.mjs +857 -0
  48. package/lib/commands/search.mjs +2 -2
  49. package/lib/commands/sync.mjs +824 -0
  50. package/lib/commands/telemetry.mjs +390 -0
  51. package/lib/commands/trigger.mjs +196 -0
  52. package/lib/commands/verify.mjs +187 -0
  53. package/lib/config/io.mjs +232 -0
  54. package/lib/config/migrate.mjs +223 -0
  55. package/lib/config/schema.mjs +901 -0
  56. package/lib/detect.mjs +162 -19
  57. package/lib/feedback/drafts.mjs +129 -0
  58. package/lib/find-ship-root.mjs +16 -10
  59. package/lib/http.mjs +237 -11
  60. package/lib/state/idempotency.mjs +183 -0
  61. package/lib/state/lockfile.mjs +180 -0
  62. package/lib/telemetry/outbox.mjs +224 -0
  63. package/lib/templates.mjs +53 -65
  64. package/lib/verify/checks/agents-on-disk.mjs +58 -0
  65. package/lib/verify/checks/api-reachable.mjs +39 -0
  66. package/lib/verify/checks/artifacts-up-to-date.mjs +78 -0
  67. package/lib/verify/checks/bootstrap-files.mjs +67 -0
  68. package/lib/verify/checks/cache-integrity.mjs +51 -0
  69. package/lib/verify/checks/ci-secrets.mjs +86 -0
  70. package/lib/verify/checks/config-present.mjs +39 -0
  71. package/lib/verify/checks/gitignore-cache.mjs +51 -0
  72. package/lib/verify/checks/rules-markers.mjs +135 -0
  73. package/lib/verify/checks/stack-enums.mjs +33 -0
  74. package/lib/verify/checks/tracker-labels.mjs +91 -0
  75. package/lib/verify/registry.mjs +120 -0
  76. package/lib/version.mjs +34 -0
  77. package/package.json +10 -3
  78. package/bin/ship.mjs +0 -68
@@ -0,0 +1,355 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import readline from "node:readline";
4
+ import { spawn } from "node:child_process";
5
+ import {
6
+ findShipRoot,
7
+ readConfig,
8
+ } from "../config/io.mjs";
9
+ import {
10
+ draftsDir,
11
+ createDraft,
12
+ listDrafts,
13
+ readDraft,
14
+ removeDraft,
15
+ moveDraftToSent,
16
+ } from "../feedback/drafts.mjs";
17
+ import { postFeedback } from "../http.mjs";
18
+ import { appendEvent } from "../telemetry/outbox.mjs";
19
+
20
+ const ALLOWED_KINDS = ["pattern", "tool", "collection", "doc"];
21
+
22
+ function parseArgs(rest) {
23
+ const out = {
24
+ cwd: process.cwd(),
25
+ yes: false,
26
+ kind: null,
27
+ id: null,
28
+ version: null,
29
+ title: null,
30
+ summary: null,
31
+ recommendation: null,
32
+ positional: [],
33
+ };
34
+ const copy = [...rest];
35
+ const strFlag = (name, key) => {
36
+ if (copy[0] === name && copy[1] !== undefined) {
37
+ copy.shift();
38
+ out[key] = String(copy.shift());
39
+ return true;
40
+ }
41
+ const p = `${name}=`;
42
+ if (copy[0] && copy[0].startsWith(p)) {
43
+ out[key] = copy[0].slice(p.length);
44
+ copy.shift();
45
+ return true;
46
+ }
47
+ return false;
48
+ };
49
+ while (copy.length) {
50
+ if (copy[0] === "--cwd" && copy[1]) {
51
+ copy.shift();
52
+ out.cwd = String(copy.shift());
53
+ continue;
54
+ }
55
+ if (copy[0] && copy[0].startsWith("--cwd=")) {
56
+ out.cwd = copy.shift().slice("--cwd=".length);
57
+ continue;
58
+ }
59
+ if (copy[0] === "--yes" || copy[0] === "-y") {
60
+ out.yes = true;
61
+ copy.shift();
62
+ continue;
63
+ }
64
+ if (strFlag("--kind", "kind")) continue;
65
+ if (strFlag("--id", "id")) continue;
66
+ if (strFlag("--version", "version")) continue;
67
+ if (strFlag("--title", "title")) continue;
68
+ if (strFlag("--summary", "summary")) continue;
69
+ if (strFlag("--recommendation", "recommendation")) continue;
70
+ out.positional.push(copy.shift());
71
+ }
72
+ return out;
73
+ }
74
+
75
+ function requireShipRoot(cwd) {
76
+ const root = findShipRoot(cwd);
77
+ if (!root) {
78
+ console.error(".ship/ not found. Run 'shipctl config init' first.");
79
+ process.exit(10);
80
+ }
81
+ return root;
82
+ }
83
+
84
+ async function promptLine(msg) {
85
+ const rl = readline.createInterface({ input: process.stdin, output: process.stderr });
86
+ try {
87
+ return await new Promise((resolve) => rl.question(msg, resolve));
88
+ } finally {
89
+ rl.close();
90
+ }
91
+ }
92
+
93
+ function resolveBaseUrl(ctx, config) {
94
+ return (
95
+ ctx?.baseUrl ||
96
+ process.env.SHIP_API_BASE ||
97
+ config?.api?.base_url ||
98
+ "https://ship.elmundi.com"
99
+ ).replace(/\/$/, "");
100
+ }
101
+
102
+ async function cmdDraft(root, args) {
103
+ if (!args.kind || !ALLOWED_KINDS.includes(args.kind)) {
104
+ console.error(`--kind is required and must be one of: ${ALLOWED_KINDS.join(", ")}`);
105
+ process.exit(1);
106
+ }
107
+ if (!args.id) {
108
+ console.error("--id is required");
109
+ process.exit(1);
110
+ }
111
+
112
+ let title = args.title;
113
+ let summary = args.summary;
114
+
115
+ if ((!title || !summary) && process.stdin.isTTY) {
116
+ if (!title) title = (await promptLine("title: ")).trim();
117
+ if (!summary) summary = (await promptLine("summary: ")).trim();
118
+ }
119
+
120
+ if (!title || !summary) {
121
+ console.error("--title and --summary are required (interactive prompts unavailable).");
122
+ process.exit(1);
123
+ }
124
+
125
+ const { config } = readConfig(root);
126
+ const fp = createDraft(root, {
127
+ kind: args.kind,
128
+ id: args.id,
129
+ version: args.version,
130
+ title,
131
+ summary,
132
+ recommendation: args.recommendation,
133
+ stack: config.stack || {},
134
+ });
135
+ console.log(fp);
136
+ }
137
+
138
+ function cmdList(root) {
139
+ const drafts = listDrafts(root);
140
+ if (drafts.length === 0) {
141
+ console.log("(no feedback drafts)");
142
+ return;
143
+ }
144
+ for (const fp of drafts) {
145
+ try {
146
+ const { meta } = readDraft(fp);
147
+ const sent = fp.includes(`${path.sep}sent${path.sep}`) ? " [sent]" : "";
148
+ const base = path.basename(fp);
149
+ const ts = (base.match(/^([0-9]{4}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2}-[0-9]{2})/) || [])[1] || "-";
150
+ const kind = meta.kind || "-";
151
+ const id = meta.id || "-";
152
+ const version = meta.version || "-";
153
+ const title = meta.title || "(untitled)";
154
+ console.log(`${ts} ${kind}/${id}@${version} — ${title}${sent}`);
155
+ } catch (e) {
156
+ console.log(`${fp} (invalid: ${e.message})`);
157
+ }
158
+ }
159
+ }
160
+
161
+ function cmdShow(args) {
162
+ const file = args.positional[1];
163
+ if (!file) {
164
+ console.error("usage: shipctl feedback show <draft-file>");
165
+ process.exit(1);
166
+ }
167
+ const resolved = path.resolve(file);
168
+ if (!fs.existsSync(resolved)) {
169
+ console.error(`not found: ${resolved}`);
170
+ process.exit(1);
171
+ }
172
+ console.log(fs.readFileSync(resolved, "utf8"));
173
+ }
174
+
175
+ function cmdEdit(args) {
176
+ const file = args.positional[1];
177
+ if (!file) {
178
+ console.error("usage: shipctl feedback edit <draft-file>");
179
+ process.exit(1);
180
+ }
181
+ const resolved = path.resolve(file);
182
+ if (!fs.existsSync(resolved)) {
183
+ console.error(`not found: ${resolved}`);
184
+ process.exit(1);
185
+ }
186
+ const editor = process.env.EDITOR || process.env.VISUAL;
187
+ if (!editor) {
188
+ console.log(resolved);
189
+ console.error("(hint: set $EDITOR to open drafts automatically)");
190
+ return;
191
+ }
192
+ const [cmd, ...cmdArgs] = editor.split(/\s+/);
193
+ const child = spawn(cmd, [...cmdArgs, resolved], { stdio: "inherit" });
194
+ child.on("exit", (code) => {
195
+ if (code !== 0) process.exit(code || 1);
196
+ });
197
+ }
198
+
199
+ async function cmdSubmit(ctx, root, args) {
200
+ const file = args.positional[1];
201
+ if (!file) {
202
+ console.error("usage: shipctl feedback submit <draft-file>");
203
+ process.exit(1);
204
+ }
205
+ const resolved = path.resolve(file);
206
+ if (!fs.existsSync(resolved)) {
207
+ console.error(`not found: ${resolved}`);
208
+ process.exit(1);
209
+ }
210
+
211
+ const { meta, body } = readDraft(resolved);
212
+ const missing = [];
213
+ if (!meta.kind) missing.push("kind");
214
+ if (!meta.id) missing.push("id");
215
+ if (!meta.title) missing.push("title");
216
+ // Summary lives in body (after '**Summary**:'); derive it for validation.
217
+ const summaryMatch = body.match(/\*\*Summary\*\*:\s*([^\n]+)/);
218
+ const summary = summaryMatch ? summaryMatch[1].trim() : "";
219
+ if (!summary) missing.push("summary");
220
+ if (missing.length) {
221
+ console.error(`missing required fields: ${missing.join(", ")}`);
222
+ process.exit(1);
223
+ }
224
+
225
+ if (!args.yes && process.stdin.isTTY) {
226
+ const ok = (await promptLine(`submit ${path.basename(resolved)}? [y/N] `)).trim();
227
+ if (!/^y(es)?$/i.test(ok)) {
228
+ console.error("aborted.");
229
+ process.exit(1);
230
+ }
231
+ }
232
+
233
+ const { config } = readConfig(root);
234
+ const baseUrl = resolveBaseUrl(ctx, config);
235
+
236
+ const stack = config.stack || {};
237
+ const stackStr = `tracker=${stack.tracker || "-"}, ci=${stack.ci || "-"}, agents=${
238
+ Array.isArray(stack.agents) ? stack.agents.join("+") || "-" : "-"
239
+ }, preset=${stack.preset || "-"}`;
240
+
241
+ const recommendationMatch = body.match(/\*\*Recommendation\*\*:\s*([^\n]+)/);
242
+ const recommendation = recommendationMatch ? recommendationMatch[1].trim() : "";
243
+ const recommendations = recommendation ? [recommendation] : [];
244
+
245
+ const payload = {
246
+ title: meta.title,
247
+ summary,
248
+ recommendations,
249
+ source_context: stackStr,
250
+ artifact: {
251
+ kind: meta.kind,
252
+ id: meta.id,
253
+ version: meta.version || undefined,
254
+ },
255
+ };
256
+
257
+ let data;
258
+ try {
259
+ data = await postFeedback(baseUrl, payload);
260
+ } catch (e) {
261
+ console.error(e.message || String(e));
262
+ process.exit(20);
263
+ }
264
+
265
+ const issueUrl = data?.issue_url || "(no issue_url in response)";
266
+ console.log(issueUrl);
267
+ if (data?.deduplicated) console.log("(deduplicated: comment added to existing issue)");
268
+
269
+ const sentPath = moveDraftToSent(root, resolved);
270
+ console.log(`moved: ${sentPath}`);
271
+
272
+ if (
273
+ config.telemetry?.share === true &&
274
+ config.telemetry?.scope?.improvement_drafts === true
275
+ ) {
276
+ try {
277
+ appendEvent(root, {
278
+ type: "feedback.submit",
279
+ anonymous_id: config.telemetry.anonymous_id,
280
+ payload: {
281
+ artifact: {
282
+ kind: meta.kind,
283
+ id: meta.id,
284
+ version: meta.version || null,
285
+ },
286
+ summary,
287
+ suggestion: recommendation || null,
288
+ stack: {
289
+ tracker: stack.tracker || null,
290
+ ci: stack.ci || null,
291
+ agents: stack.agents || [],
292
+ preset: stack.preset || null,
293
+ },
294
+ },
295
+ });
296
+ } catch (e) {
297
+ if (process.env.SHIP_DEBUG === "1") {
298
+ console.error(`[ship:feedback] failed to append telemetry: ${e.message}`);
299
+ }
300
+ }
301
+ }
302
+ }
303
+
304
+ function cmdRemove(args) {
305
+ const file = args.positional[1];
306
+ if (!file) {
307
+ console.error("usage: shipctl feedback remove <draft-file>");
308
+ process.exit(1);
309
+ }
310
+ const resolved = path.resolve(file);
311
+ if (!fs.existsSync(resolved)) {
312
+ console.error(`not found: ${resolved}`);
313
+ process.exit(1);
314
+ }
315
+ removeDraft(resolved);
316
+ console.log(`removed: ${resolved}`);
317
+ }
318
+
319
+ export async function feedbackCommand(ctx, rest) {
320
+ const args = parseArgs(rest);
321
+ const sub = args.positional[0];
322
+ const root = requireShipRoot(args.cwd);
323
+ // ensure drafts dir exists lazily when commands need it
324
+ if (sub === "list" || sub === "draft") fs.mkdirSync(draftsDir(root), { recursive: true });
325
+
326
+ switch (sub) {
327
+ case "draft":
328
+ await cmdDraft(root, args);
329
+ return;
330
+ case "list":
331
+ cmdList(root);
332
+ return;
333
+ case "show":
334
+ cmdShow(args);
335
+ return;
336
+ case "edit":
337
+ cmdEdit(args);
338
+ return;
339
+ case "submit":
340
+ await cmdSubmit(ctx, root, args);
341
+ return;
342
+ case "remove":
343
+ cmdRemove(args);
344
+ return;
345
+ default:
346
+ console.error(
347
+ "usage: shipctl feedback <draft|list|show|edit|submit|remove> [...]\n" +
348
+ " draft --kind <k> --id <id> [--version <v>] --title '...' --summary '...' [--recommendation '...']\n" +
349
+ " list\n" +
350
+ " show|edit|remove <draft-file>\n" +
351
+ " submit <draft-file> [--yes]",
352
+ );
353
+ process.exit(2);
354
+ }
355
+ }
@@ -1,39 +1,174 @@
1
1
  export function printHelp() {
2
- console.log(`Ship CLI methodology on ship.elmundi.com (or SHIP_API_BASE) + init.
2
+ console.log(`shipctl — adopt Ship in a repo, sync the catalog, run lanes, report Runs.
3
3
 
4
- ONE FLOW
5
- 1) ship search <query> — vector search (POST /search) over docs + prompts + README
6
- 2) ship docs fetch <path> — full markdown file by repo-relative path (POST /fetch { path })
7
- ship pattern|tool|workflow|collection fetch <id> catalog entry body (POST /fetch { kind, id })
8
- 3) ship docs feedback … — improvement / retro note (POST /feedback)
4
+ Bootstrap a new or existing repo (init / new / doctor), pull the
5
+ methodology catalog into .ship/cache (sync), execute one-shot lanes or
6
+ emit prompts for the workspace runner (run / lanes / kickoff /
7
+ callback). Talks to the methodology + orchestration APIs over HTTPS.
8
+
9
+ VOCABULARY
10
+ lanes: (.ship/config.yml) → operator console: Automations
11
+ pattern: (artifact kind) → operator console: Plays
12
+ pipeline_runs (DB / API) → operator console: Runs
13
+ attention surface → operator console: Inbox
14
+
15
+ The protocol-stable terms (lanes:, pattern:, pipeline_runs) stay
16
+ literal in YAML, CLI flags, and HTTP. Operator-facing prose uses the
17
+ console nouns.
18
+
19
+ GLOBAL FLAGS
20
+ --base-url URL Methodology API (default: SHIP_API_BASE or
21
+ https://ship.elmundi.com/api/methodology)
22
+ --json Machine-readable JSON output where supported
23
+ --version, -v Print shipctl version and exit
24
+ --help, -h Print this help
9
25
 
10
26
  COMMANDS
11
- ship help
12
- ship search <query> [--top-k N]
13
27
 
14
- ship docs fetch <repo-relative-path>
15
- ship docs feedback --title "..." --summary "..." [--recommendation "…"]... [--source-context "…"]
28
+ Setup
29
+ shipctl init [--yes] [--force] [--dry-run] [--json] [--cwd <dir>]
30
+ [--agents <csv>]
31
+ [--tracker <name>] [--ci <name>] [--preset <name>]
32
+ [--language <name>] [--channel stable|edge]
33
+ [--copy-rules] [--copy-playbook] [--bootstrap]
34
+ [--telemetry on|off|ask]
35
+ — bootstrap .ship/, fetch artifacts, install
36
+ agent rules in an existing repo.
37
+ shipctl new <name> [--preset ...] [--tracker ...] [--ci ...] [--agents ...]
38
+ [--here] [--yes]
39
+ — bootstrap a fresh repo: git init + README +
40
+ .ship/config.yml.
41
+ shipctl doctor [--json] [--cwd <dir>] [--write-inventory] [--no-network]
42
+ — inspect the repo, propose a stack, optionally
43
+ write .ship/inventory.json for
44
+ 'shipctl init --bootstrap'.
45
+ shipctl config init|get|set|validate|show|path
46
+ — .ship/config.yml management.
16
47
 
17
- ship pattern list | ship pattern show <id> | ship pattern fetch <id> | ship pattern search <query> [--top-k N]
18
- ship tool | ship workflow … | ship collection … (same subcommands; plural aliases: patterns, tools, …)
48
+ Catalog
49
+ shipctl search <query> [--top-k N]
50
+ — vector search over docs + prompts (POST /search).
51
+ shipctl docs fetch <repo-relative-path>
52
+ shipctl docs feedback --title "..." --summary "..." [--recommendation "..."]...
53
+ [--source-context "..."]
54
+ — fetch markdown bodies; submit improvement /
55
+ retro notes (POST /feedback).
56
+ shipctl pattern list | shipctl pattern show <id> | shipctl pattern fetch <id>
57
+ | shipctl pattern search <query> [--top-k N]
58
+ — versioned artifact bodies (POST /fetch
59
+ { kind, id, version? }). 'pattern' is the
60
+ protocol-stable artifact kind; in the operator
61
+ console it shows up as a Play.
62
+ shipctl tool … | shipctl collection …
63
+ — same subcommands; plural aliases:
64
+ patterns, tools, collections.
65
+ shipctl sync [--check-only] [--only <kind:id>]... [--channel <c>]
66
+ [--force-unpin] [--dry-run] [--lock] [--json] [--cwd <dir>]
67
+ — fetch artifacts into .ship/cache. With --lock,
68
+ also writes .ship/shipctl.lock.json covering
69
+ every pattern the declared lanes depend on.
19
70
 
20
- ship init [--yes] [--force] [--dry-run] [--only <id>] [--cwd <dir>]
71
+ Run
72
+ shipctl trigger --event schedule --repo <id|owner/name> [--workspace <id>] [--json]
73
+ — ask Ship which configured lanes are
74
+ due for the current GitHub trigger.
75
+ shipctl run --lane <id> [--pattern <id>] [--fanout matrix|sequential|concurrent]
76
+ [--trigger event|schedule|manual|once]
77
+ [--dry-run] [--offline] [--json] [--cwd <dir>]
78
+ [--ship-run-id <uuid>] [--ship-callback-url <url>] [--ship-run-token <jwt>]
79
+ — one-shot dispatch entry point. 'kind: once'
80
+ lanes execute fully here; 'kind: lane / event /
81
+ schedule' lanes are queued for the workspace
82
+ runner via .github/workflows/run-agent.yml.
83
+ Reports its terminal status via the callback URL
84
+ Ship injected into the workflow.
85
+ shipctl lanes install [--only <csv>] [--ref <git-ref>] [--owner <gh>] [--repo <name>]
86
+ [--shipctl-version <v>] [--dry-run] [--force] [--json] [--cwd <dir>]
87
+ shipctl lanes list [--json] [--cwd <dir>]
88
+ shipctl lanes remove [--only <csv>] [--dry-run] [--json] [--cwd <dir>]
89
+ — generate / inspect / delete the
90
+ .github/workflows/ship-<lane>.yml thin wrappers
91
+ that delegate to the reusable run-agent.yml.
92
+ shipctl kickoff [--pattern <id>] [--version <v>] [--raw] [--json] [--cwd <dir>]
93
+ — print a pattern body for piping into the
94
+ customer's agent in CI.
95
+ shipctl callback --status <ok|fail|cancelled> [--summary "..."] [--metric k=v]...
96
+ [--outcome-text "..."] [--findings-count N] [--severity high=N]...
97
+ [--artifact pr:"..."]... [--escalation clarification:"..."]...
98
+ — report a Run's terminal status (and
99
+ RunSummary outcome) back to Ship so it can
100
+ render the outcome row and route any
101
+ escalations into the Inbox.
21
102
 
22
- GLOBAL FLAGS
23
- --base-url URL Methodology API (default: SHIP_API_BASE or https://ship.elmundi.com/api/methodology)
24
- --json Machine-readable JSON
103
+ Knowledge
104
+ shipctl knowledge init [--workspace <id>] [--repo <id|owner/name>] [--only <csv>] [--json]
105
+ compatibility: open a PR that seeds
106
+ .ship/knowledge starter docs.
107
+ shipctl knowledge fetch <bucket-slug> [--workspace <id>] [--json]
108
+ — read Ship-owned bucket articles and
109
+ source sync state.
110
+ shipctl knowledge bootstrap [--workspace <id>] [--repo <id|owner/name>] [--json]
111
+ — post-merge action entry point: analyze
112
+ repo and open generated knowledge PR.
113
+ shipctl knowledge refresh-intel [--workspace <id>] [--repo <id|owner/name>] [--json]
114
+ — refresh the generated repository-context
115
+ bucket for an activated repo.
116
+ Reads SHIP_API_TOKEN.
117
+
118
+ Telemetry & feedback
119
+ shipctl telemetry status|on|off|show-id|reset-id|flush|export|delete-my-data|buffer
120
+ — opt-in anonymous usage (RFC-0003); default OFF.
121
+ '--scope artifact_usage,improvement_drafts,errors'
122
+ on 'on'; '--dry-run' on 'flush';
123
+ '--out <file>' on 'export'.
124
+ shipctl feedback draft|list|show|edit|submit|remove
125
+ — local markdown drafts; submit creates a
126
+ GitHub issue via POST /feedback and moves the
127
+ draft to sent/.
128
+
129
+ Misc
130
+ shipctl verify [--no-network] [--check <id,...>] [--severity warn|error|info] [--json]
131
+ — post-adoption liveness checks
132
+ (local + config + network).
133
+ shipctl migrate [--dry-run] [--yes] [--json] [--cwd <dir>]
134
+ — upgrade .ship/config.yml from v1 to v2
135
+ (lanes-as-config).
136
+ shipctl bootstrap (stub)
137
+ shipctl help — show this help.
25
138
 
26
139
  LOCAL TREE
27
- pattern / tool / workflow / collection list|show|fetch read manifests from disk when cwd or SHIP_REPO
28
- is inside the Ship monorepo (search always uses HTTP).
140
+ pattern / tool / collection list|show|fetch scan
141
+ artifacts/<plural>/<id>/ARTIFACT.md on disk when cwd or SHIP_REPO is inside
142
+ the Ship monorepo (search always uses HTTP).
29
143
 
30
144
  INIT FLAGS
31
- --yes Non-interactive apply (use --dry-run first)
32
- --force Replace existing injected blocks
33
- --dry-run Preview only
34
- --only cursor | agents-md | claude-md | codex | copilot
35
- --cwd Target repo root
145
+ --yes Non-interactive apply (use --dry-run first)
146
+ --force Replace existing rule blocks and overwrite generated files
147
+ --dry-run Preview only
148
+ --json Emit a JSON summary suitable for CI
149
+ --agents <csv> Comma-separated agent ids. See list below.
150
+ --tracker <name> Stack tracker: linear|jira|github-issues|azure-boards|clickup|spreadsheet|none
151
+ --ci <name> Stack CI: gh-actions|gitlab-ci|buildkite|circleci|azure-pipelines|jenkins|manual
152
+ --preset <name> Stack preset: web-app|api-backend|mobile-app|cli|monorepo|adoption-minimum
153
+ --language <name> Stack language: ts|js|py|go|rust|java|kotlin|swift|dart|multi
154
+ --channel <name> Override api.channel: stable|edge
155
+ --copy-rules Install collection/agent-rules-<agent> files at their install_target
156
+ --copy-playbook Fetch collection/adoption-playbook into .ship/cache/ (skipped on 404)
157
+ --bootstrap Render CI/tracker scaffolding (mobile-app+gh-actions+linear skeletons today;
158
+ other combos emit SHIP_BOOTSTRAP_PLAN.md)
159
+ --telemetry on|off|ask — override the interactive telemetry prompt
160
+ --cwd Target repo root
161
+
162
+ SUPPORTED AGENTS
163
+ cursor, codex, claude, aider, cline, continue, windsurf, zed,
164
+ gemini, opencode, copilot, cursor-cloud, agents-md, claude-md
165
+
166
+ REFERENCE
167
+ Artifacts protocol: RFC-0001 (POST /search, POST /fetch). Every consumed
168
+ artifact should be recorded in the PR as \`<kind>:<id>@<version>\`.
169
+ HTTP schemas: artifacts/tools/methodology-api/ARTIFACT.md in the Ship repo.
170
+ Operator IA (Plays / Automations / Runs / Inbox): RFC-0010.
36
171
 
37
- For HTTP schemas see documentation/tools/backend-api.md in the Ship repo.
172
+ Package: @elmundi/ship-cli (binary: shipctl).
38
173
  `);
39
174
  }