@topce/pizx 0.1.0 → 0.4.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/dist/cli.js CHANGED
@@ -11,6 +11,11 @@ import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:f
11
11
  import { homedir as homedir2 } from "node:os";
12
12
  import { join as join2 } from "node:path";
13
13
 
14
+ // src/utils.ts
15
+ function getErrorMessage(err) {
16
+ return err instanceof Error ? err.message : String(err);
17
+ }
18
+
14
19
  // src/load-pi-settings.ts
15
20
  import { existsSync, readFileSync } from "node:fs";
16
21
  import { homedir } from "node:os";
@@ -88,46 +93,21 @@ function loadPiAuth() {
88
93
  }
89
94
  }
90
95
  } catch (err) {
91
- console.warn(`pizx: failed to parse ${path}: ${err instanceof Error ? err.message : err}`);
96
+ console.warn(`pizx: failed to parse ${path}: ${getErrorMessage(err)}`);
92
97
  }
93
98
  }
94
99
  }
95
100
 
96
101
  // src/patterns/types.ts
97
- import { completeSimple, getEnvApiKey, getModels, getProviders } from "@earendil-works/pi-ai";
98
- var PatternOutput = class {
99
- constructor(text, startTime = Date.now(), endTime = Date.now()) {
100
- this.text = text;
101
- this.startTime = startTime;
102
- this.endTime = endTime;
103
- }
104
- text;
105
- startTime;
106
- endTime;
107
- /** Duration in milliseconds */
108
- get duration() {
109
- return this.endTime - this.startTime;
110
- }
111
- toString() {
112
- return this.text;
113
- }
114
- valueOf() {
115
- return this.text;
116
- }
117
- [Symbol.toPrimitive]() {
118
- return this.text;
119
- }
120
- };
121
- var PatternPromise = class extends Promise {
122
- };
123
- function build(pieces, args) {
124
- let s = "";
125
- for (let i = 0; i < pieces.length; i++) {
126
- s += pieces[i];
127
- if (i < args.length) s += String(args[i]);
128
- }
129
- return s.trim();
130
- }
102
+ import { createInterface } from "node:readline";
103
+ import { completeSimple } from "@earendil-works/pi-ai";
104
+
105
+ // src/model-picker.ts
106
+ import {
107
+ getEnvApiKey,
108
+ getModels,
109
+ getProviders
110
+ } from "@earendil-works/pi-ai";
131
111
  var _piSettings;
