@koda-sl/baker-bridge 0.68.1 → 0.68.2

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 (39) hide show
  1. package/README.md +1 -5
  2. package/dist/hono/agent.d.ts.map +1 -1
  3. package/dist/hono/agent.js +4 -23
  4. package/dist/hono/agent.js.map +1 -1
  5. package/dist/hono/agent.test.js +69 -50
  6. package/dist/hono/agent.test.js.map +1 -1
  7. package/dist/hono/bridge-job.d.ts.map +1 -1
  8. package/dist/hono/bridge-job.js +0 -2
  9. package/dist/hono/bridge-job.js.map +1 -1
  10. package/dist/hono/bridge-job.test.js +0 -12
  11. package/dist/hono/bridge-job.test.js.map +1 -1
  12. package/dist/hono/slash-command-allowlist.d.ts +5 -0
  13. package/dist/hono/slash-command-allowlist.d.ts.map +1 -0
  14. package/dist/hono/slash-command-allowlist.js +42 -0
  15. package/dist/hono/slash-command-allowlist.js.map +1 -0
  16. package/dist/hono/slash-command-allowlist.test.d.ts +2 -0
  17. package/dist/hono/slash-command-allowlist.test.d.ts.map +1 -0
  18. package/dist/hono/slash-command-allowlist.test.js +44 -0
  19. package/dist/hono/slash-command-allowlist.test.js.map +1 -0
  20. package/dist/hono/slash-commands.d.ts +2 -0
  21. package/dist/hono/slash-commands.d.ts.map +1 -1
  22. package/dist/hono/slash-commands.js +4 -0
  23. package/dist/hono/slash-commands.js.map +1 -1
  24. package/package.json +5 -6
  25. package/dist/hono/plugins.d.ts +0 -3
  26. package/dist/hono/plugins.d.ts.map +0 -1
  27. package/dist/hono/plugins.js +0 -7
  28. package/dist/hono/plugins.js.map +0 -1
  29. package/dist/hono/plugins.test.d.ts +0 -2
  30. package/dist/hono/plugins.test.d.ts.map +0 -1
  31. package/dist/hono/plugins.test.js +0 -98
  32. package/dist/hono/plugins.test.js.map +0 -1
  33. package/plugins/reports/.claude-plugin/plugin.json +0 -7
  34. package/plugins/reports/skills/build/SKILL.md +0 -73
  35. package/plugins/reports/skills/build/references/report-contracts.md +0 -103
  36. package/plugins/reports/skills/build/scripts/report-paths.mjs +0 -19
  37. package/plugins/reports/skills/build/scripts/scaffold-report.mjs +0 -108
  38. package/plugins/reports/skills/build/templates/report-definition.md +0 -22
  39. package/plugins/reports/skills/build/templates/report-index.html +0 -20
