@pebblehouse/odin-cli 0.10.0 → 0.10.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.
Files changed (2) hide show
  1. package/dist/index.js +282 -4
  2. 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 Command13 } from "commander";
4
+ import { Command as Command18 } from "commander";
5
5
 
6
6
  // src/auth/login.ts
7
7
  import { createServer } from "http";
@@ -865,12 +865,286 @@ blueprintsCmd.command("assets <id>").description("List assets for a blueprint").
865
865
  if (res.error) process.exit(1);
866
866
  });
867
867
 
868
- // src/index.ts
868
+ // src/commands/bets.ts
869
+ import { Command as Command14 } from "commander";
869
870
  import { readFileSync as readFileSync10 } from "fs";
871
+
872
+ // src/commands/bet-dependencies.ts
873
+ import { Command as Command13 } from "commander";
874
+ var depsCmd = new Command13("deps").description("Manage bet dependencies (blocked-by graph)");
875
+ depsCmd.command("list <slug> <bet-id>").description("List dependencies for a bet (blocked_by + blocking)").action(async (slug, betId) => {
876
+ const res = await apiRequest(
877
+ `/pebbles/${slug}/bets/${betId}/dependencies`
878
+ );
879
+ process.stdout.write(JSON.stringify(res) + "\n");
880
+ if (res.error) process.exit(1);
881
+ });
882
+ depsCmd.command("add <slug> <bet-id>").description("Add a blocked-by dependency").requiredOption("--blocked-by <id>", "Bet ID that blocks this bet").action(async (slug, betId, opts) => {
883
+ const res = await apiRequest(
884
+ `/pebbles/${slug}/bets/${betId}/dependencies`,
885
+ {
886
+ method: "POST",
887
+ body: { blocked_by_bet_id: opts.blockedBy }
888
+ }
889
+ );
890
+ process.stdout.write(JSON.stringify(res) + "\n");
891
+ if (res.error) process.exit(1);
892
+ });
893
+ depsCmd.command("remove <slug> <bet-id>").description("Remove a blocked-by dependency").requiredOption("--blocked-by <id>", "Bet ID to unlink").action(async (slug, betId, opts) => {
894
+ const res = await apiRequest(
895
+ `/pebbles/${slug}/bets/${betId}/dependencies/${opts.blockedBy}`,
896
+ { method: "DELETE" }
897
+ );
898
+ process.stdout.write(JSON.stringify(res) + "\n");
899
+ if (res.error) process.exit(1);
900
+ });
901
+
902
+ // src/commands/bets.ts
903
+ var betsCmd = new Command14("bets").description("Manage bets");
904
+ betsCmd.command("list <slug>").description("List bets for a pebble").option("--status <status>", "Filter by status").action(async (slug, opts) => {
905
+ const params = {};
906
+ if (opts.status) params.status = opts.status;
907
+ const res = await apiRequest(`/pebbles/${slug}/bets`, { params });
908
+ process.stdout.write(JSON.stringify(res) + "\n");
909
+ if (res.error) process.exit(1);
910
+ });
911
+ betsCmd.command("get <slug> <id>").description("Get a bet by ID").action(async (slug, id) => {
912
+ const res = await apiRequest(`/pebbles/${slug}/bets/${id}`);
913
+ process.stdout.write(JSON.stringify(res) + "\n");
914
+ if (res.error) process.exit(1);
915
+ });
916
+ 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>", "Bet status").option("--priority <priority>", "Bet priority").option("--appetite <appetite>", "Bet appetite").action(async (slug, title, opts) => {
917
+ const content = opts.file ? readFileSync10(opts.file, "utf-8") : opts.content ?? "";
918
+ const res = await apiRequest(`/pebbles/${slug}/bets`, {
919
+ method: "POST",
920
+ body: { title, source: opts.source, content, status: opts.status, priority: opts.priority, appetite: opts.appetite }
921
+ });
922
+ process.stdout.write(JSON.stringify(res) + "\n");
923
+ if (res.error) process.exit(1);
924
+ });
925
+ 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").option("--priority <priority>", "New priority").option("--appetite <appetite>", "New appetite").action(async (slug, id, opts) => {
926
+ const body = {};
927
+ if (opts.title) body.title = opts.title;
928
+ if (opts.file) body.content = readFileSync10(opts.file, "utf-8");
929
+ else if (opts.content) body.content = opts.content;
930
+ if (opts.status) body.status = opts.status;
931
+ if (opts.priority) body.priority = opts.priority;
932
+ if (opts.appetite) body.appetite = opts.appetite;
933
+ const res = await apiRequest(`/pebbles/${slug}/bets/${id}`, {
934
+ method: "PUT",
935
+ body
936
+ });
937
+ process.stdout.write(JSON.stringify(res) + "\n");
938
+ if (res.error) process.exit(1);
939
+ });
940
+ betsCmd.command("delete <slug> <id>").description("Delete a bet").action(async (slug, id) => {
941
+ const res = await apiRequest(`/pebbles/${slug}/bets/${id}`, {
942
+ method: "DELETE"
943
+ });
944
+ process.stdout.write(JSON.stringify(res) + "\n");
945
+ if (res.error) process.exit(1);
946
+ });
947
+ betsCmd.addCommand(depsCmd);
948
+
949
+ // src/commands/tasks.ts
950
+ import { Command as Command15 } from "commander";
951
+ var tasksCmd = new Command15("tasks").description("Manage tasks");
952
+ tasksCmd.command("list <slug>").description("List tasks for a pebble").option("--status <status>", "Filter by status").option("--bet <bet-id>", "Filter by bet ID").action(async (slug, opts) => {
953
+ const params = {};
954
+ if (opts.status) params.status = opts.status;
955
+ if (opts.bet) params.bet_id = opts.bet;
956
+ const res = await apiRequest(`/pebbles/${slug}/tasks`, { params });
957
+ process.stdout.write(JSON.stringify(res) + "\n");
958
+ if (res.error) process.exit(1);
959
+ });
960
+ tasksCmd.command("get <slug> <id>").description("Get a task by ID").action(async (slug, id) => {
961
+ const res = await apiRequest(`/pebbles/${slug}/tasks/${id}`);
962
+ process.stdout.write(JSON.stringify(res) + "\n");
963
+ if (res.error) process.exit(1);
964
+ });
965
+ tasksCmd.command("create <slug> <title>").description("Create a task").option("--source <source>", "Source agent", "claude-code").option("--description <description>", "Task description").option("--bet <bet-id>", "Parent bet ID").option("--status <status>", "Task status").option("--priority <priority>", "Task priority").action(async (slug, title, opts) => {
966
+ const res = await apiRequest(`/pebbles/${slug}/tasks`, {
967
+ method: "POST",
968
+ body: { title, source: opts.source, description: opts.description, bet_id: opts.bet, status: opts.status, priority: opts.priority }
969
+ });
970
+ process.stdout.write(JSON.stringify(res) + "\n");
971
+ if (res.error) process.exit(1);
972
+ });
973
+ tasksCmd.command("update <slug> <id>").description("Update a task").option("--title <title>", "New title").option("--description <description>", "New description").option("--status <status>", "New status").option("--priority <priority>", "New priority").option("--bet <bet-id>", "New parent bet ID").action(async (slug, id, opts) => {
974
+ const body = {};
975
+ if (opts.title) body.title = opts.title;
976
+ if (opts.description) body.description = opts.description;
977
+ if (opts.status) body.status = opts.status;
978
+ if (opts.priority) body.priority = opts.priority;
979
+ if (opts.bet) body.bet_id = opts.bet;
980
+ const res = await apiRequest(`/pebbles/${slug}/tasks/${id}`, {
981
+ method: "PUT",
982
+ body
983
+ });
984
+ process.stdout.write(JSON.stringify(res) + "\n");
985
+ if (res.error) process.exit(1);
986
+ });
987
+ tasksCmd.command("delete <slug> <id>").description("Delete a task").action(async (slug, id) => {
988
+ const res = await apiRequest(`/pebbles/${slug}/tasks/${id}`, {
989
+ method: "DELETE"
990
+ });
991
+ process.stdout.write(JSON.stringify(res) + "\n");
992
+ if (res.error) process.exit(1);
993
+ });
994
+
995
+ // src/commands/roadmaps.ts
996
+ import { Command as Command16 } from "commander";
997
+ import { readFileSync as readFileSync11 } from "fs";
998
+ var roadmapsCmd = new Command16("roadmaps").description("Manage roadmaps");
999
+ roadmapsCmd.command("list <slug>").description("List roadmaps for a pebble").option("--status <status>", "Filter by status").action(async (slug, opts) => {
1000
+ const params = {};
1001
+ if (opts.status) params.status = opts.status;
1002
+ const res = await apiRequest(`/pebbles/${slug}/roadmaps`, { params });
1003
+ process.stdout.write(JSON.stringify(res) + "\n");
1004
+ if (res.error) process.exit(1);
1005
+ });
1006
+ roadmapsCmd.command("get <slug> <id>").description("Get a roadmap by ID (includes bets)").action(async (slug, id) => {
1007
+ const res = await apiRequest(`/pebbles/${slug}/roadmaps/${id}`);
1008
+ process.stdout.write(JSON.stringify(res) + "\n");
1009
+ if (res.error) process.exit(1);
1010
+ });
1011
+ roadmapsCmd.command("create <slug> <title>").description("Create a roadmap").option("--source <source>", "Source agent", "claude-code").option("--content <content>", "Roadmap content").option("--file <path>", "Read content from file").option("--status <status>", "Roadmap status").action(async (slug, title, opts) => {
1012
+ const content = opts.file ? readFileSync11(opts.file, "utf-8") : opts.content ?? "";
1013
+ const res = await apiRequest(`/pebbles/${slug}/roadmaps`, {
1014
+ method: "POST",
1015
+ body: { title, source: opts.source, content, status: opts.status }
1016
+ });
1017
+ process.stdout.write(JSON.stringify(res) + "\n");
1018
+ if (res.error) process.exit(1);
1019
+ });
1020
+ roadmapsCmd.command("update <slug> <id>").description("Update a roadmap").option("--title <title>", "New title").option("--content <content>", "New content").option("--file <path>", "Read content from file").option("--status <status>", "New status").action(async (slug, id, opts) => {
1021
+ const body = {};
1022
+ if (opts.title) body.title = opts.title;
1023
+ if (opts.file) body.content = readFileSync11(opts.file, "utf-8");
1024
+ else if (opts.content) body.content = opts.content;
1025
+ if (opts.status) body.status = opts.status;
1026
+ const res = await apiRequest(`/pebbles/${slug}/roadmaps/${id}`, {
1027
+ method: "PUT",
1028
+ body
1029
+ });
1030
+ process.stdout.write(JSON.stringify(res) + "\n");
1031
+ if (res.error) process.exit(1);
1032
+ });
1033
+ roadmapsCmd.command("delete <slug> <id>").description("Delete a roadmap").action(async (slug, id) => {
1034
+ const res = await apiRequest(`/pebbles/${slug}/roadmaps/${id}`, {
1035
+ method: "DELETE"
1036
+ });
1037
+ process.stdout.write(JSON.stringify(res) + "\n");
1038
+ if (res.error) process.exit(1);
1039
+ });
1040
+
1041
+ // src/commands/persona-runs.ts
1042
+ import { Command as Command17 } from "commander";
1043
+ import { readFileSync as readFileSync12 } from "fs";
1044
+ import { join as join2 } from "path";
1045
+ import { homedir as homedir2 } from "os";
1046
+ var personaRunsCmd = new Command17("persona-runs").description(
1047
+ "Manage per-persona per-session token usage rows"
1048
+ );
1049
+ personaRunsCmd.command("list <slug>").description("List persona runs for a pebble").option("--bet-id <id>", "Filter by bet_id").option("--plan-id <id>", "Filter by plan_id").option("--persona <name>", "Filter by persona name").option("--limit <n>", "Max rows", "50").action(async (slug, opts) => {
1050
+ const params = {};
1051
+ if (opts.betId) params.bet_id = opts.betId;
1052
+ if (opts.planId) params.plan_id = opts.planId;
1053
+ if (opts.persona) params.persona = opts.persona;
1054
+ if (opts.limit) params.limit = String(opts.limit);
1055
+ const res = await apiRequest(
1056
+ `/pebbles/${slug}/persona-runs`,
1057
+ { params }
1058
+ );
1059
+ process.stdout.write(JSON.stringify(res) + "\n");
1060
+ if (res.error) process.exit(1);
1061
+ });
1062
+ personaRunsCmd.command("get <slug> <id>").description("Get a persona run by id").action(async (slug, id) => {
1063
+ const res = await apiRequest(
1064
+ `/pebbles/${slug}/persona-runs/${id}`
1065
+ );
1066
+ process.stdout.write(JSON.stringify(res) + "\n");
1067
+ if (res.error) process.exit(1);
1068
+ });
1069
+ personaRunsCmd.command("create <slug>").description("Create/upsert a persona run from a JSON file").requiredOption("--file <path>", "JSON payload matching PersonaRunCreate").action(async (slug, opts) => {
1070
+ const body = JSON.parse(readFileSync12(opts.file, "utf-8"));
1071
+ const res = await apiRequest(`/pebbles/${slug}/persona-runs`, {
1072
+ method: "POST",
1073
+ body
1074
+ });
1075
+ process.stdout.write(JSON.stringify(res) + "\n");
1076
+ if (res.error) process.exit(1);
1077
+ });
1078
+ personaRunsCmd.command("capture").description("Read marker + transcript, compute tokens, POST to endpoint (hook path)").requiredOption("--session-id <id>", "Claude Code session id").requiredOption("--transcript <path>", "Path to transcript JSONL").option("--marker <path>", "Explicit marker path (default: ~/.claude/odin-context/<session-id>.json)").action(async (opts) => {
1079
+ const markerPath = opts.marker ?? join2(homedir2(), ".claude", "odin-context", `${opts.sessionId}.json`);
1080
+ let marker = {};
1081
+ try {
1082
+ marker = JSON.parse(readFileSync12(markerPath, "utf-8"));
1083
+ } catch {
1084
+ process.exit(0);
1085
+ }
1086
+ if (!marker.pebble_slug || !marker.persona) {
1087
+ process.exit(0);
1088
+ }
1089
+ let input = 0, output = 0, cacheRead = 0, cacheCreate = 0;
1090
+ let minTs = null, maxTs = null;
1091
+ try {
1092
+ const raw = readFileSync12(opts.transcript, "utf-8");
1093
+ for (const line of raw.split("\n")) {
1094
+ if (!line.trim()) continue;
1095
+ let obj;
1096
+ try {
1097
+ obj = JSON.parse(line);
1098
+ } catch {
1099
+ continue;
1100
+ }
1101
+ if (obj.type !== "assistant") continue;
1102
+ const u = obj.message?.usage;
1103
+ if (!u) continue;
1104
+ input += Number(u.input_tokens ?? 0);
1105
+ output += Number(u.output_tokens ?? 0);
1106
+ cacheRead += Number(u.cache_read_input_tokens ?? 0);
1107
+ cacheCreate += Number(u.cache_creation_input_tokens ?? 0);
1108
+ const ts = obj.timestamp;
1109
+ if (ts) {
1110
+ if (!minTs || ts < minTs) minTs = ts;
1111
+ if (!maxTs || ts > maxTs) maxTs = ts;
1112
+ }
1113
+ }
1114
+ } catch {
1115
+ process.exit(0);
1116
+ }
1117
+ const payload = {
1118
+ persona: marker.persona,
1119
+ session_id: opts.sessionId,
1120
+ input_tokens: input,
1121
+ output_tokens: output,
1122
+ cache_read_tokens: cacheRead,
1123
+ cache_creation_tokens: cacheCreate,
1124
+ bet_id: marker.bet_id ?? null,
1125
+ plan_id: marker.plan_id ?? null,
1126
+ phase_id: marker.phase_id ?? null,
1127
+ started_at: minTs,
1128
+ ended_at: maxTs
1129
+ };
1130
+ const res = await apiRequest(
1131
+ `/pebbles/${marker.pebble_slug}/persona-runs`,
1132
+ { method: "POST", body: payload }
1133
+ );
1134
+ if (res.error) {
1135
+ process.stderr.write(`odin persona-runs capture: ${res.error}
1136
+ `);
1137
+ process.exit(1);
1138
+ }
1139
+ process.stdout.write(JSON.stringify(res.data) + "\n");
1140
+ });
1141
+
1142
+ // src/index.ts
1143
+ import { readFileSync as readFileSync13 } from "fs";
870
1144
  import { fileURLToPath } from "url";
