@artale/pi-pai 4.5.0 → 4.6.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.
@@ -1,215 +0,0 @@
1
- /**
2
- * pi-pai — Personal AI Infrastructure (Core)
3
- * Kelsey Hightower MVI: lean core, optional modules loaded on demand.
4
- * Progressive complexity: simple tasks get zero algorithm overhead.
5
- *
6
- * Based on PAI v4.0.3 methodology (ISC, phases, criteria, learning signals).
7
- * Optional features moved to separate extensions.
8
- *
9
- * Install: pi install npm:pi-pai
10
- *
11
- * /pai mission|goal|done|loop|next|isc|isca|status|reset
12
- * capture_learning tool — store signals for cross-session improvement
13
- */
14
-
15
- import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
16
- import { Type } from "@sinclair/typebox";
17
- import { existsSync, mkdirSync, writeFileSync, readFileSync, appendFileSync, readdirSync } from "fs";
18
- import { homedir } from "os";
19
- import { join } from "path";
20
-
21
- const PAI_DIR = join(homedir(), ".config", "PAI-pi");
22
- const MEMORY_DIR = join(PAI_DIR, "memory");
23
- const WORK_DIR = join(MEMORY_DIR, "work");
24
- const LEARNING_DIR = join(MEMORY_DIR, "learning");
25
-
26
- for (const dir of [MEMORY_DIR, WORK_DIR, LEARNING_DIR]) {
27
- if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
28
- }
29
-
30
- // ISC minimums per effort level
31
- const ISC_MINIMUMS: Record<string, number> = {
32
- standard: 8, extended: 16, advanced: 24, deep: 40, comprehensive: 64,
33
- };
34
-
35
- type EffortLevel = keyof typeof ISC_MINIMUMS;
36
- type AlgorithmPhase = "OBSERVE" | "THINK" | "PLAN" | "BUILD" | "EXECUTE" | "VERIFY" | "LEARN";
37
- const PHASES: AlgorithmPhase[] = ["OBSERVE", "THINK", "PLAN", "BUILD", "EXECUTE", "VERIFY", "LEARN"];
38
-
39
- function iscSplittingTest(criterion: string): { pass: boolean; reason?: string } {
40
- if (/\b(and|as well as|along with)\b/i.test(criterion))
41
- return { pass: false, reason: "Compound criterion — split into separate ISCs" };
42
- if (/\b(all|every|each|any|both)\b/i.test(criterion))
43
- return { pass: false, reason: "Scope word detected — be more specific" };
44
- const words = criterion.trim().split(/\s+/).length;
45
- if (words > 20) return { pass: false, reason: "Too long (" + words + " words) — shorten" };
46
- return { pass: true };
47
- }
48
-
49
- interface AntiCriterion {
50
- id: string;
51
- description: string;
52
- severity: "critical" | "high" | "medium";
53
- }
54
-
55
- interface Learning {
56
- insight: string;
57
- confidence: number;
58
- category: string;
59
- timestamp: Date;
60
- }
61
-
62
- function readMemory<T>(slug: string): T[] {
63
- const file = join(MEMORY_DIR, slug + ".jsonl");
64
- if (!existsSync(file)) return [];
65
- return readFileSync(file, "utf-8").trim().split("\n").filter(Boolean).map(function(l) { return JSON.parse(l); });
66
- }
67
-
68
- function writeMemory<T>(slug: string, data: T): void {
69
- const file = join(MEMORY_DIR, slug + ".jsonl");
70
- try { appendFileSync(file, JSON.stringify(data) + "\n"); } catch { /* best effort */ }
71
- }
72
-
73
- interface AlgorithmLoop {
74
- phase: AlgorithmPhase;
75
- goal: string;
76
- effort: EffortLevel;
77
- isc: string[];
78
- iscA: AntiCriterion[];
79
- startTime: number;
80
- data: Record<string, string>;
81
- }
82
-
83
- let loop: AlgorithmLoop | null = null;
84
- let goals: { id: string; title: string; status: string }[] = [];
85
- let learnings: Learning[] = [];
86
-
87
- export default function (pi: ExtensionAPI) {
88
- pi.registerCommand("pai", {
89
- description: "PAI: mission|goal|done|loop|next|isc|isca|status|reset",
90
- handler: async function(args, ctx) {
91
- var parts = (args || "").trim().split(/\s+/);
92
- var sub = parts[0] ? parts[0].toLowerCase() : "";
93
- var restStr = parts.slice(1).join(" ");
94
-
95
- switch (sub) {
96
- case "mission":
97
- if (!restStr) { ctx.ui.notify("Usage: /pai mission <statement>", "error"); return; }
98
- writeMemory("mission", { mission: restStr, ts: new Date().toISOString() });
99
- ctx.ui.notify("MISSION: " + restStr, "info");
100
- break;
101
-
102
- case "goal":
103
- if (!restStr) { ctx.ui.notify("Usage: /pai goal <title>", "error"); return; }
104
- var id = "g" + goals.length;
105
- goals.push({ id: id, title: restStr, status: "active" });
106
- writeMemory("goals", { id: id, title: restStr, status: "active" });
107
- ctx.ui.notify("GOAL " + id + ": " + restStr, "info");
108
- break;
109
-
110
- case "done":
111
- var g = goals.find(function(g) { return g.id === restStr; });
112
- if (!g) { ctx.ui.notify("Goal " + restStr + " not found", "error"); return; }
113
- g.status = "completed";
114
- ctx.ui.notify("DONE: " + g.title, "info");
115
- break;
116
-
117
- case "loop":
118
- loop = {
119
- phase: "OBSERVE",
120
- goal: restStr || "unnamed",
121
- effort: "standard",
122
- isc: [],
123
- iscA: [],
124
- startTime: Date.now(),
125
- data: {},
126
- };
127
- ctx.ui.notify("ALGORITHM: " + loop.goal + " [OBSERVE]", "info");
128
- break;
129
-
130
- case "isc":
131
- if (!loop) { ctx.ui.notify("No active loop — /pai loop first", "error"); return; }
132
- var test = iscSplittingTest(restStr);
133
- if (!test.pass) { ctx.ui.notify("ISC FAIL: " + test.reason, "warning"); return; }
134
- loop.isc.push(restStr);
135
- ctx.ui.notify("ISC-" + loop.isc.length, "info");
136
- break;
137
-
138
- case "isca":
139
- if (!loop) { ctx.ui.notify("No active loop", "error"); return; }
140
- var sev = /critical|security|data.?loss/i.test(restStr) ? "critical" : "medium" as const;
141
- loop.iscA.push({ id: "a" + loop.iscA.length, description: restStr, severity: sev });
142
- ctx.ui.notify("ANTI [" + sev + "]", "info");
143
- break;
144
-
145
- case "next":
146
- if (!loop) { ctx.ui.notify("No active loop", "error"); return; }
147
- var idx = PHASES.indexOf(loop.phase);
148
- if (restStr) loop.data[loop.phase] = restStr;
149
-
150
- // ISC count gate before EXECUTE
151
- if (loop.phase === "PLAN") {
152
- var min = ISC_MINIMUMS[loop.effort];
153
- if (loop.isc.length < min) {
154
- ctx.ui.notify("ISC GATE: " + loop.isc.length + "/" + min, "warning");
155
- return;
156
- }
157
- }
158
-
159
- if (idx < PHASES.length - 1) {
160
- loop.phase = PHASES[idx + 1];
161
- ctx.ui.notify("-> " + loop.phase, "info");
162
- } else {
163
- var elapsed = Math.round((Date.now() - loop.startTime) / 1000);
164
- writeMemory("cycles", { goal: loop.goal, isc: loop.isc.length, elapsed: elapsed, ts: new Date().toISOString() });
165
- ctx.ui.notify("LOOP DONE (" + elapsed + "s) " + loop.isc.length + " ISC", "info");
166
- loop = null;
167
- }
168
- break;
169
-
170
- case "status":
171
- var memory = readMemory<Record<string, any>>("mission");
172
- var cycles = readMemory<Record<string, any>>("cycles");
173
- var mission = memory.length ? memory[memory.length - 1].mission : "Not set";
174
- var fields: string[] = ["MISSION: " + mission];
175
- fields.push("Goals: " + goals.filter(function(g) { return g.status === "active"; }).length + " active");
176
- fields.push("Cycles: " + cycles.length + " completed");
177
- if (loop) fields.push("LOOP: [" + loop.phase + "] " + loop.isc.length + " ISC");
178
- ctx.ui.notify(fields.join(" | "), "info");
179
- break;
180
-
181
- case "reset":
182
- loop = null;
183
- goals = [];
184
- learnings = [];
185
- ctx.ui.notify("PAI reset", "warning");
186
- break;
187
-
188
- default:
189
- ctx.ui.notify("/pai mission|goal|done|loop|next|isc|isca|status|reset", "info");
190
- }
191
- },
192
- });
193
-
194
- // Learning signal tool (on demand, not at startup)
195
- pi.registerTool({
196
- name: "capture_learning",
197
- label: "Learn",
198
- description: "Capture a learning signal for cross-session improvement",
199
- parameters: Type.Object({
200
- insight: Type.String({ description: "What you learned" }),
201
- category: Type.Optional(Type.String({ description: "algorithm|domain|system|process" })),
202
- }),
203
- async execute(_callId: string, params: { insight: string; category?: string }) {
204
- var entry = { insight: params.insight, category: params.category || "domain", confidence: 0.8, timestamp: new Date().toISOString() };
205
- writeMemory("signals", entry);
206
- learnings.push(entry);
207
- return { content: [{ type: "text" as const, text: "LEARNED: " + params.insight.slice(0, 60) }] };
208
- },
209
- });
210
-
211
- // Simple status line — no heavy initialization
212
- pi.on("agent_start", async function(_event, ctx) {
213
- ctx.ui.setStatus("pai", "PAI Core | Algorithm ready");
214
- });
215
- }