@pebblehouse/odin-cli 0.8.0 → 0.10.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.
Files changed (2) hide show
  1. package/dist/index.js +161 -11
  2. package/package.json +8 -3
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 Command11 } from "commander";
4
+ import { Command as Command13 } from "commander";
5
5
 
6
6
  // src/auth/login.ts
7
7
  import { createServer } from "http";
@@ -408,13 +408,19 @@ ${supersededTitles.map((t) => ` \u21B3 "${t}"`).join("\n")}
408
408
 
409
409
  // src/commands/context.ts
410
410
  import { Command as Command4 } from "commander";
411
- var contextCmd = new Command4("context").description("Get Tier 1 context for a pebble").argument("<slug>", "Pebble slug").action(async (slug) => {
412
- console.error(`[odin context] T1 context endpoint is being rebuilt (Phase 5).`);
413
- console.error(`In the meantime, use:`);
414
- console.error(` odin guidelines list`);
415
- console.error(` odin docs get ${slug} <type>`);
416
- console.error(` odin decisions list ${slug}`);
417
- process.exit(0);
411
+ var contextCmd = new Command4("context").description("Get Tier 1 context packet for a pebble (brief + rules + guidelines)").argument("<slug>", "Pebble slug").action(async (slug) => {
412
+ const res = await apiRequest(`/pebbles/${slug}/context`);
413
+ if (res.error) {
414
+ console.error(`error: ${res.error}`);
415
+ process.exit(1);
416
+ }
417
+ if (res.data?.context) {
418
+ process.stdout.write(res.data.context + "\n");
419
+ } else {
420
+ console.error(`No context available for "${slug}". Create a brief first:`);
421
+ console.error(` odin briefs update ${slug} --file brief.md`);
422
+ process.exit(0);
423
+ }
418
424
  });
419
425
 
420
426
  // src/commands/search.ts
@@ -717,12 +723,154 @@ rulesCmd.command("deprecate <slug> <id>").description("Deprecate a rule").action
717
723
  if (res.error) process.exit(1);
718
724
  });
719
725
 
720
- // src/index.ts
726
+ // src/commands/briefs.ts
727
+ import { Command as Command11 } from "commander";
721
728
  import { readFileSync as readFileSync8 } from "fs";
