@topce/pizx 0.3.0 → 0.5.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.
package/README.md CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  [![GitHub Sponsors](https://img.shields.io/github/sponsors/topce?style=social&logo=github)](https://github.com/sponsors/topce)
4
4
 
5
+ ![pizx — zx fork with native Pi AI integration](github-social-banner.png)
6
+
5
7
  > **zx fork with native Pi AI integration** — 15 template tags for shell scripting, AI text generation, coding agents, agentic patterns, communication, and orchestration topologies.
6
8
 
7
9
  ## Quick Start
@@ -46,7 +48,7 @@ npm install @topce/pizx
46
48
 
47
49
  **Prerequisites:**
48
50
  - Node.js >= 22.19.0
49
- - [Pi AI](https://github.com/earendil-works/pi-ai) installed and configured (`pi auth login`)
51
+ - [Pi AI](https://github.com/earendil-works/pi) installed and configured (`pi auth login`)
50
52
  - Shell commands from [zx](https://github.com/google/zx) (`$`, `cd`, `echo`, `fetch`, etc.)
51
53
 
52
54
  ## Writing Scripts
@@ -66,16 +68,25 @@ echo(intro)
66
68
  ```js
67
69
  import { $, π, Π, Ρ, Φ, Σ } from '@topce/pizx'
68
70
 
71
+ // Greek letters work everywhere...
69
72
  const output = await $`ls src/ | grep '.ts'`
70
73
  console.log(output.stdout)
71
74
 
72
75
  const review = await π`review this code for issues:\n${output.stdout}`
73
76
  console.log(review.text)
74
77
 
75
- // Use the coding agent to fix issues
76
- await Π`fix the TypeScript errors in src/`
78
+ // ...and so do English word aliases:
79
+ import { pi, Pi, ralph, fleet, subagent } from '@topce/pizx'
80
+
81
+ const answer = await pi`explain async/await`
82
+ await Pi`fix the TypeScript errors in src/`
83
+ await fleet`review all files in src/`
77
84
  ```
78
85
 
86
+ > **English word aliases**: Every Greek letter tag has an English alternative.
87
+ > `pi` (alias for `π`), `Pi` (alias for `Π`), `fleet` (alias for `Φ`), `ralph` (alias for `Ρ`),
88
+ > `pipeline` (alias for `Λ`), etc. — use whichever style you prefer. See [full mapping](#english-aliases) below.
89
+
79
90
  ### CLI Quick Queries
80
91
 
81
92
  ```bash
@@ -126,6 +137,24 @@ Each tag has detailed documentation in [`docs/`](docs/):
126
137
  | `Χ` | Chi | Analyze traces → extract patterns | [docs/chi.md](docs/chi.md) |
127
138
  | `Τ` | Tau | Define schema → write → refine → consolidate | [docs/tau.md](docs/tau.md) |
128
139
 
140
+ ### English Aliases
141
+
142
+ Every Greek letter tag has an equivalent English word. They're interchangeable — use whichever style you prefer.
143
+
144
+ | Greek | English | Greek | English |
145
+ |-------|---------|-------|---------|
146
+ | `π` | `pi` | `Π` | `Pi` |
147
+ | `Ρ` | `ralph` | `Φ` | `fleet` |
148
+ | `Σ` | `subagent` | `Δ` | `debate` |
149
+ | `Λ` | `pipeline` | `Ψ` | `critique` |
150
+ | `Ω` | `orchestrator` | `Ν` | `team` |
151
+ | `Θ` | `thread` | `Μ` | `memory` |
152
+ | `Β` | `broadcast` | `Α` | `adaptive` |
153
+ | `Γ` | `graph` | `Χ` | `learn` |
154
+ | `Τ` | `store` | | |
155
+
156
+ See [`english-examples/`](english-examples/) for runnable examples using all English aliases.
157
+
129
158
  ## Architecture
130
159
 
131
160
  See [docs/decisions/](docs/decisions/) for Architecture Decision Records covering the key design choices:
@@ -289,6 +318,57 @@ configurePi({ model: 'anthropic/claude-sonnet-4-5', maxTokens: 8000, timeoutMs:
289
318
  configureAgent({ maxTurns: 5, excludeTools: ['write'] })
290
319
  ```
291
320
 
321
+ ### System Prompt Overrides
322
+
323
+ All tags accept `system` (replaces default) and `appendSystemPrompt` (appended after system).
324
+
325
+ ```js
326
+ // π: custom system prompt
327
+ await π({ system: 'You are a security auditor' })`review this code`
328
+
329
+ // Π: set system prompt and append extra instructions
330
+ await Π({ system: 'You are a test engineer', appendSystemPrompt: 'Write tests first' })`add tests for auth`
331
+
332
+ // Patterns: inject system context via mergeSystem
333
+ await Ω({ system: 'Prioritize security over performance' })`design login flow`
334
+ ```
335
+
336
+ ### Thinking Budgets
337
+
338
+ Fine-grained token budgets per reasoning level. Passes through to providers via `thinkingBudgets`.
339
+
340
+ ```js
341
+ // Per-call
342
+ await π({ thinkingBudgets: { medium: 16384, high: 65536 } })`analyze`
343
+
344
+ // Global default
345
+ configurePi({ thinkingBudgets: { medium: 20480, high: 131072 } })
346
+
347
+ // Patterns support it too
348
+ await Ω({ thinkingBudgets: { high: 65536 } })`deep analysis task`
349
+ ```
350
+
351
+ ### Skill Integration
352
+
353
+ Load Pi agent skills from disk and inject them as system context. Skills are discovered from the same paths as `skill.sh`: `.pi/skills`, `.agents/skills`, `~/.pi/agent/skills`, etc.
354
+
355
+ ```js
356
+ import { loadSkillContent, loadSkillContents } from '@topce/pizx'
357
+
358
+ // Load a single skill
359
+ const codeStyle = await loadSkillContent('code-simplification')
360
+ if (codeStyle) {
361
+ await π({ system: codeStyle })`refactor auth.ts`
362
+ }
363
+
364
+ // Load multiple skills
365
+ const skills = await loadSkillContents(['test-driven-development', 'spec-driven-development'])
366
+
367
+ // Π and all patterns accept skills option
368
+ await Π({ skills: ['code-simplification'] })`clean up this file`
369
+ await Ω({ skills: ['spec-driven-development', 'incremental-implementation'] })`build the feature`
370
+ ```
371
+
292
372
  ## CLI Reference
293
373
 
294
374
  ```bash
@@ -344,3 +424,10 @@ See [`examples/`](examples/) for runnable examples of every pattern and feature:
344
424
  ## License
345
425
 
346
426
  MIT
427
+
428
+ ## Credits
429
+
430
+ Built on the shoulders of two outstanding tools:
431
+
432
+ - [**zx**](https://github.com/google/zx) by [Anton Medvedev](https://github.com/antonmedv) — the original shell scripting tool for Node.js that popularized template-tag ergonomics for command execution. pizx preserves every zx API (`$`, `cd`, `echo`, `fetch`, `chalk`, etc.) unchanged.
433
+ - [**Pi**](https://github.com/earendil-works/pi) by Mario Zechner / Earendil Works — the unified LLM API and coding agent harness that powers all `π`, `Π`, and pattern tags through `@earendil-works/pi-ai` and `@earendil-works/pi-coding-agent`.
package/dist/cli.js CHANGED
@@ -179,6 +179,37 @@ function pickModel(preferred) {
179
179
  return models[0];
180
180
  }
181
181
 
182
+ // src/skill-loader.ts
183
+ import { readFile } from "node:fs/promises";
184
+ import { homedir as homedir3 } from "node:os";
185
+ import { join as join3 } from "node:path";
186
+ var SKILL_PATHS = [
187
+ ".pi/skills",
188
+ ".agents/skills",
189
+ "skills",
190
+ join3(homedir3(), ".pi", "agent", "skills"),
191
+ join3(homedir3(), ".codewhale", "skills"),
192
+ join3(homedir3(), ".claude", "skills")
193
+ ];
194
+ async function loadSkillContent(name) {
195
+ for (const base of SKILL_PATHS) {
196
+ const candidate = join3(base, name, "SKILL.md");
197
+ try {
198
+ return await readFile(candidate, "utf-8");
199
+ } catch {
200
+ }
201
+ }
202
+ return void 0;
203
+ }
204
+ async function loadSkillContents(names) {
205
+ const map = /* @__PURE__ */ new Map();
206
+ for (const name of names) {
207
+ const content = await loadSkillContent(name);
208
+ if (content) map.set(name, content);
209
+ }
210
+ return map;
211
+ }
212
+
182
213
  // src/patterns/types.ts
183
214
  var PatternOutput = class {
184
215
  constructor(text, startTime = Date.now(), endTime = Date.now()) {
@@ -309,18 +340,35 @@ async function confirmPhase(description, opts) {
309
340
  async function ask(prompt, opts = {}) {
310
341
  const model = pickModel(opts.model);
311
342
  if (!model) throw new Error("pizx/patterns: No AI models configured. Run `pi auth login` first.");
343
+ let systemPrompt = opts.system;
344
+ if (opts.skills && opts.skills.length > 0) {
345
+ const skillMap = await loadSkillContents(opts.skills);
346
+ if (skillMap.size > 0) {
347
+ const skillBlocks = [];
348
+ for (const [name, content] of skillMap) {
349
+ skillBlocks.push(`Skill context (${name}):
350
+ ${content}`);
351
+ }
352
+ const skillContext = skillBlocks.join("\n\n");
353
+ systemPrompt = systemPrompt ? `${systemPrompt}
354
+
355
+ ${skillContext}` : skillContext;
356
+ }
357
+ }
312
358
  const t0 = Date.now();
313
359
  const result = await completeSimple(
314
360
  model,
315
361
  {
316
- systemPrompt: opts.system,
362
+ systemPrompt,
317
363
  messages: [{ role: "user", content: prompt, timestamp: Date.now() }]
318
364
  },
319
365
  {
320
366
  maxTokens: opts.maxTokens ?? 4096,
321
367
  reasoning: opts.thinkingLevel ?? "medium",
368
+ thinkingBudgets: opts.thinkingBudgets,
322
369
  timeoutMs: opts.timeoutMs,
323
- maxRetries: opts.maxRetries
370
+ maxRetries: opts.maxRetries,
371
+ apiKey: opts.apiKey
324
372
  }
325
373
  );
326
374
  const durationMs = Date.now() - t0;
@@ -857,9 +905,9 @@ var defaults4 = {
857
905
  rounds: 1
858
906
  };
859
907
  var CritiqueRound = class {
860
- constructor(content, critique, round) {
908
+ constructor(content, critique2, round) {
861
909
  this.content = content;
862
- this.critique = critique;
910
+ this.critique = critique2;
863
911
  this.round = round;
864
912
  }
865
913
  content;
@@ -923,12 +971,12 @@ Revise the content based on the critique.`,
923
971
  }
924
972
  if (!opts.quiet) process.stderr.write(` \u2192 Critiquing (round ${r + 1})...
925
973
  `);
926
- const critique = await ask(currentContent, {
974
+ const critique2 = await ask(currentContent, {
927
975
  ...opts,
928
976
  model: plannerModel,
929
977
  system: mergeSystem(opts.system, CRITIQUE_SYSTEM)
930
978
  });
931
- critiqueRounds.push(new CritiqueRound(currentContent, critique, r));
979
+ critiqueRounds.push(new CritiqueRound(currentContent, critique2, r));
932
980
  }
933
981
  const t1 = Date.now();
934
982
  const finalContent = currentContent;
@@ -2414,21 +2462,21 @@ ${prompt}`, {
2414
2462
  }
2415
2463
  return { keys, roles: roles.slice(0, agentCount), assignments };
2416
2464
  }
2417
- function formatStore(store) {
2418
- const entries = Object.entries(store).filter(([, v]) => v);
2465
+ function formatStore(store2) {
2466
+ const entries = Object.entries(store2).filter(([, v]) => v);
2419
2467
  if (entries.length === 0) return "(empty \u2014 you are the first contributor)";
2420
2468
  return entries.map(([k, v]) => `[${k}]: ${v}`).join("\n\n");
2421
2469
  }
2422
- function mergeEntry(store, key, content) {
2423
- if (store[key]) {
2424
- store[key] += `
2470
+ function mergeEntry(store2, key, content) {
2471
+ if (store2[key]) {
2472
+ store2[key] += `
2425
2473
 
2426
2474
  ${content}`;
2427
2475
  } else {
2428
- store[key] = content;
2476
+ store2[key] = content;
2429
2477
  }
2430
2478
  }
2431
- async function executeRound(roles, assignments, store, round, opts) {
2479
+ async function executeRound(roles, assignments, store2, round, opts) {
2432
2480
  const workerModel = opts.workerModel ?? opts.model;
2433
2481
  const isWrite = round === 1;
2434
2482
  const operation = isWrite ? "write" : "update";
@@ -2436,7 +2484,7 @@ async function executeRound(roles, assignments, store, round, opts) {
2436
2484
  roles.map(async (role) => {
2437
2485
  const assignedKeys = assignments.get(role) ?? ["General"];
2438
2486
  const keysStr = assignedKeys.join(", ");
2439
- const storeText = formatStore(store);
2487
+ const storeText = formatStore(store2);
2440
2488
  const systemPrompt = isWrite ? WRITE_SYSTEM(role, keysStr).replace("{store}", storeText) : UPDATE_SYSTEM(role, keysStr).replace("{store}", storeText);
2441
2489
  const task = isWrite ? `Write your initial findings to your assigned keys: ${keysStr}` : `Review the shared context and update your entries for keys: ${keysStr}`;
2442
2490
  const response = await ask(task, {
@@ -2448,7 +2496,7 @@ async function executeRound(roles, assignments, store, round, opts) {
2448
2496
  })
2449
2497
  );
2450
2498
  const entries = [];
2451
- const newStore = { ...store };
2499
+ const newStore = { ...store2 };
2452
2500
  for (const r of roundResults) {
2453
2501
  if (r.status !== "fulfilled") continue;
2454
2502
  const { role, response } = r.value;
@@ -2471,8 +2519,8 @@ async function executeRound(roles, assignments, store, round, opts) {
2471
2519
  }
2472
2520
  return { entries, store: newStore };
2473
2521
  }
2474
- async function consolidateStore(task, store, opts) {
2475
- const storeText = formatStore(store);
2522
+ async function consolidateStore(task, store2, opts) {
2523
+ const storeText = formatStore(store2);
2476
2524
  return ask(
2477
2525
  `Original task: ${task}
2478
2526
 
@@ -2513,7 +2561,7 @@ async function execute14(pieces, args, opts) {
2513
2561
  }
2514
2562
  }
2515
2563
  const allEntries = [];
2516
- let store = {};
2564
+ let store2 = {};
2517
2565
  for (let round = 1; round <= totalRounds; round++) {
2518
2566
  const label = round === 1 ? "Writing" : "Updating";
2519
2567
  if (!opts.quiet) process.stderr.write(` \u2192 Round ${round}/${totalRounds}: ${label}...
@@ -2521,15 +2569,15 @@ async function execute14(pieces, args, opts) {
2521
2569
  const { entries, store: updatedStore } = await executeRound(
2522
2570
  roles,
2523
2571
  assignments,
2524
- store,
2572
+ store2,
2525
2573
  round,
2526
2574
  opts
2527
2575
  );
2528
2576
  allEntries.push(...entries);
2529
- store = updatedStore;
2577
+ store2 = updatedStore;
2530
2578
  }
2531
2579
  if (!opts.quiet) process.stderr.write(" \u2192 Consolidating store...\n");
2532
- const synthesis = await consolidateStore(task, store, { ...opts, plannerModel });
2580
+ const synthesis = await consolidateStore(task, store2, { ...opts, plannerModel });
2533
2581
  if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2534
2582
  const qualityReview = await runQualityReview(task, synthesis, opts);
2535
2583
  const t1 = Date.now();
@@ -2540,7 +2588,7 @@ async function execute14(pieces, args, opts) {
2540
2588
  `Entries: ${allEntries.length}`,
2541
2589
  `Synthesis: ${synthesis}`
2542
2590
  ].join("\n\n");
2543
- return new TauOutput(summary, allEntries, store, synthesis, t0, t1, qualityReview);
2591
+ return new TauOutput(summary, allEntries, store2, synthesis, t0, t1, qualityReview);
2544
2592
  }
2545
2593
  var \u03A4 = createPatternTag(defaults14, execute14);
2546
2594
 
@@ -2581,10 +2629,10 @@ The conversation so far:
2581
2629
  Respond as your role. Be specific, build on or challenge what others have said.
2582
2630
  Keep your response under 200 words.`;
2583
2631
  var SYNTHESIS_SYSTEM6 = `You are a neutral facilitator. Synthesize the multi-agent conversation thread into a clear, actionable conclusion. Weigh the evidence, resolve conflicts, and present the best path forward.`;
2584
- function buildThreadPrompt(role, thread) {
2632
+ function buildThreadPrompt(role, thread2) {
2585
2633
  return THREAD_PROMPT.replace("{role}", role).replace(
2586
2634
  "{thread}",
2587
- thread || "(This is the first message in the thread.)"
2635
+ thread2 || "(This is the first message in the thread.)"
2588
2636
  );
2589
2637
  }
2590
2638
  async function execute15(pieces, args, opts) {
@@ -2602,17 +2650,17 @@ async function execute15(pieces, args, opts) {
2602
2650
  `);
2603
2651
  }
2604
2652
  const messages = [];
2605
- let thread = `Topic: ${topic}
2653
+ let thread2 = `Topic: ${topic}
2606
2654
  `;
2607
2655
  for (let turn = 1; turn <= maxTurns; turn++) {
2608
2656
  if (!opts.quiet) process.stderr.write(` \u2192 Turn ${turn}/${maxTurns}
2609
2657
  `);
2610
2658
  for (let a = 0; a < roles.length; a++) {
2611
2659
  const role = roles[a] ?? `Agent ${a + 1}`;
2612
- const prompt = buildThreadPrompt(role, thread);
2660
+ const prompt = buildThreadPrompt(role, thread2);
2613
2661
  const response = await ask(prompt, { ...opts, model: workerModel });
2614
2662
  messages.push(new ThreadMessage(role, turn, response));
2615
- thread += `
2663
+ thread2 += `
2616
2664
  [${role}] (Turn ${turn}): ${response}
2617
2665
  `;
2618
2666
  }
@@ -2622,7 +2670,7 @@ async function execute15(pieces, args, opts) {
2622
2670
  `Topic: ${topic}
2623
2671
 
2624
2672
  Conversation thread:
2625
- ${thread}
2673
+ ${thread2}
2626
2674
 
2627
2675
  Synthesize a clear, actionable conclusion.`,
2628
2676
  {
@@ -2642,6 +2690,23 @@ Synthesize a clear, actionable conclusion.`,
2642
2690
  }
2643
2691
  var \u0398 = createPatternTag(defaults15, execute15);
2644
2692
 
2693
+ // src/patterns/index.ts
2694
+ var ralph = \u03A1;
2695
+ var fleet = \u03A6;
2696
+ var subagent = \u03A3;
2697
+ var debate = \u0394;
2698
+ var pipeline = \u039B;
2699
+ var critique = \u03A8;
2700
+ var orchestrator = \u03A9;
2701
+ var thread = \u0398;
2702
+ var memory = \u039C;
2703
+ var broadcast = \u0392;
2704
+ var adaptive = \u0391;
2705
+ var graph = \u0393;
2706
+ var team = \u039D;
2707
+ var learn = \u03A7;
2708
+ var store = \u03A4;
2709
+
2645
2710
  // src/pi.ts
2646
2711
  import {
2647
2712
  streamSimple
@@ -2709,8 +2774,11 @@ var defaults16 = {
2709
2774
  maxTokens: 4096
2710
2775
  };
2711
2776
  function makeContext(pieces, args, opts) {
2777
+ const systemParts = [];
2778
+ if (opts.system) systemParts.push(opts.system);
2779
+ if (opts.appendSystemPrompt) systemParts.push(opts.appendSystemPrompt);
2712
2780
  return {
2713
- systemPrompt: opts.system,
2781
+ systemPrompt: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
2714
2782
  messages: [
2715
2783
  {
2716
2784
  role: "user",
@@ -2724,8 +2792,10 @@ function makeOpts(opts) {
2724
2792
  return {
2725
2793
  maxTokens: opts.maxTokens,
2726
2794
  reasoning: opts.thinkingLevel,
2795
+ thinkingBudgets: opts.thinkingBudgets,
2727
2796
  timeoutMs: opts.timeoutMs,
2728
- maxRetries: opts.maxRetries
2797
+ maxRetries: opts.maxRetries,
2798
+ apiKey: opts.apiKey
2729
2799
  };
2730
2800
  }
2731
2801
  async function run(pieces, args, opts) {
@@ -2810,7 +2880,10 @@ function makePi(opts = {}) {
2810
2880
  var \u03C0 = makePi();
2811
2881
 
2812
2882
  // src/pi-agent.ts
2813
- import { createAgentSession as createAgentSession2 } from "@earendil-works/pi-coding-agent";
2883
+ import {
2884
+ createAgentSession as createAgentSession2,
2885
+ DefaultResourceLoader
2886
+ } from "@earendil-works/pi-coding-agent";
2814
2887
  var _agentDefaults = {
2815
2888
  quiet: false,
2816
2889
  maxTurns: 10
@@ -2842,14 +2915,43 @@ var AgentOutput = class {
2842
2915
  var AgentPromise = class extends Promise {
2843
2916
  };
2844
2917
  var _sharedSession = null;
2918
+ function createLoader(opts) {
2919
+ const hasSystem = opts.system !== void 0;
2920
+ const hasAppend = opts.appendSystemPrompt !== void 0;
2921
+ const hasSkills = opts.skills && opts.skills.length > 0;
2922
+ if (!hasSystem && !hasAppend && !hasSkills) return void 0;
2923
+ return new DefaultResourceLoader({
2924
+ cwd: opts.cwd ?? process.cwd(),
2925
+ agentDir: "",
2926
+ systemPrompt: opts.system,
2927
+ appendSystemPrompt: opts.appendSystemPrompt ? [opts.appendSystemPrompt] : void 0
2928
+ });
2929
+ }
2845
2930
  async function getSession(opts) {
2846
2931
  if (_sharedSession && !opts.model) return _sharedSession;
2847
2932
  try {
2933
+ const loader = createLoader(opts);
2934
+ if (opts.skills && opts.skills.length > 0 && loader) {
2935
+ const skillMap = await loadSkillContents(opts.skills);
2936
+ const skillPaths = [];
2937
+ for (const [name] of skillMap) {
2938
+ for (const base of SKILL_PATHS) {
2939
+ skillPaths.push({
2940
+ path: `${base}/${name}`,
2941
+ metadata: { source: "pizx", scope: "project", origin: "top-level" }
2942
+ });
2943
+ }
2944
+ }
2945
+ if (skillPaths.length > 0) {
2946
+ loader.extendResources({ skillPaths });
2947
+ }
2948
+ }
2848
2949
  const result = await createAgentSession2({
2849
2950
  cwd: opts.cwd,
2850
2951
  thinkingLevel: opts.thinkingLevel,
2851
2952
  tools: opts.tools,
2852
- excludeTools: opts.excludeTools
2953
+ excludeTools: opts.excludeTools,
2954
+ resourceLoader: loader
2853
2955
  });
2854
2956
  _sharedSession = result.session;
2855
2957
  return _sharedSession;
@@ -3064,6 +3166,23 @@ async function runScriptMode(scriptPath) {
3064
3166
  g.\u0392 = \u0392;
3065
3167
  g.\u0391 = \u0391;
3066
3168
  g.\u0393 = \u0393;
3169
+ g.pi = \u03C0;
3170
+ g.Pi = \u03A0;
3171
+ g.ralph = ralph;
3172
+ g.fleet = fleet;
3173
+ g.subagent = subagent;
3174
+ g.debate = debate;
3175
+ g.pipeline = pipeline;
3176
+ g.critique = critique;
3177
+ g.orchestrator = orchestrator;
3178
+ g.thread = thread;
3179
+ g.memory = memory;
3180
+ g.broadcast = broadcast;
3181
+ g.adaptive = adaptive;
3182
+ g.graph = graph;
3183
+ g.team = team;
3184
+ g.learn = learn;
3185
+ g.store = store;
3067
3186
  const { createRequire: createRequire2 } = await import("node:module");
3068
3187
  const __filename = absPath;
3069
3188
  const __dirname = path.dirname(absPath);