@elmundi/ship-cli 0.11.2 → 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 +255 -22
- package/bin/shipctl.mjs +10 -7
- package/lib/bootstrap/render.mjs +49 -0
- package/lib/commands/callback.mjs +457 -17
- package/lib/commands/config.mjs +1 -1
- package/lib/commands/docs.mjs +4 -4
- package/lib/commands/help.mjs +137 -77
- package/lib/commands/kickoff.mjs +3 -3
- package/lib/commands/knowledge.mjs +211 -17
- package/lib/commands/lanes.mjs +36 -11
- package/lib/commands/manifest-catalog.mjs +5 -5
- package/lib/commands/patterns.mjs +5 -5
- package/lib/commands/run.mjs +329 -89
- package/lib/commands/search.mjs +2 -2
- package/lib/commands/sync.mjs +83 -8
- package/lib/commands/trigger.mjs +196 -0
- package/lib/config/migrate.mjs +13 -5
- package/lib/config/schema.mjs +253 -2
- package/lib/state/idempotency.mjs +1 -1
- package/lib/templates.mjs +3 -3
- package/package.json +2 -2
package/lib/commands/help.mjs
CHANGED
|
@@ -1,85 +1,140 @@
|
|
|
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
|
-
— versioned artifact body (POST /fetch { kind, id, version? })
|
|
9
|
-
3) shipctl 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.
|
|
10
8
|
|
|
11
|
-
|
|
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
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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, …)
|
|
22
|
-
|
|
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]
|
|
29
|
-
|
|
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'.
|
|
33
|
-
|
|
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)
|
|
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.
|
|
79
18
|
|
|
80
19
|
GLOBAL FLAGS
|
|
81
|
-
--base-url URL Methodology API (default: SHIP_API_BASE or
|
|
82
|
-
|
|
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
|
|
25
|
+
|
|
26
|
+
COMMANDS
|
|
27
|
+
|
|
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.
|
|
47
|
+
|
|
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.
|
|
70
|
+
|
|
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.
|
|
102
|
+
|
|
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.
|
|
83
138
|
|
|
84
139
|
LOCAL TREE
|
|
85
140
|
pattern / tool / collection list|show|fetch scan
|
|
@@ -108,7 +163,12 @@ SUPPORTED AGENTS
|
|
|
108
163
|
cursor, codex, claude, aider, cline, continue, windsurf, zed,
|
|
109
164
|
gemini, opencode, copilot, cursor-cloud, agents-md, claude-md
|
|
110
165
|
|
|
111
|
-
|
|
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.
|
|
171
|
+
|
|
112
172
|
Package: @elmundi/ship-cli (binary: shipctl).
|
|
113
173
|
`);
|
|
114
174
|
}
|
package/lib/commands/kickoff.mjs
CHANGED
|
@@ -25,7 +25,7 @@ USAGE
|
|
|
25
25
|
shipctl kickoff [--pattern <id>] [--version <semver>] [--raw] [--json] [--cwd <dir>]
|
|
26
26
|
|
|
27
27
|
DEFAULTS
|
|
28
|
-
--pattern kickoff
|
|
28
|
+
--pattern common-kickoff
|
|
29
29
|
|
|
30
30
|
FLAGS
|
|
31
31
|
--pattern Catalog pattern id (folder under artifacts/patterns/).
|
|
@@ -39,7 +39,7 @@ When .ship/config.yml sets stack.agent.provider, a one-line hint is written
|
|
|
39
39
|
to stderr so logs show which agent the repo is wired for — unless --json.
|
|
40
40
|
|
|
41
41
|
EXAMPLE (workflow step)
|
|
42
|
-
shipctl kickoff --pattern kickoff > kickoff.md
|
|
42
|
+
shipctl kickoff --pattern common-kickoff > kickoff.md
|
|
43
43
|
# …concatenate workload pattern + kickoff.md into your agent invocation…
|
|
44
44
|
`);
|
|
45
45
|
}
|
|
@@ -64,7 +64,7 @@ function resolveMethodologyBase(ctx, config) {
|
|
|
64
64
|
|
|
65
65
|
function parseKickoffArgs(rest) {
|
|
66
66
|
const out = {
|
|
67
|
-
patternId: "kickoff",
|
|
67
|
+
patternId: "common-kickoff",
|
|
68
68
|
version: null,
|
|
69
69
|
raw: false,
|
|
70
70
|
json: false,
|
|
@@ -1,25 +1,18 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* `shipctl knowledge` — manage workspace knowledge buckets.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* ``
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* - ``code-style.md`` — languages, naming, imports, tests, review checklist
|
|
11
|
-
* - ``ui-runbook.md`` — design-system usage, states, perf budgets
|
|
12
|
-
*
|
|
13
|
-
* The CLI exists so CI pipelines (and re-adoption flows where the UI
|
|
14
|
-
* wizard isn't the natural entry point) can wire the buckets without a
|
|
15
|
-
* browser round-trip.
|
|
4
|
+
* The canonical knowledge surface is now Ship-owned:
|
|
5
|
+
* ``knowledge_buckets`` contain ``bucket_articles`` and
|
|
6
|
+
* ``knowledge_sources`` records where each article came from. The
|
|
7
|
+
* historical ``init`` command remains as a compatibility wrapper for
|
|
8
|
+
* starter PRs, while ``bootstrap`` is the GitHub Actions entry point
|
|
9
|
+
* that opens the generated knowledge PR after wizard seed merge.
|
|
16
10
|
*
|
|
17
11
|
* Usage:
|
|
18
12
|
*
|
|
19
|
-
* shipctl knowledge
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
* [--json]
|
|
13
|
+
* shipctl knowledge fetch repository-context --workspace <id>
|
|
14
|
+
* shipctl knowledge bootstrap --workspace <id> --repo <id|owner/name>
|
|
15
|
+
* shipctl knowledge refresh-intel --workspace <id> --repo <id|owner/name>
|
|
23
16
|
*
|
|
24
17
|
* Auth: bearer token from ``SHIP_API_TOKEN`` (the same env var the
|
|
25
18
|
* console docs describe for CLI sessions minted under Settings →
|
|
@@ -70,6 +63,18 @@ export async function knowledgeCommand(ctx, rest) {
|
|
|
70
63
|
await knowledgeInitCommand(ctx, args);
|
|
71
64
|
return;
|
|
72
65
|
}
|
|
66
|
+
if (sub === "fetch") {
|
|
67
|
+
await knowledgeFetchCommand(ctx, args);
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (sub === "bootstrap") {
|
|
71
|
+
await knowledgeBootstrapCommand(ctx, args);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
if (sub === "refresh-intel" || sub === "refresh-context") {
|
|
75
|
+
await knowledgeRefreshIntelCommand(ctx, args);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
73
78
|
console.error(
|
|
74
79
|
`Unknown 'shipctl knowledge' subcommand: ${sub}\nRun: shipctl knowledge --help`,
|
|
75
80
|
);
|
|
@@ -82,6 +87,11 @@ function printKnowledgeHelp() {
|
|
|
82
87
|
SUBCOMMANDS
|
|
83
88
|
shipctl knowledge init [--workspace <id>] [--repo <id|owner/name>]
|
|
84
89
|
[--only <csv>] [--json]
|
|
90
|
+
shipctl knowledge fetch <bucket-slug> [--workspace <id>] [--json]
|
|
91
|
+
shipctl knowledge bootstrap [--workspace <id>] [--repo <id|owner/name>]
|
|
92
|
+
[--json]
|
|
93
|
+
shipctl knowledge refresh-intel [--workspace <id>] [--repo <id|owner/name>]
|
|
94
|
+
[--json]
|
|
85
95
|
|
|
86
96
|
INIT FLAGS
|
|
87
97
|
--workspace <id> Workspace UUID. Defaults to the only workspace
|
|
@@ -154,9 +164,116 @@ async function knowledgeInitCommand(ctx, args) {
|
|
|
154
164
|
}
|
|
155
165
|
const files = Array.isArray(result.files) ? result.files : [];
|
|
156
166
|
console.log(
|
|
157
|
-
`Seeded knowledge
|
|
167
|
+
`Seeded compatibility knowledge files for workspace ${workspaceId} / repo ${repoId}:\n` +
|
|
158
168
|
` PR #${result.pr_number}: ${result.pr_url}\n` +
|
|
159
169
|
` Branch: ${result.branch}\n` +
|
|
170
|
+
` Files: ${files.join(", ") || "(none)"}\n` +
|
|
171
|
+
`\nShip-owned repository context is refreshed separately with:\n` +
|
|
172
|
+
` shipctl knowledge refresh-intel --workspace ${workspaceId} --repo ${repoId}`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
async function knowledgeFetchCommand(ctx, args) {
|
|
177
|
+
const opts = parseFetchArgs(args);
|
|
178
|
+
const baseUrl = resolveBaseUrl(opts.baseUrl || ctx.baseUrl);
|
|
179
|
+
const token = requireToken();
|
|
180
|
+
let workspaceId = opts.workspace;
|
|
181
|
+
if (!workspaceId) {
|
|
182
|
+
workspaceId = await resolveSoleWorkspace(baseUrl, token);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const [bucket, articles, sources] = await Promise.all([
|
|
186
|
+
apiGetJson(
|
|
187
|
+
baseUrl,
|
|
188
|
+
`/v1/workspaces/${encodeURIComponent(workspaceId)}/buckets/${encodeURIComponent(opts.slug)}`,
|
|
189
|
+
token,
|
|
190
|
+
),
|
|
191
|
+
apiGetJson(
|
|
192
|
+
baseUrl,
|
|
193
|
+
`/v1/workspaces/${encodeURIComponent(workspaceId)}/buckets/${encodeURIComponent(opts.slug)}/articles`,
|
|
194
|
+
token,
|
|
195
|
+
),
|
|
196
|
+
apiGetJson(
|
|
197
|
+
baseUrl,
|
|
198
|
+
`/v1/workspaces/${encodeURIComponent(workspaceId)}/buckets/${encodeURIComponent(opts.slug)}/sources`,
|
|
199
|
+
token,
|
|
200
|
+
),
|
|
201
|
+
]);
|
|
202
|
+
|
|
203
|
+
const result = { bucket, articles, sources };
|
|
204
|
+
if (ctx.json || opts.json) {
|
|
205
|
+
console.log(JSON.stringify(result, null, 2));
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log(`${bucket.name} (${bucket.slug})`);
|
|
210
|
+
console.log(` scope: ${bucket.scope_kind} source: ${bucket.source_kind}`);
|
|
211
|
+
console.log(` articles: ${Array.isArray(articles) ? articles.length : 0}`);
|
|
212
|
+
console.log(` sources: ${Array.isArray(sources) ? sources.length : 0}`);
|
|
213
|
+
for (const article of Array.isArray(articles) ? articles : []) {
|
|
214
|
+
console.log(`\n## ${article.title} (${article.slug})`);
|
|
215
|
+
console.log(String(article.body_md || "").trim());
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
async function knowledgeRefreshIntelCommand(ctx, args) {
|
|
220
|
+
const opts = parseRefreshArgs(args);
|
|
221
|
+
const baseUrl = resolveBaseUrl(opts.baseUrl || ctx.baseUrl);
|
|
222
|
+
const token = requireToken();
|
|
223
|
+
let workspaceId = opts.workspace;
|
|
224
|
+
if (!workspaceId) {
|
|
225
|
+
workspaceId = await resolveSoleWorkspace(baseUrl, token);
|
|
226
|
+
}
|
|
227
|
+
const repoId = await resolveRepoId(baseUrl, token, workspaceId, opts.repo);
|
|
228
|
+
const result = await apiPostJson(
|
|
229
|
+
baseUrl,
|
|
230
|
+
`/v1/workspaces/${encodeURIComponent(workspaceId)}/repos/${encodeURIComponent(repoId)}/intel/harvest`,
|
|
231
|
+
{},
|
|
232
|
+
token,
|
|
233
|
+
);
|
|
234
|
+
if (ctx.json || opts.json) {
|
|
235
|
+
console.log(JSON.stringify(result, null, 2));
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
const where = result.enqueued
|
|
239
|
+
? `queued as job ${result.job_id || "(unknown)"}`
|
|
240
|
+
: `completed inline, intel_id=${result.intel_id || "(none)"}`;
|
|
241
|
+
console.log(
|
|
242
|
+
`Repository context refresh for workspace ${workspaceId} / repo ${repoId}: ${where}\n` +
|
|
243
|
+
`Fetch it with: shipctl knowledge fetch repository-context --workspace ${workspaceId}`,
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
async function knowledgeBootstrapCommand(ctx, args) {
|
|
248
|
+
const opts = parseBootstrapArgs(args);
|
|
249
|
+
const baseUrl = resolveBaseUrl(opts.baseUrl || ctx.baseUrl);
|
|
250
|
+
const token = requireToken();
|
|
251
|
+
let workspaceId = opts.workspace;
|
|
252
|
+
if (!workspaceId) {
|
|
253
|
+
workspaceId = await resolveSoleWorkspace(baseUrl, token);
|
|
254
|
+
}
|
|
255
|
+
const repoId = await resolveRepoId(baseUrl, token, workspaceId, opts.repo);
|
|
256
|
+
const result = await apiPostJson(
|
|
257
|
+
baseUrl,
|
|
258
|
+
`/v1/workspaces/${encodeURIComponent(workspaceId)}/repos/${encodeURIComponent(repoId)}/knowledge/bootstrap`,
|
|
259
|
+
{},
|
|
260
|
+
token,
|
|
261
|
+
);
|
|
262
|
+
if (ctx.json || opts.json) {
|
|
263
|
+
console.log(JSON.stringify(result, null, 2));
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
const files = Array.isArray(result.files) ? result.files : [];
|
|
267
|
+
if (result.status === "already_done") {
|
|
268
|
+
console.log(
|
|
269
|
+
`Knowledge bootstrap already completed for workspace ${workspaceId} / repo ${repoId}:\n` +
|
|
270
|
+
` PR #${result.pr_number || "?"}: ${result.pr_url || "(unknown)"}`,
|
|
271
|
+
);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
console.log(
|
|
275
|
+
`Knowledge bootstrap opened PR #${result.pr_number} for workspace ${workspaceId} / repo ${repoId}:\n` +
|
|
276
|
+
` ${result.pr_url}\n` +
|
|
160
277
|
` Files: ${files.join(", ") || "(none)"}`,
|
|
161
278
|
);
|
|
162
279
|
}
|
|
@@ -224,6 +341,83 @@ function parseInitArgs(args) {
|
|
|
224
341
|
return out;
|
|
225
342
|
}
|
|
226
343
|
|
|
344
|
+
function parseFetchArgs(args) {
|
|
345
|
+
const out = parseCommonArgs(args, { slug: null });
|
|
346
|
+
if (!out.slug) {
|
|
347
|
+
console.error("Usage: shipctl knowledge fetch <bucket-slug> [--workspace <id>] [--json]");
|
|
348
|
+
process.exit(1);
|
|
349
|
+
}
|
|
350
|
+
return out;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
function parseRefreshArgs(args) {
|
|
354
|
+
return parseCommonArgs(args, { repo: null });
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function parseBootstrapArgs(args) {
|
|
358
|
+
return parseCommonArgs(args, { repo: null });
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function parseCommonArgs(args, extra) {
|
|
362
|
+
const out = {
|
|
363
|
+
workspace: null,
|
|
364
|
+
baseUrl: null,
|
|
365
|
+
json: false,
|
|
366
|
+
...extra,
|
|
367
|
+
};
|
|
368
|
+
const copy = [...args];
|
|
369
|
+
const consume = (flag, key) => {
|
|
370
|
+
if (copy[0] === flag && copy[1] !== undefined) {
|
|
371
|
+
copy.shift();
|
|
372
|
+
out[key] = String(copy.shift());
|
|
373
|
+
return true;
|
|
374
|
+
}
|
|
375
|
+
const p = `${flag}=`;
|
|
376
|
+
if (copy[0] && copy[0].startsWith(p)) {
|
|
377
|
+
out[key] = copy[0].slice(p.length);
|
|
378
|
+
copy.shift();
|
|
379
|
+
return true;
|
|
380
|
+
}
|
|
381
|
+
return false;
|
|
382
|
+
};
|
|
383
|
+
while (copy.length) {
|
|
384
|
+
if (
|
|
385
|
+
consume("--workspace", "workspace") ||
|
|
386
|
+
consume("--repo", "repo") ||
|
|
387
|
+
consume("--base-url", "baseUrl")
|
|
388
|
+
) {
|
|
389
|
+
continue;
|
|
390
|
+
}
|
|
391
|
+
if (copy[0] === "--json") {
|
|
392
|
+
out.json = true;
|
|
393
|
+
copy.shift();
|
|
394
|
+
continue;
|
|
395
|
+
}
|
|
396
|
+
if (!String(copy[0]).startsWith("-") && "slug" in out && out.slug === null) {
|
|
397
|
+
out.slug = String(copy.shift());
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (copy[0] === "--help" || copy[0] === "-h") {
|
|
401
|
+
printKnowledgeHelp();
|
|
402
|
+
process.exit(0);
|
|
403
|
+
}
|
|
404
|
+
console.error(`Unknown flag: ${copy[0]}`);
|
|
405
|
+
process.exit(1);
|
|
406
|
+
}
|
|
407
|
+
return out;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
function requireToken() {
|
|
411
|
+
const token = process.env.SHIP_API_TOKEN || "";
|
|
412
|
+
if (!token) {
|
|
413
|
+
console.error(
|
|
414
|
+
"SHIP_API_TOKEN is required. Mint one at /settings in the Ship console.",
|
|
415
|
+
);
|
|
416
|
+
process.exit(1);
|
|
417
|
+
}
|
|
418
|
+
return token;
|
|
419
|
+
}
|
|
420
|
+
|
|
227
421
|
/**
|
|
228
422
|
* @param {string|null|undefined} explicit
|
|
229
423
|
* @returns {string}
|
package/lib/commands/lanes.mjs
CHANGED
|
@@ -24,7 +24,13 @@ import path from "node:path";
|
|
|
24
24
|
import YAML from "yaml";
|
|
25
25
|
|
|
26
26
|
import { findShipRoot, readConfig } from "../config/io.mjs";
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
validateConfig,
|
|
29
|
+
CONFIG_SCHEMA_VERSION,
|
|
30
|
+
lanePatterns,
|
|
31
|
+
lanePrimaryPattern,
|
|
32
|
+
laneFanout,
|
|
33
|
+
} from "../config/schema.mjs";
|
|
28
34
|
|
|
29
35
|
const EXIT_OK = 0;
|
|
30
36
|
const EXIT_USAGE = 2;
|
|
@@ -41,7 +47,11 @@ const DEFAULT_REUSABLE_REPO = "ship";
|
|
|
41
47
|
const DEFAULT_REUSABLE_PATH = ".github/workflows/run-agent.yml";
|
|
42
48
|
|
|
43
49
|
function printHelp() {
|
|
44
|
-
console.log(`shipctl lanes — manage GitHub Actions caller workflows
|
|
50
|
+
console.log(`shipctl lanes — manage GitHub Actions caller workflows for the
|
|
51
|
+
lanes declared in .ship/config.yml (each lane shows up as an
|
|
52
|
+
Automation in the operator console; one Run is recorded per dispatch).
|
|
53
|
+
|
|
54
|
+
Aliases: shipctl automations <subcmd> (operator-friendly name; both work)
|
|
45
55
|
|
|
46
56
|
USAGE
|
|
47
57
|
shipctl lanes install [--only <id,id>] [--ref <git-ref>] [--owner <gh-owner>]
|
|
@@ -174,14 +184,25 @@ async function installCmd(ctx, rest) {
|
|
|
174
184
|
function listCmd(ctx, rest) {
|
|
175
185
|
const args = parseListArgs(rest);
|
|
176
186
|
const { config } = loadConfig(args.cwd);
|
|
177
|
-
const rows = Object.entries(config.lanes || {}).map(([id, lane]) =>
|
|
178
|
-
lane
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
187
|
+
const rows = Object.entries(config.lanes || {}).map(([id, lane]) => {
|
|
188
|
+
const pats = lanePatterns(lane);
|
|
189
|
+
return {
|
|
190
|
+
lane: id,
|
|
191
|
+
kind: lane.kind,
|
|
192
|
+
// ``pattern`` keeps the single-string shape for humans/scripts
|
|
193
|
+
// that eyeball the first pattern; ``patterns`` always lists all
|
|
194
|
+
// so multi-pattern lanes (RFC-0008 C3.1) surface correctly.
|
|
195
|
+
pattern: pats[0] || null,
|
|
196
|
+
patterns: pats,
|
|
197
|
+
// ``fanout`` resolves to the runtime default (``matrix``) when
|
|
198
|
+
// the lane doesn't declare one, so the workflow plan step
|
|
199
|
+
// doesn't have to branch on "is this key missing?" (RFC-0008 C3.2).
|
|
200
|
+
fanout: laneFanout(lane),
|
|
201
|
+
on: lane.kind === "event" ? lane.on || null : null,
|
|
202
|
+
cron: lane.kind === "schedule" ? lane.cron || null : null,
|
|
203
|
+
idempotency_key: lane.kind === "once" ? lane.idempotency?.key || null : null,
|
|
204
|
+
};
|
|
205
|
+
});
|
|
185
206
|
if (ctx.json || args.json) {
|
|
186
207
|
console.log(JSON.stringify({ ok: true, lanes: rows }, null, 2));
|
|
187
208
|
return;
|
|
@@ -197,8 +218,12 @@ function listCmd(ctx, rest) {
|
|
|
197
218
|
: row.kind === "schedule"
|
|
198
219
|
? `cron ${JSON.stringify(row.cron)}`
|
|
199
220
|
: `once ${row.idempotency_key || ""}`;
|
|
221
|
+
const patternLabel =
|
|
222
|
+
row.patterns.length > 1
|
|
223
|
+
? `patterns=[${row.patterns.join(", ")}]`
|
|
224
|
+
: `pattern=${row.pattern || "-"}`;
|
|
200
225
|
console.log(
|
|
201
|
-
` ${row.lane.padEnd(28)} kind=${row.kind.padEnd(9)} ${trigger} (
|
|
226
|
+
` ${row.lane.padEnd(28)} kind=${row.kind.padEnd(9)} ${trigger} (${patternLabel})`,
|
|
202
227
|
);
|
|
203
228
|
}
|
|
204
229
|
}
|
|
@@ -35,10 +35,10 @@ export async function resourceManifestCommand(resource, ctx, args) {
|
|
|
35
35
|
if (!sub || sub === "help") {
|
|
36
36
|
const plural = pluralFor(spec.fetchKind);
|
|
37
37
|
console.log(`Usage:
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
42
|
|
|
43
43
|
With a local Ship tree (cwd or SHIP_REPO): scans artifacts/${plural}/<id>/ARTIFACT.md on disk.
|
|
44
44
|
Otherwise: methodology API (GET /${spec.apiPath}, POST /fetch for fetch, POST /search for search).
|
|
@@ -47,7 +47,7 @@ In a Ship workspace (.ship/config.yml), 'fetch' writes the artifact to
|
|
|
47
47
|
.ship/cache/<kind>/<id>@<version>/ARTIFACT.md and prints a 'cached:' line. Pass
|
|
48
48
|
--print to also echo the body on stdout.
|
|
49
49
|
|
|
50
|
-
Plural alias:
|
|
50
|
+
Plural alias: shipctl ${spec.apiPath} …
|
|
51
51
|
|
|
52
52
|
Global flags: --base-url URL --json`);
|
|
53
53
|
return;
|
|
@@ -132,15 +132,15 @@ export async function patternCommand(ctx, args) {
|
|
|
132
132
|
const [sub, ...rest] = args;
|
|
133
133
|
if (!sub || sub === "help") {
|
|
134
134
|
console.log(`Usage:
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
shipctl pattern list
|
|
136
|
+
shipctl pattern show <id>
|
|
137
|
+
shipctl pattern fetch <id>
|
|
138
|
+
shipctl pattern search <query> [--top-k N]
|
|
139
139
|
|
|
140
140
|
With a local Ship tree (cwd or SHIP_REPO): list/show/fetch scan artifacts/patterns/<id>/ARTIFACT.md on disk.
|
|
141
141
|
Otherwise: methodology API (GET /patterns, POST /fetch for fetch, POST /search for search).
|
|
142
142
|
|
|
143
|
-
Plural alias:
|
|
143
|
+
Plural alias: shipctl patterns …
|
|
144
144
|
|
|
145
145
|
Global flags: --base-url URL --json`);
|
|
146
146
|
return;
|