@pebblehouse/odin-cli 0.1.0 → 0.2.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/dist/index.js +228 -6
- 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";
|
|
@@ -102,8 +102,8 @@ import { Command } from "commander";
|
|
|
102
102
|
|
|
103
103
|
// src/auth/refresh.ts
|
|
104
104
|
import { createClient } from "@supabase/supabase-js";
|
|
105
|
-
var SUPABASE_URL = process.env.ODIN_SUPABASE_URL ??
|
|
106
|
-
var SUPABASE_ANON_KEY = process.env.ODIN_SUPABASE_ANON_KEY ??
|
|
105
|
+
var SUPABASE_URL = process.env.ODIN_SUPABASE_URL ?? "https://vdiwtiiksdyhlibqrngw.supabase.co";
|
|
106
|
+
var SUPABASE_ANON_KEY = process.env.ODIN_SUPABASE_ANON_KEY ?? "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InZkaXd0aWlrc2R5aGxpYnFybmd3Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NzM2ODM4NDksImV4cCI6MjA4OTI1OTg0OX0.gAccODqqhBnL_npqG7H42EvaT2CWR1P2pqo5NVjKFas";
|
|
107
107
|
async function getValidToken() {
|
|
108
108
|
const creds = getCredentials();
|
|
109
109
|
if (!creds) {
|
|
@@ -173,8 +173,9 @@ pebblesCmd.command("create <slug>").description("Create a pebble").requiredOptio
|
|
|
173
173
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
174
174
|
if (res.error) process.exit(1);
|
|
175
175
|
});
|
|
176
|
-
pebblesCmd.command("update <slug>").description("Update a pebble").option("--name <name>", "New name").option("--status <status>", "New status").option("--summary <summary>", "New summary").option("--repo-url <url>", "Repository URL").option("--domain <domain>", "Domain").action(async (slug, opts) => {
|
|
176
|
+
pebblesCmd.command("update <slug>").description("Update a pebble").option("--slug <newSlug>", "New slug (rename)").option("--name <name>", "New name").option("--status <status>", "New status").option("--summary <summary>", "New summary").option("--repo-url <url>", "Repository URL").option("--domain <domain>", "Domain").action(async (slug, opts) => {
|
|
177
177
|
const body = {};
|
|
178
|
+
if (opts.slug) body.slug = opts.slug;
|
|
178
179
|
if (opts.name) body.name = opts.name;
|
|
179
180
|
if (opts.status) body.status = opts.status;
|
|
180
181
|
if (opts.summary) body.summary = opts.summary;
|
|
@@ -208,6 +209,13 @@ docsCmd.command("create <slug> <type>").description("Create a document").require
|
|
|
208
209
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
209
210
|
if (res.error) process.exit(1);
|
|
210
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
|
+
});
|
|
211
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) => {
|
|
212
220
|
const content = readFileSync2(opts.file, "utf-8");
|
|
213
221
|
const res = await apiRequest(`/pebbles/${slug}/documents/${type}`, {
|
|
@@ -226,6 +234,11 @@ decisionsCmd.command("list <slug>").description("List decisions for a pebble").a
|
|
|
226
234
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
227
235
|
if (res.error) process.exit(1);
|
|
228
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
|
+
});
|
|
229
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) => {
|
|
230
243
|
const res = await apiRequest(`/pebbles/${slug}/decisions`, {
|
|
231
244
|
method: "POST",
|
|
@@ -261,6 +274,11 @@ sessionsCmd.command("start <slug>").description("Start a session").option("--sou
|
|
|
261
274
|
process.stdout.write(JSON.stringify(res) + "\n");
|
|
262
275
|
if (res.error) process.exit(1);
|
|
263
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
|
+
});
|
|
264
282
|
sessionsCmd.command("end <id>").description("End a session").option("--summary <summary>", "Session summary").option("--status <status>", "Final status", "completed").action(async (id, opts) => {
|
|
265
283
|
const res = await apiRequest(`/sessions/${id}`, {
|
|
266
284
|
method: "PUT",
|
|
@@ -280,11 +298,210 @@ var searchCmd = new Command6("search").description("Full-text search across Odin
|
|
|
280
298
|
if (res.error) process.exit(1);
|
|
281
299
|
});
|
|
282
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").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
|
+
}
|
|
347
|
+
});
|
|
348
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
349
|
+
if (res.error) process.exit(1);
|
|
350
|
+
});
|
|
351
|
+
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").action(async (slug, planId, phaseId, opts) => {
|
|
352
|
+
const body = {};
|
|
353
|
+
if (opts.title) body.title = opts.title;
|
|
354
|
+
if (opts.status) body.status = opts.status;
|
|
355
|
+
if (opts.notes) body.notes = opts.notes;
|
|
356
|
+
if (opts.description) body.description = opts.description;
|
|
357
|
+
const res = await apiRequest(`/pebbles/${slug}/plans/${planId}/phases/${phaseId}`, {
|
|
358
|
+
method: "PUT",
|
|
359
|
+
body
|
|
360
|
+
});
|
|
361
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
362
|
+
if (res.error) process.exit(1);
|
|
363
|
+
});
|
|
364
|
+
plansCmd.command("delete-phase <slug> <planId> <phaseId>").description("Delete a phase").action(async (slug, planId, phaseId) => {
|
|
365
|
+
const res = await apiRequest(`/pebbles/${slug}/plans/${planId}/phases/${phaseId}`, {
|
|
366
|
+
method: "DELETE"
|
|
367
|
+
});
|
|
368
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
369
|
+
if (res.error) process.exit(1);
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
// src/commands/conversations.ts
|
|
373
|
+
import { Command as Command8 } from "commander";
|
|
374
|
+
var conversationsCmd = new Command8("conversations").description("Manage conversations");
|
|
375
|
+
conversationsCmd.command("list <slug>").description("List conversations for a pebble").action(async (slug) => {
|
|
376
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations`);
|
|
377
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
378
|
+
if (res.error) process.exit(1);
|
|
379
|
+
});
|
|
380
|
+
conversationsCmd.command("get <slug> <id>").description("Get a conversation by ID").action(async (slug, id) => {
|
|
381
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations/${id}`);
|
|
382
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
383
|
+
if (res.error) process.exit(1);
|
|
384
|
+
});
|
|
385
|
+
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) => {
|
|
386
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations`, {
|
|
387
|
+
method: "POST",
|
|
388
|
+
body: {
|
|
389
|
+
title,
|
|
390
|
+
summary: opts.summary ?? null,
|
|
391
|
+
source_url: opts.sourceUrl ?? null,
|
|
392
|
+
session_id: opts.sessionId ?? null
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
396
|
+
if (res.error) process.exit(1);
|
|
397
|
+
});
|
|
398
|
+
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) => {
|
|
399
|
+
const body = {};
|
|
400
|
+
if (opts.title) body.title = opts.title;
|
|
401
|
+
if (opts.summary) body.summary = opts.summary;
|
|
402
|
+
if (opts.sourceUrl) body.source_url = opts.sourceUrl;
|
|
403
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations/${id}`, {
|
|
404
|
+
method: "PUT",
|
|
405
|
+
body
|
|
406
|
+
});
|
|
407
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
408
|
+
if (res.error) process.exit(1);
|
|
409
|
+
});
|
|
410
|
+
conversationsCmd.command("delete <slug> <id>").description("Delete a conversation").action(async (slug, id) => {
|
|
411
|
+
const res = await apiRequest(`/pebbles/${slug}/conversations/${id}`, {
|
|
412
|
+
method: "DELETE"
|
|
413
|
+
});
|
|
414
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
415
|
+
if (res.error) process.exit(1);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
// src/commands/specs.ts
|
|
419
|
+
import { Command as Command9 } from "commander";
|
|
420
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
421
|
+
var specsCmd = new Command9("specs").description("Manage superpowers specs");
|
|
422
|
+
specsCmd.command("list <slug>").description("List specs for a pebble").action(async (slug) => {
|
|
423
|
+
const res = await apiRequest(`/pebbles/${slug}/superpowers-specs`);
|
|
424
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
425
|
+
if (res.error) process.exit(1);
|
|
426
|
+
});
|
|
427
|
+
specsCmd.command("get <slug> <id>").description("Get a spec by ID").action(async (slug, id) => {
|
|
428
|
+
const res = await apiRequest(`/pebbles/${slug}/superpowers-specs/${id}`);
|
|
429
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
430
|
+
if (res.error) process.exit(1);
|
|
431
|
+
});
|
|
432
|
+
specsCmd.command("create <slug> <title>").description("Create a spec").requiredOption("--spec-type <type>", "Spec type (e.g. brainstorm, plan, research)").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) => {
|
|
433
|
+
const content = opts.file ? readFileSync4(opts.file, "utf-8") : opts.content ?? "";
|
|
434
|
+
const res = await apiRequest(`/pebbles/${slug}/superpowers-specs`, {
|
|
435
|
+
method: "POST",
|
|
436
|
+
body: {
|
|
437
|
+
title,
|
|
438
|
+
spec_type: opts.specType,
|
|
439
|
+
content,
|
|
440
|
+
session_id: opts.sessionId ?? null
|
|
441
|
+
}
|
|
442
|
+
});
|
|
443
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
444
|
+
if (res.error) process.exit(1);
|
|
445
|
+
});
|
|
446
|
+
specsCmd.command("update <slug> <id>").description("Update a spec").option("--title <title>", "New title").option("--spec-type <type>", "New spec type").option("--content <content>", "New content").option("--file <path>", "Read content from file").action(async (slug, id, opts) => {
|
|
447
|
+
const body = {};
|
|
448
|
+
if (opts.title) body.title = opts.title;
|
|
449
|
+
if (opts.specType) body.spec_type = opts.specType;
|
|
450
|
+
if (opts.file) body.content = readFileSync4(opts.file, "utf-8");
|
|
451
|
+
else if (opts.content) body.content = opts.content;
|
|
452
|
+
const res = await apiRequest(`/pebbles/${slug}/superpowers-specs/${id}`, {
|
|
453
|
+
method: "PUT",
|
|
454
|
+
body
|
|
455
|
+
});
|
|
456
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
457
|
+
if (res.error) process.exit(1);
|
|
458
|
+
});
|
|
459
|
+
specsCmd.command("delete <slug> <id>").description("Delete a spec").action(async (slug, id) => {
|
|
460
|
+
const res = await apiRequest(`/pebbles/${slug}/superpowers-specs/${id}`, {
|
|
461
|
+
method: "DELETE"
|
|
462
|
+
});
|
|
463
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
464
|
+
if (res.error) process.exit(1);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
// src/commands/executions.ts
|
|
468
|
+
import { Command as Command10 } from "commander";
|
|
469
|
+
var executionsCmd = new Command10("executions").description("View Claude Code executions");
|
|
470
|
+
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) => {
|
|
471
|
+
const params = {
|
|
472
|
+
limit: opts.limit,
|
|
473
|
+
offset: opts.offset
|
|
474
|
+
};
|
|
475
|
+
if (opts.sessionId) params.session_id = opts.sessionId;
|
|
476
|
+
const res = await apiRequest(`/pebbles/${slug}/executions`, { params });
|
|
477
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
478
|
+
if (res.error) process.exit(1);
|
|
479
|
+
});
|
|
480
|
+
executionsCmd.command("get <slug> <id>").description("Get a single execution").action(async (slug, id) => {
|
|
481
|
+
const res = await apiRequest(`/pebbles/${slug}/executions/${id}`);
|
|
482
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
483
|
+
if (res.error) process.exit(1);
|
|
484
|
+
});
|
|
485
|
+
|
|
486
|
+
// src/commands/versions.ts
|
|
487
|
+
import { Command as Command11 } from "commander";
|
|
488
|
+
var versionsCmd = new Command11("versions").description("View document version history");
|
|
489
|
+
versionsCmd.command("list <slug> <docType>").description("List versions for a document").action(async (slug, docType) => {
|
|
490
|
+
const res = await apiRequest(`/pebbles/${slug}/documents/${docType}/versions`);
|
|
491
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
492
|
+
if (res.error) process.exit(1);
|
|
493
|
+
});
|
|
494
|
+
versionsCmd.command("get <slug> <docType> <versionId>").description("Get a specific document version").action(async (slug, docType, versionId) => {
|
|
495
|
+
const res = await apiRequest(`/pebbles/${slug}/documents/${docType}/versions/${versionId}`);
|
|
496
|
+
process.stdout.write(JSON.stringify(res) + "\n");
|
|
497
|
+
if (res.error) process.exit(1);
|
|
498
|
+
});
|
|
499
|
+
|
|
283
500
|
// src/index.ts
|
|
284
501
|
var GOLD = "\x1B[33m";
|
|
285
502
|
var DIM = "\x1B[2m";
|
|
286
503
|
var RESET = "\x1B[0m";
|
|
287
|
-
var VERSION = "0.
|
|
504
|
+
var VERSION = "0.2.0";
|
|
288
505
|
function printBanner() {
|
|
289
506
|
const cwd = process.cwd();
|
|
290
507
|
console.error(`${GOLD}
|
|
@@ -298,7 +515,7 @@ ${RESET}
|
|
|
298
515
|
${DIM}${cwd}${RESET}
|
|
299
516
|
`);
|
|
300
517
|
}
|
|
301
|
-
var program = new
|
|
518
|
+
var program = new Command12();
|
|
302
519
|
program.name("odin").description("CLI for Odin \u2014 the knowledge backbone for Pebble House").version(VERSION);
|
|
303
520
|
program.command("login").description("Authenticate with Odin via browser").action(async () => {
|
|
304
521
|
printBanner();
|
|
@@ -320,6 +537,11 @@ program.addCommand(decisionsCmd);
|
|
|
320
537
|
program.addCommand(contextCmd);
|
|
321
538
|
program.addCommand(sessionsCmd);
|
|
322
539
|
program.addCommand(searchCmd);
|
|
540
|
+
program.addCommand(plansCmd);
|
|
541
|
+
program.addCommand(conversationsCmd);
|
|
542
|
+
program.addCommand(specsCmd);
|
|
543
|
+
program.addCommand(executionsCmd);
|
|
544
|
+
program.addCommand(versionsCmd);
|
|
323
545
|
program.parseAsync(process.argv).catch((err) => {
|
|
324
546
|
console.error(err instanceof Error ? err.message : err);
|
|
325
547
|
process.exit(1);
|