pi-better-harness 0.1.9 → 0.1.12

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/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  ## Quick Answer
6
6
 
7
- Use `pi-better-harness` when you want the full background-work bundle for Pi in one install. It loads:
7
+ Use `pi-better-harness` when you want the full background-work set for Pi. It manages:
8
8
 
9
9
  - `pi-better-subagents` for detached, sandboxed subagent runs.
10
10
  - `pi-better-background-tasks` for durable shell tasks and watchers.
@@ -18,43 +18,46 @@ Use `pi-better-harness` when you want the full background-work bundle for Pi in
18
18
 
19
19
  ## Install
20
20
 
21
+ Install the three extensions as standalone Pi packages, so Pi displays and manages each by its own package name:
22
+
21
23
  ```sh
22
- pi install npm:pi-better-harness
24
+ npx pi-better-harness install
23
25
  ```
24
26
 
25
- Try it for one run without saving it to Pi settings:
27
+ For project-local Pi settings:
26
28
 
27
29
  ```sh
28
- pi -e npm:pi-better-harness
30
+ npx pi-better-harness install --local
29
31
  ```
30
32
 
31
- ## Install Individual Packages
33
+ The bundled installation remains available for compatibility:
32
34
 
33
35
  ```sh
34
- pi install npm:pi-better-subagents
35
- pi install npm:pi-better-background-tasks
36
- pi install npm:pi-better-goal
36
+ pi install npm:pi-better-harness
37
37
  ```
38
38
 
39
39
  ## When To Use
40
40
 
41
- Use this package when you want all three core extensions loaded together. Install an individual package instead when you only need subagents, shell task supervision, or goal tracking.
41
+ Use the installer when you want all three core extensions with standalone package identities. Install an individual package instead when you only need subagents, shell task supervision, or goal tracking.
42
42
 
43
43
  ## Compatibility
44
44
 
45
45
  | Requirement | Support |
46
46
  |-------------|---------|
47
47
  | Pi | Required |
48
- | Install method | `pi install npm:pi-better-harness` |
48
+ | Recommended install | `npx pi-better-harness install` |
49
49
  | Development runtime | Node.js 22+ |
50
50
 
51
51
  ## Update Or Remove
52
52
 
53
+ Remove all three standalone packages:
54
+
53
55
  ```sh
54
- pi update npm:pi-better-harness
55
- pi remove npm:pi-better-harness
56
+ npx pi-better-harness uninstall
56
57
  ```
57
58
 
59
+ Add `--local` to remove them from project-local settings.
60
+
58
61
  ## More Detail
59
62
 
