@danypops/papyrus 0.29.6 → 0.29.8

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.
@@ -238,7 +238,7 @@ const checklistCriterionSchema = Type.Object({
238
238
  proof: Type.Array(proofReferenceSchema, { minItems: 1 }),
239
239
  });
240
240
 
241
- export function registerDomainTools(pi: ExtensionAPI): void {
241
+ export function registerTasksTool(pi: ExtensionAPI): void {
242
242
  pi.registerTool({
243
243
  name: "tasks",
244
244
  label: "Tasks",
@@ -445,7 +445,9 @@ export function registerDomainTools(pi: ExtensionAPI): void {
445
445
  }
446
446
  },
447
447
  });
448
+ }
448
449
 
450
+ export function registerNotesTool(pi: ExtensionAPI): void {
449
451
  pi.registerTool({
450
452
  name: "notes",
451
453
  label: "Notes",
@@ -496,7 +498,9 @@ export function registerDomainTools(pi: ExtensionAPI): void {
496
498
  }
497
499
  },
498
500
  });
501
+ }
499
502
 
503
+ export function registerDocsTool(pi: ExtensionAPI): void {
500
504
  pi.registerTool({
501
505
  name: "docs",
502
506
  label: "Documents",
@@ -556,7 +560,9 @@ export function registerDomainTools(pi: ExtensionAPI): void {
556
560
  }
557
561
  },
558
562
  });
563
+ }
559
564
 
565
+ export function registerRulesTool(pi: ExtensionAPI): void {
560
566
  pi.registerTool({
561
567
  name: "rules",
562
568
  label: "Rules",
@@ -604,7 +610,9 @@ export function registerDomainTools(pi: ExtensionAPI): void {
604
610
  }
605
611
  },
606
612
  });
613
+ }
607
614
 
615
+ export function registerPlaybooksTool(pi: ExtensionAPI): void {
608
616
  pi.registerTool({
609
617
  name: "playbooks",
610
618
  label: "Playbooks",
@@ -652,7 +660,9 @@ export function registerDomainTools(pi: ExtensionAPI): void {
652
660
  }
653
661
  },
654
662
  });
663
+ }
655
664
 
665
+ export function registerSkillsTool(pi: ExtensionAPI): void {
656
666
  pi.registerTool({
657
667
  name: "skills",
658
668
  label: "Skills",
@@ -729,7 +739,9 @@ export function registerDomainTools(pi: ExtensionAPI): void {
729
739
  }
730
740
  },
731
741
  });
742
+ }
732
743
 
744
+ export function registerDiscussTool(pi: ExtensionAPI): void {
733
745
  pi.registerTool({
734
746
  name: "discuss",
735
747
  label: "Discuss",
@@ -827,3 +839,14 @@ export function registerDomainTools(pi: ExtensionAPI): void {
827
839
  },
828
840
  });
829
841
  }