132
112
  function getPiDefaults() {
133
113
  if (_piSettings === void 0) {
@@ -171,7 +151,8 @@ function pickModel(preferred) {
171
151
  if (hit) return hit;
172
152
  }
173
153
  if (settings.defaultProvider) {
174
- const providerModels = getModels(settings.defaultProvider);
154
+ const provider = settings.defaultProvider;
155
+ const providerModels = getModels(provider);
175
156
  if (providerModels && providerModels.length > 0) {
176
157
  const configured = new Set(getConfiguredProviders());
177
158
  if (configured.has(settings.defaultProvider)) {
@@ -197,23 +178,261 @@ function pickModel(preferred) {
197
178
  }
198
179
  return models[0];
199
180
  }
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
+
213
+ // src/patterns/types.ts
214
+ var PatternOutput = class {
215
+ constructor(text, startTime = Date.now(), endTime = Date.now()) {
216
+ this.text = text;
217
+ this.startTime = startTime;
218
+ this.endTime = endTime;
219
+ }
220
+ text;
221
+ startTime;
222
+ endTime;
223
+ /** Execution trace: one entry per LLM call within this pattern run. Populated by createPatternTag. */
224
+ trace = [];
225
+ /** Structured phase log: key phases during execution, populated by each pattern. */
226
+ phaseLog = [];
227
+ /** Duration in milliseconds */
228
+ get duration() {
229
+ return this.endTime - this.startTime;
230
+ }
231
+ /** Total input tokens across all calls */
232
+ get inputTokens() {
233
+ return this.trace.reduce((s, t) => s + t.inputTokens, 0);
234
+ }
235
+ /** Total output tokens across all calls */
236
+ get outputTokens() {
237
+ return this.trace.reduce((s, t) => s + t.outputTokens, 0);
238
+ }
239
+ /** Total tokens (input + output) across all calls */
240
+ get totalTokens() {
241
+ return this.trace.reduce((s, t) => s + t.totalTokens, 0);
242
+ }
243
+ /** Total cost in USD across all calls */
244
+ get totalCost() {
245
+ return this.trace.reduce((s, t) => s + t.cost, 0);
246
+ }
247
+ /** Number of LLM calls made during this pattern execution */
248
+ get callCount() {
249
+ return this.trace.length;
250
+ }
251
+ toString() {
252
+ return this.text;
253
+ }
254
+ valueOf() {
255
+ return this.text;
256
+ }
257
+ [Symbol.toPrimitive]() {
258
+ return this.text;
259
+ }
260
+ };
261
+ var PatternPromise = class extends Promise {
262
+ };
263
+ var _trace = null;
264
+ function beginTrace() {
265
+ _trace = [];
266
+ }
267
+ function collectTrace() {
268
+ const t = _trace ?? [];
269
+ _trace = null;
270
+ return t;
271
+ }
272
+ function pushTrace(entry) {
273
+ if (_trace) _trace.push(entry);
274
+ }
275
+ function createPatternTag(defaults17, execute17) {
276
+ function make(opts = {}) {
277
+ const merged = { ...defaults17, ...opts };
278
+ const fn = ((pieces, ...args) => {
279
+ if (!Array.isArray(pieces)) {
280
+ return make({ ...merged, ...pieces });
281
+ }
282
+ beginTrace();
283
+ return new PatternPromise((resolve, reject) => {
284
+ execute17(pieces, args, merged).then(
285
+ (output) => {
286
+ output.trace = collectTrace();
287
+ resolve(output);
288
+ },
289
+ (err) => {
290
+ collectTrace();
291
+ reject(err);
292
+ }
293
+ );
294
+ });
295
+ });
296
+ let _quiet;
297
+ Object.defineProperty(fn, "quiet", {
298
+ get() {
299
+ if (!_quiet) _quiet = make({ ...merged, quiet: true });
300
+ return _quiet;
301
+ },
302
+ enumerable: true,
303
+ configurable: true
304
+ });
305
+ return fn;
306
+ }
307
+ return make();
308
+ }
309
+ function build(pieces, args) {
310
+ let s = "";
311
+ for (let i = 0; i < pieces.length; i++) {
312
+ s += pieces[i];
313
+ if (i < args.length) s += String(args[i]);
314
+ }
315
+ return s.trim();
316
+ }
317
+ function mergeSystem(userSystem, patternSystem) {
318
+ if (!userSystem) return patternSystem;
319
+ return `${userSystem}
320
+
321
+ ${patternSystem}`;
322
+ }
323
+ async function confirmPhase(description, opts) {
324
+ if (!opts.confirm) return true;
325
+ if (!opts.quiet) {
326
+ process.stderr.write(`
327
+ \u2500\u2500 Confirm \u2500\u2500
328
+ ${description}
329
+ Proceed? [Y/n] `);
330
+ }
331
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
332
+ const answer = await new Promise((resolve) => {
333
+ rl.question("", (ans) => resolve(ans));
334
+ });
335
+ rl.close();
336
+ const trimmed = answer.trim().toLowerCase();
337
+ if (trimmed === "" || trimmed === "y" || trimmed === "yes") return true;
338
+ return false;
339
+ }
200
340
  async function ask(prompt, opts = {}) {
201
341
  const model = pickModel(opts.model);
202
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
+ }
358
+ const t0 = Date.now();
203
359
  const result = await completeSimple(
204
360
  model,
205
361
  {
206
- systemPrompt: opts.system,
362
+ systemPrompt,
207
363
  messages: [{ role: "user", content: prompt, timestamp: Date.now() }]
208
364
  },
209
365
  {
210
366
  maxTokens: opts.maxTokens ?? 4096,
211
- reasoning: opts.thinkingLevel ?? "medium"
367
+ reasoning: opts.thinkingLevel ?? "medium",
368
+ thinkingBudgets: opts.thinkingBudgets,
369
+ timeoutMs: opts.timeoutMs,
370
+ maxRetries: opts.maxRetries
212
371
  }
213
372
  );
373
+ const durationMs = Date.now() - t0;
374
+ if (!result.content || !Array.isArray(result.content)) {
375
+ throw new Error("pizx/patterns: Unexpected response format from AI model.");
376
+ }
214
377
  const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
378
+ if (_trace !== null) {
379
+ pushTrace({
380
+ call: _trace.length + 1,
381
+ modelId: result.model,
382
+ promptPreview: prompt.slice(0, 200),
383
+ outputPreview: text.slice(0, 200),
384
+ inputTokens: result.usage.input,
385
+ outputTokens: result.usage.output,
386
+ cacheReadTokens: result.usage.cacheRead,
387
+ cacheWriteTokens: result.usage.cacheWrite,
388
+ totalTokens: result.usage.totalTokens,
389
+ cost: result.usage.cost.total,
390
+ durationMs
391
+ });
392
+ }
215
393
  return text.trim();
216
394
  }
395
+ var QUALITY_REVIEW_SYSTEM = `You are a quality assurance reviewer. Evaluate the final deliverable against the original request.
396
+
397
+ Output format:
398
+ SCORE: 0.XX (quality score from 0.0 to 1.0)
399
+ ASSESSMENT: (1-2 sentences \u2014 is the output complete, consistent, and actionable?)
400
+ RECOMMENDATION: (1 sentence \u2014 what would improve this output?)`;
401
+ async function runQualityReview(originalRequest, finalOutput, opts) {
402
+ if (!opts.qualityCheck) return void 0;
403
+ const reviewText = await ask(
404
+ `Original request:
405
+ ${originalRequest}
406
+
407
+ Final deliverable:
408
+ ${finalOutput}
409
+
410
+ Evaluate the quality.`,
411
+ {
412
+ model: opts.plannerModel ?? opts.model,
413
+ maxTokens: 512,
414
+ thinkingLevel: "high",
415
+ timeoutMs: opts.timeoutMs,
416
+ maxRetries: opts.maxRetries,
417
+ system: QUALITY_REVIEW_SYSTEM
418
+ }
419
+ );
420
+ const scoreMatch = reviewText.match(/SCORE:\s*([\d.]+)/i);
421
+ const assessMatch = reviewText.match(/ASSESSMENT:\s*(.+)/i);
422
+ const recMatch = reviewText.match(/RECOMMENDATION:\s*(.+)/i);
423
+ const result = {
424
+ score: scoreMatch ? parseFloat(scoreMatch[1]) : 0.5,
425
+ assessment: assessMatch?.[1]?.trim() ?? "(no assessment)",
426
+ recommendation: recMatch?.[1]?.trim() ?? "(no recommendation)"
427
+ };
428
+ if (!opts.quiet) {
429
+ process.stderr.write(` Quality score: ${result.score.toFixed(2)}
430
+ `);
431
+ process.stderr.write(` ${result.assessment.slice(0, 80)}...
432
+ `);
433
+ }
434
+ return result;
435
+ }
217
436
 
218
437
  // src/patterns/adaptive.ts
219
438
  var defaults = {
@@ -277,10 +496,10 @@ async function execute(pieces, args, opts) {
277
496
  }
278
497
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
279
498
  const planText = await ask(goal, {
499
+ ...opts,
280
500
  model: plannerModel,
281
- maxTokens: opts.maxTokens,
282
501
  thinkingLevel: "high",
283
- system: PLAN_SYSTEM
502
+ system: mergeSystem(opts.system, PLAN_SYSTEM)
284
503
  });
285
504
  const planLines = planText.split("\n");
286
505
  const plannedSteps = [];
@@ -310,10 +529,9 @@ async function execute(pieces, args, opts) {
310
529
  process.stderr.write(` \u2192 Step ${executionStep}: ${currentStep.slice(0, 60)}...
311
530
  `);
312
531
  const result = await ask(currentStep, {
532
+ ...opts,
313
533
  model: workerModel,
314
- maxTokens: opts.maxTokens,
315
- thinkingLevel: opts.thinkingLevel,
316
- system: EXECUTE_SYSTEM
534
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM)
317
535
  });
318
536
  const evaluation = await ask(
319
537
  `Goal: ${goal}
@@ -322,10 +540,11 @@ Result: ${result}
322
540
 
323
541
  Evaluate the result.`,
324
542
  {
543
+ ...opts,
325
544
  model: plannerModel,
326
545
  maxTokens: 512,
327
546
  thinkingLevel: "high",
328
- system: EVALUATE_SYSTEM
547
+ system: mergeSystem(opts.system, EVALUATE_SYSTEM)
329
548
  }
330
549
  );
331
550
  const scoreMatch = evaluation.match(/SCORE:\s*([\d.]+)/i);
@@ -348,8 +567,7 @@ Evaluate the result.`,
348
567
  break;
349
568
  }
350
569
  const adaptUpper = adaptation.toUpperCase();
351
- if (adaptUpper.startsWith("REFINE")) {
352
- } else if (adaptUpper.startsWith("SKIP_NEXT")) {
570
+ if (adaptUpper.startsWith("SKIP_NEXT")) {
353
571
  stepIndex += 2;
354
572
  } else if (adaptUpper.startsWith("ADD")) {
355
573
  const newStep = adaptation.replace(/^ADD\s*/i, "");
@@ -368,36 +586,76 @@ Evaluate the result.`,
368
586
  ).join("\n\n");
369
587
  return new AdaptiveOutput(summary, finalResult, adaptiveSteps, executionStep, t0, t1);
370
588
  }
371
- function makeAdaptive(opts = {}) {
372
- const merged = { ...defaults, ...opts };
373
- const fn = ((pieces, ...args) => {
374
- if (!Array.isArray(pieces)) {
375
- return makeAdaptive({ ...merged, ...pieces });
376
- }
377
- return new PatternPromise((resolve, reject) => {
378
- execute(pieces, args, merged).then(resolve, reject);
379
- });
380
- });
381
- let _quiet;
382
- Object.defineProperty(fn, "quiet", {
383
- get() {
384
- if (!_quiet) _quiet = makeAdaptive({ ...merged, quiet: true });
385
- return _quiet;
386
- },
387
- enumerable: true,
388
- configurable: true
389
- });
390
- return fn;
391
- }
392
- var \u0391 = makeAdaptive();
589
+ var \u0391 = createPatternTag(defaults, execute);
393
590
 
394
- // src/patterns/broadcast.ts
395
- var defaults2 = {
396
- maxTokens: 4096,
397
- thinkingLevel: "medium",
398
- workers: 4
591
+ // src/patterns/role-sets.ts
592
+ var DEBATE_ROLE_SETS = {
593
+ 2: [
594
+ "Optimist \u2014 advocate for the most ambitious approach",
595
+ "Pessimist \u2014 identify risks and failure modes"
596
+ ],
597
+ 3: [
598
+ "Optimist \u2014 advocate the benefits and opportunities",
599
+ "Pessimist \u2014 identify risks, costs, and failure modes",
600
+ "Pragmatist \u2014 focus on practical trade-offs and implementation"
601
+ ],
602
+ 4: [
603
+ "Optimist \u2014 argue for the best-case potential",
604
+ "Pessimist \u2014 highlight worst-case risks and downsides",
605
+ "Pragmatist \u2014 balance pros/cons with practical constraints",
606
+ "Innovator \u2014 propose creative alternatives and novel approaches"
607
+ ],
608
+ 5: [
609
+ "Optimist",
610
+ "Pessimist",
611
+ "Pragmatist",
612
+ "Innovator",
613
+ "User Advocate \u2014 focus on end-user experience and accessibility"
614
+ ]
615
+ };
616
+ var MEMORY_ROLE_SETS = {
617
+ 2: ["Analyst \u2014 deep analysis of core aspects", "Reviewer \u2014 check for gaps and blind spots"],
618
+ 3: [
619
+ "Analyst \u2014 deep analysis of core aspects",
620
+ "Reviewer \u2014 check for gaps, edge cases, and blind spots",
621
+ "Strategist \u2014 connect findings to actionable insights"
622
+ ],
623
+ 4: [
624
+ "Analyst",
625
+ "Reviewer",
626
+ "Strategist",
627
+ "Innovator \u2014 propose novel angles and creative solutions"
628
+ ],
629
+ 5: [
630
+ "Analyst",
631
+ "Reviewer",
632
+ "Strategist",
633
+ "Innovator",
634
+ "Skeptic \u2014 challenge assumptions and stress-test conclusions"
635
+ ]
399
636
  };
400
- var ROLE_SETS = {
637
+ var THREAD_ROLE_SETS = {
638
+ 2: ["Proposer \u2014 advocate the best approach", "Critic \u2014 identify weaknesses and gaps"],
639
+ 3: [
640
+ "Proposer \u2014 suggest the best approach",
641
+ "Critic \u2014 identify weaknesses, risks, and missing pieces",
642
+ "Synthesizer \u2014 combine the best ideas into a practical plan"
643
+ ],
644
+ 4: [
645
+ "Proposer \u2014 advocate a bold solution",
646
+ "Critic \u2014 identify risks and weaknesses",
647
+ "Pragmatist \u2014 focus on practical implementation",
648
+ "Innovator \u2014 propose creative alternatives"
649
+ ],
650
+ 5: [
651
+ "Proposer",
652
+ "Critic",
653
+ "Pragmatist",
654
+ "Innovator",
655
+ "Devil's Advocate \u2014 challenge every assumption"
656
+ ]
657
+ };
658
+ var BROADCAST_ROLE_SETS = {
401
659
  2: [
402
660
  "Technical Expert \u2014 evaluate technical feasibility",
403
661
  "Business Expert \u2014 evaluate business viability"
@@ -421,6 +679,13 @@ var ROLE_SETS = {
421
679
  "Innovation Expert \u2014 suggest novel approaches and alternatives"
422
680
  ]
423
681
  };
682
+
683
+ // src/patterns/broadcast.ts
684
+ var defaults2 = {
685
+ maxTokens: 4096,
686
+ thinkingLevel: "medium",
687
+ workers: 4
688
+ };
424
689
  var BroadcastResponse = class {
425
690
  constructor(role, response, success, error) {
426
691
  this.role = role;
@@ -434,13 +699,15 @@ var BroadcastResponse = class {
434
699
  error;
435
700
  };
436
701
  var BroadcastOutput = class extends PatternOutput {
437
- constructor(text, synthesis, responses, startTime, endTime) {
702
+ constructor(text, synthesis, responses, startTime, endTime, qualityReview) {
438
703
  super(text, startTime, endTime);
439
704
  this.synthesis = synthesis;
440
705
  this.responses = responses;
706
+ this.qualityReview = qualityReview;
441
707
  }
442
708
  synthesis;
443
709
  responses;
710
+ qualityReview;
444
711
  };
445
712
  var WORKER_PROMPT = `You are a {role}.
446
713
 
@@ -455,7 +722,7 @@ async function execute2(pieces, args, opts) {
455
722
  const question = build(pieces, args);
456
723
  const t0 = Date.now();
457
724
  const workerCount = opts.workers ?? 4;
458
- const roles = opts.roles ?? ROLE_SETS[workerCount] ?? ROLE_SETS[4] ?? [];
725
+ const roles = opts.roles ?? BROADCAST_ROLE_SETS[workerCount] ?? BROADCAST_ROLE_SETS[4] ?? [];
459
726
  const plannerModel = opts.plannerModel ?? opts.model;
460
727
  const workerModel = opts.workerModel ?? opts.model;
461
728
  if (!opts.quiet) {
@@ -470,11 +737,7 @@ async function execute2(pieces, args, opts) {
470
737
  const broadcastResults = await Promise.allSettled(
471
738
  roles.map(async (role) => {
472
739
  const prompt = WORKER_PROMPT.replace("{role}", role).replace("{question}", question);
473
- const text = await ask(prompt, {
474
- model: workerModel,
475
- maxTokens: opts.maxTokens,
476
- thinkingLevel: opts.thinkingLevel
477
- });
740
+ const text = await ask(prompt, { ...opts, model: workerModel });
478
741
  return new BroadcastResponse(role, text, true);
479
742
  })
480
743
  );
@@ -499,40 +762,21 @@ ${responsesText}
499
762
 
500
763
  Synthesize a cohesive, actionable recommendation.`,
501
764
  {
765
+ ...opts,
502
766
  model: plannerModel,
503
- maxTokens: opts.maxTokens,
504
767
  thinkingLevel: "high",
505
- system: SYNTHESIS_SYSTEM
768
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM)
506
769
  }
507
770
  );
771
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
772
+ const qualityReview = await runQualityReview(question, synthesis, opts);
508
773
  const t1 = Date.now();
509
774
  const summary = responses.map(
510
775
  (wr) => `[${wr.role}]: ${wr.response.slice(0, 150)}${wr.response.length > 150 ? "..." : ""}`
511
776
  ).join("\n");
512
- return new BroadcastOutput(summary, synthesis, responses, t0, t1);
513
- }
514
- function makeBroadcast(opts = {}) {
515
- const merged = { ...defaults2, ...opts };
516
- const fn = ((pieces, ...args) => {
517
- if (!Array.isArray(pieces)) {
518
- return makeBroadcast({ ...merged, ...pieces });
519
- }
520
- return new PatternPromise((resolve, reject) => {
521
- execute2(pieces, args, merged).then(resolve, reject);
522
- });
523
- });
524
- let _quiet;
525
- Object.defineProperty(fn, "quiet", {
526
- get() {
527
- if (!_quiet) _quiet = makeBroadcast({ ...merged, quiet: true });
528
- return _quiet;
529
- },
530
- enumerable: true,
531
- configurable: true
532
- });
533
- return fn;
777
+ return new BroadcastOutput(summary, synthesis, responses, t0, t1, qualityReview);
534
778
  }
535
- var \u0392 = makeBroadcast();
779
+ var \u0392 = createPatternTag(defaults2, execute2);
536
780
 
537
781
  // src/patterns/chi.ts
538
782
  var defaults3 = {
@@ -552,15 +796,17 @@ var LearningInsight = class {
552
796
  confidence;
553
797
  };
554
798
  var ChiOutput = class extends PatternOutput {
555
- constructor(text, insights, summary, suggestedChanges, startTime, endTime) {
799
+ constructor(text, insights, summary, suggestedChanges, startTime, endTime, qualityReview) {
556
800
  super(text, startTime, endTime);
557
801
  this.insights = insights;
558
802
  this.summary = summary;
559
803
  this.suggestedChanges = suggestedChanges;
804
+ this.qualityReview = qualityReview;
560
805
  }
561
806
  insights;
562
807
  summary;
563
808
  suggestedChanges;
809
+ qualityReview;
564
810
  };
565
811
  var ANALYSIS_SYSTEM = `You are an agent team performance analyst. Review a multi-agent execution and extract structured learnings.
566
812
 
@@ -629,12 +875,13 @@ async function execute3(pieces, args, opts) {
629
875
  `);
630
876
  }
631
877
  const response = await ask(input, {
878
+ ...opts,
632
879
  model: plannerModel,
633
- maxTokens: opts.maxTokens,
634
- thinkingLevel: opts.thinkingLevel,
635
- system: ANALYSIS_SYSTEM
880
+ system: mergeSystem(opts.system, ANALYSIS_SYSTEM)
636
881
  });
637
882
  const { insights, summary, suggestedChanges } = parseInsights(response);
883
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
884
+ const qualityReview = await runQualityReview(input, response, opts);
638
885
  const t1 = Date.now();
639
886
  const text = [
640
887
  `Insights: ${insights.length} extracted`,
@@ -646,30 +893,9 @@ Summary: ${summary}`,
646
893
  `
647
894
  Changes: ${suggestedChanges}`
648
895
  ].join("\n");
649
- return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1);
650
- }
651
- function makeChi(opts = {}) {
652
- const merged = { ...defaults3, ...opts };
653
- const fn = ((pieces, ...args) => {
654
- if (!Array.isArray(pieces)) {
655
- return makeChi({ ...merged, ...pieces });
656
- }
657
- return new PatternPromise((resolve, reject) => {
658
- execute3(pieces, args, merged).then(resolve, reject);
659
- });
660
- });
661
- let _quiet;
662
- Object.defineProperty(fn, "quiet", {
663
- get() {
664
- if (!_quiet) _quiet = makeChi({ ...merged, quiet: true });
665
- return _quiet;
666
- },
667
- enumerable: true,
668
- configurable: true
669
- });
670
- return fn;
896
+ return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1, qualityReview);
671
897
  }
672
- var \u03A7 = makeChi();
898
+ var \u03A7 = createPatternTag(defaults3, execute3);
673
899
 
674
900
  // src/patterns/critique.ts
675
901
  var defaults4 = {
@@ -724,12 +950,7 @@ async function execute4(pieces, args, opts) {
724
950
  for (let r = 0; r < rounds; r++) {
725
951
  if (r === 0) {
726
952
  if (!opts.quiet) process.stderr.write(" \u2192 Generating initial content...\n");
727
- currentContent = await ask(prompt, {
728
- model: workerModel,
729
- maxTokens: opts.maxTokens,
730
- thinkingLevel: opts.thinkingLevel,
731
- system: void 0
732
- });
953
+ currentContent = await ask(prompt, { ...opts, model: workerModel, system: opts.system });
733
954
  } else {
734
955
  if (!opts.quiet) process.stderr.write(` \u2192 Improving (round ${r + 1})...
735
956
  `);
@@ -744,21 +965,15 @@ Content to improve:
744
965
  ${currentContent}
745
966
 
746
967
  Revise the content based on the critique.`,
747
- {
748
- model: workerModel,
749
- maxTokens: opts.maxTokens,
750
- thinkingLevel: opts.thinkingLevel,
751
- system: IMPROVE_SYSTEM
752
- }
968
+ { ...opts, model: workerModel, system: mergeSystem(opts.system, IMPROVE_SYSTEM) }
753
969
  );
754
970
  }
755
971
  if (!opts.quiet) process.stderr.write(` \u2192 Critiquing (round ${r + 1})...
756
972
  `);
757
973
  const critique = await ask(currentContent, {
974
+ ...opts,
758
975
  model: plannerModel,
759
- maxTokens: opts.maxTokens,
760
- thinkingLevel: opts.thinkingLevel,
761
- system: CRITIQUE_SYSTEM
976
+ system: mergeSystem(opts.system, CRITIQUE_SYSTEM)
762
977
  });
763
978
  critiqueRounds.push(new CritiqueRound(currentContent, critique, r));
764
979
  }
@@ -771,28 +986,7 @@ Critique: ${cr.critique.slice(0, 200)}${cr.critique.length > 200 ? "..." : ""}`
771
986
  ).join("\n\n");
772
987
  return new CritiqueOutput(summary, finalContent, critiqueRounds, t0, t1);
773
988
  }
774
- function makeCritique(opts = {}) {
775
- const merged = { ...defaults4, ...opts };
776
- const fn = ((pieces, ...args) => {
777
- if (!Array.isArray(pieces)) {
778
- return makeCritique({ ...merged, ...pieces });
779
- }
780
- return new PatternPromise((resolve, reject) => {
781
- execute4(pieces, args, merged).then(resolve, reject);
782
- });
783
- });
784
- let _quiet;
785
- Object.defineProperty(fn, "quiet", {
786
- get() {
787
- if (!_quiet) _quiet = makeCritique({ ...merged, quiet: true });
788
- return _quiet;
789
- },
790
- enumerable: true,
791
- configurable: true
792
- });
793
- return fn;
794
- }
795
- var \u03A8 = makeCritique();
989
+ var \u03A8 = createPatternTag(defaults4, execute4);
796
990
 
797
991
  // src/patterns/debate.ts
798
992
  var defaults5 = {
@@ -801,30 +995,6 @@ var defaults5 = {
801
995
  perspectives: 3,
802
996
  rounds: 1
803
997
  };
804
- var ROLE_SETS2 = {
805
- 2: [
806
- "Optimist \u2014 advocate for the most ambitious approach",
807
- "Pessimist \u2014 identify risks and failure modes"
808
- ],
809
- 3: [
810
- "Optimist \u2014 advocate the benefits and opportunities",
811
- "Pessimist \u2014 identify risks, costs, and failure modes",
812
- "Pragmatist \u2014 focus on practical trade-offs and implementation"
813
- ],
814
- 4: [
815
- "Optimist \u2014 argue for the best-case potential",
816
- "Pessimist \u2014 highlight worst-case risks and downsides",
817
- "Pragmatist \u2014 balance pros/cons with practical constraints",
818
- "Innovator \u2014 propose creative alternatives and novel approaches"
819
- ],
820
- 5: [
821
- "Optimist",
822
- "Pessimist",
823
- "Pragmatist",
824
- "Innovator",
825
- "User Advocate \u2014 focus on end-user experience and accessibility"
826
- ]
827
- };
828
998
  var DebatePerspective = class {
829
999
  constructor(role, argument, round = 1) {
830
1000
  this.role = role;
@@ -836,15 +1006,17 @@ var DebatePerspective = class {
836
1006
  round;
837
1007
  };
838
1008
  var DebateOutput = class extends PatternOutput {
839
- constructor(text, conclusion, perspectives, rounds, startTime, endTime) {
1009
+ constructor(text, conclusion, perspectives, rounds, startTime, endTime, qualityReview) {
840
1010
  super(text, startTime, endTime);
841
1011
  this.conclusion = conclusion;
842
1012
  this.perspectives = perspectives;
843
1013
  this.rounds = rounds;
1014
+ this.qualityReview = qualityReview;
844
1015
  }
845
1016
  conclusion;
846
1017
  perspectives;
847
1018
  rounds;
1019
+ qualityReview;
848
1020
  };
849
1021
  var PERSPECTIVE_SYSTEM = (role) => `You are a debater with the role: ${role}. Analyze the question from your perspective. Be thorough and specific. Provide evidence and reasoning for your position.`;
850
1022
  var REBUTTAL_SYSTEM = (role) => `You are a debater with the role: ${role}. Review the debate so far \u2014 including arguments from all other perspectives \u2014 and refine your position. Address counter-arguments directly. Challenge weak points in opposing views. Strengthen your original position with rebuttals. Be specific and responsive.`;
@@ -854,7 +1026,7 @@ async function execute5(pieces, args, opts) {
854
1026
  const t0 = Date.now();
855
1027
  const count = opts.perspectives ?? 3;
856
1028
  const totalRounds = opts.rounds ?? 1;
857
- const roles = opts.roles ?? ROLE_SETS2[count] ?? ROLE_SETS2[3] ?? [];
1029
+ const roles = opts.roles ?? DEBATE_ROLE_SETS[count] ?? DEBATE_ROLE_SETS[3] ?? [];
858
1030
  const plannerModel = opts.plannerModel ?? opts.model;
859
1031
  const workerModel = opts.workerModel ?? opts.model;
860
1032
  if (!opts.quiet) {
@@ -872,10 +1044,9 @@ async function execute5(pieces, args, opts) {
872
1044
  const round1Results = await Promise.allSettled(
873
1045
  roles.map(
874
1046
  (role) => ask(question, {
1047
+ ...opts,
875
1048
  model: workerModel,
876
- maxTokens: opts.maxTokens,
877
- thinkingLevel: opts.thinkingLevel,
878
- system: PERSPECTIVE_SYSTEM(role)
1049
+ system: mergeSystem(opts.system, PERSPECTIVE_SYSTEM(role))
879
1050
  }).then((text) => new DebatePerspective(role, text, 1))
880
1051
  )
881
1052
  );
@@ -909,10 +1080,9 @@ ${othersText}
909
1080
 
910
1081
  Refine your position. Address the counter-arguments directly. Strengthen your argument with rebuttals.`;
911
1082
  return ask(prompt, {
1083
+ ...opts,
912
1084
  model: workerModel,
913
- maxTokens: opts.maxTokens,
914
- thinkingLevel: opts.thinkingLevel,
915
- system: REBUTTAL_SYSTEM(role)
1085
+ system: mergeSystem(opts.system, REBUTTAL_SYSTEM(role))
916
1086
  }).then((text) => new DebatePerspective(role, text, round));
917
1087
  })
918
1088
  );
