@pebblehouse/odin-cli 0.18.0 → 0.18.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/dist/index.js +42 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -384,7 +384,8 @@ async function apiRequest(path, options = {}) {
|
|
|
384
384
|
}
|
|
385
385
|
const headers = {
|
|
386
386
|
Authorization: `Bearer ${token}`,
|
|
387
|
-
"Content-Type": "application/json"
|
|
387
|
+
"Content-Type": "application/json",
|
|
388
|
+
"X-Odin-Client": "cli"
|
|
388
389
|
};
|
|
389
390
|
const res = await fetch(url, {
|
|
390
391
|
method,
|
|
@@ -1190,6 +1191,7 @@ blueprintsCmd.command("assets <id>").description("List assets for a blueprint").
|
|
|
1190
1191
|
// src/commands/bets.ts
|
|
1191
1192
|
import { Command as Command14 } from "commander";
|
|
1192
1193
|
import { readFileSync as readFileSync10 } from "fs";
|
|
1194
|
+
import { randomUUID } from "crypto";
|
|
1193
1195
|
|
|
1194
1196
|
// src/commands/bet-dependencies.ts
|
|
1195
1197
|
import { Command as Command13 } from "commander";
|
|
@@ -1223,8 +1225,9 @@ depsCmd.command("remove <slug> <bet-id>").description("Remove a blocked-by depen
|
|
|
1223
1225
|
|
|
1224
1226
|
// src/commands/bets.ts
|
|
1225
1227
|
var betsCmd = new Command14("bets").description("Manage bets");
|
|
1228
|
+
var STAGE_HELP = "pitch, shaping, ready, planning, building, review, qa, shipping, done, archived";
|
|
1226
1229
|
withListFlags(
|
|
1227
|
-
betsCmd.command("list <slug>").description("List bets for a pebble").option("--status <status>",
|
|
1230
|
+
betsCmd.command("list <slug>").description("List bets for a pebble").option("--status <status>", `Stage (${STAGE_HELP})`)
|
|
1228
1231
|
).action(async (slug, opts) => {
|
|
1229
1232
|
const resolved = resolveListParams(opts);
|
|
1230
1233
|
if (resolved.fatal) {
|
|
@@ -1242,21 +1245,20 @@ betsCmd.command("get <slug> <id>").description("Get a bet by ID").action(async (
|
|
|
1242
1245
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1243
1246
|
if (res.error) process.exit(1);
|
|
1244
1247
|
});
|
|
1245
|
-
betsCmd.command("create <slug> <title>").description("Create a bet").option("--source <source>", "Source agent", "claude-code").option("--content <content>", "Bet content").option("--file <path>", "Read content from file").option("--
|
|
1248
|
+
betsCmd.command("create <slug> <title>").description("Create a bet").option("--source <source>", "Source agent", "claude-code").option("--content <content>", "Bet content").option("--file <path>", "Read content from file").option("--priority <priority>", "Bet priority").option("--appetite <appetite>", "Bet appetite").option("--follow-up-of <key-or-uuid>", "Allocate a suffixed follow-up ticket in this ticket's family (e.g. ODIN-12 \u2192 ODIN-12A)").action(async (slug, title, opts) => {
|
|
1246
1249
|
const content = opts.file ? readFileSync10(opts.file, "utf-8") : opts.content ?? "";
|
|
1247
1250
|
const res = await apiRequest(`/pebbles/${slug}/bets`, {
|
|
1248
1251
|
method: "POST",
|
|
1249
|
-
body: { title, source: opts.source, content,
|
|
1252
|
+
body: { title, source: opts.source, content, priority: opts.priority, appetite: opts.appetite, follow_up_of: opts.followUpOf }
|
|
1250
1253
|
});
|
|
1251
1254
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1252
1255
|
if (res.error) process.exit(1);
|
|
1253
1256
|
});
|
|
1254
|
-
betsCmd.command("update <slug> <id>").description("Update a bet").option("--title <title>", "New title").option("--content <content>", "New content").option("--file <path>", "Read content from file").option("--
|
|
1257
|
+
betsCmd.command("update <slug> <id>").description("Update a bet's title, content, priority or appetite (stage changes: odin bets transition)").option("--title <title>", "New title").option("--content <content>", "New content").option("--file <path>", "Read content from file").option("--priority <priority>", "New priority").option("--appetite <appetite>", "New appetite").action(async (slug, id, opts) => {
|
|
1255
1258
|
const body = {};
|
|
1256
1259
|
if (opts.title) body.title = opts.title;
|
|
1257
1260
|
if (opts.file) body.content = readFileSync10(opts.file, "utf-8");
|
|
1258
1261
|
else if (opts.content) body.content = opts.content;
|
|
1259
|
-
if (opts.status) body.status = opts.status;
|
|
1260
1262
|
if (opts.priority) body.priority = opts.priority;
|
|
1261
1263
|
if (opts.appetite) body.appetite = opts.appetite;
|
|
1262
1264
|
const res = await apiRequest(`/pebbles/${slug}/bets/${id}`, {
|
|
@@ -1274,6 +1276,37 @@ betsCmd.command("delete <slug> <id>").description("Delete a bet").action(async (
|
|
|
1274
1276
|
if (res.error) process.exit(1);
|
|
1275
1277
|
});
|
|
1276
1278
|
betsCmd.addCommand(depsCmd);
|
|
1279
|
+
betsCmd.command("transition <slug> <id>").description("Apply a guarded lifecycle operation (never a raw status write)").requiredOption("--op <operation>", "start_shaping | approve_ready | handoff | complete | return_to_shaping | archive | correct_stage | open_blocker | resolve_blocker | set_queued | clear_queued").requiredOption("--expected-revision <n>", "lifecycle_revision from `odin bets get`").option("--to <stage>", `Target stage for handoff/correct_stage (${STAGE_HELP})`).option("--reason <text>", "Reason (required for return loops, skips and return_to_shaping)").option("--note <text>", "Optional correction/resolution note").option("--actor <label>", "Persona slug or handle recorded on the event", "cli").option("--actor-kind <kind>", "human | persona", "human").option("--idempotency-key <key>", "Reuse the same key when retrying after a timeout").option("--payload <json>", "Extra payload JSON merged last (check_token/exceptions, category, blocker_id, owner_persona\u2026)").action(async (slug, id, opts) => {
|
|
1280
|
+
let extra = {};
|
|
1281
|
+
if (opts.payload) {
|
|
1282
|
+
try {
|
|
1283
|
+
extra = JSON.parse(opts.payload);
|
|
1284
|
+
} catch {
|
|
1285
|
+
process.stderr.write("--payload must be valid JSON\n");
|
|
1286
|
+
process.exit(2);
|
|
1287
|
+
}
|
|
1288
|
+
}
|
|
1289
|
+
const payload = { ...opts.to ? { to_stage: opts.to } : {}, ...opts.reason ? { reason: opts.reason } : {}, ...opts.note ? { note: opts.note } : {}, ...extra };
|
|
1290
|
+
const body = { operation: opts.op, expected_revision: Number(opts.expectedRevision), idempotency_key: opts.idempotencyKey ?? randomUUID(), actor_label: opts.actor, actor_kind: opts.actorKind, payload };
|
|
1291
|
+
const res = await apiRequest(`/pebbles/${slug}/bets/${id}/lifecycle`, { method: "POST", body });
|
|
1292
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1293
|
+
if (res.error) process.exit(1);
|
|
1294
|
+
});
|
|
1295
|
+
betsCmd.command("ready-check <slug> <id>").description("Evaluate Ready prerequisites").action(async (slug, id) => {
|
|
1296
|
+
const res = await apiRequest(`/pebbles/${slug}/bets/${id}/ready-check`);
|
|
1297
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1298
|
+
if (res.error) process.exit(1);
|
|
1299
|
+
});
|
|
1300
|
+
betsCmd.command("activity <slug> <id>").description("Unified Activity (events + comments), newest first").option("--cursor <cursor>").option("--limit <n>", "1-100", "30").action(async (slug, id, opts) => {
|
|
1301
|
+
const res = await apiRequest(`/pebbles/${slug}/bets/${id}/activity`, { params: { ...opts.cursor ? { cursor: opts.cursor } : {}, limit: opts.limit } });
|
|
1302
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1303
|
+
if (res.error) process.exit(1);
|
|
1304
|
+
});
|
|
1305
|
+
betsCmd.command("lifecycle-contract").description("Stages, transition matrix, Ready requirements, blocker categories").action(async () => {
|
|
1306
|
+
const res = await apiRequest(`/bet-lifecycle/contract`);
|
|
1307
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1308
|
+
if (res.error) process.exit(1);
|
|
1309
|
+
});
|
|
1277
1310
|
|
|
1278
1311
|
// src/commands/tickets.ts
|
|
1279
1312
|
import { Command as Command15 } from "commander";
|
|
@@ -1364,11 +1397,11 @@ tasksCmd.command("update <slug> <id>").description("Update a task").option("--ti
|
|
|
1364
1397
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1365
1398
|
if (res.error) process.exit(1);
|
|
1366
1399
|
});
|
|
1367
|
-
tasksCmd.command("promote <slug> <id>").description("Promote a task to a bet, keeping its ticket ID and history").option("--content <content>", "Shaped bet markdown").option("--file <path>", "Read bet markdown from file").option("--
|
|
1400
|
+
tasksCmd.command("promote <slug> <id>").description("Promote a task to a bet, keeping its ticket ID and history (the bet starts in pitch)").option("--content <content>", "Shaped bet markdown").option("--file <path>", "Read bet markdown from file").option("--priority <priority>", "Bet priority").action(async (slug, id, opts) => {
|
|
1368
1401
|
const content = opts.file ? readFileSync12(opts.file, "utf-8") : opts.content;
|
|
1369
1402
|
const res = await apiRequest(`/pebbles/${slug}/tasks/${id}/promote`, {
|
|
1370
1403
|
method: "POST",
|
|
1371
|
-
body: { content,
|
|
1404
|
+
body: { content, priority: opts.priority }
|
|
1372
1405
|
});
|
|
1373
1406
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1374
1407
|
if (res.error) process.exit(1);
|
|
@@ -2040,7 +2073,7 @@ Re-run \`odin pebble new ${name}\` after acting.
|
|
|
2040
2073
|
const body = renderInitBetBody(slug, report);
|
|
2041
2074
|
const bet = await apiRequest(`/pebbles/${slug}/bets`, {
|
|
2042
2075
|
method: "POST",
|
|
2043
|
-
body: { title: `Initialize ${slug}`, source: "odin-cli", content: body
|
|
2076
|
+
body: { title: `Initialize ${slug}`, source: "odin-cli", content: body }
|
|
2044
2077
|
});
|
|
2045
2078
|
process.stdout.write(JSON.stringify(bet) + "\n");
|
|
2046
2079
|
if (opts.run && bet.data?.id) {
|