@@ -0,0 +1,42 @@
1
+ import { readdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { z } from "zod";
4
+ import { bridgeSlashCommandNames } from "./slash-commands.js";
5
+ // Format renderers that the document-build skill routes to on the user's behalf.
6
+ // They are real workspace skills, but nobody invokes them by name.
7
+ const ROUTED_ONLY_SKILLS = new Set(["docx", "pdf", "pptx", "xlsx"]);
8
+ const errnoSchema = z.object({ code: z.string() });
9
+ /**
10
+ * Directory names are the command names: the SDK names a command from SKILL.md
11
+ * frontmatter, and scripts/check-skills.mjs fails the build when that `name`
12
+ * differs from its directory, so reading the directory avoids parsing 24 files.
13
+ *
14
+ * An absent directory means "no skills"; any other failure must propagate so a
15
+ * stale-but-good list is kept.
16
+ */
17
+ async function readSkillDirNames(skillsDir) {
18
+ try {
19
+ const entries = await readdir(skillsDir, { withFileTypes: true });
20
+ return entries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
21
+ }
22
+ catch (error) {
23
+ if (errnoSchema.safeParse(error).data?.code === "ENOENT") {
24
+ return [];
25
+ }
26
+ throw error;
27
+ }
28
+ }
29
+ /** Names the sandbox picker is allowed to show: the workspace's own skills plus the bridge's own commands. */
30
+ export async function readAllowedSlashCommandNames(projectRoot) {
31
+ const allowed = new Set(bridgeSlashCommandNames());
32
+ for (const name of await readSkillDirNames(join(projectRoot, ".claude", "skills"))) {
33
+ if (!ROUTED_ONLY_SKILLS.has(name)) {
34
+ allowed.add(name);
35
+ }
36
+ }
37
+ return allowed;
38
+ }
39
+ export function filterAllowedSlashCommands(commands, allowedNames) {
40
+ return commands.filter((command) => allowedNames.has(command.name));
41
+ }
42
+ //# sourceMappingURL=slash-command-allowlist.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-command-allowlist.js","sourceRoot":"","sources":["../../src/hono/slash-command-allowlist.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAC3C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,iFAAiF;AACjF,mEAAmE;AACnE,MAAM,kBAAkB,GAAwB,IAAI,GAAG,CAAC,CAAC,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;AAEzF,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;AAEnD;;;;;;;GAOG;AACH,KAAK,UAAU,iBAAiB,CAAC,SAAiB;IAChD,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAClE,OAAO,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IACnF,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,WAAW,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YACzD,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,8GAA8G;AAC9G,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,WAAmB;IACpE,MAAM,OAAO,GAAG,IAAI,GAAG,CAAS,uBAAuB,EAAE,CAAC,CAAC;IAE3D,KAAK,MAAM,IAAI,IAAI,MAAM,iBAAiB,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,QAAQ,CAAC,CAAC,EAAE,CAAC;QACnF,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YAClC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QACpB,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,QAAwB,EACxB,YAAiC;IAEjC,OAAO,QAAQ,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;AACtE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=slash-command-allowlist.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-command-allowlist.test.d.ts","sourceRoot":"","sources":["../../src/hono/slash-command-allowlist.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,44 @@
1
+ import { mkdir, mkdtemp, writeFile } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { describe, expect, it } from "vitest";
5
+ import { filterAllowedSlashCommands, readAllowedSlashCommandNames } from "./slash-command-allowlist.js";
6
+ async function makeProjectRoot(skillNames) {
7
+ const root = await mkdtemp(join(tmpdir(), "baker-allowlist-"));
8
+ for (const name of skillNames) {
9
+ await mkdir(join(root, ".claude", "skills", name), { recursive: true });
10
+ }
11
+ return root;
12
+ }
13
+ const command = (name) => ({ name, description: name, argumentHint: "" });
14
+ describe("filterAllowedSlashCommands", () => {
15
+ it("drops commands whose name is not allowed", () => {
16
+ const landing = command("landing");
17
+ const commands = [command("model"), command("clear"), command("doctor"), landing];
18
+ expect(filterAllowedSlashCommands(commands, new Set(["landing"]))).toEqual([landing]);
19
+ });
20
+ });
21
+ describe("readAllowedSlashCommandNames", () => {
22
+ it("allows workspace skills found on disk plus the bridge's own commands", async () => {
23
+ const projectRoot = await makeProjectRoot(["landing", "brand-build"]);
24
+ const allowed = await readAllowedSlashCommandNames(projectRoot);
25
+ expect(allowed).toEqual(new Set(["landing", "brand-build", "compact", "publish"]));
26
+ });
27
+ it("hides workspace skills the user never invokes directly", async () => {
28
+ const projectRoot = await makeProjectRoot(["landing", "docx", "pdf", "pptx", "xlsx"]);
29
+ const allowed = await readAllowedSlashCommandNames(projectRoot);
30
+ expect(allowed).toEqual(new Set(["landing", "compact", "publish"]));
31
+ });
32
+ it("fails closed when the skills directory is missing", async () => {
33
+ const projectRoot = await mkdtemp(join(tmpdir(), "baker-no-skills-"));
34
+ const allowed = await readAllowedSlashCommandNames(projectRoot);
35
+ expect(allowed).toEqual(new Set(["compact", "publish"]));
36
+ });
37
+ it("rejects when the skills directory cannot be read for a reason other than absence", async () => {
38
+ const projectRoot = await mkdtemp(join(tmpdir(), "baker-unreadable-skills-"));
39
+ await mkdir(join(projectRoot, ".claude"), { recursive: true });
40
+ await writeFile(join(projectRoot, ".claude", "skills"), "not a directory");
41
+ await expect(readAllowedSlashCommandNames(projectRoot)).rejects.toThrow();
42
+ });
43
+ });
44
+ //# sourceMappingURL=slash-command-allowlist.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"slash-command-allowlist.test.js","sourceRoot":"","sources":["../../src/hono/slash-command-allowlist.test.ts"],"names":[],"mappings":"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"}
@@ -2,6 +2,8 @@ export declare enum BridgeSlashCommands {
2
2
  Publish = "/publish",
3
3
  Compact = "/compact"
4
4
  }
5
+ /** Bare names (no leading slash) of the commands the bridge itself handles. */
6
+ export declare function bridgeSlashCommandNames(): string[];
5
7
  export declare function verbatimSlashCommand(prompt: string): BridgeSlashCommands | null;
6
8
  export declare function isVerbatimSlashCommandPrompt(prompt: string): boolean;
7
9
  export declare function isPublishCommandPrompt(prompt: string): boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../src/hono/slash-commands.ts"],"names":[],"mappings":"AAAA,oBAAY,mBAAmB;IAC7B,OAAO,aAAa;IACpB,OAAO,aAAa;CACrB;AASD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAG/E;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D"}
1
+ {"version":3,"file":"slash-commands.d.ts","sourceRoot":"","sources":["../../src/hono/slash-commands.ts"],"names":[],"mappings":"AAAA,oBAAY,mBAAmB;IAC7B,OAAO,aAAa;IACpB,OAAO,aAAa;CACrB;AASD,+EAA+E;AAC/E,wBAAgB,uBAAuB,IAAI,MAAM,EAAE,CAElD;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,mBAAmB,GAAG,IAAI,CAG/E;AAED,wBAAgB,4BAA4B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAEpE;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D;AAED,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAE9D"}
@@ -9,6 +9,10 @@ export var BridgeSlashCommands;
9
9
  // rehydration — any of which would push the command off the front and turn it
10
10
  // into ordinary message text the model just echoes.
11
11
  const VERBATIM_COMMANDS = [BridgeSlashCommands.Publish, BridgeSlashCommands.Compact];
12
+ /** Bare names (no leading slash) of the commands the bridge itself handles. */
13
+ export function bridgeSlashCommandNames() {
14
+ return VERBATIM_COMMANDS.map((command) => command.replace("/", ""));
15
+ }
12
16
  export function verbatimSlashCommand(prompt) {
13
17
  const trimmedPrompt = prompt.trim();
14
18
  return VERBATIM_COMMANDS.find((command) => command === trimmedPrompt) ?? null;
@@ -1 +1 @@
1
- {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/hono/slash-commands.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,mBAGX;AAHD,WAAY,mBAAmB;IAC7B,2CAAoB,CAAA;IACpB,2CAAoB,CAAA;AACtB,CAAC,EAHW,mBAAmB,KAAnB,mBAAmB,QAG9B;AAED,8EAA8E;AAC9E,8EAA8E;AAC9E,sEAAsE;AACtE,8EAA8E;AAC9E,oDAAoD;AACpD,MAAM,iBAAiB,GAAmC,CAAC,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAErH,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,aAAa,CAAC,IAAI,IAAI,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,OAAO,oBAAoB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,oBAAoB,CAAC,MAAM,CAAC,KAAK,mBAAmB,CAAC,OAAO,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,oBAAoB,CAAC,MAAM,CAAC,KAAK,mBAAmB,CAAC,OAAO,CAAC;AACtE,CAAC"}
1
+ {"version":3,"file":"slash-commands.js","sourceRoot":"","sources":["../../src/hono/slash-commands.ts"],"names":[],"mappings":"AAAA,MAAM,CAAN,IAAY,mBAGX;AAHD,WAAY,mBAAmB;IAC7B,2CAAoB,CAAA;IACpB,2CAAoB,CAAA;AACtB,CAAC,EAHW,mBAAmB,KAAnB,mBAAmB,QAG9B;AAED,8EAA8E;AAC9E,8EAA8E;AAC9E,sEAAsE;AACtE,8EAA8E;AAC9E,oDAAoD;AACpD,MAAM,iBAAiB,GAAmC,CAAC,mBAAmB,CAAC,OAAO,EAAE,mBAAmB,CAAC,OAAO,CAAC,CAAC;AAErH,+EAA+E;AAC/E,MAAM,UAAU,uBAAuB;IACrC,OAAO,iBAAiB,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAc;IACjD,MAAM,aAAa,GAAG,MAAM,CAAC,IAAI,EAAE,CAAC;IACpC,OAAO,iBAAiB,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,aAAa,CAAC,IAAI,IAAI,CAAC;AAChF,CAAC;AAED,MAAM,UAAU,4BAA4B,CAAC,MAAc;IACzD,OAAO,oBAAoB,CAAC,MAAM,CAAC,KAAK,IAAI,CAAC;AAC/C,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,oBAAoB,CAAC,MAAM,CAAC,KAAK,mBAAmB,CAAC,OAAO,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,sBAAsB,CAAC,MAAc;IACnD,OAAO,oBAAoB,CAAC,MAAM,CAAC,KAAK,mBAAmB,CAAC,OAAO,CAAC;AACtE,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@koda-sl/baker-bridge",
3
- "version": "0.68.1",
3
+ "version": "0.68.2",
4
4
  "description": "HTTP server wrapping the Claude Agent SDK. Persists chat events to Convex; the dashboard subscribes via Convex realtime",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,8 +14,7 @@
14
14
  }
15
15
  },
16
16
  "files": [
17
- "dist",
18
- "plugins"
17
+ "dist"
19
18
  ],
20
19
  "engines": {
21
20
  "node": ">=18"
@@ -38,9 +37,9 @@
38
37
  "typecheck": "tsc --noEmit",
39
38
  "link:local": "bash scripts/link-local.sh",
40
39
  "unlink:local": "bash scripts/unlink-local.sh",
41
- "lint": "biome check src scripts plugins vitest.config.mts",
42
- "format:check": "biome format src scripts plugins vitest.config.mts",
43
- "format": "biome format --write src scripts plugins vitest.config.mts"
40
+ "lint": "biome check src scripts vitest.config.mts",
41
+ "format:check": "biome format src scripts vitest.config.mts",
42
+ "format": "biome format --write src scripts vitest.config.mts"
44
43
  },
45
44
  "dependencies": {
46
45
  "@anthropic-ai/claude-agent-sdk": "^0.3.220",
@@ -1,3 +0,0 @@
1
- import type { SdkPluginConfig } from "@anthropic-ai/claude-agent-sdk";
2
- export declare function bakerPlugins(): SdkPluginConfig[];
3
- //# sourceMappingURL=plugins.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins.d.ts","sourceRoot":"","sources":["../../src/hono/plugins.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gCAAgC,CAAC;AAItE,wBAAgB,YAAY,IAAI,eAAe,EAAE,CAEhD"}
@@ -1,7 +0,0 @@
1
- import { dirname, join } from "node:path";
2
- import { fileURLToPath } from "node:url";
3
- const bridgePackageRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
4
- export function bakerPlugins() {
5
- return [{ type: "local", path: join(bridgePackageRoot, "plugins", "reports") }];
6
- }
7
- //# sourceMappingURL=plugins.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins.js","sourceRoot":"","sources":["../../src/hono/plugins.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAGzC,MAAM,iBAAiB,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAEpF,MAAM,UAAU,YAAY;IAC1B,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,SAAS,EAAE,SAAS,CAAC,EAAE,CAAC,CAAC;AAClF,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=plugins.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins.test.d.ts","sourceRoot":"","sources":["../../src/hono/plugins.test.ts"],"names":[],"mappings":""}
@@ -1,98 +0,0 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
- import { existsSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
10
- import { tmpdir } from "node:os";
11
- import { dirname, join } from "node:path";
12
- import { fileURLToPath, pathToFileURL } from "node:url";
13
- import { describe, expect, it } from "vitest";
14
- import { bakerPlugins } from "./plugins.js";
15
- const packageRoot = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
16
- const repoRoot = join(packageRoot, "..", "..");
17
- const reportsRoot = join(packageRoot, "plugins", "reports");
18
- const skillRoot = join(reportsRoot, "skills", "build");
19
- function reportsManifest() {
20
- return JSON.parse(readFileSync(join(reportsRoot, ".claude-plugin", "plugin.json"), "utf8"));
21
- }
22
- function bridgePackageFiles() {
23
- const packageJson = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
24
- return Array.isArray(packageJson.files) ? packageJson.files.filter((file) => typeof file === "string") : [];
25
- }
26
- describe("bakerPlugins", () => {
27
- it("resolves the reports plugin from the bundled Bridge plugins", () => {
28
- const plugins = bakerPlugins();
29
- expect(plugins).toHaveLength(1);
30
- expect(plugins[0]).toEqual({ type: "local", path: reportsRoot });
31
- expect(existsSync(plugins[0]?.path ?? "")).toBe(true);
32
- expect(reportsManifest().name).toBe("reports");
33
- });
34
- it("declares the reports plugin metadata and /reports:build command", () => {
35
- const manifest = reportsManifest();
36
- const skill = readFileSync(join(skillRoot, "SKILL.md"), "utf8");
37
- const skillName = skill.match(/^name: (.+)$/m)?.[1];
38
- expect(manifest).toMatchObject({
39
- name: "reports",
40
- displayName: "Baker Reports",
41
- version: "0.1.0",
42
- description: "Build and repair Baker company Reports.",
43
- keywords: ["baker", "reports", "marketing"],
44
- });
45
- expect(`${manifest.name}:${skillName}`).toBe("reports:build");
46
- });
47
- it("includes the full reports capability bundle", () => {
48
- expect(existsSync(join(skillRoot, "references", "report-contracts.md"))).toBe(true);
49
- expect(existsSync(join(skillRoot, "scripts", "scaffold-report.mjs"))).toBe(true);
50
- expect(existsSync(join(skillRoot, "scripts", "report-paths.mjs"))).toBe(true);
51
- expect(existsSync(join(skillRoot, "templates", "report-definition.md"))).toBe(true);
52
- expect(existsSync(join(skillRoot, "templates", "report-index.html"))).toBe(true);
53
- });
54
- it("includes the bundled reports plugin payload in Bridge package files", () => {
55
- expect(bridgePackageFiles()).toContain("plugins");
56
- expect(existsSync(join(reportsRoot, ".claude-plugin", "plugin.json"))).toBe(true);
57
- expect(existsSync(join(skillRoot, "SKILL.md"))).toBe(true);
58
- expect(existsSync(join(skillRoot, "references", "report-contracts.md"))).toBe(true);
59
- expect(existsSync(join(skillRoot, "scripts", "scaffold-report.mjs"))).toBe(true);
60
- expect(existsSync(join(skillRoot, "scripts", "report-paths.mjs"))).toBe(true);
61
- expect(existsSync(join(skillRoot, "templates", "report-definition.md"))).toBe(true);
62
- expect(existsSync(join(skillRoot, "templates", "report-index.html"))).toBe(true);
63
- });
64
- it("keeps the vendored scaffold path contract in sync", async () => {
65
- const pluginContract = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(skillRoot, "scripts", "report-paths.mjs")).href)));
66
- const scaffoldContract = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(repoRoot, "apps", "scaffold-template", "__tooling__", "watcher", "base", "report-contract.mjs")).href)));
67
- expect(pluginContract.ReportFileContract.isValidSlug("weekly-summary")).toBe(scaffoldContract.ReportFileContract.isValidSlug("weekly-summary"));
68
- expect(pluginContract.ReportFileContract.isValidSlug("Weekly Summary")).toBe(scaffoldContract.ReportFileContract.isValidSlug("Weekly Summary"));
69
- expect(pluginContract.ReportFileContract.titleFromSlug("weekly-summary")).toBe(scaffoldContract.ReportFileContract.titleFromSlug("weekly-summary"));
70
- expect(pluginContract.ReportFileContract.paths("weekly-summary")).toEqual(scaffoldContract.ReportFileContract.paths("weekly-summary"));
71
- });
72
- it("scaffolds report files in a company repo root", async () => {
73
- const tempDir = mkdtempSync(join(tmpdir(), "baker-reports-plugin-"));
74
- try {
75
- const script = (await import(__rewriteRelativeImportExtension(pathToFileURL(join(skillRoot, "scripts", "scaffold-report.mjs")).href)));
76
- const result = script.createReportFiles("weekly-summary", tempDir);
77
- expect(result.slug).toBe("weekly-summary");
78
- expect(result.files).toEqual([
79
- join(tempDir, "reports/server/routes/reports/data/weekly-summary.get.ts"),
80
- join(tempDir, "reports/public/weekly-summary/index.html"),
81
- join(tempDir, "reports/public/weekly-summary/_definition.md"),
82
- ]);
83
- const endpointPath = join(tempDir, "reports/server/routes/reports/data/weekly-summary.get.ts");
84
- const dashboardPath = join(tempDir, "reports/public/weekly-summary/index.html");
85
- const definitionPath = join(tempDir, "reports/public/weekly-summary/_definition.md");
86
- expect(existsSync(endpointPath)).toBe(true);
87
- expect(existsSync(dashboardPath)).toBe(true);
88
- expect(existsSync(definitionPath)).toBe(true);
89
- expect(readFileSync(dashboardPath, "utf8")).not.toMatch(/__[A-Z0-9_]+__/);
90
- expect(readFileSync(definitionPath, "utf8")).toContain("/reports/data/weekly-summary");
91
- expect(readFileSync(definitionPath, "utf8")).not.toMatch(/__[A-Z0-9_]+__/);
92
- }
93
- finally {
94
- rmSync(tempDir, { recursive: true, force: true });
95
- }
96
- });
97
- });
98
- //# sourceMappingURL=plugins.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"plugins.test.js","sourceRoot":"","sources":["../../src/hono/plugins.test.ts"],"names":[],"mappings":"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"}
@@ -1,7 +0,0 @@
1
- {
2
- "name": "reports",
3
- "displayName": "Baker Reports",
4
- "version": "0.1.0",
5
- "description": "Build and repair Baker company Reports.",
6
- "keywords": ["baker", "reports", "marketing"]
7
- }
@@ -1,73 +0,0 @@
1
- ---
2
- name: build
3
- description: Create or edit company Reports in the current workspace. Use it when user ask for reports, dashboards.
4
- ---
5
-
6
- # Reports
7
-
8
- Reports are internal dashboard/sandbox live assets authored in `reports/` from the Company Repo root. A company Report is first-class backend state; scaffold files are the authored source that the watcher upserts. The sandbox builds Reports through the locked build wrapper and serves promoted built Nitro output on port `3001`; do not use or fall back to `nitro dev`.
9
-
10
- ## Start here
11
-
12
- 1. Clarify the business question and reader.
13
- 2. If the domain is specialized, consult the relevant domain/playbook skill before choosing metrics.
14
- 3. Read `src/brand/BRAND.md` and make the dashboard match the client brand. If it is empty, use neutral defaults and leave a TODO comment.
15
- 4. Create new reports with the scaffold script bundled with this skill. Resolve `scripts/scaffold-report.mjs` from this `SKILL.md` file's directory, then run it from the Company Repo root:
16
-
17
- ```bash
18
- node /absolute/path/to/this/skill/scripts/scaffold-report.mjs <slug>
19
- ```
20
-
21
- 5. Replace the generated placeholder `index.html` with the real Report Dashboard. Author HTML/CSS/JS as a real file; do not pass report HTML through a JS function/string API.
22
- 6. Edit only the generated report files unless the user explicitly asks for infrastructure work.
23
-
24
- ## Report files
25
-
26
- Each report slug owns exactly these authored files:
27
-
28
- - `reports/server/routes/reports/data/{slug}.get.ts` - JSON endpoint.
29
- - `reports/public/{slug}/index.html` - HTML dashboard rendered by the generic report route.
30
- - `reports/public/{slug}/_definition.md` - metadata contract watched by backend upsert.
31
-
32
- Do not edit report runtime infrastructure (`nitro.config.ts`, generic routes, utils, package config) for normal report creation.
33
-
34
- ## V1 contracts
35
-
36
- Read `references/report-contracts.md` before creating or repairing endpoint/refresh/metadata behavior.
37
-
38
- Core rules:
39
-
40
- - Status values: `draft`, `active`, `error`, `archived`.
41
- - `_definition.md` frontmatter is the metadata source for title, description, status, and optional `errorMessage`.
42
- - Set `status: active` only after the dashboard, endpoint, metadata, and live smoke test pass.
43
- - Endpoints return `{ ok: true, data, meta }` or `{ ok: false, error }`.
44
- - Use `runReportBaker(event, args)` for Baker CLI reads that support no-cache refresh, or `runBaker(args, { noCache })` with the v1 `fresh=true` convention when orchestration requires it.
45
- - For new or repaired Google Ads query rows, adapt Baker's flat dotted keys with `googleAdsRow(raw)` before returning report data.
46
- - HTML pages should fetch `/reports/data/{slug}` normally; never fetch `/reports/{slug}` for JSON because that route serves the authored HTML dashboard. The generic `/reports/{slug}/` route injects the Refresh control and propagates `?fresh=true` to same-origin report fetches.
47
- - Refresh invalidation is whole-report: the injected Refresh control reloads `/reports/{slug}/?fresh=true`, the runtime propagates `fresh=true` to same-origin `/reports/data/{slug}` fetches, and `runReportBaker(...)` passes `--no-cache` to Baker CLI. HTTP responses stay `no-store`; the Baker CLI cache is the cache being bypassed.
48
- - Reports are not public generated-site deployment output in v1.
49
-
50
- ## Validate
51
-
52
- For report changes, run the scaffold verification gate from the Company Repo root:
53
-
54
- ```bash
55
- pnpm run verify
56
- ```
57
-
58
- Do not invoke `__tooling__/lefthook/base/validate-reports.mjs` directly. `pnpm run verify` is the supported contract; it runs report contract validation from the correct root along with typecheck and Nitro build checks.
59
-
60
- `verify` proves contracts, typecheck, and Nitro build. It does not prove live data correctness. For a new or active report, smoke-test the built runtime before claiming success:
61
-
62
- ```bash
63
- node __tooling__/scripts/smoke-reports-runtime.mjs --slug {slug}
64
- ```
65
-
66
- In a running company sandbox, the supervisor serves the built Reports runtime at `http://localhost:3001`; you may also smoke the live URLs directly:
67
-
68
- ```bash
69
- curl -fsS http://localhost:3001/reports/{slug}/ >/tmp/report.html
70
- curl -fsS http://localhost:3001/reports/data/{slug} | tee /tmp/report-data.json
71
- ```
72
-
73
- Treat endpoint `{ "ok": false, ... }`, visible dashboard error cards, failed Baker CLI calls, provider API errors, or smoke script failures as blocking. Fix the endpoint/query and rerun the smoke test. For a new report, also visually open `http://localhost:3001/reports/{slug}/` in the sandbox and verify the dashboard renders with real data or a deliberate empty state.
@@ -1,103 +0,0 @@
1
- # Report Contracts
2
-
3
- Use this reference when creating or repairing Baker company Reports.
4
-
5
- ## Ownership
6
-
7
- - Convex owns Report lifecycle and dashboard listing state.
8
- - Scaffold owns report files and Nitro runtime behavior.
9
- - The sandbox supervisor builds `reports` through the locked `reports-build` wrapper and serves promoted built Nitro output on port `3001`; do not use `nitro dev` for Reports.
10
- - Dashboard owns visibility and links to the sandbox `reportsHost`.
11
- - V1 reports are internal dashboard/sandbox live assets, not public generated-site deployment output.
12
-
13
- ## Required files
14
-
15
- For slug `{slug}`:
16
-
17
- ```text
18
- reports/server/routes/reports/data/{slug}.get.ts
19
- reports/public/{slug}/index.html
20
- reports/public/{slug}/_definition.md
21
- ```
22
-
23
- Slug format: lowercase alphanumeric words separated by hyphens.
24
-
25
- ## `_definition.md`
26
-
27
- Frontmatter is the metadata contract consumed by watcher/upsert.
28
-
29
- ```markdown
30
- ---
31
- title: "Campaign Performance Weekly"
32
- description: "Weekly spend, conversions, and cost per lead by campaign"
33
- status: draft
34
- ---
35
- ```
36
-
37
- Allowed statuses: `draft`, `active`, `error`, `archived`. Optional `errorMessage` is surfaced with error report definitions.
38
-
39
- Keep frontmatter minimal and put business context, data-source notes, refresh notes, evolution, and source breadcrumbs in the Markdown body. Set `status: active` only when the endpoint, dashboard, metadata, and live smoke test are ready.
40
-
41
- ## Endpoint pattern
42
-
43
- Default to `runReportBaker(event, args)` so `?fresh=true` maps to Baker `--no-cache`.
44
-
45
- ```ts
46
- import { defineHandler } from "nitro";
47
- import { runReportBaker } from "../../../utils/baker.ts";
48
-
49
- export default defineHandler(async (event) => {
50
- const start = Date.now();
51
- const result = await runReportBaker<Row[]>(event, ["ads", "google", "query", "--preset", "campaign-performance"]);
52
-
53
- if (!result.ok) {
54
- return { ok: false, error: result.error };
55
- }
56
-
57
- return {
58
- ok: true,
59
- data: result.data,
60
- meta: { durationMs: Date.now() - start, cached: result.cached ?? false },
61
- };
62
- });
63
- ```
64
-
65
- For multi-call orchestration, read `freshRequested(event)` once and pass `{ noCache }` to every cacheable Baker call. Do not pass `noCache` to Baker commands that do not support `--no-cache`. Refresh invalidation is whole-report: `/reports/{slug}/?fresh=true` propagates to same-origin `/reports/data/{slug}` fetches, and Baker CLI cache is bypassed with `--no-cache`; HTTP responses remain `no-store`.
66
-
67
- Google Ads query rows arrive from Baker CLI with flat dotted keys such as `campaign.name`, `campaign.advertising_channel_type`, `metrics.cost_micros`, and `segments.conversion_action_name`. `runReportBaker` preserves those flat keys and explicitly adds nested aliases for compatibility with existing reports. In new or repaired Google Ads report endpoints, adapt rows with `googleAdsRow(raw)` from `reports/server/utils/googleAdsRow.ts` before returning report data; the adapter preserves the input row as `row.raw`. Do not use top-level aliases such as `row.name` or `row.spend`; derive those explicitly from the Google Ads fields.
68
-
69
- ## HTML dashboard pattern
70
-
71
- The page is served by the generic route at `/reports/{slug}/`, which injects a whole-report Refresh control. Keep dashboard JS simple:
72
-
73
- - Fetch `/reports/data/{slug}` without manually appending `fresh=true`.
74
- - Let the injected bootstrap propagate `?fresh=true` from the page URL to same-origin report fetches.
75
- - Do not implement a second custom refresh button unless the user explicitly asks for custom UX.
76
- - Keep chart data available in tables for accessibility.
77
-
78
- ## Validation
79
-
80
- Run from the scaffold harness root:
81
-
82
- ```bash
83
- pnpm run verify
84
- ```
85
-
86
- Do not invoke `__tooling__/lefthook/base/validate-reports.mjs` directly. `pnpm run verify` is the supported contract and runs report validation with the correct working directory, plus typecheck and Nitro build checks.
87
-
88
- The report validator checks report slug shape, `_definition.md` frontmatter/status, required dashboard files, active report endpoints, and legacy dashboard fetches. Legacy `/reports/{slug}` data fetches/routes must be fixed manually to use `/reports/data/{slug}`. The validator reports these issues but does not mutate source files or execute report data endpoints.
89
-
90
- Before marking a new or active report done, smoke-test the built runtime:
91
-
92
- ```bash
93
- node __tooling__/scripts/smoke-reports-runtime.mjs --slug {slug}
94
- ```
95
-
96
- In a running company sandbox, the supervisor serves Reports at port `3001`, so direct probes are also valid:
97
-
98
- ```bash
99
- curl -fsS http://localhost:3001/reports/{slug}/ >/tmp/report.html
100
- curl -fsS http://localhost:3001/reports/data/{slug} | tee /tmp/report-data.json
101
- ```
102
-
103
- Endpoint `{ "ok": false, ... }`, visible dashboard error cards, failed Baker CLI calls, provider API errors, or smoke script failures are blocking. Fix the endpoint/query and rerun the smoke test before claiming success.
@@ -1,19 +0,0 @@
1
- const REPORT_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2
-
3
- function titleFromSlug(slug) {
4
- return slug.replace(/-/g, " ").replace(/\b\w/g, (letter) => letter.toUpperCase());
5
- }
6
-
7
- function paths(slug) {
8
- return {
9
- definition: `reports/public/${slug}/_definition.md`,
10
- dashboard: `reports/public/${slug}/index.html`,
11
- endpoint: `reports/server/routes/reports/data/${slug}.get.ts`,
12
- };
13
- }
14
-
15
- export const ReportFileContract = {
16
- isValidSlug: (slug) => REPORT_SLUG_RE.test(slug),
17
- titleFromSlug,
18
- paths,
19
- };
@@ -1,108 +0,0 @@
1
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
- import { dirname, join, resolve } from "node:path";
3
- import { fileURLToPath, pathToFileURL } from "node:url";
4
- import { ReportFileContract } from "./report-paths.mjs";
5
-
6
- function endpointTemplate() {
7
- return `import { defineHandler } from "nitro";
8
- import { runReportBaker } from "../../../utils/baker.ts";
9
-
10
- export default defineHandler(async (event) => {
11
- const start = Date.now();
12
-
13
- // TODO: replace with the Baker command for this report.
14
- // runReportBaker maps ?fresh=true to Baker --no-cache for commands that support it.
15
- const result = await runReportBaker(event, ["TODO", "replace", "with", "baker", "args"]);
16
-
17
- if (!result.ok) {
18
- return { ok: false, error: result.error };
19
- }
20
-
21
- return {
22
- ok: true,
23
- data: result.data,
24
- meta: {
25
- durationMs: Date.now() - start,
26
- cached: result.cached ?? false,
27
- },
28
- };
29
- });
30
- `;
31
- }
32
-
33
- const scriptDir = dirname(fileURLToPath(import.meta.url));
34
-
35
- function readTemplate(name) {
36
- return readFileSync(resolve(scriptDir, "..", "templates", name), "utf8");
37
- }
38
-
39
- const TEMPLATE_TOKEN_RE = /__[A-Z0-9_]+__/g;
40
-
41
- function renderTemplate(template, values) {
42
- let rendered = template;
43
- for (const [key, value] of Object.entries(values)) {
44
- rendered = rendered.replaceAll(`__${key.toUpperCase()}__`, value);
45
- }
46
-
47
- const unresolved = rendered.match(TEMPLATE_TOKEN_RE);
48
- if (unresolved) {
49
- throw new Error(`Unresolved template token(s): ${[...new Set(unresolved)].join(", ")}`);
50
- }
51
-
52
- return rendered;
53
- }
54
-
55
- function pageTemplate(slug, title) {
56
- return renderTemplate(readTemplate("report-index.html"), { slug, title });
57
- }
58
-
59
- function definitionTemplate(slug, title, today = new Date().toISOString().slice(0, 10)) {
60
- return renderTemplate(readTemplate("report-definition.md"), { slug, title, today });
61
- }
62
-
63
- export function createReportFiles(slug, projectRoot = process.cwd()) {
64
- if (!ReportFileContract.isValidSlug(slug)) {
65
- throw new Error(`Invalid slug "${slug}". Must match /^[a-z0-9]+(-[a-z0-9]+)*$/`);
66
- }
67
-
68
- const paths = ReportFileContract.paths(slug);
69
- const pageDir = join(projectRoot, "reports", "public", slug);
70
- const dashboardPath = join(pageDir, "index.html");
71
- const endpointPath = resolve(projectRoot, paths.endpoint);
72
- const definitionPath = resolve(projectRoot, paths.definition);
73
-
74
- if (existsSync(pageDir)) {
75
- throw new Error(`Dashboard page already exists: ${pageDir}`);
76
- }
77
-
78
- if (existsSync(endpointPath)) {
79
- throw new Error(`Endpoint already exists: ${endpointPath}`);
80
- }
81
-
82
- const title = ReportFileContract.titleFromSlug(slug);
83
-
84
- mkdirSync(pageDir, { recursive: true });
85
- mkdirSync(dirname(endpointPath), { recursive: true });
86
- writeFileSync(dashboardPath, pageTemplate(slug, title));
87
- writeFileSync(definitionPath, definitionTemplate(slug, title));
88
- writeFileSync(endpointPath, endpointTemplate());
89
-
90
- return { slug, files: [endpointPath, dashboardPath, definitionPath] };
91
- }
92
-
93
- const isDirectRun = process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
94
- if (isDirectRun) {
95
- const slug = process.argv[2];
96
-
97
- if (!slug) {
98
- console.error("Usage: node scaffold-report.mjs <slug>");
99
- process.exit(1);
100
- }
101
-
102
- try {
103
- process.stdout.write(`${JSON.stringify(createReportFiles(slug), null, 2)}\n`);
104
- } catch (error) {
105
- process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
106
- process.exit(1);
107
- }
108
- }