@@ -936,37 +1106,26 @@ Refine your position. Address the counter-arguments directly. Strengthen your ar
936
1106
 
937
1107
  Synthesize a balanced conclusion from the full debate above. Weigh the evidence from all rounds.`,
938
1108
  {
1109
+ ...opts,
939
1110
  model: plannerModel,
940
- maxTokens: opts.maxTokens,
941
1111
  thinkingLevel: "high",
942
- system: SYNTHESIS_SYSTEM2
1112
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM2)
943
1113
  }
944
1114
  );
1115
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1116
+ const qualityReview = await runQualityReview(question, conclusion, opts);
945
1117
  const t1 = Date.now();
946
- return new DebateOutput(conclusion, conclusion, allPerspectives, totalRounds, t0, t1);
947
- }
948
- function makeDebate(opts = {}) {
949
- const merged = { ...defaults5, ...opts };
950
- const fn = ((pieces, ...args) => {
951
- if (!Array.isArray(pieces)) {
952
- return makeDebate({ ...merged, ...pieces });
953
- }
954
- return new PatternPromise((resolve, reject) => {
955
- execute5(pieces, args, merged).then(resolve, reject);
956
- });
957
- });
958
- let _quiet;
959
- Object.defineProperty(fn, "quiet", {
960
- get() {
961
- if (!_quiet) _quiet = makeDebate({ ...merged, quiet: true });
962
- return _quiet;
963
- },
964
- enumerable: true,
965
- configurable: true
966
- });
967
- return fn;
1118
+ return new DebateOutput(
1119
+ conclusion,
1120
+ conclusion,
1121
+ allPerspectives,
1122
+ totalRounds,
1123
+ t0,
1124
+ t1,
1125
+ qualityReview
1126
+ );
968
1127
  }
969
- var \u0394 = makeDebate();
1128
+ var \u0394 = createPatternTag(defaults5, execute5);
970
1129
 
971
1130
  // src/patterns/fleet.ts
972
1131
  var defaults6 = {
@@ -987,11 +1146,13 @@ var FleetMemberOutput = class {
987
1146
  error;
988
1147
  };
989
1148
  var FleetOutput = class extends PatternOutput {
990
- constructor(text, members, startTime, endTime) {
1149
+ constructor(text, members, startTime, endTime, qualityReview) {
991
1150
  super(text, startTime, endTime);
992
1151
  this.members = members;
1152
+ this.qualityReview = qualityReview;
993
1153
  }
994
1154
  members;
1155
+ qualityReview;
995
1156
  /** Number of successful members */
996
1157
  get successCount() {
997
1158
  return this.members.filter((m) => m.success).length;
@@ -1011,20 +1172,30 @@ function parseTasks(template, explicitTasks) {
1011
1172
  if (lines.length > 1) return lines;
1012
1173
  return [template];
1013
1174
  }
1175
+ function describeTask(task) {
1176
+ if (typeof task === "function") return "(composed pattern)";
1177
+ return task;
1178
+ }
1014
1179
  var FLEET_SYSTEM = `You are a focused task specialist. Complete the assigned task concisely and accurately. Output only the result \u2014 no commentary about being an AI.`;
1015
1180
  async function executeTask(task, opts, workerModel) {
1181
+ if (typeof task === "function") {
1182
+ try {
1183
+ const text = await task("");
1184
+ return new FleetMemberOutput("(composed pattern)", text, true);
1185
+ } catch (err) {
1186
+ return new FleetMemberOutput("(composed pattern)", "", false, getErrorMessage(err));
1187
+ }
1188
+ }
1016
1189
  const model = workerModel ?? opts.model;
1017
1190
  try {
1018
1191
  const text = await ask(task, {
1192
+ ...opts,
1019
1193
  model,
1020
- maxTokens: opts.maxTokens,
1021
- thinkingLevel: opts.thinkingLevel,
1022
- system: opts.system ?? FLEET_SYSTEM
1194
+ system: mergeSystem(opts.system, FLEET_SYSTEM)
1023
1195
  });
1024
1196
  return new FleetMemberOutput(task, text, true);
1025
1197
  } catch (err) {
1026
- const msg = err instanceof Error ? err.message : String(err);
1027
- return new FleetMemberOutput(task, "", false, msg);
1198
+ return new FleetMemberOutput(task, "", false, getErrorMessage(err));
1028
1199
  }
1029
1200
  }
1030
1201
  async function execute6(pieces, args, opts) {
@@ -1036,11 +1207,16 @@ async function execute6(pieces, args, opts) {
1036
1207
  process.stderr.write(`\u03A6: Fleet executing ${tasks.length} task(s) in parallel
1037
1208
  `);
1038
1209
  for (let i = 0; i < tasks.length; i++) {
1039
- const t = tasks[i];
1210
+ const t = describeTask(tasks[i]);
1040
1211
  process.stderr.write(` [${i + 1}] ${t.slice(0, 60)}${t.length > 60 ? "..." : ""}
1041
1212
  `);
1042
1213
  }
1043
1214
  }
1215
+ const taskSummary = `Execute ${tasks.length} fleet task(s)?
1216
+ ${tasks.map((t, i) => `${i + 1}. ${describeTask(t).slice(0, 80)}`).join("\n ")}`;
1217
+ if (!await confirmPhase(taskSummary, opts)) {
1218
+ throw new Error("pizx/\u03A6: Execution cancelled by user.");
1219
+ }
1044
1220
  const results = [];
1045
1221
  const concurrency = opts.concurrency ?? 5;
1046
1222
  for (let i = 0; i < tasks.length; i += concurrency) {
@@ -1052,7 +1228,9 @@ async function execute6(pieces, args, opts) {
1052
1228
  if (r.status === "fulfilled") {
1053
1229
  results.push(r.value);
1054
1230
  } else {
1055
- results.push(new FleetMemberOutput(batch[idx], "", false, r.reason?.toString()));
1231
+ results.push(
1232
+ new FleetMemberOutput(describeTask(batch[idx]), "", false, r.reason?.toString())
1233
+ );
1056
1234
  }
1057
1235
  });
1058
1236
  }
@@ -1064,30 +1242,11 @@ async function execute6(pieces, args, opts) {
1064
1242
  const header = `Fleet Results: ${results.filter((r) => r.success).length}/${results.length} succeeded
1065
1243
 
1066
1244
  `;
1067
- return new FleetOutput(header + summary, results, t0, t1);
1068
- }
1069
- function makeFleet(opts = {}) {
1070
- const merged = { ...defaults6, ...opts };
1071
- const fn = ((pieces, ...args) => {
1072
- if (!Array.isArray(pieces)) {
1073
- return makeFleet({ ...merged, ...pieces });
1074
- }
1075
- return new PatternPromise((resolve, reject) => {
1076
- execute6(pieces, args, merged).then(resolve, reject);
1077
- });
1078
- });
1079
- let _quiet;
1080
- Object.defineProperty(fn, "quiet", {
1081
- get() {
1082
- if (!_quiet) _quiet = makeFleet({ ...merged, quiet: true });
1083
- return _quiet;
1084
- },
1085
- enumerable: true,
1086
- configurable: true
1087
- });
1088
- return fn;
1245
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1246
+ const qualityReview = await runQualityReview(template, header + summary, opts);
1247
+ return new FleetOutput(header + summary, results, t0, t1, qualityReview);
1089
1248
  }
1090
- var \u03A6 = makeFleet();
1249
+ var \u03A6 = createPatternTag(defaults6, execute6);
1091
1250
 
1092
1251
  // src/patterns/graph.ts
1093
1252
  var defaults7 = {
@@ -1107,13 +1266,15 @@ var GraphNodeResult = class {
1107
1266
  success;
1108
1267
  };
1109
1268
  var GraphOutput = class extends PatternOutput {
1110
- constructor(text, finalOutput, nodeResults, startTime, endTime) {
1269
+ constructor(text, finalOutput, nodeResults, startTime, endTime, qualityReview) {
1111
1270
  super(text, startTime, endTime);
1112
1271
  this.finalOutput = finalOutput;
1113
1272
  this.nodeResults = nodeResults;
1273
+ this.qualityReview = qualityReview;
1114
1274
  }
1115
1275
  finalOutput;
1116
1276
  nodeResults;
1277
+ qualityReview;
1117
1278
  };
1118
1279
  function parseGraph(template, separator) {
1119
1280
  const sep = separator ?? "\u2192";
@@ -1209,10 +1370,9 @@ Your task: ${node.task}`;
1209
1370
  }