729
+ var briefsCmd = new Command11("briefs").description("Manage pebble context briefs");
730
+ briefsCmd.command("get <slug>").description("Get the context brief for a pebble").action(async (slug) => {
731
+ const res = await apiRequest(`/pebbles/${slug}/brief`);
732
+ process.stdout.write(JSON.stringify(res) + "\n");
733
+ if (res.error) process.exit(1);
734
+ });
735
+ briefsCmd.command("update <slug>").description("Create or update the context brief").option("--content <content>", "Brief content").option("--file <path>", "Read content from file").option("--title <title>", "Brief title").option("--source <source>", "Source", "manual").action(async (slug, opts) => {
736
+ const content = opts.file ? readFileSync8(opts.file, "utf-8") : opts.content;
737
+ if (!content) {
738
+ console.error("error: --content or --file is required");
739
+ process.exit(1);
740
+ }
741
+ const body = { content, source: opts.source };
742
+ if (opts.title) body.title = opts.title;
743
+ const res = await apiRequest(`/pebbles/${slug}/brief`, {
744
+ method: "PUT",
745
+ body
746
+ });
747
+ process.stdout.write(JSON.stringify(res) + "\n");
748
+ if (res.error) process.exit(1);
749
+ });
750
+ briefsCmd.command("delete <slug>").description("Delete the context brief for a pebble").action(async (slug) => {
751
+ const res = await apiRequest(`/pebbles/${slug}/brief`, { method: "DELETE" });
752
+ process.stdout.write(JSON.stringify(res) + "\n");
753
+ if (res.error) process.exit(1);
754
+ });
755
+ briefsCmd.command("versions <slug>").description("List version history for a pebble's brief").action(async (slug) => {
756
+ const res = await apiRequest(`/pebbles/${slug}/brief/versions`);
757
+ process.stdout.write(JSON.stringify(res) + "\n");
758
+ if (res.error) process.exit(1);
759
+ });
760
+ briefsCmd.command("generate [slug]").description("Generate/regenerate brief for a pebble (or all pebbles)").option("--secret <secret>", "Worker secret (defaults to ODIN_WORKER_SECRET env var)").action(async (slug, opts) => {
761
+ const secret = opts.secret ?? process.env.ODIN_WORKER_SECRET;
762
+ if (!secret) {
763
+ console.error("error: ODIN_WORKER_SECRET env var or --secret flag required");
764
+ process.exit(1);
765
+ }
766
+ const apiUrl = process.env.ODIN_API_URL ?? "https://odin.mu";
767
+ const url = slug ? `${apiUrl}/api/internal/briefs/generate?slug=${slug}` : `${apiUrl}/api/internal/briefs/generate`;
768
+ try {
769
+ const res = await fetch(url, {
770
+ method: "POST",
771
+ headers: { Authorization: `Bearer ${secret}` }
772
+ });
773
+ const body = await res.json();
774
+ if (!res.ok) {
775
+ console.error(`error: ${body.error ?? res.statusText}`);
776
+ process.exit(1);
777
+ }
778
+ process.stdout.write(JSON.stringify(body, null, 2) + "\n");
779
+ } catch (err) {
780
+ console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
781
+ process.exit(1);
782
+ }
783
+ });
784
+
785
+ // src/commands/blueprints.ts
786
+ import { Command as Command12 } from "commander";
787
+ import { readFileSync as readFileSync9 } from "fs";
788
+ var blueprintsCmd = new Command12("blueprints").description("Manage global blueprints");
789
+ blueprintsCmd.command("list").description("List blueprints").option("--tag <tag>", "Filter by tag").option("--status <status>", "Filter by status (default: active)").option("--all", "Include deprecated/experimental blueprints").action(async (opts) => {
790
+ const params = new URLSearchParams();
791
+ if (opts.tag) params.set("tag", opts.tag);
792
+ if (opts.all) params.set("status", "all");
793
+ else if (opts.status) params.set("status", opts.status);
794
+ const qs = params.toString();
795
+ const res = await apiRequest(`/blueprints${qs ? `?${qs}` : ""}`);
796
+ process.stdout.write(JSON.stringify(res) + "\n");
797
+ if (res.error) process.exit(1);
798
+ });
799
+ blueprintsCmd.command("get <id>").description("Get a blueprint by ID (includes assets)").action(async (id) => {
800
+ const res = await apiRequest(`/blueprints/${id}`);
801
+ process.stdout.write(JSON.stringify(res) + "\n");
802
+ if (res.error) process.exit(1);
803
+ });
804
+ blueprintsCmd.command("create <title>").description("Create a blueprint").option("--content <content>", "Blueprint content").option("--file <path>", "Read content from file").option("--tags <tags>", "Comma-separated tags").option("--status <status>", "Status (active|experimental)", "active").option("--source <source>", "Source", "manual").action(async (title, opts) => {
805
+ const content = opts.file ? readFileSync9(opts.file, "utf-8") : opts.content ?? "";
806
+ const tags = opts.tags ? opts.tags.split(",").map((t) => t.trim()) : [];
807
+ const res = await apiRequest("/blueprints", {
808
+ method: "POST",
809
+ body: { title, content, tags, status: opts.status, source: opts.source }
810
+ });
811
+ process.stdout.write(JSON.stringify(res) + "\n");
812
+ if (res.error) process.exit(1);
813
+ });
814
+ blueprintsCmd.command("update <id>").description("Update a blueprint").option("--title <title>", "New title").option("--content <content>", "New content").option("--file <path>", "Read content from file").option("--tags <tags>", "Comma-separated tags (replaces all)").option("--status <status>", "New status (active|deprecated|experimental)").option("--source <source>", "Source").action(async (id, opts) => {
815
+ const body = {};
816
+ if (opts.title) body.title = opts.title;
817
+ if (opts.file) body.content = readFileSync9(opts.file, "utf-8");
818
+ else if (opts.content) body.content = opts.content;
819
+ if (opts.tags) body.tags = opts.tags.split(",").map((t) => t.trim());
820
+ if (opts.status) body.status = opts.status;
821
+ if (opts.source) body.source = opts.source;
822
+ const res = await apiRequest(`/blueprints/${id}`, {
823
+ method: "PUT",
824
+ body
825
+ });
826
+ process.stdout.write(JSON.stringify(res) + "\n");
827
+ if (res.error) process.exit(1);
828
+ });
829
+ blueprintsCmd.command("delete <id>").description("Delete a blueprint").action(async (id) => {
830
+ const res = await apiRequest(`/blueprints/${id}`, {
831
+ method: "DELETE"
832
+ });
833
+ process.stdout.write(JSON.stringify(res) + "\n");
834
+ if (res.error) process.exit(1);
835
+ });
836
+ blueprintsCmd.command("versions <id>").description("List version history for a blueprint").action(async (id) => {
837
+ const res = await apiRequest(`/blueprints/${id}/versions`);
838
+ process.stdout.write(JSON.stringify(res) + "\n");
839
+ if (res.error) process.exit(1);
840
+ });
841
+ blueprintsCmd.command("deprecate <id>").description("Deprecate a blueprint").action(async (id) => {
842
+ const res = await apiRequest(`/blueprints/${id}`, {
843
+ method: "PUT",
844
+ body: { status: "deprecated" }
845
+ });
846
+ process.stdout.write(JSON.stringify(res) + "\n");
847
+ if (res.error) process.exit(1);
848
+ });
849
+ blueprintsCmd.command("add-asset <id>").description("Add an asset reference to a blueprint").requiredOption("--title <title>", "Asset title").requiredOption("--url <url>", "Asset URL").option("--type <type>", "Asset type (screenshot|diagram|code-snippet|figma-link|github-link|raw-text|other)", "other").option("--description <description>", "Asset description").action(async (id, opts) => {
850
+ const res = await apiRequest(`/blueprints/${id}/assets`, {
851
+ method: "POST",
852
+ body: {
853
+ title: opts.title,
854
+ asset_url: opts.url,
855
+ type: opts.type,
856
+ description: opts.description ?? ""
857
+ }
858
+ });
859
+ process.stdout.write(JSON.stringify(res) + "\n");
860
+ if (res.error) process.exit(1);
861
+ });
862
+ blueprintsCmd.command("assets <id>").description("List assets for a blueprint").action(async (id) => {
863
+ const res = await apiRequest(`/blueprints/${id}/assets`);
864
+ process.stdout.write(JSON.stringify(res) + "\n");
865
+ if (res.error) process.exit(1);
866
+ });
867
+
868
+ // src/index.ts
869
+ import { readFileSync as readFileSync10 } from "fs";
722
870
  import { fileURLToPath } from "url";