842
+
843
+ /** Thin orchestrator: each domain's tool is independently navigable/testable via its own registerXTool function. */
844
+ export function registerDomainTools(pi: ExtensionAPI): void {
845
+ registerTasksTool(pi);
846
+ registerNotesTool(pi);
847
+ registerDocsTool(pi);
848
+ registerRulesTool(pi);
849
+ registerPlaybooksTool(pi);
850
+ registerSkillsTool(pi);
851
+ registerDiscussTool(pi);
852
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.29.6",
3
+ "version": "0.29.8",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
6
  "keywords": ["pi-package"],
package/src/ops.ts CHANGED
@@ -507,53 +507,38 @@ function readBoundedGateFile(path: string): string {
507
507
  return readFileSync(path, "utf-8") as string;
508
508
  }
509
509
 
510
+ /** Shared by the sync and async process-gate runners so "test" is never a second, independently
511
+ * maintained copy of "command"'s own command-template/timeout selection. */
512
+ function processGateCommand(gate: Gate): { command: string; timeout: number } {
513
+ if (gate.type === "test") return { command: `npx vitest run ${gate.target} --reporter=dot`, timeout: GATE_TEST_TIMEOUT_MS };
514
+ return { command: gate.target, timeout: GATE_COMMAND_TIMEOUT_MS };
515
+ }
516
+
517
+ /**
518
+ * spawnSync + manual stdout/stderr concatenation, not execSync: execSync's return value is stdout
519
+ * only. Many real commands (bun test's own per-test lines and its pass/fail summary among them, and
520
+ * vitest's own "test" gate output) write their actual output to stderr, so an execSync-based match
521
+ * against gate.expect saw only the first line of a banner and never the result -- every such gate
522
+ * failed regardless of whether the command actually passed. This one function now serves both
523
+ * "command" and "test" gates; previously "test" was a second, separately-maintained execSync path
524
+ * that never checked gate.expect at all.
525
+ */
526
+ function runProcessGateSync(gate: Gate, cwd?: string): GateResult {
527
+ const { spawnSync } = require_("node:child_process");
528
+ const { command, timeout } = processGateCommand(gate);
529
+ const result = spawnSync(command, { shell: true, encoding: "utf-8", timeout, ...(cwd ? { cwd } : {}) });
530
+ if (result.error) return { gate, passed: false, output: result.error.message.slice(0, GATE_OUTPUT_LIMIT) };
531
+ const combined = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
532
+ const passed = result.status === 0 && (gate.expect ? combined.includes(gate.expect) : true);
533
+ return { gate, passed, output: combined.slice(0, GATE_OUTPUT_LIMIT) || (result.status === 0 ? "ok" : `command exited with code ${result.status}`) };
534
+ }
535
+
510
536
  export function runGates(db: Db, artifactId: string, options: GateRunOptions = {}): GateResult[] {
511
537
  const art = getArtifact(db, artifactId);
512
538
  if (!art) throw new Error("artifact not found");
513
539
  const gates = (art.extra["gates"] as Gate[]) ?? [];
514
540
  const cwd = options.cwd;
515
- return gates.map((gate) => {
516
- switch (gate.type) {
517
- case "file-exists": {
518
- const { existsSync } = require_("node:fs");
519
- const exists = existsSync(gate.target);
520
- return { gate, passed: exists, output: exists ? "exists" : "not found" };
521
- }
522
- case "contains": {
523
- try {
524
- const content = readBoundedGateFile(gate.target);
525
- const found = gate.expect ? content.includes(gate.expect) : content.length > 0;
526
- return { gate, passed: found, output: found ? "found" : `"${gate.expect ?? ""}" not found` };
527
- } catch {
528
- return { gate, passed: false, output: "file not readable" };
529
- }
530
- }
531
- case "command": {
532
- // spawnSync + manual stdout/stderr concatenation, not execSync: execSync's return value is
533
- // stdout only. Many real commands (bun test's own per-test lines and its pass/fail summary
534
- // among them) write their actual output to stderr, so an execSync-based match against
535
- // gate.expect saw only the first line of a banner and never the result -- every such gate
536
- // failed regardless of whether the command actually passed.
537
- const { spawnSync } = require_("node:child_process");
538
- const result = spawnSync(gate.target, { shell: true, encoding: "utf-8", timeout: GATE_COMMAND_TIMEOUT_MS, ...(cwd ? { cwd } : {}) });
539
- if (result.error) return { gate, passed: false, output: result.error.message.slice(0, GATE_OUTPUT_LIMIT) };
540
- const combined = `${result.stdout ?? ""}${result.stderr ?? ""}`.trim();
541
- const passed = result.status === 0 && (gate.expect ? combined.includes(gate.expect) : true);
542
- return { gate, passed, output: combined.slice(0, GATE_OUTPUT_LIMIT) || (result.status === 0 ? "ok" : `command exited with code ${result.status}`) };
543
- }
544
- case "test": {
545
- const { execSync } = require_("node:child_process");
546
- try {
547
- execSync(`npx vitest run ${gate.target} --reporter=dot`, { encoding: "utf-8", timeout: GATE_TEST_TIMEOUT_MS, stdio: ["pipe", "pipe", "pipe"], ...(cwd ? { cwd } : {}) });
548
- return { gate, passed: true, output: "tests passed" };
549
- } catch (e) {
550
- return { gate, passed: false, output: e instanceof Error ? e.message.slice(0, GATE_OUTPUT_LIMIT) : "tests failed" };
551
- }
552
- }
553
- default:
554
- return { gate, passed: false, output: `unknown gate type: ${String(gate.type)}` };
555
- }
556
- });
541
+ return gates.map((gate) => (gate.type === "command" || gate.type === "test") ? runProcessGateSync(gate, cwd) : runNonProcessGate(gate));
557
542
  }
558
543
 
559
544
  /**
@@ -652,8 +637,7 @@ export async function runGatesAsync(db: Db, artifactId: string, options: GateRun
652
637
  continue;
653
638
  }
654
639
  if (gate.type === "command" || gate.type === "test") {
655
- const command = gate.type === "test" ? `npx vitest run ${gate.target} --reporter=dot` : gate.target;
656
- const configuredTimeout = gate.type === "test" ? GATE_TEST_TIMEOUT_MS : GATE_COMMAND_TIMEOUT_MS;
640
+ const { command, timeout: configuredTimeout } = processGateCommand(gate);
657
641
  const timeout = remainingMs === undefined ? configuredTimeout : Math.max(1, Math.min(configuredTimeout, remainingMs));
658
642
  const executed = await executeGateCommand(command, timeout, options.cwd);
659
643
  results.push({