1210
1371
  }
1211
1372
  const text = await ask(context, {
1373
+ ...opts,
1212
1374
  model: workerModel,
1213
- maxTokens: opts.maxTokens,
1214
- thinkingLevel: opts.thinkingLevel,
1215
- system: NODE_SYSTEM
1375
+ system: mergeSystem(opts.system, NODE_SYSTEM)
1216
1376
  });
1217
1377
  return { nodeId, task: node.task, text, success: true };
1218
1378
  })
@@ -1230,34 +1390,15 @@ Your task: ${node.task}`;
1230
1390
  const lastBatch = batches[batches.length - 1] ?? [];
1231
1391
  const finalNodeResults = lastBatch.map((id) => results.get(id)).filter(Boolean);
1232
1392
  const finalOutput = finalNodeResults.length > 0 ? finalNodeResults.join("\n\n") : "";
1393
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1394
+ const qualityReview = await runQualityReview(template, finalOutput, opts);
1233
1395
  const summary = nodeResults.map(
1234
1396
  (nr) => `[${nr.nodeId}] ${nr.task.slice(0, 80)}...
1235
1397
  ${nr.output.slice(0, 200)}${nr.output.length > 200 ? "..." : ""}`
1236
1398
  ).join("\n\n");
1237
- return new GraphOutput(summary, finalOutput, nodeResults, t0, t1);
1238
- }
1239
- function makeGraph(opts = {}) {
1240
- const merged = { ...defaults7, ...opts };
1241
- const fn = ((pieces, ...args) => {
1242
- if (!Array.isArray(pieces)) {
1243
- return makeGraph({ ...merged, ...pieces });
1244
- }
1245
- return new PatternPromise((resolve, reject) => {
1246
- execute7(pieces, args, merged).then(resolve, reject);
1247
- });
1248
- });
1249
- let _quiet;
1250
- Object.defineProperty(fn, "quiet", {
1251
- get() {
1252
- if (!_quiet) _quiet = makeGraph({ ...merged, quiet: true });
1253
- return _quiet;
1254
- },
1255
- enumerable: true,
1256
- configurable: true
1257
- });
1258
- return fn;
1399
+ return new GraphOutput(summary, finalOutput, nodeResults, t0, t1, qualityReview);
1259
1400
  }
1260
- var \u0393 = makeGraph();
1401
+ var \u0393 = createPatternTag(defaults7, execute7);
1261
1402
 
1262
1403
  // src/patterns/memory.ts
1263
1404
  var defaults8 = {
@@ -1266,27 +1407,6 @@ var defaults8 = {
1266
1407
  agents: 3,
1267
1408
  rounds: 1
1268
1409
  };
1269
- var ROLE_SETS3 = {
1270
- 2: ["Analyst \u2014 deep analysis of core aspects", "Reviewer \u2014 check for gaps and blind spots"],
1271
- 3: [
1272
- "Analyst \u2014 deep analysis of core aspects",
1273
- "Reviewer \u2014 check for gaps, edge cases, and blind spots",
1274
- "Strategist \u2014 connect findings to actionable insights"
1275
- ],
1276
- 4: [
1277
- "Analyst",
1278
- "Reviewer",
1279
- "Strategist",
1280
- "Innovator \u2014 propose novel angles and creative solutions"
1281
- ],
1282
- 5: [
1283
- "Analyst",
1284
- "Reviewer",
1285
- "Strategist",
1286
- "Innovator",
1287
- "Skeptic \u2014 challenge assumptions and stress-test conclusions"
1288
- ]
1289
- };
1290
1410
  var MemoryEntry = class {
1291
1411
  constructor(role, round, content) {
1292
1412
  this.role = role;
@@ -1298,13 +1418,15 @@ var MemoryEntry = class {
1298
1418
  content;
1299
1419
  };
1300
1420
  var MemoryOutput = class extends PatternOutput {
1301
- constructor(text, synthesis, entries, startTime, endTime) {
1421
+ constructor(text, synthesis, entries, startTime, endTime, qualityReview) {
1302
1422
  super(text, startTime, endTime);
1303
1423
  this.synthesis = synthesis;
1304
1424
  this.entries = entries;
1425
+ this.qualityReview = qualityReview;
1305
1426
  }
1306
1427
  synthesis;
1307
1428
  entries;
1429
+ qualityReview;
1308
1430
  };
1309
1431
  var WRITER_PROMPT = `You are a specialist with role: {role}.
1310
1432
 
