@elmundi/ship-cli 0.8.1 → 0.11.2
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/README.md +415 -22
- package/bin/shipctl.mjs +165 -0
- package/lib/adapters/_fs.mjs +165 -0
- package/lib/adapters/agents/index.mjs +26 -0
- package/lib/adapters/ci/azure-pipelines.mjs +23 -0
- package/lib/adapters/ci/buildkite.mjs +24 -0
- package/lib/adapters/ci/circleci.mjs +23 -0
- package/lib/adapters/ci/gh-actions.mjs +29 -0
- package/lib/adapters/ci/gitlab-ci.mjs +23 -0
- package/lib/adapters/ci/jenkins.mjs +23 -0
- package/lib/adapters/ci/manual.mjs +18 -0
- package/lib/adapters/index.mjs +122 -0
- package/lib/adapters/language/dart.mjs +23 -0
- package/lib/adapters/language/go.mjs +23 -0
- package/lib/adapters/language/java.mjs +27 -0
- package/lib/adapters/language/js.mjs +32 -0
- package/lib/adapters/language/kotlin.mjs +48 -0
- package/lib/adapters/language/py.mjs +34 -0
- package/lib/adapters/language/rust.mjs +23 -0
- package/lib/adapters/language/swift.mjs +37 -0
- package/lib/adapters/language/ts.mjs +35 -0
- package/lib/adapters/trackers/azure-boards.mjs +49 -0
- package/lib/adapters/trackers/clickup.mjs +43 -0
- package/lib/adapters/trackers/github-issues.mjs +52 -0
- package/lib/adapters/trackers/jira.mjs +72 -0
- package/lib/adapters/trackers/linear.mjs +62 -0
- package/lib/adapters/trackers/none.mjs +18 -0
- package/lib/adapters/trackers/spreadsheet.mjs +28 -0
- package/lib/artifacts/fs-index.mjs +230 -0
- package/lib/bootstrap/render.mjs +373 -0
- package/lib/cache/store.mjs +422 -0
- package/lib/commands/bootstrap.mjs +4 -0
- package/lib/commands/callback.mjs +302 -0
- package/lib/commands/config.mjs +257 -0
- package/lib/commands/docs.mjs +1 -1
- package/lib/commands/doctor.mjs +583 -0
- package/lib/commands/feedback.mjs +355 -0
- package/lib/commands/help.mjs +96 -21
- package/lib/commands/init.mjs +830 -158
- package/lib/commands/kickoff.mjs +192 -0
- package/lib/commands/knowledge.mjs +368 -0
- package/lib/commands/lanes.mjs +502 -0
- package/lib/commands/manifest-catalog.mjs +102 -38
- package/lib/commands/migrate.mjs +204 -0
- package/lib/commands/new.mjs +452 -0
- package/lib/commands/patterns.mjs +9 -43
- package/lib/commands/run.mjs +617 -0
- package/lib/commands/sync.mjs +749 -0
- package/lib/commands/telemetry.mjs +390 -0
- package/lib/commands/verify.mjs +187 -0
- package/lib/config/io.mjs +232 -0
- package/lib/config/migrate.mjs +215 -0
- package/lib/config/schema.mjs +650 -0
- package/lib/detect.mjs +162 -19
- package/lib/feedback/drafts.mjs +129 -0
- package/lib/find-ship-root.mjs +16 -10
- package/lib/http.mjs +237 -11
- package/lib/state/idempotency.mjs +183 -0
- package/lib/state/lockfile.mjs +180 -0
- package/lib/telemetry/outbox.mjs +224 -0
- package/lib/templates.mjs +53 -65
- package/lib/verify/checks/agents-on-disk.mjs +58 -0
- package/lib/verify/checks/api-reachable.mjs +39 -0
- package/lib/verify/checks/artifacts-up-to-date.mjs +78 -0
- package/lib/verify/checks/bootstrap-files.mjs +67 -0
- package/lib/verify/checks/cache-integrity.mjs +51 -0
- package/lib/verify/checks/ci-secrets.mjs +86 -0
- package/lib/verify/checks/config-present.mjs +39 -0
- package/lib/verify/checks/gitignore-cache.mjs +51 -0
- package/lib/verify/checks/rules-markers.mjs +135 -0
- package/lib/verify/checks/stack-enums.mjs +33 -0
- package/lib/verify/checks/tracker-labels.mjs +91 -0
- package/lib/verify/registry.mjs +120 -0
- package/lib/version.mjs +34 -0
- package/package.json +10 -3
- 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
|
+
}
|
package/lib/commands/help.mjs
CHANGED
|
@@ -1,39 +1,114 @@
|
|
|
1
1
|
export function printHelp() {
|
|
2
|
-
console.log(`Ship CLI —
|
|
2
|
+
console.log(`Ship CLI — artifacts protocol on ship.elmundi.com (or SHIP_API_BASE) + init.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
1)
|
|
6
|
-
2)
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
ARTIFACTS PROTOCOL (RFC-0001)
|
|
5
|
+
1) shipctl search <query> — vector search (POST /search) over docs + prompts
|
|
6
|
+
2) shipctl docs fetch <path> — full markdown body by repo-relative path
|
|
7
|
+
shipctl pattern|tool|collection show|fetch <id>
|
|
8
|
+
— versioned artifact body (POST /fetch { kind, id, version? })
|
|
9
|
+
3) shipctl docs feedback … — improvement / retro note (POST /feedback)
|
|
10
|
+
|
|
11
|
+
Every consumed artifact should be recorded in the PR as \`<kind>:<id>@<version>\`.
|
|
9
12
|
|
|
10
13
|
COMMANDS
|
|
11
|
-
|
|
12
|
-
|
|
14
|
+
shipctl help
|
|
15
|
+
shipctl search <query> [--top-k N]
|
|
16
|
+
|
|
17
|
+
shipctl docs fetch <repo-relative-path>
|
|
18
|
+
shipctl docs feedback --title "..." --summary "..." [--recommendation "…"]... [--source-context "…"]
|
|
19
|
+
|
|
20
|
+
shipctl pattern list | shipctl pattern show <id> | shipctl pattern fetch <id> | shipctl pattern search <query> [--top-k N]
|
|
21
|
+
shipctl tool … | shipctl collection … (same subcommands; plural aliases: patterns, tools, …)
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
23
|
+
shipctl init [--yes] [--force] [--dry-run] [--json] [--cwd <dir>]
|
|
24
|
+
[--agents <csv>]
|
|
25
|
+
[--tracker <name>] [--ci <name>] [--preset <name>]
|
|
26
|
+
[--language <name>] [--channel stable|edge]
|
|
27
|
+
[--copy-rules] [--copy-playbook] [--bootstrap]
|
|
28
|
+
[--telemetry on|off|ask]
|
|
16
29
|
|
|
17
|
-
|
|
18
|
-
|
|
30
|
+
shipctl doctor [--json] [--cwd <dir>] [--write-inventory] [--no-network]
|
|
31
|
+
— inspect the repo, propose a stack, optionally write
|
|
32
|
+
.ship/inventory.json for 'shipctl init --bootstrap'.
|
|
19
33
|
|
|
20
|
-
|
|
34
|
+
shipctl config init|get|set|validate|show|path — .ship/config.yml management
|
|
35
|
+
shipctl sync [--check-only] [--only <kind:id>...] [--channel <c>] [--force-unpin]
|
|
36
|
+
[--dry-run] [--lock] [--json]
|
|
37
|
+
— fetch artifacts into .ship/cache. With --lock,
|
|
38
|
+
also writes .ship/shipctl.lock.json covering every
|
|
39
|
+
pattern the declared lanes depend on (Phase 4).
|
|
40
|
+
|
|
41
|
+
shipctl new <name> [--preset ...] [--tracker ...] [--ci ...] [--agents ...] [--here] [--yes]
|
|
42
|
+
— bootstrap a fresh repo: git init + README + .ship/config.yml
|
|
43
|
+
shipctl verify [--no-network] [--check <id,...>] [--severity warn|error|info] [--json]
|
|
44
|
+
— post-adoption liveness checks (local + config + network)
|
|
45
|
+
shipctl telemetry status|on|off|show-id|reset-id|flush|export|delete-my-data|buffer
|
|
46
|
+
— opt-in anonymous usage (RFC-0003); default OFF.
|
|
47
|
+
'--scope artifact_usage,improvement_drafts,errors' on 'on'
|
|
48
|
+
'--dry-run' on 'flush'; '--out <file>' on 'export'
|
|
49
|
+
shipctl feedback draft|list|show|edit|submit|remove
|
|
50
|
+
— local markdown drafts; submit creates a GitHub issue
|
|
51
|
+
via POST /feedback and moves the draft to sent/.
|
|
52
|
+
shipctl callback --status <ok|fail|cancelled> [--summary "..."] [--metric k=v]...
|
|
53
|
+
— report a pipeline run's terminal status to Ship.
|
|
54
|
+
Used inside workflow.yml 'if: always()' steps;
|
|
55
|
+
reads SHIP_RUN_TOKEN + SHIP_CALLBACK_URL from env.
|
|
56
|
+
shipctl kickoff [--pattern kickoff] [--version …] [--raw] [--json] [--cwd …]
|
|
57
|
+
— print the kickoff / workload pattern body for piping
|
|
58
|
+
into the customer's agent in CI (see artifacts/patterns/kickoff).
|
|
59
|
+
shipctl migrate [--dry-run] [--yes] [--json] [--cwd …]
|
|
60
|
+
— upgrade .ship/config.yml from v1 to v2 (lanes-as-config).
|
|
61
|
+
shipctl run --lane <id> [--trigger event|schedule|manual|once]
|
|
62
|
+
[--dry-run] [--offline] [--json] [--cwd …]
|
|
63
|
+
[--ship-run-id …] [--ship-callback-url …] [--ship-run-token …]
|
|
64
|
+
— RFC-0007 entry-point: resolve a lane from
|
|
65
|
+
.ship/config.yml, fetch its pattern, check idempotency,
|
|
66
|
+
emit the prompt, and report the callback.
|
|
67
|
+
shipctl lanes install [--only <csv>] [--ref <git-ref>] [--owner …] [--repo …]
|
|
68
|
+
[--shipctl-version <v>] [--dry-run] [--force] [--json] [--cwd …]
|
|
69
|
+
— generate .github/workflows/ship-<lane>.yml thin
|
|
70
|
+
wrappers that delegate to the reusable run-agent.yml.
|
|
71
|
+
shipctl lanes list [--json] [--cwd …]
|
|
72
|
+
— print the lane map from .ship/config.yml.
|
|
73
|
+
shipctl lanes remove [--only <csv>] [--dry-run] [--json] [--cwd …]
|
|
74
|
+
— delete generated ship-<lane>.yml wrappers.
|
|
75
|
+
shipctl knowledge init [--workspace <id>] [--repo <id|owner/name>] [--only <csv>] [--json]
|
|
76
|
+
— open a PR that seeds .ship/knowledge/*.md starter
|
|
77
|
+
buckets (code-style, ui-runbook). Reads SHIP_API_TOKEN.
|
|
78
|
+
shipctl bootstrap (stub)
|
|
21
79
|
|
|
22
80
|
GLOBAL FLAGS
|
|
23
81
|
--base-url URL Methodology API (default: SHIP_API_BASE or https://ship.elmundi.com/api/methodology)
|
|
24
82
|
--json Machine-readable JSON
|
|
25
83
|
|
|
26
84
|
LOCAL TREE
|
|
27
|
-
pattern / tool /
|
|
28
|
-
|
|
85
|
+
pattern / tool / collection list|show|fetch scan
|
|
86
|
+
artifacts/<plural>/<id>/ARTIFACT.md on disk when cwd or SHIP_REPO is inside
|
|
87
|
+
the Ship monorepo (search always uses HTTP).
|
|
29
88
|
|
|
30
89
|
INIT FLAGS
|
|
31
|
-
--yes
|
|
32
|
-
--force
|
|
33
|
-
--dry-run
|
|
34
|
-
--
|
|
35
|
-
--
|
|
90
|
+
--yes Non-interactive apply (use --dry-run first)
|
|
91
|
+
--force Replace existing rule blocks and overwrite generated files
|
|
92
|
+
--dry-run Preview only
|
|
93
|
+
--json Emit a JSON summary suitable for CI
|
|
94
|
+
--agents <csv> Comma-separated agent ids. See list below.
|
|
95
|
+
--tracker <name> Stack tracker: linear|jira|github-issues|azure-boards|clickup|spreadsheet|none
|
|
96
|
+
--ci <name> Stack CI: gh-actions|gitlab-ci|buildkite|circleci|azure-pipelines|jenkins|manual
|
|
97
|
+
--preset <name> Stack preset: web-app|api-backend|mobile-app|cli|monorepo|adoption-minimum
|
|
98
|
+
--language <name> Stack language: ts|js|py|go|rust|java|kotlin|swift|dart|multi
|
|
99
|
+
--channel <name> Override api.channel: stable|edge
|
|
100
|
+
--copy-rules Install collection/agent-rules-<agent> files at their install_target
|
|
101
|
+
--copy-playbook Fetch collection/adoption-playbook into .ship/cache/ (skipped on 404)
|
|
102
|
+
--bootstrap Render CI/tracker scaffolding (mobile-app+gh-actions+linear skeletons today;
|
|
103
|
+
other combos emit SHIP_BOOTSTRAP_PLAN.md)
|
|
104
|
+
--telemetry on|off|ask — override the interactive telemetry prompt
|
|
105
|
+
--cwd Target repo root
|
|
106
|
+
|
|
107
|
+
SUPPORTED AGENTS
|
|
108
|
+
cursor, codex, claude, aider, cline, continue, windsurf, zed,
|
|
109
|
+
gemini, opencode, copilot, cursor-cloud, agents-md, claude-md
|
|
36
110
|
|
|
37
|
-
For HTTP schemas see
|
|
111
|
+
For HTTP schemas see artifacts/tools/methodology-api/ARTIFACT.md in the Ship repo.
|
|
112
|
+
Package: @elmundi/ship-cli (binary: shipctl).
|
|
38
113
|
`);
|
|
39
114
|
}
|