@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.
- package/README.md +651 -25
- package/bin/shipctl.mjs +168 -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 +422 -0
- package/lib/cache/store.mjs +422 -0
- package/lib/commands/bootstrap.mjs +4 -0
- package/lib/commands/callback.mjs +742 -0
- package/lib/commands/config.mjs +257 -0
- package/lib/commands/docs.mjs +4 -4
- package/lib/commands/doctor.mjs +583 -0
- package/lib/commands/feedback.mjs +355 -0
- package/lib/commands/help.mjs +159 -24
- package/lib/commands/init.mjs +830 -158
- package/lib/commands/kickoff.mjs +192 -0
- package/lib/commands/knowledge.mjs +562 -0
- package/lib/commands/lanes.mjs +527 -0
- package/lib/commands/manifest-catalog.mjs +106 -42
- package/lib/commands/migrate.mjs +204 -0
- package/lib/commands/new.mjs +452 -0
- package/lib/commands/patterns.mjs +14 -48
- package/lib/commands/run.mjs +857 -0
- package/lib/commands/search.mjs +2 -2
- package/lib/commands/sync.mjs +824 -0
- package/lib/commands/telemetry.mjs +390 -0
- package/lib/commands/trigger.mjs +196 -0
- package/lib/commands/verify.mjs +187 -0
- package/lib/config/io.mjs +232 -0
- package/lib/config/migrate.mjs +223 -0
- package/lib/config/schema.mjs +901 -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,174 @@
|
|
|
1
1
|
export function printHelp() {
|
|
2
|
-
console.log(`Ship
|
|
2
|
+
console.log(`shipctl — adopt Ship in a repo, sync the catalog, run lanes, report Runs.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
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
|
-
|
|
15
|
-
|
|
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
|
-
|
|
18
|
-
|
|
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
|
-
|
|
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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 /
|
|
28
|
-
|
|
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
|
|
32
|
-
--force
|
|
33
|
-
--dry-run
|
|
34
|
-
--
|
|
35
|
-
--
|
|
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
|
-
|
|
172
|
+
Package: @elmundi/ship-cli (binary: shipctl).
|
|
38
173
|
`);
|
|
39
174
|
}
|