@@ -1325,7 +1447,7 @@ async function execute8(pieces, args, opts) {
1325
1447
  const t0 = Date.now();
1326
1448
  const agentCount = opts.agents ?? 3;
1327
1449
  const totalRounds = opts.rounds ?? 1;
1328
- const roles = opts.roles ?? ROLE_SETS3[agentCount] ?? ROLE_SETS3[3] ?? [];
1450
+ const roles = opts.roles ?? MEMORY_ROLE_SETS[agentCount] ?? MEMORY_ROLE_SETS[3] ?? [];
1329
1451
  const plannerModel = opts.plannerModel ?? opts.model;
1330
1452
  const workerModel = opts.workerModel ?? opts.model;
1331
1453
  if (!opts.quiet) {
@@ -1342,11 +1464,7 @@ async function execute8(pieces, args, opts) {
1342
1464
  const roundResults = await Promise.allSettled(
1343
1465
  roles.map(async (role) => {
1344
1466
  const prompt = buildWriterPrompt(role, topic, blackboard);
1345
- const text = await ask(prompt, {
1346
- model: workerModel,
1347
- maxTokens: opts.maxTokens,
1348
- thinkingLevel: opts.thinkingLevel
1349
- });
1467
+ const text = await ask(prompt, { ...opts, model: workerModel });
1350
1468
  return { role, text };
1351
1469
  })
1352
1470
  );
@@ -1368,40 +1486,21 @@ ${blackboard}
1368
1486
 
1369
1487
  Consolidate into a comprehensive, structured synthesis.`,
1370
1488
  {
1489
+ ...opts,
1371
1490
  model: plannerModel,
1372
- maxTokens: opts.maxTokens,
1373
1491
  thinkingLevel: "high",
1374
- system: CONSOLIDATOR_SYSTEM
1492
+ system: mergeSystem(opts.system, CONSOLIDATOR_SYSTEM)
1375
1493
  }
1376
1494
  );
1495
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1496
+ const qualityReview = await runQualityReview(topic, synthesis, opts);
1377
1497
  const t1 = Date.now();
1378
1498
  const summary = entries.map(
1379
1499
  (e) => `[${e.role}] Round ${e.round}: ${e.content.slice(0, 150)}${e.content.length > 150 ? "..." : ""}`
1380
1500
  ).join("\n");
1381
- return new MemoryOutput(summary, synthesis, entries, t0, t1);
1382
- }
1383
- function makeMemory(opts = {}) {
1384
- const merged = { ...defaults8, ...opts };
1385
- const fn = ((pieces, ...args) => {
1386
- if (!Array.isArray(pieces)) {
1387
- return makeMemory({ ...merged, ...pieces });
1388
- }
1389
- return new PatternPromise((resolve, reject) => {
1390
- execute8(pieces, args, merged).then(resolve, reject);
1391
- });
1392
- });
1393
- let _quiet;
1394
- Object.defineProperty(fn, "quiet", {
1395
- get() {
1396
- if (!_quiet) _quiet = makeMemory({ ...merged, quiet: true });
1397
- return _quiet;
1398
- },
1399
- enumerable: true,
1400
- configurable: true
1401
- });
1402
- return fn;
1501
+ return new MemoryOutput(summary, synthesis, entries, t0, t1, qualityReview);
1403
1502
  }
1404
- var \u039C = makeMemory();
1503
+ var \u039C = createPatternTag(defaults8, execute8);
1405
1504
 
1406
1505
  // src/patterns/nu.ts
1407
1506
  var defaults9 = {
@@ -1421,19 +1520,21 @@ var NuRole = class {
1421
1520
  goal;
1422
1521
  };
1423
1522
  var NuOutput = class extends PatternOutput {
1424
- constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime) {
1523
+ constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime, qualityReview) {
1425
1524
  super(text, startTime, endTime);
1426
1525
  this.negotiatedRoles = negotiatedRoles;
1427
1526
  this.workflow = workflow;
1428
1527
  this.workflowReasoning = workflowReasoning;
1429
1528
  this.roleResults = roleResults;
1430
1529
  this.synthesis = synthesis;
1530
+ this.qualityReview = qualityReview;
1431
1531
  }
1432
1532
  negotiatedRoles;
1433
1533
  workflow;
1434
1534
  workflowReasoning;
1435
1535
  roleResults;
1436
1536
  synthesis;
1537
+ qualityReview;
1437
1538
  };
1438
1539
  var NEGOTIATE_SYSTEM = `You are a team architect. Given a task, propose a team of specialized agents. Each role must have a distinct name, expertise, and goal.
1439
1540
 
@@ -1471,6 +1572,7 @@ async function negotiateRoles(task, opts) {
1471
1572
  const response = await ask(`Task: ${task}
1472
1573
 
1473
1574
  ${prompt}`, {
1575
+ ...opts,
1474
1576
  model: opts.plannerModel ?? opts.model,
1475
1577
  maxTokens: 2048,
1476
1578
  thinkingLevel: "high"
@@ -1507,10 +1609,11 @@ ${rolesText}
1507
1609
 
1508
1610
  Determine the best execution strategy.`,
1509
1611
  {
1612
+ ...opts,
1510
1613
  model: opts.plannerModel ?? opts.model,
1511
1614
  maxTokens: 512,
1512
1615
  thinkingLevel: "high",
1513
- system: WORKFLOW_SYSTEM
1616
+ system: mergeSystem(opts.system, WORKFLOW_SYSTEM)
1514
1617
  }
1515
1618
  );
1516
1619
  const wfMatch = response.match(/WORKFLOW\s*:\s*(.+)/i);
@@ -1529,10 +1632,9 @@ async function executeRoles(roles, task, workflow, opts) {
1529
1632
  let context = task;
1530
1633
  for (const role of roles) {
1531
1634
  const output = await ask(context, {
1635
+ ...opts,
1532
1636
  model: workerModel,
1533
- maxTokens: opts.maxTokens,
1534
- thinkingLevel: opts.thinkingLevel,
1535
- system: EXECUTE_SYSTEM2(role)
1637
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
1536
1638
  });
1537
1639
  results.push({ role: role.name, output });
1538
1640
  context = `Previous output from ${role.name}:
@@ -1544,10 +1646,9 @@ Continue with: ${task}`;
1544
1646
  const parallelResults = await Promise.allSettled(
1545
1647
  roles.map(
1546
1648
  (role) => ask(task, {
1649
+ ...opts,
1547
1650
  model: workerModel,
1548
- maxTokens: opts.maxTokens,
1549
- thinkingLevel: opts.thinkingLevel,
1550
- system: EXECUTE_SYSTEM2(role)
1651
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
1551
1652
  }).then((text) => ({ role: role.name, output: text })).catch((err) => ({ role: role.name, output: `(failed: ${String(err)})` }))
1552
1653
  )
1553
1654
  );
@@ -1569,10 +1670,10 @@ ${resultsText}
1569
1670
 
1570
1671
  Synthesize a comprehensive final answer.`,
1571
1672
  {
1673
+ ...opts,
1572
1674
  model: opts.plannerModel ?? opts.model,
1573
- maxTokens: opts.maxTokens,
1574
1675
  thinkingLevel: "high",
1575
- system: SYNTHESIS_SYSTEM3
1676
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM3)
1576
1677
  }
1577
1678
  );
1578
1679
  }
@@ -1612,38 +1713,29 @@ async function execute9(pieces, args, opts) {
1612
1713
  `);
1613
1714
  const roleResults = await executeRoles(roles, task, workflow, opts);
1614
1715
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing...\n");
1615
- const synthesis = await synthesize(task, roleResults, { ...opts, plannerModel });
1616
- const t1 = Date.now();
1617
- const summary = [
1618
- `Roles: ${roles.map((r) => r.name).join(", ")}`,
1619
- `Workflow: ${workflow} (${reasoning})`,
1620
- `Results: ${roleResults.length}/${roles.length} succeeded`,
1621
- `Synthesis: ${synthesis}`
1622
- ].join("\n\n");
1623
- return new NuOutput(summary, roles, workflow, reasoning, roleResults, synthesis, t0, t1);
1624
- }
1625
- function makeNu(opts = {}) {
1626
- const merged = { ...defaults9, ...opts };
1627
- const fn = ((pieces, ...args) => {
1628
- if (!Array.isArray(pieces)) {
1629
- return makeNu({ ...merged, ...pieces });
1630
- }
1631
- return new PatternPromise((resolve, reject) => {
1632
- execute9(pieces, args, merged).then(resolve, reject);
1633
- });
1634
- });
1635
- let _quiet;
1636
- Object.defineProperty(fn, "quiet", {
1637
- get() {
1638
- if (!_quiet) _quiet = makeNu({ ...merged, quiet: true });
1639
- return _quiet;
1640
- },
1641
- enumerable: true,
1642
- configurable: true
1643
- });
1644
- return fn;
1716
+ const synthesis = await synthesize(task, roleResults, { ...opts, plannerModel });
1717
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1718
+ const qualityReview = await runQualityReview(task, synthesis, opts);
1719
+ const t1 = Date.now();
1720
+ const summary = [
1721
+ `Roles: ${roles.map((r) => r.name).join(", ")}`,
1722
+ `Workflow: ${workflow} (${reasoning})`,
1723
+ `Results: ${roleResults.length}/${roles.length} succeeded`,
1724
+ `Synthesis: ${synthesis}`
1725
+ ].join("\n\n");
1726
+ return new NuOutput(
1727
+ summary,
1728
+ roles,
1729
+ workflow,
1730
+ reasoning,
1731
+ roleResults,
1732
+ synthesis,
1733
+ t0,
1734
+ t1,
1735
+ qualityReview
1736
+ );
1645
1737
  }
1646
- var \u039D = makeNu();
1738
+ var \u039D = createPatternTag(defaults9, execute9);
1647
1739
 
1648
1740
  // src/patterns/orchestrator.ts
1649
1741
  var defaults10 = {
@@ -1663,15 +1755,17 @@ var OrchestratorWorkerResult = class {
1663
1755
  success;
1664
1756
  };
1665
1757
  var OrchestratorOutput = class extends PatternOutput {
1666
- constructor(text, plan, synthesis, workerResults, startTime, endTime) {
1758
+ constructor(text, plan, synthesis, workerResults, startTime, endTime, qualityReview) {
1667
1759
  super(text, startTime, endTime);
1668
1760
  this.plan = plan;
1669
1761
  this.synthesis = synthesis;
1670
1762
  this.workerResults = workerResults;
1763
+ this.qualityReview = qualityReview;
1671
1764
  }
1672
1765
  plan;
1673
1766
  synthesis;
1674
1767
  workerResults;
1768
+ qualityReview;
1675
1769
  };
1676
1770
  var PLANNER_SYSTEM = `You are a senior architect and project planner. Given a high-level request, create a detailed execution plan.
1677
1771
 
@@ -1694,6 +1788,7 @@ async function execute10(pieces, args, opts) {
1694
1788
  const request = build(pieces, args);
1695
1789
  const t0 = Date.now();
1696
1790
  const workerCount = opts.workers ?? 3;
1791
+ const phases = [];
1697
1792
  const plannerModel = opts.plannerModel ?? opts.model;
1698
1793
  const workerModel = opts.workerModel ?? opts.model;
1699
1794
  if (!opts.quiet) {
@@ -1703,11 +1798,18 @@ async function execute10(pieces, args, opts) {
1703
1798
  );
1704
1799
  }
1705
1800
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
1801
+ const planStart = Date.now();
1706
1802
  const planText = await ask(request, {
1803
+ ...opts,
1707
1804
  model: plannerModel,
1708
- maxTokens: opts.maxTokens,
1709
1805
  thinkingLevel: "high",
1710
- system: PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount))
1806
+ system: mergeSystem(opts.system, PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount)))
1807
+ });
1808
+ phases.push({
1809
+ phase: "plan",
1810
+ durationMs: Date.now() - planStart,
1811
+ description: `Generated plan with ${workerCount} workers`,
1812
+ modelUsed: plannerModel
1711
1813
  });
1712
1814
  const subTasks = [];
1713
1815
  const taskLines = planText.split("\n");
@@ -1731,27 +1833,37 @@ async function execute10(pieces, args, opts) {
1731
1833
  `);
1732
1834
  }
1733
1835
  }
1836
+ const planSummary = tasks.length > 0 ? `Execute ${tasks.length} sub-task(s) as planned?
1837
+ ${tasks.map((t, i) => `${i + 1}. ${t.slice(0, 80)}`).join("\n ")}` : `Execute the plan?`;
1838
+ if (!await confirmPhase(planSummary, opts)) {
1839
+ throw new Error("pizx/\u03A9: Execution cancelled by user.");
1840
+ }
1734
1841
  const workerResults = [];
1735
1842
  const concurrency = opts.concurrency ?? 3;
1843
+ const dispatchStart = Date.now();
1736
1844
  for (let i = 0; i < tasks.length; i += concurrency) {
1737
1845
  const batch = tasks.slice(i, i + concurrency);
1738
1846
  const batchResults = await Promise.allSettled(
1739
1847
  batch.map(
1740
- (task) => ask(task, {
1741
- model: workerModel,
1742
- maxTokens: opts.maxTokens,
1743
- thinkingLevel: opts.thinkingLevel,
1744
- system: WORKER_SYSTEM
1745
- }).then((text) => new OrchestratorWorkerResult(task, text, true)).catch((err) => new OrchestratorWorkerResult(task, String(err), false))
1848
+ (task) => ask(task, { ...opts, model: workerModel, system: mergeSystem(opts.system, WORKER_SYSTEM) }).then((text) => new OrchestratorWorkerResult(task, text, true)).catch((err) => new OrchestratorWorkerResult(task, String(err), false))
1746
1849
  )
1747
1850
  );
1748
1851
  batchResults.forEach((r) => {
1749
1852
  if (r.status === "fulfilled") workerResults.push(r.value);
1750
1853
  });
1751
1854
  }
1855
+ const succeeded = workerResults.filter((w) => w.success).length;
1856
+ phases.push({
1857
+ phase: "dispatch",
1858
+ durationMs: Date.now() - dispatchStart,
1859
+ description: `Executed ${workerResults.length} worker(s), ${succeeded} succeeded`,
1860
+ modelUsed: workerModel,
1861
+ callCount: workerResults.length
1862
+ });
1752
1863
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
1753
1864
  const workerTexts = workerResults.map((wr, i) => `Task ${i + 1}: ${wr.task}
1754
1865
  Result: ${wr.output}`).join("\n\n");
1866
+ const synthStart = Date.now();
1755
1867
  const synthesis = await ask(
1756
1868
  `Original request:
1757
1869
  ${request}
@@ -1764,46 +1876,60 @@ ${workerTexts}
1764
1876
 
1765
1877
  Synthesize a final deliverable.`,
1766
1878
  {
1879
+ ...opts,
1767
1880
  model: plannerModel,
1768
- maxTokens: opts.maxTokens,
1769
1881
  thinkingLevel: "high",
1770
- system: SYNTHESIS_SYSTEM4
1882
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM4)
1771
1883
  }
1772
1884
  );
1885
+ phases.push({
1886
+ phase: "synthesize",
1887
+ durationMs: Date.now() - synthStart,
1888
+ description: "Synthesized worker results into final deliverable",
1889
+ modelUsed: plannerModel
1890
+ });
1891
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1892
+ const qualityStart = Date.now();
1893
+ const qualityReview = await runQualityReview(request, synthesis, opts);
1894
+ if (qualityReview) {
1895
+ phases.push({
1896
+ phase: "quality-review",
1897
+ durationMs: Date.now() - qualityStart,
1898
+ description: `Score: ${qualityReview.score.toFixed(2)} \u2014 ${qualityReview.assessment.slice(0, 60)}`,
1899
+ modelUsed: plannerModel
1900
+ });
1901
+ }
1773
1902
  const t1 = Date.now();
1903
+ const reviewSection = qualityReview ? `
1904
+
1905
+ Quality Review: ${qualityReview.score.toFixed(2)} \u2014 ${qualityReview.assessment}
1906
+ Recommendation: ${qualityReview.recommendation}` : "";
1774
1907
  const summary = `Plan:
1775
1908
  ${planText}
1776
1909
 
1777
1910
  Workers: ${workerResults.filter((w) => w.success).length}/${workerResults.length} succeeded
1778
1911
 
1779
1912
  Synthesis:
1780
- ${synthesis}`;
1781
- return new OrchestratorOutput(summary, planText, synthesis, workerResults, t0, t1);
1782
- }
1783
- function makeOrchestrator(opts = {}) {
1784
- const merged = { ...defaults10, ...opts };
1785
- const fn = ((pieces, ...args) => {
1786
- if (!Array.isArray(pieces)) {
1787
- return makeOrchestrator({ ...merged, ...pieces });
1788
- }
1789
- return new PatternPromise((resolve, reject) => {
1790
- execute10(pieces, args, merged).then(resolve, reject);
1791
- });
1792
- });
1793
- let _quiet;
1794
- Object.defineProperty(fn, "quiet", {
1795
- get() {
1796
- if (!_quiet) _quiet = makeOrchestrator({ ...merged, quiet: true });
1797
- return _quiet;
1798
- },
1799
- enumerable: true,
1800
- configurable: true
1801
- });
1802
- return fn;
1913
+ ${synthesis}${reviewSection}`;
1914
+ const output = new OrchestratorOutput(
1915
+ summary,
1916
+ planText,
1917
+ synthesis,
1918
+ workerResults,
1919
+ t0,
1920
+ t1,
1921
+ qualityReview
1922
+ );
1923
+ output.phaseLog = phases;
1924
+ return output;
1803
1925
  }
1804
- var \u03A9 = makeOrchestrator();
1926
+ var \u03A9 = createPatternTag(defaults10, execute10);
1805
1927
 
1806
1928
  // src/patterns/pipeline.ts
1929
+ function describeStage(stage) {
1930
+ if (typeof stage === "function") return "(composed pattern)";
1931
+ return stage;
1932
+ }
1807
1933
  var defaults11 = {
1808
1934
  maxTokens: 4096,
1809
1935
  thinkingLevel: "medium"
@@ -1819,13 +1945,15 @@ var PipelineStageResult = class {
1819
1945
  index;
1820
1946
  };
1821
1947
  var PipelineOutput = class extends PatternOutput {
1822
- constructor(text, finalOutput, stages, startTime, endTime) {
1948
+ constructor(text, finalOutput, stages, startTime, endTime, qualityReview) {
1823
1949
  super(text, startTime, endTime);
1824
1950
  this.finalOutput = finalOutput;
1825
1951
  this.stages = stages;
1952
+ this.qualityReview = qualityReview;
1826
1953
  }
1827
1954
  finalOutput;
1828
1955
  stages;
1956
+ qualityReview;
1829
1957
  };
1830
1958
  function parseStages(template, explicitStages, separator) {
1831
1959
  if (explicitStages && explicitStages.length > 0) return explicitStages;
@@ -1867,55 +1995,46 @@ async function execute11(pieces, args, opts) {
1867
1995
  `);
1868
1996
  }
1869
1997
  }
1998
+ const stageSummary = `Run ${stages.length} pipeline stage(s)?
1999
+ ${stages.map((s, i) => `${i + 1}. ${describeStage(s)}`).join("\n ")}`;
2000
+ if (!await confirmPhase(stageSummary, opts)) {
2001
+ throw new Error("pizx/\u039B: Execution cancelled by user.");
2002
+ }
1870
2003
  const stageResults = [];
1871
2004
  let currentInput = "";
1872
2005
  for (let i = 0; i < stages.length; i++) {
1873
2006
  const stage = stages[i];
2007
+ const stageLabel = describeStage(stage);
1874
2008
  const customPrompt = opts.stagePrompts?.[i];
1875
2009
  if (!opts.quiet)
1876
- process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stage.slice(0, 50)}...
2010
+ process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stageLabel.slice(0, 50)}...
1877
2011
  `);
1878
- const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
1879
- const systemMessage = i === 0 ? `You are a specialist executing stage ${i + 1}: ${stage}. Focus only on this stage's output.` : `You are a specialist executing stage ${i + 1}: ${stage}. Process the previous stage's output according to your instructions. Maintain all important information from previous stages.`;
1880
- const output = await ask(prompt, {
1881
- model: workerModel,
1882
- maxTokens: opts.maxTokens,
1883
- thinkingLevel: opts.thinkingLevel,
1884
- system: systemMessage
1885
- });
1886
- stageResults.push(new PipelineStageResult(stage, output, i));
2012
+ let output;
2013
+ if (typeof stage === "function") {
2014
+ output = await stage(currentInput);
2015
+ } else {
2016
+ const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
2017
+ const systemMessage = i === 0 ? `You are a specialist executing stage ${i + 1}: ${stage}. Focus only on this stage's output.` : `You are a specialist executing stage ${i + 1}: ${stage}. Process the previous stage's output according to your instructions. Maintain all important information from previous stages.`;
2018
+ output = await ask(prompt, {
2019
+ ...opts,
2020
+ model: workerModel,
2021
+ system: mergeSystem(opts.system, systemMessage)
2022
+ });
2023
+ }
2024
+ stageResults.push(new PipelineStageResult(stageLabel, output, i));
1887
2025
  currentInput = output;
1888
2026
  }
1889
2027
  const t1 = Date.now();
1890
2028
  const finalOutput = currentInput;
2029
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2030
+ const qualityReview = await runQualityReview(template, finalOutput, opts);
1891
2031
  const summary = stageResults.map(
1892
2032
  (sr) => `Stage ${sr.index + 1} (${sr.stage}):
1893
2033
  ${sr.output.slice(0, 200)}${sr.output.length > 200 ? "..." : ""}`
1894
2034
  ).join("\n\n");
1895
- return new PipelineOutput(summary, finalOutput, stageResults, t0, t1);
1896
- }
1897
- function makePipeline(opts = {}) {
1898
- const merged = { ...defaults11, ...opts };
1899
- const fn = ((pieces, ...args) => {
1900
- if (!Array.isArray(pieces)) {
1901
- return makePipeline({ ...merged, ...pieces });
1902
- }
1903
- return new PatternPromise((resolve, reject) => {
1904
- execute11(pieces, args, merged).then(resolve, reject);
1905
- });
1906
- });
1907
- let _quiet;
1908
- Object.defineProperty(fn, "quiet", {
1909
- get() {
1910
- if (!_quiet) _quiet = makePipeline({ ...merged, quiet: true });
1911
- return _quiet;
1912
- },
1913
- enumerable: true,
1914
- configurable: true
1915
- });
1916
- return fn;
2035
+ return new PipelineOutput(summary, finalOutput, stageResults, t0, t1, qualityReview);
1917
2036
  }
1918
- var \u039B = makePipeline();
2037
+ var \u039B = createPatternTag(defaults11, execute11);
1919
2038
 
1920
2039
  // src/patterns/ralph.ts
1921
2040
  import { createAgentSession } from "@earendil-works/pi-coding-agent";
@@ -1950,17 +2069,17 @@ async function executeWithTools(goal, opts) {
1950
2069
  });
1951
2070
  try {
1952
2071
  await session.sendUserMessage(goal);
1953
- const msgs = session.messages ?? [];
1954
- for (let i = msgs.length - 1; i >= 0; i--) {
1955
- if (msgs[i].role === "assistant") {
1956
- const c = msgs[i].content;
1957
- if (typeof c === "string") return c.trim();
1958
- if (Array.isArray(c)) {
1959
- const texts = c.filter(
1960
- (x) => x.type === "text" && typeof x.text === "string"
1961
- ).map((x) => x.text);
1962
- if (texts.length > 0) return texts.join("").trim();
1963
- }
2072
+ const messages = session.messages;
2073
+ for (let i = messages.length - 1; i >= 0; i--) {
2074
+ const msg = messages[i];
2075
+ if (msg?.role !== "assistant") continue;
2076
+ const c = "content" in msg ? msg.content : void 0;
2077
+ if (typeof c === "string") return c.trim();
2078
+ if (Array.isArray(c)) {
2079
+ const texts = c.filter(
2080
+ (x) => x.type === "text" && typeof x.text === "string"
2081
+ ).map((x) => x.text);
2082
+ if (texts.length > 0) return texts.join("").trim();
1964
2083
  }
1965
2084
  }
1966
2085
  return "(no assistant response)";
@@ -1995,10 +2114,9 @@ async function execute12(pieces, args, opts) {
1995
2114
  }
1996
2115
  if (!opts.quiet) process.stderr.write(" \u2192 Analyzing...\n");
1997
2116
  const analysis = await ask(currentGoal, {
2117
+ ...opts,
1998
2118
  model: plannerModel,
1999
- maxTokens: opts.maxTokens,
2000
- thinkingLevel: opts.thinkingLevel,
2001
- system: ANALYSIS_SYSTEM2
2119
+ system: mergeSystem(opts.system, ANALYSIS_SYSTEM2)
2002
2120
  });
2003
2121
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
2004
2122
  const plan = await ask(
@@ -2007,12 +2125,7 @@ async function execute12(pieces, args, opts) {
2007
2125
  Analysis: ${analysis}
2008
2126
 
2009
2127
  Generate an implementation plan.`,
2010
- {
2011
- model: plannerModel,
2012
- maxTokens: opts.maxTokens,
2013
- thinkingLevel: opts.thinkingLevel,
2014
- system: PLAN_SYSTEM2
2015
- }
2128
+ { ...opts, model: plannerModel, system: mergeSystem(opts.system, PLAN_SYSTEM2) }
2016
2129
  );
2017
2130
  if (!opts.quiet) process.stderr.write(" \u2192 Executing...\n");
2018
2131
  const result = opts.useTools ? await executeWithTools(`Implement this plan:
@@ -2025,9 +2138,8 @@ Goal: ${currentGoal}`, {
2025
2138
  ${plan}
2026
2139
 
2027
2140
  Goal: ${currentGoal}`, {
2028
- model: workerModel,
2029
- maxTokens: opts.maxTokens,
2030
- thinkingLevel: opts.thinkingLevel
2141
+ ...opts,
2142
+ model: workerModel
2031
2143
  });
2032
2144
  if (!opts.quiet) process.stderr.write(" \u2192 Reviewing...\n");
2033
2145
  const review = await ask(`Plan:
@@ -2037,10 +2149,11 @@ Result:
2037
2149
  ${result}
2038
2150
 
2039
2151
  Review the implementation.`, {
2152
+ ...opts,
2040
2153
  model: plannerModel,
2041
2154
  maxTokens: 1024,
2042
2155
  thinkingLevel: "high",
2043
- system: REVIEW_SYSTEM
2156
+ system: mergeSystem(opts.system, REVIEW_SYSTEM)
2044
2157
  });
2045
2158
  const shouldContinue = review.includes("ITERATE") && !review.includes("DONE");
2046
2159
  iterations.push({
@@ -2076,28 +2189,7 @@ Original goal: ${goal}`;
2076
2189
  t1
2077
2190
  );
2078
2191
  }
2079
- function makeRalph(opts = {}) {
2080
- const merged = { ...defaults12, ...opts };
2081
- const fn = ((pieces, ...args) => {
2082
- if (!Array.isArray(pieces)) {
2083
- return makeRalph({ ...merged, ...pieces });
2084
- }
2085
- return new PatternPromise((resolve, reject) => {
2086
- execute12(pieces, args, merged).then(resolve, reject);
2087
- });
2088
- });
2089
- let _quiet;
2090
- Object.defineProperty(fn, "quiet", {
2091
- get() {
2092
- if (!_quiet) _quiet = makeRalph({ ...merged, quiet: true });
2093
- return _quiet;
2094
- },
2095
- enumerable: true,
2096
- configurable: true
2097
- });
2098
- return fn;
2099
- }
2100
- var \u03A1 = makeRalph();
2192
+ var \u03A1 = createPatternTag(defaults12, execute12);
2101
2193
 
2102
2194
  // src/patterns/subagent.ts
2103
2195
  var defaults13 = {
@@ -2117,13 +2209,15 @@ var SubagentResult = class {
2117
2209
  success;
2118
2210
  };
2119
2211
  var SubagentOutput = class extends PatternOutput {
2120
- constructor(text, synthesis, subResults, startTime, endTime) {
2212
+ constructor(text, synthesis, subResults, startTime, endTime, qualityReview) {
2121
2213
  super(text, startTime, endTime);
2122
2214
  this.synthesis = synthesis;
2123
2215
  this.subResults = subResults;
2216
+ this.qualityReview = qualityReview;
2124
2217
  }
2125
2218
  synthesis;
2126
2219
  subResults;
2220
+ qualityReview;
2127
2221
  };
2128
2222
  var DECOMPOSE_SYSTEM = `You are a task decomposition specialist. Break down complex tasks into independent sub-tasks that can be worked on in parallel. Output ONLY a JSON array of strings, each being a self-contained sub-task description. No markdown, no explanation.`;
2129
2223
  var SYNTHESIS_SYSTEM5 = `You are a synthesis specialist. Combine the results from multiple sub-agent analyses into a coherent, comprehensive answer. Identify patterns, conflicts, and gaps.`;
@@ -2136,10 +2230,11 @@ ${task}
2136
2230
 
2137
2231
  Output a JSON array of strings.`,
2138
2232
  {
2233
+ ...opts,
2139
2234
  model: opts.model,
2140
2235
  maxTokens: 1024,
2141
2236
  thinkingLevel: "medium",
2142
- system: DECOMPOSE_SYSTEM
2237
+ system: mergeSystem(opts.system, DECOMPOSE_SYSTEM)
2143
2238
  }
2144
2239
  );
2145
2240
  try {
@@ -2160,6 +2255,7 @@ var SUBAGENT_SYSTEM = `You are a domain specialist. Complete your assigned sub-t
2160
2255
  async function execute13(pieces, args, opts) {
2161
2256
  const task = build(pieces, args);
2162
2257
  const t0 = Date.now();
2258
+ const phases = [];
2163
2259
  const plannerModel = opts.plannerModel ?? opts.model;
2164
2260
  const workerModel = opts.workerModel ?? opts.model;
2165
2261
  if (!opts.quiet) {
@@ -2169,7 +2265,14 @@ async function execute13(pieces, args, opts) {
2169
2265
  );
2170
2266
  }
2171
2267
  if (!opts.quiet) process.stderr.write(" \u2192 Decomposing task into sub-tasks...\n");
2268
+ const decomposeStart = Date.now();
2172
2269
  const subTasks = await decomposeTask(task, { ...opts, model: plannerModel });
2270
+ phases.push({
2271
+ phase: "decompose",
2272
+ durationMs: Date.now() - decomposeStart,
2273
+ description: `Decomposed into ${subTasks.length} sub-task(s)`,
2274
+ modelUsed: plannerModel
2275
+ });
2173
2276
  if (!opts.quiet) {
2174
2277
  process.stderr.write(` \u2192 ${subTasks.length} sub-task(s) identified:
2175
2278
  `);
@@ -2179,27 +2282,37 @@ async function execute13(pieces, args, opts) {
2179
2282
  `);
2180
2283
  }
2181
2284
  }
2285
+ const subTaskSummary = `Execute ${subTasks.length} sub-task(s)?
2286
+ ${subTasks.map((st, i) => `${i + 1}. ${st.slice(0, 80)}`).join("\n ")}`;
2287
+ if (!await confirmPhase(subTaskSummary, opts)) {
2288
+ throw new Error("pizx/\u03A3: Execution cancelled by user.");
2289
+ }
2182
2290
  const subResults = [];
2183
2291
  const concurrency = opts.concurrency ?? 4;
2292
+ const execStart = Date.now();
2184
2293
  for (let i = 0; i < subTasks.length; i += concurrency) {
2185
2294
  const batch = subTasks.slice(i, i + concurrency);
2186
2295
  const batchResults = await Promise.allSettled(
2187
2296
  batch.map(
2188
- (st) => ask(st, {
2189
- model: workerModel,
2190
- maxTokens: opts.maxTokens,
2191
- thinkingLevel: opts.thinkingLevel,
2192
- system: SUBAGENT_SYSTEM
2193
- }).then((text) => new SubagentResult(st, text, true)).catch((err) => new SubagentResult(st, String(err), false))
2297
+ (st) => ask(st, { ...opts, model: workerModel, system: mergeSystem(opts.system, SUBAGENT_SYSTEM) }).then((text) => new SubagentResult(st, text, true)).catch((err) => new SubagentResult(st, String(err), false))
2194
2298
  )
2195
2299
  );
2196
2300
  batchResults.forEach((r) => {
2197
2301
  if (r.status === "fulfilled") subResults.push(r.value);
2198
2302
  });
2199
2303
  }
2304
+ const succeeded = subResults.filter((r) => r.success).length;
2305
+ phases.push({
2306
+ phase: "execute",
2307
+ durationMs: Date.now() - execStart,
2308
+ description: `Executed ${subResults.length} sub-task(s), ${succeeded} succeeded`,
2309
+ modelUsed: workerModel,
2310
+ callCount: subResults.length
2311
+ });
2200
2312
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
2201
2313
  const subResultsText = subResults.map((sr, i) => `Sub-task ${i + 1}: ${sr.subTask}
2202
2314
  Result: ${sr.text}`).join("\n\n");
2315
+ const synthStart = Date.now();
2203
2316
  const synthesis = await ask(
2204
2317
  `Original task:
2205
2318
  ${task}
@@ -2208,38 +2321,31 @@ Sub-task results:
2208
2321
  ${subResultsText}
2209
2322
 
2210
2323
  Synthesize a comprehensive answer.`,
2211
- {
2212
- model: plannerModel,
2213
- maxTokens: opts.maxTokens,
2214
- thinkingLevel: opts.thinkingLevel,
2215
- system: SYNTHESIS_SYSTEM5
2216
- }
2324
+ { ...opts, model: plannerModel, system: mergeSystem(opts.system, SYNTHESIS_SYSTEM5) }
2217
2325
  );
2218
- const t1 = Date.now();
2219
- return new SubagentOutput(synthesis, synthesis, subResults, t0, t1);
2220
- }
2221
- function makeSubagent(opts = {}) {
2222
- const merged = { ...defaults13, ...opts };
2223
- const fn = ((pieces, ...args) => {
2224
- if (!Array.isArray(pieces)) {
2225
- return makeSubagent({ ...merged, ...pieces });
2226
- }
2227
- return new PatternPromise((resolve, reject) => {
2228
- execute13(pieces, args, merged).then(resolve, reject);
2229
- });
2230
- });
2231
- let _quiet;
2232
- Object.defineProperty(fn, "quiet", {
2233
- get() {
2234
- if (!_quiet) _quiet = makeSubagent({ ...merged, quiet: true });
2235
- return _quiet;
2236
- },
2237
- enumerable: true,
2238
- configurable: true
2326
+ phases.push({
2327
+ phase: "synthesize",
2328
+ durationMs: Date.now() - synthStart,
2329
+ description: "Synthesized sub-agent results",
2330
+ modelUsed: plannerModel
2239
2331
  });
2240
- return fn;
2332
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2333
+ const qStart = Date.now();
2334
+ const qualityReview = await runQualityReview(task, synthesis, opts);
2335
+ if (qualityReview) {
2336
+ phases.push({
2337
+ phase: "quality-review",
2338
+ durationMs: Date.now() - qStart,
2339
+ description: `Score: ${qualityReview.score.toFixed(2)}`,
2340
+ modelUsed: plannerModel
2341
+ });
2342
+ }
2343
+ const t1 = Date.now();
2344
+ const output = new SubagentOutput(synthesis, synthesis, subResults, t0, t1, qualityReview);
2345
+ output.phaseLog = phases;
2346
+ return output;
2241
2347
  }
2242
- var \u03A3 = makeSubagent();
2348
+ var \u03A3 = createPatternTag(defaults13, execute13);
2243
2349
 
2244
2350
  // src/patterns/tau.ts
2245
2351
  var defaults14 = {
@@ -2263,15 +2369,17 @@ var ToolMediatedEntry = class {
2263
2369
  content;
2264
2370
  };
2265
2371
  var TauOutput = class extends PatternOutput {
2266
- constructor(text, entries, finalState, synthesis, startTime, endTime) {
2372
+ constructor(text, entries, finalState, synthesis, startTime, endTime, qualityReview) {
2267
2373
  super(text, startTime, endTime);
2268
2374
  this.entries = entries;
2269
2375
  this.finalState = finalState;
2270
2376
  this.synthesis = synthesis;
2377
+ this.qualityReview = qualityReview;
2271
2378
  }
2272
2379
  entries;
2273
2380
  finalState;
2274
2381
  synthesis;
2382
+ qualityReview;
2275
2383
  };
2276
2384
  var SCHEMA_SYSTEM = `You are a coordination architect. Given a task, design a shared structured context for agent collaboration.
2277
2385
 
@@ -2326,6 +2434,7 @@ async function defineSchema(task, opts) {
2326
2434
  const response = await ask(`Task: ${task}
2327
2435
 
2328
2436
  ${prompt}`, {
2437
+ ...opts,
2329
2438
  model: opts.plannerModel ?? opts.model,
2330
2439
  maxTokens: 1024,
2331
2440
  thinkingLevel: "high"
@@ -2378,10 +2487,9 @@ async function executeRound(roles, assignments, store, round, opts) {
2378
2487
  const systemPrompt = isWrite ? WRITE_SYSTEM(role, keysStr).replace("{store}", storeText) : UPDATE_SYSTEM(role, keysStr).replace("{store}", storeText);
2379
2488
  const task = isWrite ? `Write your initial findings to your assigned keys: ${keysStr}` : `Review the shared context and update your entries for keys: ${keysStr}`;
2380
2489
  const response = await ask(task, {
2490
+ ...opts,
2381
2491
  model: workerModel,
2382
- maxTokens: opts.maxTokens,
2383
- thinkingLevel: opts.thinkingLevel,
2384
- system: systemPrompt
2492
+ system: mergeSystem(opts.system, systemPrompt)
2385
2493
  });
2386
2494
  return { role, response };
2387
2495
  })
@@ -2421,10 +2529,10 @@ ${storeText}
2421
2529
 
2422
2530
  Consolidate into a comprehensive, well-structured synthesis.`,
2423
2531
  {
2532
+ ...opts,
2424
2533
  model: opts.plannerModel ?? opts.model,
2425
- maxTokens: opts.maxTokens,
2426
2534
  thinkingLevel: "high",
2427
- system: CONSOLIDATE_SYSTEM
2535
+ system: mergeSystem(opts.system, CONSOLIDATE_SYSTEM)
2428
2536
  }
2429
2537
  );
2430
2538
  }
@@ -2469,6 +2577,8 @@ async function execute14(pieces, args, opts) {
2469
2577
  }
2470
2578
  if (!opts.quiet) process.stderr.write(" \u2192 Consolidating store...\n");
2471
2579
  const synthesis = await consolidateStore(task, store, { ...opts, plannerModel });
2580
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2581
+ const qualityReview = await runQualityReview(task, synthesis, opts);
2472
2582
  const t1 = Date.now();
2473
2583
  const summary = [
2474
2584
  `Schema keys: ${keys.join(", ")}`,
@@ -2477,30 +2587,9 @@ async function execute14(pieces, args, opts) {
2477
2587
  `Entries: ${allEntries.length}`,
2478
2588
  `Synthesis: ${synthesis}`
2479
2589
  ].join("\n\n");
2480
- return new TauOutput(summary, allEntries, store, synthesis, t0, t1);
2481
- }
2482
- function makeTau(opts = {}) {
2483
- const merged = { ...defaults14, ...opts };
2484
- const fn = ((pieces, ...args) => {
2485
- if (!Array.isArray(pieces)) {
2486
- return makeTau({ ...merged, ...pieces });
2487
- }
2488
- return new PatternPromise((resolve, reject) => {
2489
- execute14(pieces, args, merged).then(resolve, reject);
2490
- });
2491
- });
2492
- let _quiet;
2493
- Object.defineProperty(fn, "quiet", {
2494
- get() {
2495
- if (!_quiet) _quiet = makeTau({ ...merged, quiet: true });
2496
- return _quiet;
2497
- },
2498
- enumerable: true,
2499
- configurable: true
2500
- });
2501
- return fn;
2590
+ return new TauOutput(summary, allEntries, store, synthesis, t0, t1, qualityReview);
2502
2591
  }
2503
- var \u03A4 = makeTau();
2592
+ var \u03A4 = createPatternTag(defaults14, execute14);
2504
2593
 
2505
2594
  // src/patterns/thread.ts
2506
2595
  var defaults15 = {
@@ -2509,27 +2598,6 @@ var defaults15 = {
2509
2598
  agents: 3,
2510
2599
  turns: 3
2511
2600
  };
2512
- var ROLE_SETS4 = {
2513
- 2: ["Proposer \u2014 advocate the best approach", "Critic \u2014 identify weaknesses and gaps"],
2514
- 3: [
2515
- "Proposer \u2014 suggest the best approach",
2516
- "Critic \u2014 identify weaknesses, risks, and missing pieces",
2517
- "Synthesizer \u2014 combine the best ideas into a practical plan"
2518
- ],
2519
- 4: [
2520
- "Proposer \u2014 advocate a bold solution",
2521
- "Critic \u2014 identify risks and weaknesses",
2522
- "Pragmatist \u2014 focus on practical implementation",
2523
- "Innovator \u2014 propose creative alternatives"
2524
- ],
2525
- 5: [
2526
- "Proposer",
2527
- "Critic",
2528
- "Pragmatist",
2529
- "Innovator",
2530
- "Devil's Advocate \u2014 challenge every assumption"
2531
- ]
2532
- };
2533
2601
  var ThreadMessage = class {
2534
2602
  constructor(role, turn, content) {
2535
2603
  this.role = role;
@@ -2541,13 +2609,15 @@ var ThreadMessage = class {
2541
2609
  content;
2542
2610
  };
2543
2611
  var ThreadOutput = class extends PatternOutput {
2544
- constructor(text, conclusion, messages, startTime, endTime) {
2612
+ constructor(text, conclusion, messages, startTime, endTime, qualityReview) {
2545
2613
  super(text, startTime, endTime);
2546
2614
  this.conclusion = conclusion;
2547
2615
  this.messages = messages;
2616
+ this.qualityReview = qualityReview;
2548
2617
  }
2549
2618
  conclusion;
2550
2619
  messages;
2620
+ qualityReview;
2551
2621
  };
2552
2622
  var THREAD_PROMPT = `You are an agent with the role: {role}.
2553
2623
  Engage in a multi-agent conversation about the topic.
@@ -2569,7 +2639,7 @@ async function execute15(pieces, args, opts) {
2569
2639
  const t0 = Date.now();
2570
2640
  const agentCount = opts.agents ?? 3;
2571
2641
  const maxTurns = opts.turns ?? 3;
2572
- const roles = opts.roles ?? ROLE_SETS4[agentCount] ?? ROLE_SETS4[3] ?? [];
2642
+ const roles = opts.roles ?? THREAD_ROLE_SETS[agentCount] ?? THREAD_ROLE_SETS[3] ?? [];
2573
2643
  const plannerModel = opts.plannerModel ?? opts.model;
2574
2644
  const workerModel = opts.workerModel ?? opts.model;
2575
2645
  if (!opts.quiet) {
@@ -2587,11 +2657,7 @@ async function execute15(pieces, args, opts) {
2587
2657
  for (let a = 0; a < roles.length; a++) {
2588
2658
  const role = roles[a] ?? `Agent ${a + 1}`;
2589
2659
  const prompt = buildThreadPrompt(role, thread);
2590
- const response = await ask(prompt, {
2591
- model: workerModel,
2592
- maxTokens: opts.maxTokens,
2593
- thinkingLevel: opts.thinkingLevel
2594
- });
2660
+ const response = await ask(prompt, { ...opts, model: workerModel });
2595
2661
  messages.push(new ThreadMessage(role, turn, response));
2596
2662
  thread += `
2597
2663
  [${role}] (Turn ${turn}): ${response}
@@ -2607,46 +2673,24 @@ ${thread}
2607
2673
 
2608
2674
  Synthesize a clear, actionable conclusion.`,
2609
2675
  {
2676
+ ...opts,
2610
2677
  model: plannerModel,
2611
- maxTokens: opts.maxTokens,
2612
2678
  thinkingLevel: "high",
2613
- system: SYNTHESIS_SYSTEM6
2679
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM6)
2614
2680
  }
2615
2681
  );
2682
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2683
+ const qualityReview = await runQualityReview(topic, conclusion, opts);
2616
2684
  const t1 = Date.now();
2617
2685
  const summary = messages.map(
2618
2686
  (m) => `[${m.role}] Turn ${m.turn}: ${m.content.slice(0, 150)}${m.content.length > 150 ? "..." : ""}`
2619
2687
  ).join("\n");
2620
- return new ThreadOutput(summary, conclusion, messages, t0, t1);
2621
- }
2622
- function makeThread(opts = {}) {
2623
- const merged = { ...defaults15, ...opts };
2624
- const fn = ((pieces, ...args) => {
2625
- if (!Array.isArray(pieces)) {
2626
- return makeThread({ ...merged, ...pieces });
2627
- }
2628
- return new PatternPromise((resolve, reject) => {
2629
- execute15(pieces, args, merged).then(resolve, reject);
2630
- });
2631
- });
2632
- let _quiet;
2633
- Object.defineProperty(fn, "quiet", {
2634
- get() {
2635
- if (!_quiet) _quiet = makeThread({ ...merged, quiet: true });
2636
- return _quiet;
2637
- },
2638
- enumerable: true,
2639
- configurable: true
2640
- });
2641
- return fn;
2688
+ return new ThreadOutput(summary, conclusion, messages, t0, t1, qualityReview);
2642
2689
  }
2643
- var \u0398 = makeThread();
2690
+ var \u0398 = createPatternTag(defaults15, execute15);
2644
2691
 
2645
2692
  // src/pi.ts
2646
2693
  import {
2647
- getEnvApiKey as getEnvApiKey2,
2648
- getModels as getModels2,
2649
- getProviders as getProviders2,
2650
2694
  streamSimple
2651
2695
  } from "@earendil-works/pi-ai";
2652
2696
 
@@ -2664,10 +2708,28 @@ var PiOutput = class {
2664
2708
  events;
2665
2709
  startTime;
2666
2710
  endTime;
2711
+ /** Trace entry for this single LLM call. Populated by the π tag. */
2712
+ trace = [];
2667
2713
  /** Duration in milliseconds */
2668
2714
  get duration() {
2669
2715
  return this.endTime - this.startTime;
2670
2716
  }
2717
+ /** Total input tokens (convenience accessor) */
2718
+ get inputTokens() {
2719
+ return this.trace[0]?.inputTokens ?? 0;
2720
+ }
2721
+ /** Total output tokens (convenience accessor) */
2722
+ get outputTokens() {
2723
+ return this.trace[0]?.outputTokens ?? 0;
2724
+ }
2725
+ /** Total tokens (convenience accessor) */
2726
+ get totalTokens() {
2727
+ return this.trace[0]?.totalTokens ?? 0;
2728
+ }
2729
+ /** Total cost in USD (convenience accessor) */
2730
+ get totalCost() {
2731
+ return this.trace[0]?.cost ?? 0;
2732
+ }
2671
2733
  toString() {
2672
2734
  return this.text;
2673
2735
  }
@@ -2693,90 +2755,16 @@ var defaults16 = {
2693
2755
  quiet: false,
2694
2756
  maxTokens: 4096
2695
2757
  };
2696
- var _piSettings2;
2697
- function getPiDefaults2() {
2698
- if (_piSettings2 === void 0) {
2699
- _piSettings2 = isPiInstalled() ? loadPiSettings() : {};
2700
- }
2701
- return _piSettings2;
2702
- }
2703
- function allModels2() {
2704
- const result = [];
2705
- for (const p of getProviders2()) {
2706
- const ms = getModels2(p);
2707
- if (ms && ms.length > 0) result.push(...ms);
2708
- }
2709
- return result;
2710
- }
2711
- function getConfiguredProviders2() {
2712
- return getProviders2().filter((p) => getEnvApiKey2(p) !== void 0);
2713
- }
2714
- function configuredModels2() {
2715
- const configured = new Set(getConfiguredProviders2());
2716
- return allModels2().filter((m) => configured.has(m.provider));
2717
- }
2718
- function findModelById2(id) {
2719
- const all = allModels2();
2720
- if (id.includes("/")) {
2721
- const [provider, modelId] = id.split("/", 2);
2722
- return all.find(
2723
- (m) => m.provider === provider && (m.id === modelId || m.id.endsWith(`/${modelId}`))
2724
- );
2725
- }
2726
- return all.find((m) => m.id === id || m.id.endsWith(`/${id}`));
2727
- }
2728
- function pickModel2(preferred) {
2729
- if (preferred) {
2730
- const hit = findModelById2(preferred);
2731
- if (hit) return hit;
2732
- }
2733
- const settings = getPiDefaults2();
2734
- if (settings.defaultModel) {
2735
- const hit = findModelById2(settings.defaultModel);
2736
- if (hit) return hit;
2737
- }
2738
- if (settings.defaultProvider) {
2739
- const providerModels = getModels2(settings.defaultProvider);
2740
- if (providerModels && providerModels.length > 0) {
2741
- const configured = new Set(getConfiguredProviders2());
2742
- if (configured.has(settings.defaultProvider)) {
2743
- return providerModels[0];
2744
- }
2745
- }
2746
- }
2747
- const available = configuredModels2();
2748
- if (available.length > 0) {
2749
- const order2 = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
2750
- for (const id of order2) {
2751
- const m = available.find((m2) => m2.id.includes(id));
2752
- if (m) return m;
2753
- }
2754
- return available[0];
2755
- }
2756
- const models = allModels2();
2757
- if (models.length === 0) return void 0;
2758
- const order = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
2759
- for (const id of order) {
2760
- const m = models.find((m2) => m2.id.includes(id));
2761
- if (m) return m;
2762
- }
2763
- return models[0];
2764
- }
2765
- function build2(pieces, args) {
2766
- let s = "";
2767
- for (let i = 0; i < pieces.length; i++) {
2768
- s += pieces[i];
2769
- if (i < args.length) s += String(args[i]);
2770
- }
2771
- return s;
2772
- }
2773
2758
  function makeContext(pieces, args, opts) {
2759
+ const systemParts = [];
2760
+ if (opts.system) systemParts.push(opts.system);
2761
+ if (opts.appendSystemPrompt) systemParts.push(opts.appendSystemPrompt);
2774
2762
  return {
2775
- systemPrompt: opts.system,
2763
+ systemPrompt: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
2776
2764
  messages: [
2777
2765
  {
2778
2766
  role: "user",
2779
- content: build2(pieces, args),
2767
+ content: build(pieces, args),
2780
2768
  timestamp: Date.now()
2781
2769
  }
2782
2770
  ]
@@ -2785,25 +2773,52 @@ function makeContext(pieces, args, opts) {
2785
2773
  function makeOpts(opts) {
2786
2774
  return {
2787
2775
  maxTokens: opts.maxTokens,
2788
- reasoning: opts.thinkingLevel
2776
+ reasoning: opts.thinkingLevel,
2777
+ thinkingBudgets: opts.thinkingBudgets,
2778
+ timeoutMs: opts.timeoutMs,
2779
+ maxRetries: opts.maxRetries
2789
2780
  };
2790
2781
  }
2791
2782
  async function run(pieces, args, opts) {
2792
- const model = pickModel2(opts.model);
2783
+ const model = pickModel(opts.model);
2793
2784
  if (!model) throw new Error("pizx/\u03C0: No AI models configured. Run `pi auth login` first.");
2794
2785
  const t0 = Date.now();
2795
2786
  let text = "";
2796
- for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2797
- if (ev.type === "text_delta") {
2798
- text += ev.delta;
2799
- if (!opts.quiet) process.stdout.write(ev.delta);
2787
+ let traceEntry;
2788
+ try {
2789
+ for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2790
+ if (ev.type === "text_delta") {
2791
+ text += ev.delta;
2792
+ if (!opts.quiet) process.stdout.write(ev.delta);
2793
+ } else if (ev.type === "done") {
2794
+ const msg = ev.message;
2795
+ if (msg?.usage) {
2796
+ traceEntry = {
2797
+ call: 1,
2798
+ modelId: model.id,
2799
+ promptPreview: build(pieces, args).slice(0, 200),
2800
+ outputPreview: text.slice(0, 200),
2801
+ inputTokens: msg.usage.input,
2802
+ outputTokens: msg.usage.output,
2803
+ cacheReadTokens: msg.usage.cacheRead,
2804
+ cacheWriteTokens: msg.usage.cacheWrite,
2805
+ totalTokens: msg.usage.totalTokens,
2806
+ cost: msg.usage.cost.total,
2807
+ durationMs: Date.now() - t0
2808
+ };
2809
+ }
2810
+ }
2800
2811
  }
2812
+ } catch (err) {
2813
+ throw new Error(`pizx/\u03C0: AI generation failed: ${getErrorMessage(err)}`);
2801
2814
  }
2802
2815
  if (!opts.quiet && text) process.stdout.write("\n");
2803
- return new PiOutput(text.trim(), model.id, [], t0, Date.now());
2816
+ const output = new PiOutput(text.trim(), model.id, [], t0, Date.now());
2817
+ if (traceEntry) output.trace = [traceEntry];
2818
+ return output;
2804
2819
  }
2805
2820
  async function* runStream(pieces, args, opts) {
2806
- const model = pickModel2(opts.model);
2821
+ const model = pickModel(opts.model);
2807
2822
  if (!model) throw new Error("pizx/\u03C0: No AI models configured");
2808
2823
  for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2809
2824
  if (ev.type === "text_delta") yield ev.delta;
@@ -2846,7 +2861,10 @@ function makePi(opts = {}) {
2846
2861
  var \u03C0 = makePi();
2847
2862
 
2848
2863
  // src/pi-agent.ts
2849
- import { createAgentSession as createAgentSession2 } from "@earendil-works/pi-coding-agent";
2864
+ import {
2865
+ createAgentSession as createAgentSession2,
2866
+ DefaultResourceLoader
2867
+ } from "@earendil-works/pi-coding-agent";
2850
2868
  var _agentDefaults = {
2851
2869
  quiet: false,
2852
2870
  maxTurns: 10
@@ -2878,24 +2896,49 @@ var AgentOutput = class {
2878
2896
  var AgentPromise = class extends Promise {
2879
2897
  };
2880
2898
  var _sharedSession = null;
2881
- async function getSession(opts) {
2882
- if (_sharedSession && !opts.model) return _sharedSession;
2883
- const result = await createAgentSession2({
2884
- cwd: opts.cwd,
2885
- thinkingLevel: opts.thinkingLevel,
2886
- tools: opts.tools,
2887
- excludeTools: opts.excludeTools
2899
+ function createLoader(opts) {
2900
+ const hasSystem = opts.system !== void 0;
2901
+ const hasAppend = opts.appendSystemPrompt !== void 0;
2902
+ const hasSkills = opts.skills && opts.skills.length > 0;
2903
+ if (!hasSystem && !hasAppend && !hasSkills) return void 0;
2904
+ return new DefaultResourceLoader({
2905
+ cwd: opts.cwd ?? process.cwd(),
2906
+ agentDir: "",
2907
+ systemPrompt: opts.system,
2908
+ appendSystemPrompt: opts.appendSystemPrompt ? [opts.appendSystemPrompt] : void 0
2888
2909
  });
2889
- _sharedSession = result.session;
2890
- return _sharedSession;
2891
2910
  }
2892
- function build3(pieces, args) {
2893
- let s = "";
2894
- for (let i = 0; i < pieces.length; i++) {
2895
- s += pieces[i];
2896
- if (i < args.length) s += String(args[i]);
2911
+ async function getSession(opts) {
2912
+ if (_sharedSession && !opts.model) return _sharedSession;
2913
+ try {
2914
+ const loader = createLoader(opts);
2915
+ if (opts.skills && opts.skills.length > 0 && loader) {
2916
+ const skillMap = await loadSkillContents(opts.skills);
2917
+ const skillPaths = [];
2918
+ for (const [name] of skillMap) {
2919
+ for (const base of SKILL_PATHS) {
2920
+ skillPaths.push({
2921
+ path: `${base}/${name}`,
2922
+ metadata: { source: "pizx", scope: "project", origin: "top-level" }
2923
+ });
2924
+ }
2925
+ }
2926
+ if (skillPaths.length > 0) {
2927
+ loader.extendResources({ skillPaths });
2928
+ }
2929
+ }
2930
+ const result = await createAgentSession2({
2931
+ cwd: opts.cwd,
2932
+ thinkingLevel: opts.thinkingLevel,
2933
+ tools: opts.tools,
2934
+ excludeTools: opts.excludeTools,
2935
+ resourceLoader: loader
2936
+ });
2937
+ _sharedSession = result.session;
2938
+ return _sharedSession;
2939
+ } catch (err) {
2940
+ throw new Error(`pizx/\u03A0: Failed to create agent session: ${getErrorMessage(err)}`);
2897
2941
  }
2898
- return s.trim();
2899
2942
  }
2900
2943
  function getMessageText(msg) {
2901
2944
  const content = msg.content;
@@ -2917,7 +2960,7 @@ function getLastAssistantText(session) {
2917
2960
  return "";
2918
2961
  }
2919
2962
  async function execute16(pieces, args, opts) {
2920
- const prompt = build3(pieces, args);
2963
+ const prompt = build(pieces, args);
2921
2964
  const session = await getSession(opts);
2922
2965
  const t0 = Date.now();
2923
2966
  if (!opts.quiet) {
@@ -2928,15 +2971,12 @@ async function execute16(pieces, args, opts) {
2928
2971
  await session.sendUserMessage(prompt);
2929
2972
  const t1 = Date.now();
2930
2973
  const text = getLastAssistantText(session);
2931
- const turnCount = session.messages.filter(
2932
- (m) => m.role === "assistant"
2933
- ).length;
2974
+ const turnCount = session.messages.filter((m) => m.role === "assistant").length;
2934
2975
  return new AgentOutput(text || "(no assistant response)", turnCount, t0, t1);
2935
2976
  } catch (err) {
2936
2977
  const t1 = Date.now();
2937
- const msg = err instanceof Error ? err.message : String(err);
2938
2978
  console.error("\u03A0 error:", err);
2939
- return new AgentOutput(`\u03A0 error: ${msg}`, 0, t0, t1);
2979
+ return new AgentOutput(`\u03A0 error: ${getErrorMessage(err)}`, 0, t0, t1);
2940
2980
  }
2941
2981
  }
2942
2982
  function makeAgent(opts = {}) {
@@ -2998,7 +3038,7 @@ function parseArgs(argv) {
2998
3038
  if (argv[i + 1] && !argv[i + 1].startsWith("-")) flags.model = argv[++i];
2999
3039
  break;
3000
3040
  case "--system":
3001
- if (argv[i + 1]) flags.system = argv[++i];
3041
+ if (argv[i + 1] && !argv[i + 1].startsWith("-")) flags.system = argv[++i];
3002
3042
  break;
3003
3043
  case "-q":
3004
3044
  case "--quiet":
@@ -3039,6 +3079,7 @@ function printHelp() {
3039
3079
  \`\u039B\` Pipeline \u2014 sequential agent chain
3040
3080
  \`\u03A8\` Critique \u2014 generate, critique, improve
3041
3081
  \`\u03A9\` Orchestrator \u2014 plan, dispatch, synthesize
3082
+ \`\u039D\` Nu \u2014 Self-Organizing Teams
3042
3083
 
3043
3084
  ${chalk.bold("Communication Patterns")}
3044
3085
  \`\u0398\` Thread \u2014 multi-agent conversation
@@ -3048,6 +3089,8 @@ function printHelp() {
3048
3089
  ${chalk.bold("Orchestration Topologies")}
3049
3090
  \`\u0391\` Adaptive \u2014 self-adjusting orchestration
3050
3091
  \`\u0393\` Graph \u2014 DAG-based task execution
3092
+ \`\u03A7\` Chi \u2014 Cross-Agent Learning
3093
+ \`\u03A4\` Tau \u2014 Tool-Mediated Orchestration
3051
3094
 
3052
3095
  ${chalk.bold("Example Script")}
3053
3096
  #!/usr/bin/env pizx
@@ -3078,7 +3121,7 @@ async function runPrintMode(flags, args) {
3078
3121
  `);
3079
3122
  }
3080
3123
  } catch (err) {
3081
- console.error("pizx: pi-ai error:", err instanceof Error ? err.message : err);
3124
+ console.error("pizx: pi-ai error:", getErrorMessage(err));
3082
3125
  process2.exit(1);
3083
3126
  }
3084
3127
  }
@@ -3098,6 +3141,9 @@ async function runScriptMode(scriptPath) {
3098
3141
  g.\u03A9 = \u03A9;
3099
3142
  g.\u0398 = \u0398;
3100
3143
  g.\u039C = \u039C;
3144
+ g.\u039D = \u039D;
3145
+ g.\u03A4 = \u03A4;
3146
+ g.\u03A7 = \u03A7;
3101
3147
  g.\u0392 = \u0392;
3102
3148
  g.\u0391 = \u0391;
3103
3149
  g.\u0393 = \u0393;
@@ -3109,11 +3155,7 @@ async function runScriptMode(scriptPath) {
3109
3155
  try {
3110
3156
  await import(url.pathToFileURL(absPath).toString());
3111
3157
  } catch (err) {
3112
- if (err instanceof Error) {
3113
- console.error("pizx:", err.message);
3114
- } else {
3115
- console.error("pizx:", err);
3116
- }
3158
+ console.error("pizx:", getErrorMessage(err));
3117
3159
  process2.exit(1);
3118
3160
  }
3119
3161
  }
@@ -3140,7 +3182,7 @@ async function main() {
3140
3182
  await runScriptMode(script);
3141
3183
  }
3142
3184
  main().catch((err) => {
3143
- console.error("pizx:", err instanceof Error ? err.message : err);
3185
+ console.error("pizx:", getErrorMessage(err));
3144
3186
  process2.exit(1);
3145
3187
  });
3146
3188
  //# sourceMappingURL=cli.js.map