@pebblehouse/odin-cli 0.18.0 → 0.18.1
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 +38 -4
- 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 (legacy: shaped, betting, active, blocked)";
|
|
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>", `Status (${STAGE_HELP})`)
|
|
1228
1231
|
).action(async (slug, opts) => {
|
|
1229
1232
|
const resolved = resolveListParams(opts);
|
|
1230
1233
|
if (resolved.fatal) {
|
|
@@ -1242,7 +1245,7 @@ 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("--status <status>", "
|
|
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("--status <status>", "pitch (default) or shaped (transitional)").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",
|
|
@@ -1251,7 +1254,7 @@ betsCmd.command("create <slug> <title>").description("Create a bet").option("--s
|
|
|
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("--status <status>",
|
|
1257
|
+
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("--status <status>", `New status \u2014 legacy-governed bets only; lifecycle-governed bets use \`odin bets transition\` (${STAGE_HELP})`).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");
|
|
@@ -1274,6 +1277,37 @@ betsCmd.command("delete <slug> <id>").description("Delete a bet").action(async (
|
|
|
1274
1277
|
if (res.error) process.exit(1);
|
|
1275
1278
|
});
|
|
1276
1279
|
betsCmd.addCommand(depsCmd);
|
|
1280
|
+
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) => {
|
|
1281
|
+
let extra = {};
|
|
1282
|
+
if (opts.payload) {
|
|
1283
|
+
try {
|
|
1284
|
+
extra = JSON.parse(opts.payload);
|
|
1285
|
+
} catch {
|
|
1286
|
+
process.stderr.write("--payload must be valid JSON\n");
|
|
1287
|
+
process.exit(2);
|
|
1288
|
+
}
|
|
1289
|
+
}
|
|
1290
|
+
const payload = { ...opts.to ? { to_stage: opts.to } : {}, ...opts.reason ? { reason: opts.reason } : {}, ...opts.note ? { note: opts.note } : {}, ...extra };
|
|
1291
|
+
const body = { operation: opts.op, expected_revision: Number(opts.expectedRevision), idempotency_key: opts.idempotencyKey ?? randomUUID(), actor_label: opts.actor, actor_kind: opts.actorKind, payload };
|
|
1292
|
+
const res = await apiRequest(`/pebbles/${slug}/bets/${id}/lifecycle`, { method: "POST", body });
|
|
1293
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1294
|
+
if (res.error) process.exit(1);
|
|
1295
|
+
});
|
|
1296
|
+
betsCmd.command("ready-check <slug> <id>").description("Evaluate Ready prerequisites").action(async (slug, id) => {
|
|
1297
|
+
const res = await apiRequest(`/pebbles/${slug}/bets/${id}/ready-check`);
|
|
1298
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1299
|
+
if (res.error) process.exit(1);
|
|
1300
|
+
});
|
|
1301
|
+
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) => {
|
|
1302
|
+
const res = await apiRequest(`/pebbles/${slug}/bets/${id}/activity`, { params: { ...opts.cursor ? { cursor: opts.cursor } : {}, limit: opts.limit } });
|
|
1303
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1304
|
+
if (res.error) process.exit(1);
|
|
1305
|
+
});
|
|
1306
|
+
betsCmd.command("lifecycle-contract").description("Stages, transition matrix, Ready requirements, blocker categories").action(async () => {
|
|
1307
|
+
const res = await apiRequest(`/bet-lifecycle/contract`);
|
|
1308
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
1309
|
+
if (res.error) process.exit(1);
|
|
1310
|
+
});
|
|
1277
1311
|
|
|
1278
1312
|
// src/commands/tickets.ts
|
|
1279
1313
|
import { Command as Command15 } from "commander";
|