@alphacoder-v0/pi-loops 0.17.3

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 (68) hide show
  1. package/CHANGELOG.md +1989 -0
  2. package/LICENSE +21 -0
  3. package/README.md +370 -0
  4. package/README.zh-CN.md +606 -0
  5. package/docs/cli.md +252 -0
  6. package/docs/configuration.md +110 -0
  7. package/docs/design.md +155 -0
  8. package/docs/goal.md +59 -0
  9. package/docs/hooks.md +110 -0
  10. package/docs/loops.md +315 -0
  11. package/docs/mcp.md +90 -0
  12. package/docs/screenshot.png +0 -0
  13. package/docs/session-archive.md +64 -0
  14. package/docs/triggers.md +140 -0
  15. package/docs/troubleshooting.md +111 -0
  16. package/docs/web-ui-parity.md +305 -0
  17. package/examples/README.md +18 -0
  18. package/examples/mcp-notify-server.mjs +35 -0
  19. package/examples/mcp.toml +8 -0
  20. package/package.json +71 -0
  21. package/skills/pi-loops/SKILL.md +50 -0
  22. package/src/archive.ts +476 -0
  23. package/src/args.ts +217 -0
  24. package/src/cli-entry.mjs +23 -0
  25. package/src/cli.ts +610 -0
  26. package/src/config.ts +103 -0
  27. package/src/danger.ts +128 -0
  28. package/src/goal.ts +178 -0
  29. package/src/hooks.ts +436 -0
  30. package/src/host-control-channel.ts +243 -0
  31. package/src/host-control.ts +203 -0
  32. package/src/host-entry.mjs +14 -0
  33. package/src/host-runtime.ts +222 -0
  34. package/src/host.ts +329 -0
  35. package/src/inbox.ts +232 -0
  36. package/src/job-edit.ts +113 -0
  37. package/src/job-health.ts +29 -0
  38. package/src/lock.ts +193 -0
  39. package/src/log.ts +108 -0
  40. package/src/mcp-pool.ts +141 -0
  41. package/src/mcp.ts +885 -0
  42. package/src/paths.ts +33 -0
  43. package/src/pi-loops.ts +2292 -0
  44. package/src/pi-resolver.mjs +49 -0
  45. package/src/presence.ts +143 -0
  46. package/src/protocol.ts +198 -0
  47. package/src/redact.ts +61 -0
  48. package/src/register-pi.mjs +3 -0
  49. package/src/runner.ts +149 -0
  50. package/src/schedule.ts +431 -0
  51. package/src/scheduler.ts +981 -0
  52. package/src/sdk-runner.ts +536 -0
  53. package/src/share.ts +121 -0
  54. package/src/slots.ts +76 -0
  55. package/src/snapshot.ts +40 -0
  56. package/src/store.ts +599 -0
  57. package/src/subagent-guard.ts +47 -0
  58. package/src/thinking.ts +30 -0
  59. package/src/toml.ts +244 -0
  60. package/src/tools.ts +455 -0
  61. package/src/transcript.ts +84 -0
  62. package/src/trigger-runtime.ts +711 -0
  63. package/src/triggers.ts +558 -0
  64. package/src/trust.ts +47 -0
  65. package/src/ts-entry.mjs +67 -0
  66. package/src/ui-prefs.ts +84 -0
  67. package/src/version.ts +14 -0
  68. package/src/web.mjs +4484 -0