871
1145
  import { dirname as dirname2, resolve } from "path";
872
1146
  var __dirname = dirname2(fileURLToPath(import.meta.url));
873
- var pkg = JSON.parse(readFileSync10(resolve(__dirname, "../package.json"), "utf-8"));
1147
+ var pkg = JSON.parse(readFileSync13(resolve(__dirname, "../package.json"), "utf-8"));
874
1148
  var GOLD = "\x1B[33m";
875
1149
  var DIM = "\x1B[2m";
876
1150
  var RESET = "\x1B[0m";
@@ -888,7 +1162,7 @@ ${RESET}
888
1162
  ${DIM}${cwd}${RESET}
889
1163
  `);
890
1164
  }
891
- var program = new Command13();
1165
+ var program = new Command18();
892
1166
  program.name("odin").description("CLI for Odin \u2014 the knowledge backbone for Pebble House").version(VERSION);
893
1167
  program.command("login").description("Authenticate with Odin via browser").action(async () => {
894
1168
  printBanner();
@@ -916,6 +1190,10 @@ program.addCommand(guidelinesCmd);
916
1190
  program.addCommand(rulesCmd);
917
1191
  program.addCommand(briefsCmd);
918
1192
  program.addCommand(blueprintsCmd);
1193
+ program.addCommand(betsCmd);
1194
+ program.addCommand(tasksCmd);
1195
+ program.addCommand(roadmapsCmd);
1196
+ program.addCommand(personaRunsCmd);
919
1197
  process.on("exit", () => {
920
1198
  process.stderr.write("\n\n\n\n\n");
921
1199
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pebblehouse/odin-cli",
3
- "version": "0.10.0",
3
+ "version": "0.10.1",
4
4
  "type": "module",
5
5
  "description": "CLI for Odin — the knowledge backbone for Pebble House",
6
6
  "bin": {