723
871
  import { dirname as dirname2, resolve } from "path";
724
872
  var __dirname = dirname2(fileURLToPath(import.meta.url));
725
- var pkg = JSON.parse(readFileSync8(resolve(__dirname, "../package.json"), "utf-8"));
873
+ var pkg = JSON.parse(readFileSync10(resolve(__dirname, "../package.json"), "utf-8"));
726
874
  var GOLD = "\x1B[33m";
727
875
  var DIM = "\x1B[2m";
728
876
  var RESET = "\x1B[0m";
@@ -740,7 +888,7 @@ ${RESET}
740
888
  ${DIM}${cwd}${RESET}
741
889
  `);
742
890
  }
743
- var program = new Command11();
891
+ var program = new Command13();
744
892
  program.name("odin").description("CLI for Odin \u2014 the knowledge backbone for Pebble House").version(VERSION);
745
893
  program.command("login").description("Authenticate with Odin via browser").action(async () => {
746
894
  printBanner();
@@ -766,6 +914,8 @@ program.addCommand(artifactsCmd);
766
914
  program.addCommand(versionsCmd);
767
915
  program.addCommand(guidelinesCmd);
768
916
  program.addCommand(rulesCmd);
917
+ program.addCommand(briefsCmd);
918
+ program.addCommand(blueprintsCmd);
769
919
  process.on("exit", () => {
770
920
  process.stderr.write("\n\n\n\n\n");
771
921
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pebblehouse/odin-cli",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "type": "module",
5
5
  "description": "CLI for Odin — the knowledge backbone for Pebble House",
6
6
  "bin": {
@@ -11,7 +11,10 @@
11
11
  ],
12
12
  "scripts": {
13
13
  "build": "tsup",
14
- "dev": "tsup --watch"
14
+ "dev": "tsup --watch",
15
+ "test": "vitest run",
16
+ "test:watch": "vitest",
17
+ "test:coverage": "vitest run --coverage"
15
18
  },
16
19
  "keywords": [
17
20
  "odin",
@@ -36,7 +39,9 @@
36
39
  "open": "^10.0.0"
37
40
  },
38
41
  "devDependencies": {
42
+ "@vitest/coverage-v8": "^4.1.2",
39
43
  "tsup": "^8.0.0",
40
- "typescript": "^5.3.3"
44
+ "typescript": "^5.3.3",
45
+ "vitest": "^4.1.2"
41
46
  }
42
47
  }