@@ -0,0 +1,84 @@
1
+ /**
2
+ * `ui.json`: the preferences that outlive a session, and the one place that writes them.
3
+ *
4
+ * Three parts of this project keep something here — the browser front end remembers the model and
5
+ * the thinking level you last chose, `/cron panel on|off` remembers whether the panel shows, and
6
+ * the launcher reads the first two back when it starts the next session. A writer that puts its own
7
+ * key into the file and nothing else erases the others', which is how turning the panel off used to
8
+ * send the next morning's session back to pi's default model. So a write here is always
9
+ * read-modify-write: the key is merged into whatever the file already holds, including keys this
10
+ * module has never heard of.
11
+ *
12
+ * The file has a writer outside TypeScript. `src/web.mjs` is plain JS loaded without a TypeScript
13
+ * loader, so it cannot import this module and keeps its own copy of the merge; the shape on disk is
14
+ * therefore a contract, not an implementation detail. It is JSON with a two-space indent and a
15
+ * trailing newline — the spelling the front end already wrote, kept because this is a file people
16
+ * open and read — and unknown keys are left exactly as they were found.
17
+ */
18
+ import * as fs from "node:fs";
19
+ import * as path from "node:path";
20
+ import { writeFileAtomic } from "./lock.ts";
21
+
22
+ export interface UiPrefs {
23
+ /** Whether `/cron`'s panel shows above the editor. Absent means nobody has turned it off. */
24
+ panel?: boolean;
25
+ /** The model and thinking level last chosen in the browser front end. */
26
+ model?: string;
27
+ thinking?: string;
28
+ }
29
+
30
+ const fileIn = (dir: string): string => path.join(dir, "ui.json");
31
+
32
+ /**
33
+ * The file as a plain object. A file that is not there and a file somebody edited into something
34
+ * that is not an object read the same — as no preferences — because the only thing either can mean
35
+ * is "nothing has been remembered yet", and failing a command over it would be worse than the
36
+ * default it falls back to.
37
+ */
38
+ function readRaw(dir: string): Record<string, unknown> {
39
+ try {
40
+ const doc: unknown = JSON.parse(fs.readFileSync(fileIn(dir), "utf8"));
41
+ return doc && typeof doc === "object" && !Array.isArray(doc) ? (doc as Record<string, unknown>) : {};
42
+ } catch {
43
+ // No file yet, or one that is not readable as JSON: the next write replaces it.
44
+ return {};
45
+ }
46
+ }
47
+
48
+ /** The preferences this project understands, each one only when it is the type it should be. */
49
+ export function readUiPrefs(dir: string): UiPrefs {
50
+ const doc = readRaw(dir);
51
+ const prefs: UiPrefs = {};
52
+ if (typeof doc.panel === "boolean") prefs.panel = doc.panel;
53
+ if (typeof doc.model === "string") prefs.model = doc.model;
54
+ if (typeof doc.thinking === "string") prefs.thinking = doc.thinking;
55
+ return prefs;
56
+ }
57
+
58
+ /**
59
+ * Whether the panel shows. Only an explicit `false` hides it: a fresh install, a deleted file and a
60
+ * file that no longer parses all mean "never turned off", and the panel is what `/cron` advertises.
61
+ */
62
+ export function panelEnabled(prefs: UiPrefs): boolean {
63
+ return prefs.panel !== false;
64
+ }
65
+
66
+ /**
67
+ * Remember one preference, keeping every other key in the file.
68
+ *
69
+ * There is no lock around the read and the write. The writer outside TypeScript cannot take one, so
70
+ * a lock here would serialise this module against itself and promise a mutual exclusion it does not
71
+ * have; what it would buy is a window of a few microseconds between two writes a person makes by
72
+ * hand, minutes apart. The atomic write is the part that matters — a reader never sees half a file.
73
+ */
74
+ export function writeUiPref<K extends keyof UiPrefs>(dir: string, key: K, value: NonNullable<UiPrefs[K]>): void {
75
+ const doc = readRaw(dir);
76
+ if (doc[key] === value) return;
77
+ doc[key] = value;
78
+ try {
79
+ writeFileAtomic(fileIn(dir), `${JSON.stringify(doc, null, 2)}\n`);
80
+ } catch {
81
+ // A preference is not worth failing the command that set it: the panel still changes for this
82
+ // session, it just will not be that way in the next one.
83
+ }
84
+ }
package/src/version.ts ADDED
@@ -0,0 +1,14 @@
1
+ /** pi-loops' own version, read from package.json once. */
2
+ import * as fs from "node:fs";
3
+ import { fileURLToPath } from "node:url";
4
+
5
+ export const PI_LOOPS_VERSION: string = (() => {
6
+ try {
7
+ // `fileURLToPath`, not `new URL(...).pathname`: a pathname is percent-encoded, so a checkout
8
+ // under a directory with a space or a `#` in its name reads no package.json at all — and the
9
+ // fallback below is silent, which put "0.0.0" in every manifest, log line and `/pi-loops`.
10
+ return String(JSON.parse(fs.readFileSync(fileURLToPath(new URL("../package.json", import.meta.url)), "utf8")).version ?? "0.0.0");
11
+ } catch {
12
+ return "0.0.0";
13
+ }
14
+ })();