@pebblehouse/odin-cli 0.1.1 → 0.2.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 +232 -3
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
|
-
import { Command as
|
|
4
|
+
import { Command as Command12 } from "commander";
|
|
5
5
|
|
|
6
6
|
// src/auth/login.ts
|
|
7
7
|
import { createServer } from "http";
|
|
@@ -209,6 +209,13 @@ docsCmd.command("create <slug> <type>").description("Create a document").require
|
|
|
209
209
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
210
210
|
if (res.error) process.exit(1);
|
|
211
211
|
});
|
|
212
|
+
docsCmd.command("delete <slug> <type>").description("Delete a document").action(async (slug, type) => {
|
|
213
|
+
const res = await apiRequest(`/pebbles/${slug}/documents/${type}`, {
|
|
214
|
+
method: "DELETE"
|
|
215
|
+
});
|
|
216
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
217
|
+
if (res.error) process.exit(1);
|
|
218
|
+
});
|
|
212
219
|
docsCmd.command("update <slug> <type>").description("Update document content").requiredOption("--file <path>", "Read content from file").option("--source <source>", "Source", "manual").action(async (slug, type, opts) => {
|
|
213
220
|
const content = readFileSync2(opts.file, "utf-8");
|
|
214
221
|
const res = await apiRequest(`/pebbles/${slug}/documents/${type}`, {
|
|
@@ -227,6 +234,11 @@ decisionsCmd.command("list <slug>").description("List decisions for a pebble").a
|
|
|
227
234
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
228
235
|
if (res.error) process.exit(1);
|
|
229
236
|
});
|
|
237
|
+
decisionsCmd.command("get <slug> <id>").description("Get a decision by ID").action(async (slug, id) => {
|
|
238
|
+
const res = await apiRequest(`/pebbles/${slug}/decisions/${id}`);
|
|
239
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
240
|
+
if (res.error) process.exit(1);
|
|
241
|
+
});
|
|
230
242
|
decisionsCmd.command("log <slug> <title>").description("Log a decision").option("--rationale <rationale>", "Decision rationale").option("--alternatives <alternatives>", "Alternatives considered").option("--session-id <id>", "Link to active session").action(async (slug, title, opts) => {
|
|
231
243
|
const res = await apiRequest(`/pebbles/${slug}/decisions`, {
|
|
232
244
|
method: "POST",
|
|
@@ -262,6 +274,11 @@ sessionsCmd.command("start <slug>").description("Start a session").option("--sou
|
|
|
262
274
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
263
275
|
if (res.error) process.exit(1);
|
|
264
276
|
});
|
|
277
|
+
sessionsCmd.command("list <slug>").description("List sessions for a pebble").action(async (slug) => {
|
|
278
|
+
const res = await apiRequest(`/sessions/by-pebble/${slug}`);
|
|
279
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
280
|
+
if (res.error) process.exit(1);
|
|
281
|
+
});
|
|
265
282
|
sessionsCmd.command("end <id>").description("End a session").option("--summary <summary>", "Session summary").option("--status <status>", "Final status", "completed").action(async (id, opts) => {
|
|
266
283
|
const res = await apiRequest(`/sessions/${id}`, {
|
|
267
284
|
method: "PUT",
|
|
@@ -281,11 +298,218 @@ var searchCmd = new Command6("search").description("Full-text search across Odin
|
|
|
281
298
|
if (res.error) process.exit(1);
|
|
282
299
|
});
|
|
283
300
|
|
|
301
|
+
// src/commands/plans.ts
|
|
302
|
+
import { Command as Command7 } from "commander";
|
|
303
|
+
import { readFileSync as readFileSync3 } from "fs";
|
|
304
|
+
var plansCmd = new Command7("plans").description("Manage plans");
|
|
305
|
+
plansCmd.command("list <slug>").description("List plans for a pebble").action(async (slug) => {
|
|
306
|
+
const res = await apiRequest(`/pebbles/${slug}/plans`);
|
|
307
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
308
|
+
if (res.error) process.exit(1);
|
|
309
|
+
});
|
|
310
|
+
plansCmd.command("get <slug> <id>").description("Get plan with phases").action(async (slug, id) => {
|
|
311
|
+
const planRes = await apiRequest(`/pebbles/${slug}/plans/${id}`);
|
|
312
|
+
if (planRes.error) {
|
|
313
|
+
process.stdout.write(JSON.stringify(planRes) + "\n");
|
|
314
|
+
process.exit(1);
|
|
315
|
+
}
|
|
316
|
+
const phasesRes = await apiRequest(`/pebbles/${slug}/plans/${id}/phases`);
|
|
317
|
+
const result = {
|
|
318
|
+
data: { ...planRes.data, phases: phasesRes.data ?? [] },
|
|
319
|
+
error: null
|
|
320
|
+
};
|
|
321
|
+
process.stdout.write(JSON.stringify(result) + "\n");
|
|
322
|
+
});
|
|
323
|
+
plansCmd.command("create <slug> <title>").description("Create a plan").option("--description <description>", "Plan description").option("--file <path>", "Read description from file").option("--source <source>", "Source", "claude-code").action(async (slug, title, opts) => {
|
|
324
|
+
const description = opts.file ? readFileSync3(opts.file, "utf-8") : opts.description ?? null;
|
|
325
|
+
const res = await apiRequest(`/pebbles/${slug}/plans`, {
|
|
326
|
+
method: "POST",
|
|
327
|
+
body: { title, description, source: opts.source }
|
|
328
|
+
});
|
|
329
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
330
|
+
if (res.error) process.exit(1);
|
|
331
|
+
});
|
|
332
|
+
plansCmd.command("delete <slug> <id>").description("Delete a plan").action(async (slug, id) => {
|
|
333
|
+
const res = await apiRequest(`/pebbles/${slug}/plans/${id}`, {
|
|
334
|
+
method: "DELETE"
|
|
335
|
+
});
|
|
336
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
337
|
+
if (res.error) process.exit(1);
|
|
338
|
+
});
|
|
339
|
+
plansCmd.command("add-phase <slug> <planId> <title>").description("Add a phase to a plan").requiredOption("--phase-number <number>", "Phase number", parseInt).option("--description <description>", "Phase description").option("--detail-type <type>", "Detail type (e.g. agent_spec, document)").option("--detail-id <id>", "Detail ID (UUID of linked entity)").action(async (slug, planId, title, opts) => {
|
|
340
|
+
const res = await apiRequest(`/pebbles/${slug}/plans/${planId}/phases`, {
|
|
341
|
+
method: "POST",
|
|
342
|
+
body: {
|
|
343
|
+
phase_number: opts.phaseNumber,
|
|
344
|
+
title,
|
|
345
|
+
description: opts.description ?? null,
|
|
346
|
+
detail_type: opts.detailType ?? null,
|
|
347
|
+
detail_id: opts.detailId ?? null
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
351
|
+
if (res.error) process.exit(1);
|
|
352
|
+
});
|
|
353
|
+
plansCmd.command("update-phase <slug> <planId> <phaseId>").description("Update a phase").option("--title <title>", "Phase title").option("--status <status>", "Phase status (pending|in_progress|completed|skipped)").option("--notes <notes>", "Phase notes").option("--description <description>", "Phase description").option("--detail-type <type>", "Detail type (e.g. agent_spec, document)").option("--detail-id <id>", "Detail ID (UUID of linked entity)").action(async (slug, planId, phaseId, opts) => {
|
|
354
|
+
const body = {};
|
|
355
|
+
if (opts.title) body.title = opts.title;
|
|
356
|
+
if (opts.status) body.status = opts.status;
|
|
357
|
+
if (opts.notes) body.notes = opts.notes;
|
|
358
|
+
if (opts.description) body.description = opts.description;
|
|
359
|
+
if (opts.detailType) body.detail_type = opts.detailType;
|
|
360
|
+
if (opts.detailId) body.detail_id = opts.detailId;
|
|
361
|
+
const res = await apiRequest(`/pebbles/${slug}/plans/${planId}/phases/${phaseId}`, {
|
|
362
|
+
method: "PUT",
|
|
363
|
+
body
|
|
364
|
+
});
|
|
365
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
366
|
+
if (res.error) process.exit(1);
|
|
367
|
+
});
|
|
368
|
+
plansCmd.command("delete-phase <slug> <planId> <phaseId>").description("Delete a phase").action(async (slug, planId, phaseId) => {
|
|
369
|
+
const res = await apiRequest(`/pebbles/${slug}/plans/${planId}/phases/${phaseId}`, {
|
|
370
|
+
method: "DELETE"
|
|
371
|
+
});
|
|
372
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
373
|
+
if (res.error) process.exit(1);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// src/commands/conversations.ts
|
|
377
|
+
import { Command as Command8 } from "commander";
|
|
378
|
+
var conversationsCmd = new Command8("conversations").description("Manage conversations");
|
|
379
|
+
conversationsCmd.command("list <slug>").description("List conversations for a pebble").action(async (slug) => {
|
|
380
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations`);
|
|
381
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
382
|
+
if (res.error) process.exit(1);
|
|
383
|
+
});
|
|
384
|
+
conversationsCmd.command("get <slug> <id>").description("Get a conversation by ID").action(async (slug, id) => {
|
|
385
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations/${id}`);
|
|
386
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
387
|
+
if (res.error) process.exit(1);
|
|
388
|
+
});
|
|
389
|
+
conversationsCmd.command("create <slug> <title>").description("Create a conversation").option("--summary <summary>", "Conversation summary").option("--source-url <url>", "Source URL").option("--session-id <id>", "Link to active session").action(async (slug, title, opts) => {
|
|
390
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations`, {
|
|
391
|
+
method: "POST",
|
|
392
|
+
body: {
|
|
393
|
+
title,
|
|
394
|
+
summary: opts.summary ?? null,
|
|
395
|
+
source_url: opts.sourceUrl ?? null,
|
|
396
|
+
session_id: opts.sessionId ?? null
|
|
397
|
+
}
|
|
398
|
+
});
|
|
399
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
400
|
+
if (res.error) process.exit(1);
|
|
401
|
+
});
|
|
402
|
+
conversationsCmd.command("update <slug> <id>").description("Update a conversation").option("--title <title>", "New title").option("--summary <summary>", "New summary").option("--source-url <url>", "New source URL").action(async (slug, id, opts) => {
|
|
403
|
+
const body = {};
|
|
404
|
+
if (opts.title) body.title = opts.title;
|
|
405
|
+
if (opts.summary) body.summary = opts.summary;
|
|
406
|
+
if (opts.sourceUrl) body.source_url = opts.sourceUrl;
|
|
407
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations/${id}`, {
|
|
408
|
+
method: "PUT",
|
|
409
|
+
body
|
|
410
|
+
});
|
|
411
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
412
|
+
if (res.error) process.exit(1);
|
|
413
|
+
});
|
|
414
|
+
conversationsCmd.command("delete <slug> <id>").description("Delete a conversation").action(async (slug, id) => {
|
|
415
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations/${id}`, {
|
|
416
|
+
method: "DELETE"
|
|
417
|
+
});
|
|
418
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
419
|
+
if (res.error) process.exit(1);
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
// src/commands/specs.ts
|
|
423
|
+
import { Command as Command9 } from "commander";
|
|
424
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
425
|
+
var specsCmd = new Command9("specs").description("Manage agent specs");
|
|
426
|
+
specsCmd.command("list <slug>").description("List specs for a pebble").option("--source <source>", "Filter by source (claude-code, superpowers, etc.)").action(async (slug, opts) => {
|
|
427
|
+
const params = {};
|
|
428
|
+
if (opts.source) params.source = opts.source;
|
|
429
|
+
const res = await apiRequest(`/pebbles/${slug}/agent-specs`, { params });
|
|
430
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
431
|
+
if (res.error) process.exit(1);
|
|
432
|
+
});
|
|
433
|
+
specsCmd.command("get <slug> <id>").description("Get a spec by ID").action(async (slug, id) => {
|
|
434
|
+
const res = await apiRequest(`/pebbles/${slug}/agent-specs/${id}`);
|
|
435
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
436
|
+
if (res.error) process.exit(1);
|
|
437
|
+
});
|
|
438
|
+
specsCmd.command("create <slug> <title>").description("Create a spec").requiredOption("--spec-type <type>", "Spec type (e.g. brainstorm, plan, research)").option("--source <source>", "Source agent", "claude-code").option("--content <content>", "Spec content").option("--file <path>", "Read content from file").option("--session-id <id>", "Link to active session").action(async (slug, title, opts) => {
|
|
439
|
+
const content = opts.file ? readFileSync4(opts.file, "utf-8") : opts.content ?? "";
|
|
440
|
+
const res = await apiRequest(`/pebbles/${slug}/agent-specs`, {
|
|
441
|
+
method: "POST",
|
|
442
|
+
body: {
|
|
443
|
+
title,
|
|
444
|
+
spec_type: opts.specType,
|
|
445
|
+
source: opts.source,
|
|
446
|
+
content,
|
|
447
|
+
session_id: opts.sessionId ?? null
|
|
448
|
+
}
|
|
449
|
+
});
|
|
450
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
451
|
+
if (res.error) process.exit(1);
|
|
452
|
+
});
|
|
453
|
+
specsCmd.command("update <slug> <id>").description("Update a spec").option("--title <title>", "New title").option("--spec-type <type>", "New spec type").option("--source <source>", "New source").option("--content <content>", "New content").option("--file <path>", "Read content from file").action(async (slug, id, opts) => {
|
|
454
|
+
const body = {};
|
|
455
|
+
if (opts.title) body.title = opts.title;
|
|
456
|
+
if (opts.specType) body.spec_type = opts.specType;
|
|
457
|
+
if (opts.source) body.source = opts.source;
|
|
458
|
+
if (opts.file) body.content = readFileSync4(opts.file, "utf-8");
|
|
459
|
+
else if (opts.content) body.content = opts.content;
|
|
460
|
+
const res = await apiRequest(`/pebbles/${slug}/agent-specs/${id}`, {
|
|
461
|
+
method: "PUT",
|
|
462
|
+
body
|
|
463
|
+
});
|
|
464
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
465
|
+
if (res.error) process.exit(1);
|
|
466
|
+
});
|
|
467
|
+
specsCmd.command("delete <slug> <id>").description("Delete a spec").action(async (slug, id) => {
|
|
468
|
+
const res = await apiRequest(`/pebbles/${slug}/agent-specs/${id}`, {
|
|
469
|
+
method: "DELETE"
|
|
470
|
+
});
|
|
471
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
472
|
+
if (res.error) process.exit(1);
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
// src/commands/executions.ts
|
|
476
|
+
import { Command as Command10 } from "commander";
|
|
477
|
+
var executionsCmd = new Command10("executions").description("View Claude Code executions");
|
|
478
|
+
executionsCmd.command("list <slug>").description("List executions for a pebble").option("--session-id <id>", "Filter by session ID").option("--limit <n>", "Max results", "50").option("--offset <n>", "Offset for pagination", "0").action(async (slug, opts) => {
|
|
479
|
+
const params = {
|
|
480
|
+
limit: opts.limit,
|
|
481
|
+
offset: opts.offset
|
|
482
|
+
};
|
|
483
|
+
if (opts.sessionId) params.session_id = opts.sessionId;
|
|
484
|
+
const res = await apiRequest(`/pebbles/${slug}/executions`, { params });
|
|
485
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
486
|
+
if (res.error) process.exit(1);
|
|
487
|
+
});
|
|
488
|
+
executionsCmd.command("get <slug> <id>").description("Get a single execution").action(async (slug, id) => {
|
|
489
|
+
const res = await apiRequest(`/pebbles/${slug}/executions/${id}`);
|
|
490
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
491
|
+
if (res.error) process.exit(1);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
// src/commands/versions.ts
|
|
495
|
+
import { Command as Command11 } from "commander";
|
|
496
|
+
var versionsCmd = new Command11("versions").description("View document version history");
|
|
497
|
+
versionsCmd.command("list <slug> <docType>").description("List versions for a document").action(async (slug, docType) => {
|
|
498
|
+
const res = await apiRequest(`/pebbles/${slug}/documents/${docType}/versions`);
|
|
499
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
500
|
+
if (res.error) process.exit(1);
|
|
501
|
+
});
|
|
502
|
+
versionsCmd.command("get <slug> <docType> <versionId>").description("Get a specific document version").action(async (slug, docType, versionId) => {
|
|
503
|
+
const res = await apiRequest(`/pebbles/${slug}/documents/${docType}/versions/${versionId}`);
|
|
504
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
505
|
+
if (res.error) process.exit(1);
|
|
506
|
+
});
|
|
507
|
+
|
|
284
508
|
// src/index.ts
|
|
285
509
|
var GOLD = "\x1B[33m";
|
|
286
510
|
var DIM = "\x1B[2m";
|
|
287
511
|
var RESET = "\x1B[0m";
|
|
288
|
-
var VERSION = "0.
|
|
512
|
+
var VERSION = "0.2.0";
|
|
289
513
|
function printBanner() {
|
|
290
514
|
const cwd = process.cwd();
|
|
291
515
|
console.error(`${GOLD}
|
|
@@ -299,7 +523,7 @@ ${RESET}
|
|
|
299
523
|
${DIM}${cwd}${RESET}
|
|
300
524
|
`);
|
|
301
525
|
}
|
|
302
|
-
var program = new
|
|
526
|
+
var program = new Command12();
|
|
303
527
|
program.name("odin").description("CLI for Odin \u2014 the knowledge backbone for Pebble House").version(VERSION);
|
|
304
528
|
program.command("login").description("Authenticate with Odin via browser").action(async () => {
|
|
305
529
|
printBanner();
|
|
@@ -321,6 +545,11 @@ program.addCommand(decisionsCmd);
|
|
|
321
545
|
program.addCommand(contextCmd);
|
|
322
546
|
program.addCommand(sessionsCmd);
|
|
323
547
|
program.addCommand(searchCmd);
|
|
548
|
+
program.addCommand(plansCmd);
|
|
549
|
+
program.addCommand(conversationsCmd);
|
|
550
|
+
program.addCommand(specsCmd);
|
|
551
|
+
program.addCommand(executionsCmd);
|
|
552
|
+
program.addCommand(versionsCmd);
|
|
324
553
|
program.parseAsync(process.argv).catch((err) => {
|
|
325
554
|
console.error(err instanceof Error ? err.message : err);
|
|
326
555
|
process.exit(1);
|