60
63
  - Repository: https://github.com/1aboveio/pi-better-harness
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { run } from "../lib/cli.mjs";
4
+
5
+ process.exitCode = run(process.argv.slice(2));
@@ -0,0 +1 @@
1
+ export { default } from "../../node_modules/pi-better-background-tasks/src/index.ts";
@@ -0,0 +1 @@
1
+ export { default } from "../../node_modules/pi-better-goal/src/index.ts";
@@ -0,0 +1 @@
1
+ export { default } from "../../node_modules/pi-better-subagents/index.ts";
package/lib/cli.mjs ADDED
@@ -0,0 +1,62 @@
1
+ import { spawnSync } from "node:child_process";
2
+ import { readFileSync } from "node:fs";
3
+ import { dirname, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+
6
+ const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
7
+ const { version } = JSON.parse(readFileSync(resolve(packageRoot, "package.json"), "utf8"));
8
+
9
+ export const componentPackages = [
10
+ "pi-better-subagents",
11
+ "pi-better-background-tasks",
12
+ "pi-better-goal",
13
+ ];
14
+
15
+ const usage = `Usage: pi-better-harness <install|uninstall> [--local]
16
+
17
+ Commands:
18
+ install Configure all Pi Better Harness component packages
19
+ uninstall Remove all Pi Better Harness component packages
20
+
21
+ Options:
22
+ -l, --local Use project-local Pi settings
23
+ -h, --help Show this help
24
+ -v, --version Show the package version`;
25
+
26
+ export function run(argv, options = {}) {
27
+ const spawn = options.spawn ?? spawnSync;
28
+ const stdout = options.stdout ?? process.stdout;
29
+ const stderr = options.stderr ?? process.stderr;
30
+
31
+ if (argv.includes("--help") || argv.includes("-h")) {
32
+ stdout.write(`${usage}\n`);
33
+ return 0;
34
+ }
35
+ if (argv.includes("--version") || argv.includes("-v")) {
36
+ stdout.write(`${version}\n`);
37
+ return 0;
38
+ }
39
+
40
+ const local = argv.includes("--local") || argv.includes("-l");
41
+ const positional = argv.filter((arg) => !["--local", "-l"].includes(arg));
42
+ const command = positional[0];
43
+ if (!command || positional.length !== 1 || !["install", "uninstall"].includes(command)) {
44
+ stderr.write(`${usage}\n`);
45
+ return 2;
46
+ }
47
+
48
+ const piCommand = process.platform === "win32" ? "pi.cmd" : "pi";
49
+ const piAction = command === "install" ? "install" : "remove";
50
+ for (const packageName of componentPackages) {
51
+ const args = [piAction, `npm:${packageName}`, ...(local ? ["--local"] : [])];
52
+ const result = spawn(piCommand, args, { stdio: "inherit" });
53
+ if (result.error) {
54
+ stderr.write(`Failed to run Pi for ${packageName}: ${result.error.message}\n`);
55
+ return 1;
56
+ }
57
+ if (result.status !== 0) {
58
+ return result.status ?? 1;
59
+ }
60
+ }
61
+ return 0;
62
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-background-tasks",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "description": "Pi extension for durable background shell tasks, watchers, logs, and status inspection.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-goal",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "description": "Pi extension for goal tracking with background-aware continuation.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,7 +30,7 @@ import {
30
30
  type BackgroundWorkRow,
31
31
  } from "./shared-navigator.ts";
32
32
  import { spawnDetached, type SpawnResult } from "./spawn.ts";
33
- import { parseRun, resetParseRunCursor, type Usage } from "./parse.ts";
33
+ import { parseRun, resetParseRunCursor, tailLog, type Usage } from "./parse.ts";
34
34
  import { finalizeRun as finalizeRunCore } from "./finalization.ts";
35
35
  import { loadConfig, normalizeTools, resolveExtensionPath, SAFE_DEFAULT_TOOLS, SAFE_CLEAN_TOOLS, DEFAULT_MAX_CONCURRENT } from "./config.ts";
36
36
  import { resolveExtensions, extensionArgs } from "./extensions.ts";
@@ -659,8 +659,7 @@ function isTerminalNavigatorStatus(status: string): boolean {
659
659
  }
660
660
 
661
661
  /** Live detail snapshot for one run (registry + log parse + health). */
662
- function navigatorDetail(id: string) {
663
- const now = Date.now();
662
+ function navigatorDetail(id: string, now: number = Date.now()) {
664
663
  return buildNavigatorDetail(id, {
665
664
  readMeta,
666
665
  effectiveStatus,
@@ -742,9 +741,10 @@ function subagentWorkRows(now: number): BackgroundWorkRow[] {
742
741
  });
743
742
  }
744
743
 
745
- function subagentWorkDetail(id: string, now: number): BackgroundWorkDetail | null {
746
- const detail = navigatorDetail(id);
744
+ function subagentWorkDetail(id: string, now: number, options?: { logTailLines?: number }): BackgroundWorkDetail | null {
745
+ const detail = navigatorDetail(id, now);
747
746
  if (!detail) return null;
747
+ const logTailLines = options?.logTailLines ?? 10;
748
748
  const metadata = [
749
749
  { label: "provider", value: "Subagents" },
750
750
  { label: "model", value: detail.effort ? `${detail.model} · effort ${detail.effort}` : detail.model },
@@ -762,7 +762,11 @@ function subagentWorkDetail(id: string, now: number): BackgroundWorkDetail | nul
762
762
  statusTone: statusTone(detail.status),
763
763
  subtitle: detail.currentTool ? `current tool ${detail.currentTool}` : undefined,
764
764
  metadata,
765
- evidence: { label: "output", text: detail.output || "(no output yet)" },
765
+ // The shared navigator refreshes this provider once a second while the
766
+ // detail overlay is open. Read the selected logical tail rows each
767
+ // time so the evidence behaves like `tail -f`, not a single parsed
768
+ // activity/result snapshot.
769
+ evidence: { label: "log tail", text: tailLog(id, logTailLines) },
766
770
  footerActions: [detail.status === "running" || detail.status === "orphaned" ? "x stop" : "x dismiss"],
767
771
  };
768
772
  }
@@ -775,7 +779,7 @@ function ensureSubagentProvider(): void {
775
779
  priority: 10,
776
780
  visibleCount: () => navigatorRunningCount(),
777
781
  listRows: (now) => subagentWorkRows(now),
778
- detail: (id, now) => subagentWorkDetail(id, now),
782
+ detail: (id, now, options) => subagentWorkDetail(id, now, options),
779
783
  armCloseLabel: (row) => row.status === "running" || row.status === "orphaned" ? "x again to stop" : "x again to dismiss",
780
784
  close: (id) => {
781
785
  const outcome = navigatorCloseRun(id) as { action: string; id: string; status?: string };
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-subagents",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "description": "Pi extension for detached, sandboxed subagent runs that keep the foreground session free.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -26,7 +26,7 @@ import {
26
26
  readSync,
27
27
  statSync,
28
28
  } from "node:fs";
29
- import { readBoundedTail } from "./shared-log-utils.ts";
29
+ import { readBoundedTail, tailTerminalDisplay } from "./shared-log-utils.ts";
30
30
  import { readAppendedLines, type LogCursor } from "./log-cursor.ts";
31
31
  import { logPathFor } from "./registry.ts";
32
32
 
@@ -89,13 +89,11 @@ interface TailRead {
89
89
  */
90
90
  const readTail: (path: string, maxBytes: number) => TailRead = readBoundedTail;
91
91
 
92
- /** Last `n` lines of a run's log, or a placeholder if empty/unreadable. */
92
+ /** Last `n` terminal display rows of a run's log, or a placeholder if empty/unreadable. */
93
93
  export function tailLog(id: string, n: number, maxBytes = maxRawTailBytes()): string {
94
94
  const tail = readTail(logPathFor(id), maxBytes);
95
95
  if (tail.error || tail.text.trim() === "") return "(no output yet)";
96
- const lines = tail.text.split("\n");
97
- const kept = lines.slice(Math.max(0, lines.length - n));
98
- const out = kept.join("\n").trim();
96
+ const out = tailTerminalDisplay(tail.text, n).trim();
99
97
  return out === "" ? "(no output yet)" : out;
100
98
  }
101
99
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-harness",
3
- "version": "0.1.9",
3
+ "version": "0.1.12",
4
4
  "description": "Pi extension bundle for subagents, durable background tasks, and goal tracking.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -14,9 +14,9 @@
14
14
  ],
15
15
  "pi": {
16
16
  "extensions": [
17
- "node_modules/pi-better-subagents/index.ts",
18
- "node_modules/pi-better-background-tasks/src/index.ts",
19
- "node_modules/pi-better-goal/src/index.ts"
17
+ "extensions/subagents/index.ts",
18
+ "extensions/background-tasks/index.ts",
19
+ "extensions/goal/index.ts"
20
20
  ],
21
21
  "image": "https://raw.githubusercontent.com/1aboveio/pi-better-harness/main/docs/images/package-gallery/pi-better-harness.png"
22
22
  },
@@ -29,16 +29,23 @@
29
29
  "url": "https://github.com/1aboveio/pi-better-harness/issues"
30
30
  },
31
31
  "homepage": "https://github.com/1aboveio/pi-better-harness/tree/main/packages/pi-better-harness#readme",
32
+ "bin": {
33
+ "pi-better-harness": "./bin/pi-better-harness.mjs"
34
+ },
32
35
  "files": [
33
- "README.md"
36
+ "README.md",
37
+ "bin/*.mjs",
38
+ "lib/*.mjs",
39
+ "extensions/**/*.ts"
34
40
  ],
35
41
  "scripts": {
36
- "prepack": "node ../../scripts/stage-harness-dependencies.mjs"
42
+ "prepack": "node ../../scripts/stage-harness-dependencies.mjs",
43
+ "test": "node --test test/*.test.mjs"
37
44
  },
38
45
  "dependencies": {
39
- "pi-better-background-tasks": "0.1.9",
40
- "pi-better-goal": "0.1.9",
41
- "pi-better-subagents": "0.1.9"
46
+ "pi-better-background-tasks": "0.1.12",
47
+ "pi-better-goal": "0.1.12",
48
+ "pi-better-subagents": "0.1.12"
42
49
  },
43
50
  "bundledDependencies": [
44
51
  "pi-better-background-tasks",