@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/index.js CHANGED
@@ -2,7 +2,15 @@
2
2
  export * from "zx";
3
3
 
4
4
  // src/patterns/types.ts
5
- import { completeSimple, getEnvApiKey, getModels, getProviders } from "@earendil-works/pi-ai";
5
+ import { createInterface } from "node:readline";
6
+ import { completeSimple } from "@earendil-works/pi-ai";
7
+
8
+ // src/model-picker.ts
9
+ import {
10
+ getEnvApiKey,
11
+ getModels,
12
+ getProviders
13
+ } from "@earendil-works/pi-ai";
6
14
 
7
15
  // src/load-pi-settings.ts
8
16
  import { existsSync, readFileSync } from "node:fs";
@@ -33,40 +41,7 @@ function loadPiSettings(agentDir) {
33
41
  }
34
42
  }
35
43
 
36
- // src/patterns/types.ts
37
- var PatternOutput = class {
38
- constructor(text, startTime = Date.now(), endTime = Date.now()) {
39
- this.text = text;
40
- this.startTime = startTime;
41
- this.endTime = endTime;
42
- }
43
- text;
44
- startTime;
45
- endTime;
46
- /** Duration in milliseconds */
47
- get duration() {
48
- return this.endTime - this.startTime;
49
- }
50
- toString() {
51
- return this.text;
52
- }
53
- valueOf() {
54
- return this.text;
55
- }
56
- [Symbol.toPrimitive]() {
57
- return this.text;
58
- }
59
- };
60
- var PatternPromise = class extends Promise {
61
- };
62
- function build(pieces, args) {
63
- let s = "";
64
- for (let i = 0; i < pieces.length; i++) {
65
- s += pieces[i];
66
- if (i < args.length) s += String(args[i]);
67
- }
68
- return s.trim();
69
- }
44
+ // src/model-picker.ts
70
45
  var _piSettings;
71
46
  function getPiDefaults() {
72
47
  if (_piSettings === void 0) {
@@ -110,7 +85,8 @@ function pickModel(preferred) {
110
85
  if (hit) return hit;
111
86
  }
112
87
  if (settings.defaultProvider) {
113
- const providerModels = getModels(settings.defaultProvider);
88
+ const provider = settings.defaultProvider;
89
+ const providerModels = getModels(provider);
114
90
  if (providerModels && providerModels.length > 0) {
115
91
  const configured = new Set(getConfiguredProviders());
116
92
  if (configured.has(settings.defaultProvider)) {
@@ -136,23 +112,261 @@ function pickModel(preferred) {
136
112
  }
137
113
  return models[0];
138
114
  }
115
+
116
+ // src/skill-loader.ts
117
+ import { readFile } from "node:fs/promises";
118
+ import { homedir as homedir2 } from "node:os";
119
+ import { join as join2 } from "node:path";
120
+ var SKILL_PATHS = [
121
+ ".pi/skills",
122
+ ".agents/skills",
123
+ "skills",
124
+ join2(homedir2(), ".pi", "agent", "skills"),
125
+ join2(homedir2(), ".codewhale", "skills"),
126
+ join2(homedir2(), ".claude", "skills")
127
+ ];
128
+ async function loadSkillContent(name) {
129
+ for (const base of SKILL_PATHS) {
130
+ const candidate = join2(base, name, "SKILL.md");
131
+ try {
132
+ return await readFile(candidate, "utf-8");
133
+ } catch {
134
+ }
135
+ }
136
+ return void 0;
137
+ }
138
+ async function loadSkillContents(names) {
139
+ const map = /* @__PURE__ */ new Map();
140
+ for (const name of names) {
141
+ const content = await loadSkillContent(name);
142
+ if (content) map.set(name, content);
143
+ }
144
+ return map;
145
+ }
146
+
147
+ // src/patterns/types.ts
148
+ var PatternOutput = class {
149
+ constructor(text, startTime = Date.now(), endTime = Date.now()) {
150
+ this.text = text;
151
+ this.startTime = startTime;
152
+ this.endTime = endTime;
153
+ }
154
+ text;
155
+ startTime;
156
+ endTime;
157
+ /** Execution trace: one entry per LLM call within this pattern run. Populated by createPatternTag. */
158
+ trace = [];
159
+ /** Structured phase log: key phases during execution, populated by each pattern. */
160
+ phaseLog = [];
161
+ /** Duration in milliseconds */
162
+ get duration() {
163
+ return this.endTime - this.startTime;
164
+ }
165
+ /** Total input tokens across all calls */
166
+ get inputTokens() {
167
+ return this.trace.reduce((s, t) => s + t.inputTokens, 0);
168
+ }
169
+ /** Total output tokens across all calls */
170
+ get outputTokens() {
171
+ return this.trace.reduce((s, t) => s + t.outputTokens, 0);
172
+ }
173
+ /** Total tokens (input + output) across all calls */
174
+ get totalTokens() {
175
+ return this.trace.reduce((s, t) => s + t.totalTokens, 0);
176
+ }
177
+ /** Total cost in USD across all calls */
178
+ get totalCost() {
179
+ return this.trace.reduce((s, t) => s + t.cost, 0);
180
+ }
181
+ /** Number of LLM calls made during this pattern execution */
182
+ get callCount() {
183
+ return this.trace.length;
184
+ }
185
+ toString() {
186
+ return this.text;
187
+ }
188
+ valueOf() {
189
+ return this.text;
190
+ }
191
+ [Symbol.toPrimitive]() {
192
+ return this.text;
193
+ }
194
+ };
195
+ var PatternPromise = class extends Promise {
196
+ };
197
+ var _trace = null;
198
+ function beginTrace() {
199
+ _trace = [];
200
+ }
201
+ function collectTrace() {
202
+ const t = _trace ?? [];
203
+ _trace = null;
204
+ return t;
205
+ }
206
+ function pushTrace(entry) {
207
+ if (_trace) _trace.push(entry);
208
+ }
209
+ function createPatternTag(defaults17, execute17) {
210
+ function make(opts = {}) {
211
+ const merged = { ...defaults17, ...opts };
212
+ const fn = ((pieces, ...args) => {
213
+ if (!Array.isArray(pieces)) {
214
+ return make({ ...merged, ...pieces });
215
+ }
216
+ beginTrace();
217
+ return new PatternPromise((resolve, reject) => {
218
+ execute17(pieces, args, merged).then(
219
+ (output) => {
220
+ output.trace = collectTrace();
221
+ resolve(output);
222
+ },
223
+ (err) => {
224
+ collectTrace();
225
+ reject(err);
226
+ }
227
+ );
228
+ });
229
+ });
230
+ let _quiet;
231
+ Object.defineProperty(fn, "quiet", {
232
+ get() {
233
+ if (!_quiet) _quiet = make({ ...merged, quiet: true });
234
+ return _quiet;
235
+ },
236
+ enumerable: true,
237
+ configurable: true
238
+ });
239
+ return fn;
240
+ }
241
+ return make();
242
+ }
243
+ function build(pieces, args) {
244
+ let s = "";
245
+ for (let i = 0; i < pieces.length; i++) {
246
+ s += pieces[i];
247
+ if (i < args.length) s += String(args[i]);
248
+ }
249
+ return s.trim();
250
+ }
251
+ function mergeSystem(userSystem, patternSystem) {
252
+ if (!userSystem) return patternSystem;
253
+ return `${userSystem}
254
+
255
+ ${patternSystem}`;
256
+ }
257
+ async function confirmPhase(description, opts) {
258
+ if (!opts.confirm) return true;
259
+ if (!opts.quiet) {
260
+ process.stderr.write(`
261
+ \u2500\u2500 Confirm \u2500\u2500
262
+ ${description}
263
+ Proceed? [Y/n] `);
264
+ }
265
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
266
+ const answer = await new Promise((resolve) => {
267
+ rl.question("", (ans) => resolve(ans));
268
+ });
269
+ rl.close();
270
+ const trimmed = answer.trim().toLowerCase();
271
+ if (trimmed === "" || trimmed === "y" || trimmed === "yes") return true;
272
+ return false;
273
+ }
139
274
  async function ask(prompt, opts = {}) {
140
275
  const model = pickModel(opts.model);
141
276
  if (!model) throw new Error("pizx/patterns: No AI models configured. Run `pi auth login` first.");
277
+ let systemPrompt = opts.system;
278
+ if (opts.skills && opts.skills.length > 0) {
279
+ const skillMap = await loadSkillContents(opts.skills);
280
+ if (skillMap.size > 0) {
281
+ const skillBlocks = [];
282
+ for (const [name, content] of skillMap) {
283
+ skillBlocks.push(`Skill context (${name}):
284
+ ${content}`);
285
+ }
286
+ const skillContext = skillBlocks.join("\n\n");
287
+ systemPrompt = systemPrompt ? `${systemPrompt}
288
+
289
+ ${skillContext}` : skillContext;
290
+ }
291
+ }
292
+ const t0 = Date.now();
142
293
  const result = await completeSimple(
143
294
  model,
144
295
  {
145
- systemPrompt: opts.system,
296
+ systemPrompt,
146
297
  messages: [{ role: "user", content: prompt, timestamp: Date.now() }]
147
298
  },
148
299
  {
149
300
  maxTokens: opts.maxTokens ?? 4096,
150
- reasoning: opts.thinkingLevel ?? "medium"
301
+ reasoning: opts.thinkingLevel ?? "medium",
302
+ thinkingBudgets: opts.thinkingBudgets,
303
+ timeoutMs: opts.timeoutMs,
304
+ maxRetries: opts.maxRetries
151
305
  }
152
306
  );
307
+ const durationMs = Date.now() - t0;
308
+ if (!result.content || !Array.isArray(result.content)) {
309
+ throw new Error("pizx/patterns: Unexpected response format from AI model.");
310
+ }
153
311
  const text = result.content.filter((c) => c.type === "text").map((c) => c.text).join("");
312
+ if (_trace !== null) {
313
+ pushTrace({
314
+ call: _trace.length + 1,
315
+ modelId: result.model,
316
+ promptPreview: prompt.slice(0, 200),
317
+ outputPreview: text.slice(0, 200),
318
+ inputTokens: result.usage.input,
319
+ outputTokens: result.usage.output,
320
+ cacheReadTokens: result.usage.cacheRead,
321
+ cacheWriteTokens: result.usage.cacheWrite,
322
+ totalTokens: result.usage.totalTokens,
323
+ cost: result.usage.cost.total,
324
+ durationMs
325
+ });
326
+ }
154
327
  return text.trim();
155
328
  }
329
+ var QUALITY_REVIEW_SYSTEM = `You are a quality assurance reviewer. Evaluate the final deliverable against the original request.
330
+
331
+ Output format:
332
+ SCORE: 0.XX (quality score from 0.0 to 1.0)
333
+ ASSESSMENT: (1-2 sentences \u2014 is the output complete, consistent, and actionable?)
334
+ RECOMMENDATION: (1 sentence \u2014 what would improve this output?)`;
335
+ async function runQualityReview(originalRequest, finalOutput, opts) {
336
+ if (!opts.qualityCheck) return void 0;
337
+ const reviewText = await ask(
338
+ `Original request:
339
+ ${originalRequest}
340
+
341
+ Final deliverable:
342
+ ${finalOutput}
343
+
344
+ Evaluate the quality.`,
345
+ {
346
+ model: opts.plannerModel ?? opts.model,
347
+ maxTokens: 512,
348
+ thinkingLevel: "high",
349
+ timeoutMs: opts.timeoutMs,
350
+ maxRetries: opts.maxRetries,
351
+ system: QUALITY_REVIEW_SYSTEM
352
+ }
353
+ );
354
+ const scoreMatch = reviewText.match(/SCORE:\s*([\d.]+)/i);
355
+ const assessMatch = reviewText.match(/ASSESSMENT:\s*(.+)/i);
356
+ const recMatch = reviewText.match(/RECOMMENDATION:\s*(.+)/i);
357
+ const result = {
358
+ score: scoreMatch ? parseFloat(scoreMatch[1]) : 0.5,
359
+ assessment: assessMatch?.[1]?.trim() ?? "(no assessment)",
360
+ recommendation: recMatch?.[1]?.trim() ?? "(no recommendation)"
361
+ };
362
+ if (!opts.quiet) {
363
+ process.stderr.write(` Quality score: ${result.score.toFixed(2)}
364
+ `);
365
+ process.stderr.write(` ${result.assessment.slice(0, 80)}...
366
+ `);
367
+ }
368
+ return result;
369
+ }
156
370
 
157
371
  // src/patterns/adaptive.ts
158
372
  var defaults = {
@@ -216,10 +430,10 @@ async function execute(pieces, args, opts) {
216
430
  }
217
431
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
218
432
  const planText = await ask(goal, {
433
+ ...opts,
219
434
  model: plannerModel,
220
- maxTokens: opts.maxTokens,
221
435
  thinkingLevel: "high",
222
- system: PLAN_SYSTEM
436
+ system: mergeSystem(opts.system, PLAN_SYSTEM)
223
437
  });
224
438
  const planLines = planText.split("\n");
225
439
  const plannedSteps = [];
@@ -249,10 +463,9 @@ async function execute(pieces, args, opts) {
249
463
  process.stderr.write(` \u2192 Step ${executionStep}: ${currentStep.slice(0, 60)}...
250
464
  `);
251
465
  const result = await ask(currentStep, {
466
+ ...opts,
252
467
  model: workerModel,
253
- maxTokens: opts.maxTokens,
254
- thinkingLevel: opts.thinkingLevel,
255
- system: EXECUTE_SYSTEM
468
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM)
256
469
  });
257
470
  const evaluation = await ask(
258
471
  `Goal: ${goal}
@@ -261,10 +474,11 @@ Result: ${result}
261
474
 
262
475
  Evaluate the result.`,
263
476
  {
477
+ ...opts,
264
478
  model: plannerModel,
265
479
  maxTokens: 512,
266
480
  thinkingLevel: "high",
267
- system: EVALUATE_SYSTEM
481
+ system: mergeSystem(opts.system, EVALUATE_SYSTEM)
268
482
  }
269
483
  );
270
484
  const scoreMatch = evaluation.match(/SCORE:\s*([\d.]+)/i);
@@ -287,8 +501,7 @@ Evaluate the result.`,
287
501
  break;
288
502
  }
289
503
  const adaptUpper = adaptation.toUpperCase();
290
- if (adaptUpper.startsWith("REFINE")) {
291
- } else if (adaptUpper.startsWith("SKIP_NEXT")) {
504
+ if (adaptUpper.startsWith("SKIP_NEXT")) {
292
505
  stepIndex += 2;
293
506
  } else if (adaptUpper.startsWith("ADD")) {
294
507
  const newStep = adaptation.replace(/^ADD\s*/i, "");
@@ -307,36 +520,76 @@ Evaluate the result.`,
307
520
  ).join("\n\n");
308
521
  return new AdaptiveOutput(summary, finalResult, adaptiveSteps, executionStep, t0, t1);
309
522
  }
310
- function makeAdaptive(opts = {}) {
311
- const merged = { ...defaults, ...opts };
312
- const fn = ((pieces, ...args) => {
313
- if (!Array.isArray(pieces)) {
314
- return makeAdaptive({ ...merged, ...pieces });
315
- }
316
- return new PatternPromise((resolve, reject) => {
317
- execute(pieces, args, merged).then(resolve, reject);
318
- });
319
- });
320
- let _quiet;
321
- Object.defineProperty(fn, "quiet", {
322
- get() {
323
- if (!_quiet) _quiet = makeAdaptive({ ...merged, quiet: true });
324
- return _quiet;
325
- },
326
- enumerable: true,
327
- configurable: true
328
- });
329
- return fn;
330
- }
331
- var \u0391 = makeAdaptive();
523
+ var \u0391 = createPatternTag(defaults, execute);
332
524
 
333
- // src/patterns/broadcast.ts
334
- var defaults2 = {
335
- maxTokens: 4096,
336
- thinkingLevel: "medium",
337
- workers: 4
525
+ // src/patterns/role-sets.ts
526
+ var DEBATE_ROLE_SETS = {
527
+ 2: [
528
+ "Optimist \u2014 advocate for the most ambitious approach",
529
+ "Pessimist \u2014 identify risks and failure modes"
530
+ ],
531
+ 3: [
532
+ "Optimist \u2014 advocate the benefits and opportunities",
533
+ "Pessimist \u2014 identify risks, costs, and failure modes",
534
+ "Pragmatist \u2014 focus on practical trade-offs and implementation"
535
+ ],
536
+ 4: [
537
+ "Optimist \u2014 argue for the best-case potential",
538
+ "Pessimist \u2014 highlight worst-case risks and downsides",
539
+ "Pragmatist \u2014 balance pros/cons with practical constraints",
540
+ "Innovator \u2014 propose creative alternatives and novel approaches"
541
+ ],
542
+ 5: [
543
+ "Optimist",
544
+ "Pessimist",
545
+ "Pragmatist",
546
+ "Innovator",
547
+ "User Advocate \u2014 focus on end-user experience and accessibility"
548
+ ]
549
+ };
550
+ var MEMORY_ROLE_SETS = {
551
+ 2: ["Analyst \u2014 deep analysis of core aspects", "Reviewer \u2014 check for gaps and blind spots"],
552
+ 3: [
553
+ "Analyst \u2014 deep analysis of core aspects",
554
+ "Reviewer \u2014 check for gaps, edge cases, and blind spots",
555
+ "Strategist \u2014 connect findings to actionable insights"
556
+ ],
557
+ 4: [
558
+ "Analyst",
559
+ "Reviewer",
560
+ "Strategist",
561
+ "Innovator \u2014 propose novel angles and creative solutions"
562
+ ],
563
+ 5: [
564
+ "Analyst",
565
+ "Reviewer",
566
+ "Strategist",
567
+ "Innovator",
568
+ "Skeptic \u2014 challenge assumptions and stress-test conclusions"
569
+ ]
570
+ };
571
+ var THREAD_ROLE_SETS = {
572
+ 2: ["Proposer \u2014 advocate the best approach", "Critic \u2014 identify weaknesses and gaps"],
573
+ 3: [
574
+ "Proposer \u2014 suggest the best approach",
575
+ "Critic \u2014 identify weaknesses, risks, and missing pieces",
576
+ "Synthesizer \u2014 combine the best ideas into a practical plan"
577
+ ],
578
+ 4: [
579
+ "Proposer \u2014 advocate a bold solution",
580
+ "Critic \u2014 identify risks and weaknesses",
581
+ "Pragmatist \u2014 focus on practical implementation",
582
+ "Innovator \u2014 propose creative alternatives"
583
+ ],
584
+ 5: [
585
+ "Proposer",
586
+ "Critic",
587
+ "Pragmatist",
588
+ "Innovator",
589
+ "Devil's Advocate \u2014 challenge every assumption"
590
+ ]
338
591
  };
339
- var ROLE_SETS = {
592
+ var BROADCAST_ROLE_SETS = {
340
593
  2: [
341
594
  "Technical Expert \u2014 evaluate technical feasibility",
342
595
  "Business Expert \u2014 evaluate business viability"
@@ -360,6 +613,13 @@ var ROLE_SETS = {
360
613
  "Innovation Expert \u2014 suggest novel approaches and alternatives"
361
614
  ]
362
615
  };
616
+
617
+ // src/patterns/broadcast.ts
618
+ var defaults2 = {
619
+ maxTokens: 4096,
620
+ thinkingLevel: "medium",
621
+ workers: 4
622
+ };
363
623
  var BroadcastResponse = class {
364
624
  constructor(role, response, success, error) {
365
625
  this.role = role;
@@ -373,13 +633,15 @@ var BroadcastResponse = class {
373
633
  error;
374
634
  };
375
635
  var BroadcastOutput = class extends PatternOutput {
376
- constructor(text, synthesis, responses, startTime, endTime) {
636
+ constructor(text, synthesis, responses, startTime, endTime, qualityReview) {
377
637
  super(text, startTime, endTime);
378
638
  this.synthesis = synthesis;
379
639
  this.responses = responses;
640
+ this.qualityReview = qualityReview;
380
641
  }
381
642
  synthesis;
382
643
  responses;
644
+ qualityReview;
383
645
  };
384
646
  var WORKER_PROMPT = `You are a {role}.
385
647
 
@@ -394,7 +656,7 @@ async function execute2(pieces, args, opts) {
394
656
  const question = build(pieces, args);
395
657
  const t0 = Date.now();
396
658
  const workerCount = opts.workers ?? 4;
397
- const roles = opts.roles ?? ROLE_SETS[workerCount] ?? ROLE_SETS[4] ?? [];
659
+ const roles = opts.roles ?? BROADCAST_ROLE_SETS[workerCount] ?? BROADCAST_ROLE_SETS[4] ?? [];
398
660
  const plannerModel = opts.plannerModel ?? opts.model;
399
661
  const workerModel = opts.workerModel ?? opts.model;
400
662
  if (!opts.quiet) {
@@ -409,11 +671,7 @@ async function execute2(pieces, args, opts) {
409
671
  const broadcastResults = await Promise.allSettled(
410
672
  roles.map(async (role) => {
411
673
  const prompt = WORKER_PROMPT.replace("{role}", role).replace("{question}", question);
412
- const text = await ask(prompt, {
413
- model: workerModel,
414
- maxTokens: opts.maxTokens,
415
- thinkingLevel: opts.thinkingLevel
416
- });
674
+ const text = await ask(prompt, { ...opts, model: workerModel });
417
675
  return new BroadcastResponse(role, text, true);
418
676
  })
419
677
  );
@@ -438,40 +696,21 @@ ${responsesText}
438
696
 
439
697
  Synthesize a cohesive, actionable recommendation.`,
440
698
  {
699
+ ...opts,
441
700
  model: plannerModel,
442
- maxTokens: opts.maxTokens,
443
701
  thinkingLevel: "high",
444
- system: SYNTHESIS_SYSTEM
702
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM)
445
703
  }
446
704
  );
705
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
706
+ const qualityReview = await runQualityReview(question, synthesis, opts);
447
707
  const t1 = Date.now();
448
708
  const summary = responses.map(
449
709
  (wr) => `[${wr.role}]: ${wr.response.slice(0, 150)}${wr.response.length > 150 ? "..." : ""}`
450
710
  ).join("\n");
451
- return new BroadcastOutput(summary, synthesis, responses, t0, t1);
452
- }
453
- function makeBroadcast(opts = {}) {
454
- const merged = { ...defaults2, ...opts };
455
- const fn = ((pieces, ...args) => {
456
- if (!Array.isArray(pieces)) {
457
- return makeBroadcast({ ...merged, ...pieces });
458
- }
459
- return new PatternPromise((resolve, reject) => {
460
- execute2(pieces, args, merged).then(resolve, reject);
461
- });
462
- });
463
- let _quiet;
464
- Object.defineProperty(fn, "quiet", {
465
- get() {
466
- if (!_quiet) _quiet = makeBroadcast({ ...merged, quiet: true });
467
- return _quiet;
468
- },
469
- enumerable: true,
470
- configurable: true
471
- });
472
- return fn;
711
+ return new BroadcastOutput(summary, synthesis, responses, t0, t1, qualityReview);
473
712
  }
474
- var \u0392 = makeBroadcast();
713
+ var \u0392 = createPatternTag(defaults2, execute2);
475
714
 
476
715
  // src/patterns/chi.ts
477
716
  var defaults3 = {
@@ -491,15 +730,17 @@ var LearningInsight = class {
491
730
  confidence;
492
731
  };
493
732
  var ChiOutput = class extends PatternOutput {
494
- constructor(text, insights, summary, suggestedChanges, startTime, endTime) {
733
+ constructor(text, insights, summary, suggestedChanges, startTime, endTime, qualityReview) {
495
734
  super(text, startTime, endTime);
496
735
  this.insights = insights;
497
736
  this.summary = summary;
498
737
  this.suggestedChanges = suggestedChanges;
738
+ this.qualityReview = qualityReview;
499
739
  }
500
740
  insights;
501
741
  summary;
502
742
  suggestedChanges;
743
+ qualityReview;
503
744
  };
504
745
  var ANALYSIS_SYSTEM = `You are an agent team performance analyst. Review a multi-agent execution and extract structured learnings.
505
746
 
@@ -568,12 +809,13 @@ async function execute3(pieces, args, opts) {
568
809
  `);
569
810
  }
570
811
  const response = await ask(input, {
812
+ ...opts,
571
813
  model: plannerModel,
572
- maxTokens: opts.maxTokens,
573
- thinkingLevel: opts.thinkingLevel,
574
- system: ANALYSIS_SYSTEM
814
+ system: mergeSystem(opts.system, ANALYSIS_SYSTEM)
575
815
  });
576
816
  const { insights, summary, suggestedChanges } = parseInsights(response);
817
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
818
+ const qualityReview = await runQualityReview(input, response, opts);
577
819
  const t1 = Date.now();
578
820
  const text = [
579
821
  `Insights: ${insights.length} extracted`,
@@ -585,30 +827,9 @@ Summary: ${summary}`,
585
827
  `
586
828
  Changes: ${suggestedChanges}`
587
829
  ].join("\n");
588
- return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1);
830
+ return new ChiOutput(text, insights, summary, suggestedChanges, t0, t1, qualityReview);
589
831
  }
590
- function makeChi(opts = {}) {
591
- const merged = { ...defaults3, ...opts };
592
- const fn = ((pieces, ...args) => {
593
- if (!Array.isArray(pieces)) {
594
- return makeChi({ ...merged, ...pieces });
595
- }
596
- return new PatternPromise((resolve, reject) => {
597
- execute3(pieces, args, merged).then(resolve, reject);
598
- });
599
- });
600
- let _quiet;
601
- Object.defineProperty(fn, "quiet", {
602
- get() {
603
- if (!_quiet) _quiet = makeChi({ ...merged, quiet: true });
604
- return _quiet;
605
- },
606
- enumerable: true,
607
- configurable: true
608
- });
609
- return fn;
610
- }
611
- var \u03A7 = makeChi();
832
+ var \u03A7 = createPatternTag(defaults3, execute3);
612
833
 
613
834
  // src/patterns/critique.ts
614
835
  var defaults4 = {
@@ -663,12 +884,7 @@ async function execute4(pieces, args, opts) {
663
884
  for (let r = 0; r < rounds; r++) {
664
885
  if (r === 0) {
665
886
  if (!opts.quiet) process.stderr.write(" \u2192 Generating initial content...\n");
666
- currentContent = await ask(prompt, {
667
- model: workerModel,
668
- maxTokens: opts.maxTokens,
669
- thinkingLevel: opts.thinkingLevel,
670
- system: void 0
671
- });
887
+ currentContent = await ask(prompt, { ...opts, model: workerModel, system: opts.system });
672
888
  } else {
673
889
  if (!opts.quiet) process.stderr.write(` \u2192 Improving (round ${r + 1})...
674
890
  `);
@@ -683,21 +899,15 @@ Content to improve:
683
899
  ${currentContent}
684
900
 
685
901
  Revise the content based on the critique.`,
686
- {
687
- model: workerModel,
688
- maxTokens: opts.maxTokens,
689
- thinkingLevel: opts.thinkingLevel,
690
- system: IMPROVE_SYSTEM
691
- }
902
+ { ...opts, model: workerModel, system: mergeSystem(opts.system, IMPROVE_SYSTEM) }
692
903
  );
693
904
  }
694
905
  if (!opts.quiet) process.stderr.write(` \u2192 Critiquing (round ${r + 1})...
695
906
  `);
696
907
  const critique = await ask(currentContent, {
908
+ ...opts,
697
909
  model: plannerModel,
698
- maxTokens: opts.maxTokens,
699
- thinkingLevel: opts.thinkingLevel,
700
- system: CRITIQUE_SYSTEM
910
+ system: mergeSystem(opts.system, CRITIQUE_SYSTEM)
701
911
  });
702
912
  critiqueRounds.push(new CritiqueRound(currentContent, critique, r));
703
913
  }
@@ -710,28 +920,7 @@ Critique: ${cr.critique.slice(0, 200)}${cr.critique.length > 200 ? "..." : ""}`
710
920
  ).join("\n\n");
711
921
  return new CritiqueOutput(summary, finalContent, critiqueRounds, t0, t1);
712
922
  }
713
- function makeCritique(opts = {}) {
714
- const merged = { ...defaults4, ...opts };
715
- const fn = ((pieces, ...args) => {
716
- if (!Array.isArray(pieces)) {
717
- return makeCritique({ ...merged, ...pieces });
718
- }
719
- return new PatternPromise((resolve, reject) => {
720
- execute4(pieces, args, merged).then(resolve, reject);
721
- });
722
- });
723
- let _quiet;
724
- Object.defineProperty(fn, "quiet", {
725
- get() {
726
- if (!_quiet) _quiet = makeCritique({ ...merged, quiet: true });
727
- return _quiet;
728
- },
729
- enumerable: true,
730
- configurable: true
731
- });
732
- return fn;
733
- }
734
- var \u03A8 = makeCritique();
923
+ var \u03A8 = createPatternTag(defaults4, execute4);
735
924
 
736
925
  // src/patterns/debate.ts
737
926
  var defaults5 = {
@@ -740,30 +929,6 @@ var defaults5 = {
740
929
  perspectives: 3,
741
930
  rounds: 1
742
931
  };
743
- var ROLE_SETS2 = {
744
- 2: [
745
- "Optimist \u2014 advocate for the most ambitious approach",
746
- "Pessimist \u2014 identify risks and failure modes"
747
- ],
748
- 3: [
749
- "Optimist \u2014 advocate the benefits and opportunities",
750
- "Pessimist \u2014 identify risks, costs, and failure modes",
751
- "Pragmatist \u2014 focus on practical trade-offs and implementation"
752
- ],
753
- 4: [
754
- "Optimist \u2014 argue for the best-case potential",
755
- "Pessimist \u2014 highlight worst-case risks and downsides",
756
- "Pragmatist \u2014 balance pros/cons with practical constraints",
757
- "Innovator \u2014 propose creative alternatives and novel approaches"
758
- ],
759
- 5: [
760
- "Optimist",
761
- "Pessimist",
762
- "Pragmatist",
763
- "Innovator",
764
- "User Advocate \u2014 focus on end-user experience and accessibility"
765
- ]
766
- };
767
932
  var DebatePerspective = class {
768
933
  constructor(role, argument, round = 1) {
769
934
  this.role = role;
@@ -775,15 +940,17 @@ var DebatePerspective = class {
775
940
  round;
776
941
  };
777
942
  var DebateOutput = class extends PatternOutput {
778
- constructor(text, conclusion, perspectives, rounds, startTime, endTime) {
943
+ constructor(text, conclusion, perspectives, rounds, startTime, endTime, qualityReview) {
779
944
  super(text, startTime, endTime);
780
945
  this.conclusion = conclusion;
781
946
  this.perspectives = perspectives;
782
947
  this.rounds = rounds;
948
+ this.qualityReview = qualityReview;
783
949
  }
784
950
  conclusion;
785
951
  perspectives;
786
952
  rounds;
953
+ qualityReview;
787
954
  };
788
955
  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.`;
789
956
  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.`;
@@ -793,7 +960,7 @@ async function execute5(pieces, args, opts) {
793
960
  const t0 = Date.now();
794
961
  const count = opts.perspectives ?? 3;
795
962
  const totalRounds = opts.rounds ?? 1;
796
- const roles = opts.roles ?? ROLE_SETS2[count] ?? ROLE_SETS2[3] ?? [];
963
+ const roles = opts.roles ?? DEBATE_ROLE_SETS[count] ?? DEBATE_ROLE_SETS[3] ?? [];
797
964
  const plannerModel = opts.plannerModel ?? opts.model;
798
965
  const workerModel = opts.workerModel ?? opts.model;
799
966
  if (!opts.quiet) {
@@ -811,10 +978,9 @@ async function execute5(pieces, args, opts) {
811
978
  const round1Results = await Promise.allSettled(
812
979
  roles.map(
813
980
  (role) => ask(question, {
981
+ ...opts,
814
982
  model: workerModel,
815
- maxTokens: opts.maxTokens,
816
- thinkingLevel: opts.thinkingLevel,
817
- system: PERSPECTIVE_SYSTEM(role)
983
+ system: mergeSystem(opts.system, PERSPECTIVE_SYSTEM(role))
818
984
  }).then((text) => new DebatePerspective(role, text, 1))
819
985
  )
820
986
  );
@@ -848,10 +1014,9 @@ ${othersText}
848
1014
 
849
1015
  Refine your position. Address the counter-arguments directly. Strengthen your argument with rebuttals.`;
850
1016
  return ask(prompt, {
1017
+ ...opts,
851
1018
  model: workerModel,
852
- maxTokens: opts.maxTokens,
853
- thinkingLevel: opts.thinkingLevel,
854
- system: REBUTTAL_SYSTEM(role)
1019
+ system: mergeSystem(opts.system, REBUTTAL_SYSTEM(role))
855
1020
  }).then((text) => new DebatePerspective(role, text, round));
856
1021
  })
857
1022
  );
@@ -875,37 +1040,31 @@ Refine your position. Address the counter-arguments directly. Strengthen your ar
875
1040
 
876
1041
  Synthesize a balanced conclusion from the full debate above. Weigh the evidence from all rounds.`,
877
1042
  {
1043
+ ...opts,
878
1044
  model: plannerModel,
879
- maxTokens: opts.maxTokens,
880
1045
  thinkingLevel: "high",
881
- system: SYNTHESIS_SYSTEM2
1046
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM2)
882
1047
  }
883
1048
  );
1049
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1050
+ const qualityReview = await runQualityReview(question, conclusion, opts);
884
1051
  const t1 = Date.now();
885
- return new DebateOutput(conclusion, conclusion, allPerspectives, totalRounds, t0, t1);
1052
+ return new DebateOutput(
1053
+ conclusion,
1054
+ conclusion,
1055
+ allPerspectives,
1056
+ totalRounds,
1057
+ t0,
1058
+ t1,
1059
+ qualityReview
1060
+ );
886
1061
  }
887
- function makeDebate(opts = {}) {
888
- const merged = { ...defaults5, ...opts };
889
- const fn = ((pieces, ...args) => {
890
- if (!Array.isArray(pieces)) {
891
- return makeDebate({ ...merged, ...pieces });
892
- }
893
- return new PatternPromise((resolve, reject) => {
894
- execute5(pieces, args, merged).then(resolve, reject);
895
- });
896
- });
897
- let _quiet;
898
- Object.defineProperty(fn, "quiet", {
899
- get() {
900
- if (!_quiet) _quiet = makeDebate({ ...merged, quiet: true });
901
- return _quiet;
902
- },
903
- enumerable: true,
904
- configurable: true
905
- });
906
- return fn;
1062
+ var \u0394 = createPatternTag(defaults5, execute5);
1063
+
1064
+ // src/utils.ts
1065
+ function getErrorMessage(err) {
1066
+ return err instanceof Error ? err.message : String(err);
907
1067
  }
908
- var \u0394 = makeDebate();
909
1068
 
910
1069
  // src/patterns/fleet.ts
911
1070
  var defaults6 = {
@@ -926,11 +1085,13 @@ var FleetMemberOutput = class {
926
1085
  error;
927
1086
  };
928
1087
  var FleetOutput = class extends PatternOutput {
929
- constructor(text, members, startTime, endTime) {
1088
+ constructor(text, members, startTime, endTime, qualityReview) {
930
1089
  super(text, startTime, endTime);
931
1090
  this.members = members;
1091
+ this.qualityReview = qualityReview;
932
1092
  }
933
1093
  members;
1094
+ qualityReview;
934
1095
  /** Number of successful members */
935
1096
  get successCount() {
936
1097
  return this.members.filter((m) => m.success).length;
@@ -950,20 +1111,30 @@ function parseTasks(template, explicitTasks) {
950
1111
  if (lines.length > 1) return lines;
951
1112
  return [template];
952
1113
  }
1114
+ function describeTask(task) {
1115
+ if (typeof task === "function") return "(composed pattern)";
1116
+ return task;
1117
+ }
953
1118
  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.`;
954
1119
  async function executeTask(task, opts, workerModel) {
1120
+ if (typeof task === "function") {
1121
+ try {
1122
+ const text = await task("");
1123
+ return new FleetMemberOutput("(composed pattern)", text, true);
1124
+ } catch (err) {
1125
+ return new FleetMemberOutput("(composed pattern)", "", false, getErrorMessage(err));
1126
+ }
1127
+ }
955
1128
  const model = workerModel ?? opts.model;
956
1129
  try {
957
1130
  const text = await ask(task, {
1131
+ ...opts,
958
1132
  model,
959
- maxTokens: opts.maxTokens,
960
- thinkingLevel: opts.thinkingLevel,
961
- system: opts.system ?? FLEET_SYSTEM
1133
+ system: mergeSystem(opts.system, FLEET_SYSTEM)
962
1134
  });
963
1135
  return new FleetMemberOutput(task, text, true);
964
1136
  } catch (err) {
965
- const msg = err instanceof Error ? err.message : String(err);
966
- return new FleetMemberOutput(task, "", false, msg);
1137
+ return new FleetMemberOutput(task, "", false, getErrorMessage(err));
967
1138
  }
968
1139
  }
969
1140
  async function execute6(pieces, args, opts) {
@@ -975,11 +1146,16 @@ async function execute6(pieces, args, opts) {
975
1146
  process.stderr.write(`\u03A6: Fleet executing ${tasks.length} task(s) in parallel
976
1147
  `);
977
1148
  for (let i = 0; i < tasks.length; i++) {
978
- const t = tasks[i];
1149
+ const t = describeTask(tasks[i]);
979
1150
  process.stderr.write(` [${i + 1}] ${t.slice(0, 60)}${t.length > 60 ? "..." : ""}
980
1151
  `);
981
1152
  }
982
1153
  }
1154
+ const taskSummary = `Execute ${tasks.length} fleet task(s)?
1155
+ ${tasks.map((t, i) => `${i + 1}. ${describeTask(t).slice(0, 80)}`).join("\n ")}`;
1156
+ if (!await confirmPhase(taskSummary, opts)) {
1157
+ throw new Error("pizx/\u03A6: Execution cancelled by user.");
1158
+ }
983
1159
  const results = [];
984
1160
  const concurrency = opts.concurrency ?? 5;
985
1161
  for (let i = 0; i < tasks.length; i += concurrency) {
@@ -991,7 +1167,9 @@ async function execute6(pieces, args, opts) {
991
1167
  if (r.status === "fulfilled") {
992
1168
  results.push(r.value);
993
1169
  } else {
994
- results.push(new FleetMemberOutput(batch[idx], "", false, r.reason?.toString()));
1170
+ results.push(
1171
+ new FleetMemberOutput(describeTask(batch[idx]), "", false, r.reason?.toString())
1172
+ );
995
1173
  }
996
1174
  });
997
1175
  }
@@ -1003,30 +1181,11 @@ async function execute6(pieces, args, opts) {
1003
1181
  const header = `Fleet Results: ${results.filter((r) => r.success).length}/${results.length} succeeded
1004
1182
 
1005
1183
  `;
1006
- return new FleetOutput(header + summary, results, t0, t1);
1007
- }
1008
- function makeFleet(opts = {}) {
1009
- const merged = { ...defaults6, ...opts };
1010
- const fn = ((pieces, ...args) => {
1011
- if (!Array.isArray(pieces)) {
1012
- return makeFleet({ ...merged, ...pieces });
1013
- }
1014
- return new PatternPromise((resolve, reject) => {
1015
- execute6(pieces, args, merged).then(resolve, reject);
1016
- });
1017
- });
1018
- let _quiet;
1019
- Object.defineProperty(fn, "quiet", {
1020
- get() {
1021
- if (!_quiet) _quiet = makeFleet({ ...merged, quiet: true });
1022
- return _quiet;
1023
- },
1024
- enumerable: true,
1025
- configurable: true
1026
- });
1027
- return fn;
1184
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1185
+ const qualityReview = await runQualityReview(template, header + summary, opts);
1186
+ return new FleetOutput(header + summary, results, t0, t1, qualityReview);
1028
1187
  }
1029
- var \u03A6 = makeFleet();
1188
+ var \u03A6 = createPatternTag(defaults6, execute6);
1030
1189
 
1031
1190
  // src/patterns/graph.ts
1032
1191
  var defaults7 = {
@@ -1046,13 +1205,15 @@ var GraphNodeResult = class {
1046
1205
  success;
1047
1206
  };
1048
1207
  var GraphOutput = class extends PatternOutput {
1049
- constructor(text, finalOutput, nodeResults, startTime, endTime) {
1208
+ constructor(text, finalOutput, nodeResults, startTime, endTime, qualityReview) {
1050
1209
  super(text, startTime, endTime);
1051
1210
  this.finalOutput = finalOutput;
1052
1211
  this.nodeResults = nodeResults;
1212
+ this.qualityReview = qualityReview;
1053
1213
  }
1054
1214
  finalOutput;
1055
1215
  nodeResults;
1216
+ qualityReview;
1056
1217
  };
1057
1218
  function parseGraph(template, separator) {
1058
1219
  const sep = separator ?? "\u2192";
@@ -1148,10 +1309,9 @@ Your task: ${node.task}`;
1148
1309
  }
1149
1310
  }
1150
1311
  const text = await ask(context, {
1312
+ ...opts,
1151
1313
  model: workerModel,
1152
- maxTokens: opts.maxTokens,
1153
- thinkingLevel: opts.thinkingLevel,
1154
- system: NODE_SYSTEM
1314
+ system: mergeSystem(opts.system, NODE_SYSTEM)
1155
1315
  });
1156
1316
  return { nodeId, task: node.task, text, success: true };
1157
1317
  })
@@ -1169,34 +1329,15 @@ Your task: ${node.task}`;
1169
1329
  const lastBatch = batches[batches.length - 1] ?? [];
1170
1330
  const finalNodeResults = lastBatch.map((id) => results.get(id)).filter(Boolean);
1171
1331
  const finalOutput = finalNodeResults.length > 0 ? finalNodeResults.join("\n\n") : "";
1332
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1333
+ const qualityReview = await runQualityReview(template, finalOutput, opts);
1172
1334
  const summary = nodeResults.map(
1173
1335
  (nr) => `[${nr.nodeId}] ${nr.task.slice(0, 80)}...
1174
1336
  ${nr.output.slice(0, 200)}${nr.output.length > 200 ? "..." : ""}`
1175
1337
  ).join("\n\n");
1176
- return new GraphOutput(summary, finalOutput, nodeResults, t0, t1);
1177
- }
1178
- function makeGraph(opts = {}) {
1179
- const merged = { ...defaults7, ...opts };
1180
- const fn = ((pieces, ...args) => {
1181
- if (!Array.isArray(pieces)) {
1182
- return makeGraph({ ...merged, ...pieces });
1183
- }
1184
- return new PatternPromise((resolve, reject) => {
1185
- execute7(pieces, args, merged).then(resolve, reject);
1186
- });
1187
- });
1188
- let _quiet;
1189
- Object.defineProperty(fn, "quiet", {
1190
- get() {
1191
- if (!_quiet) _quiet = makeGraph({ ...merged, quiet: true });
1192
- return _quiet;
1193
- },
1194
- enumerable: true,
1195
- configurable: true
1196
- });
1197
- return fn;
1338
+ return new GraphOutput(summary, finalOutput, nodeResults, t0, t1, qualityReview);
1198
1339
  }
1199
- var \u0393 = makeGraph();
1340
+ var \u0393 = createPatternTag(defaults7, execute7);
1200
1341
 
1201
1342
  // src/patterns/memory.ts
1202
1343
  var defaults8 = {
@@ -1205,27 +1346,6 @@ var defaults8 = {
1205
1346
  agents: 3,
1206
1347
  rounds: 1
1207
1348
  };
1208
- var ROLE_SETS3 = {
1209
- 2: ["Analyst \u2014 deep analysis of core aspects", "Reviewer \u2014 check for gaps and blind spots"],
1210
- 3: [
1211
- "Analyst \u2014 deep analysis of core aspects",
1212
- "Reviewer \u2014 check for gaps, edge cases, and blind spots",
1213
- "Strategist \u2014 connect findings to actionable insights"
1214
- ],
1215
- 4: [
1216
- "Analyst",
1217
- "Reviewer",
1218
- "Strategist",
1219
- "Innovator \u2014 propose novel angles and creative solutions"
1220
- ],
1221
- 5: [
1222
- "Analyst",
1223
- "Reviewer",
1224
- "Strategist",
1225
- "Innovator",
1226
- "Skeptic \u2014 challenge assumptions and stress-test conclusions"
1227
- ]
1228
- };
1229
1349
  var MemoryEntry = class {
1230
1350
  constructor(role, round, content) {
1231
1351
  this.role = role;
@@ -1237,13 +1357,15 @@ var MemoryEntry = class {
1237
1357
  content;
1238
1358
  };
1239
1359
  var MemoryOutput = class extends PatternOutput {
1240
- constructor(text, synthesis, entries, startTime, endTime) {
1360
+ constructor(text, synthesis, entries, startTime, endTime, qualityReview) {
1241
1361
  super(text, startTime, endTime);
1242
1362
  this.synthesis = synthesis;
1243
1363
  this.entries = entries;
1364
+ this.qualityReview = qualityReview;
1244
1365
  }
1245
1366
  synthesis;
1246
1367
  entries;
1368
+ qualityReview;
1247
1369
  };
1248
1370
  var WRITER_PROMPT = `You are a specialist with role: {role}.
1249
1371
 
@@ -1264,7 +1386,7 @@ async function execute8(pieces, args, opts) {
1264
1386
  const t0 = Date.now();
1265
1387
  const agentCount = opts.agents ?? 3;
1266
1388
  const totalRounds = opts.rounds ?? 1;
1267
- const roles = opts.roles ?? ROLE_SETS3[agentCount] ?? ROLE_SETS3[3] ?? [];
1389
+ const roles = opts.roles ?? MEMORY_ROLE_SETS[agentCount] ?? MEMORY_ROLE_SETS[3] ?? [];
1268
1390
  const plannerModel = opts.plannerModel ?? opts.model;
1269
1391
  const workerModel = opts.workerModel ?? opts.model;
1270
1392
  if (!opts.quiet) {
@@ -1281,11 +1403,7 @@ async function execute8(pieces, args, opts) {
1281
1403
  const roundResults = await Promise.allSettled(
1282
1404
  roles.map(async (role) => {
1283
1405
  const prompt = buildWriterPrompt(role, topic, blackboard);
1284
- const text = await ask(prompt, {
1285
- model: workerModel,
1286
- maxTokens: opts.maxTokens,
1287
- thinkingLevel: opts.thinkingLevel
1288
- });
1406
+ const text = await ask(prompt, { ...opts, model: workerModel });
1289
1407
  return { role, text };
1290
1408
  })
1291
1409
  );
@@ -1307,40 +1425,21 @@ ${blackboard}
1307
1425
 
1308
1426
  Consolidate into a comprehensive, structured synthesis.`,
1309
1427
  {
1428
+ ...opts,
1310
1429
  model: plannerModel,
1311
- maxTokens: opts.maxTokens,
1312
1430
  thinkingLevel: "high",
1313
- system: CONSOLIDATOR_SYSTEM
1431
+ system: mergeSystem(opts.system, CONSOLIDATOR_SYSTEM)
1314
1432
  }
1315
1433
  );
1434
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1435
+ const qualityReview = await runQualityReview(topic, synthesis, opts);
1316
1436
  const t1 = Date.now();
1317
1437
  const summary = entries.map(
1318
1438
  (e) => `[${e.role}] Round ${e.round}: ${e.content.slice(0, 150)}${e.content.length > 150 ? "..." : ""}`
1319
1439
  ).join("\n");
1320
- return new MemoryOutput(summary, synthesis, entries, t0, t1);
1321
- }
1322
- function makeMemory(opts = {}) {
1323
- const merged = { ...defaults8, ...opts };
1324
- const fn = ((pieces, ...args) => {
1325
- if (!Array.isArray(pieces)) {
1326
- return makeMemory({ ...merged, ...pieces });
1327
- }
1328
- return new PatternPromise((resolve, reject) => {
1329
- execute8(pieces, args, merged).then(resolve, reject);
1330
- });
1331
- });
1332
- let _quiet;
1333
- Object.defineProperty(fn, "quiet", {
1334
- get() {
1335
- if (!_quiet) _quiet = makeMemory({ ...merged, quiet: true });
1336
- return _quiet;
1337
- },
1338
- enumerable: true,
1339
- configurable: true
1340
- });
1341
- return fn;
1440
+ return new MemoryOutput(summary, synthesis, entries, t0, t1, qualityReview);
1342
1441
  }
1343
- var \u039C = makeMemory();
1442
+ var \u039C = createPatternTag(defaults8, execute8);
1344
1443
 
1345
1444
  // src/patterns/nu.ts
1346
1445
  var defaults9 = {
@@ -1360,19 +1459,21 @@ var NuRole = class {
1360
1459
  goal;
1361
1460
  };
1362
1461
  var NuOutput = class extends PatternOutput {
1363
- constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime) {
1462
+ constructor(text, negotiatedRoles, workflow, workflowReasoning, roleResults, synthesis, startTime, endTime, qualityReview) {
1364
1463
  super(text, startTime, endTime);
1365
1464
  this.negotiatedRoles = negotiatedRoles;
1366
1465
  this.workflow = workflow;
1367
1466
  this.workflowReasoning = workflowReasoning;
1368
1467
  this.roleResults = roleResults;
1369
1468
  this.synthesis = synthesis;
1469
+ this.qualityReview = qualityReview;
1370
1470
  }
1371
1471
  negotiatedRoles;
1372
1472
  workflow;
1373
1473
  workflowReasoning;
1374
1474
  roleResults;
1375
1475
  synthesis;
1476
+ qualityReview;
1376
1477
  };
1377
1478
  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.
1378
1479
 
@@ -1410,6 +1511,7 @@ async function negotiateRoles(task, opts) {
1410
1511
  const response = await ask(`Task: ${task}
1411
1512
 
1412
1513
  ${prompt}`, {
1514
+ ...opts,
1413
1515
  model: opts.plannerModel ?? opts.model,
1414
1516
  maxTokens: 2048,
1415
1517
  thinkingLevel: "high"
@@ -1446,10 +1548,11 @@ ${rolesText}
1446
1548
 
1447
1549
  Determine the best execution strategy.`,
1448
1550
  {
1551
+ ...opts,
1449
1552
  model: opts.plannerModel ?? opts.model,
1450
1553
  maxTokens: 512,
1451
1554
  thinkingLevel: "high",
1452
- system: WORKFLOW_SYSTEM
1555
+ system: mergeSystem(opts.system, WORKFLOW_SYSTEM)
1453
1556
  }
1454
1557
  );
1455
1558
  const wfMatch = response.match(/WORKFLOW\s*:\s*(.+)/i);
@@ -1468,10 +1571,9 @@ async function executeRoles(roles, task, workflow, opts) {
1468
1571
  let context = task;
1469
1572
  for (const role of roles) {
1470
1573
  const output = await ask(context, {
1574
+ ...opts,
1471
1575
  model: workerModel,
1472
- maxTokens: opts.maxTokens,
1473
- thinkingLevel: opts.thinkingLevel,
1474
- system: EXECUTE_SYSTEM2(role)
1576
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
1475
1577
  });
1476
1578
  results.push({ role: role.name, output });
1477
1579
  context = `Previous output from ${role.name}:
@@ -1483,10 +1585,9 @@ Continue with: ${task}`;
1483
1585
  const parallelResults = await Promise.allSettled(
1484
1586
  roles.map(
1485
1587
  (role) => ask(task, {
1588
+ ...opts,
1486
1589
  model: workerModel,
1487
- maxTokens: opts.maxTokens,
1488
- thinkingLevel: opts.thinkingLevel,
1489
- system: EXECUTE_SYSTEM2(role)
1590
+ system: mergeSystem(opts.system, EXECUTE_SYSTEM2(role))
1490
1591
  }).then((text) => ({ role: role.name, output: text })).catch((err) => ({ role: role.name, output: `(failed: ${String(err)})` }))
1491
1592
  )
1492
1593
  );
@@ -1508,10 +1609,10 @@ ${resultsText}
1508
1609
 
1509
1610
  Synthesize a comprehensive final answer.`,
1510
1611
  {
1612
+ ...opts,
1511
1613
  model: opts.plannerModel ?? opts.model,
1512
- maxTokens: opts.maxTokens,
1513
1614
  thinkingLevel: "high",
1514
- system: SYNTHESIS_SYSTEM3
1615
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM3)
1515
1616
  }
1516
1617
  );
1517
1618
  }
@@ -1549,40 +1650,31 @@ async function execute9(pieces, args, opts) {
1549
1650
  }
1550
1651
  if (!opts.quiet) process.stderr.write(` \u2192 Executing (${workflow})...
1551
1652
  `);
1552
- const roleResults = await executeRoles(roles, task, workflow, opts);
1553
- if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing...\n");
1554
- const synthesis = await synthesize(task, roleResults, { ...opts, plannerModel });
1555
- const t1 = Date.now();
1556
- const summary = [
1557
- `Roles: ${roles.map((r) => r.name).join(", ")}`,
1558
- `Workflow: ${workflow} (${reasoning})`,
1559
- `Results: ${roleResults.length}/${roles.length} succeeded`,
1560
- `Synthesis: ${synthesis}`
1561
- ].join("\n\n");
1562
- return new NuOutput(summary, roles, workflow, reasoning, roleResults, synthesis, t0, t1);
1563
- }
1564
- function makeNu(opts = {}) {
1565
- const merged = { ...defaults9, ...opts };
1566
- const fn = ((pieces, ...args) => {
1567
- if (!Array.isArray(pieces)) {
1568
- return makeNu({ ...merged, ...pieces });
1569
- }
1570
- return new PatternPromise((resolve, reject) => {
1571
- execute9(pieces, args, merged).then(resolve, reject);
1572
- });
1573
- });
1574
- let _quiet;
1575
- Object.defineProperty(fn, "quiet", {
1576
- get() {
1577
- if (!_quiet) _quiet = makeNu({ ...merged, quiet: true });
1578
- return _quiet;
1579
- },
1580
- enumerable: true,
1581
- configurable: true
1582
- });
1583
- return fn;
1653
+ const roleResults = await executeRoles(roles, task, workflow, opts);
1654
+ if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing...\n");
1655
+ const synthesis = await synthesize(task, roleResults, { ...opts, plannerModel });
1656
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1657
+ const qualityReview = await runQualityReview(task, synthesis, opts);
1658
+ const t1 = Date.now();
1659
+ const summary = [
1660
+ `Roles: ${roles.map((r) => r.name).join(", ")}`,
1661
+ `Workflow: ${workflow} (${reasoning})`,
1662
+ `Results: ${roleResults.length}/${roles.length} succeeded`,
1663
+ `Synthesis: ${synthesis}`
1664
+ ].join("\n\n");
1665
+ return new NuOutput(
1666
+ summary,
1667
+ roles,
1668
+ workflow,
1669
+ reasoning,
1670
+ roleResults,
1671
+ synthesis,
1672
+ t0,
1673
+ t1,
1674
+ qualityReview
1675
+ );
1584
1676
  }
1585
- var \u039D = makeNu();
1677
+ var \u039D = createPatternTag(defaults9, execute9);
1586
1678
 
1587
1679
  // src/patterns/orchestrator.ts
1588
1680
  var defaults10 = {
@@ -1602,15 +1694,17 @@ var OrchestratorWorkerResult = class {
1602
1694
  success;
1603
1695
  };
1604
1696
  var OrchestratorOutput = class extends PatternOutput {
1605
- constructor(text, plan, synthesis, workerResults, startTime, endTime) {
1697
+ constructor(text, plan, synthesis, workerResults, startTime, endTime, qualityReview) {
1606
1698
  super(text, startTime, endTime);
1607
1699
  this.plan = plan;
1608
1700
  this.synthesis = synthesis;
1609
1701
  this.workerResults = workerResults;
1702
+ this.qualityReview = qualityReview;
1610
1703
  }
1611
1704
  plan;
1612
1705
  synthesis;
1613
1706
  workerResults;
1707
+ qualityReview;
1614
1708
  };
1615
1709
  var PLANNER_SYSTEM = `You are a senior architect and project planner. Given a high-level request, create a detailed execution plan.
1616
1710
 
@@ -1633,6 +1727,7 @@ async function execute10(pieces, args, opts) {
1633
1727
  const request = build(pieces, args);
1634
1728
  const t0 = Date.now();
1635
1729
  const workerCount = opts.workers ?? 3;
1730
+ const phases = [];
1636
1731
  const plannerModel = opts.plannerModel ?? opts.model;
1637
1732
  const workerModel = opts.workerModel ?? opts.model;
1638
1733
  if (!opts.quiet) {
@@ -1642,11 +1737,18 @@ async function execute10(pieces, args, opts) {
1642
1737
  );
1643
1738
  }
1644
1739
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
1740
+ const planStart = Date.now();
1645
1741
  const planText = await ask(request, {
1742
+ ...opts,
1646
1743
  model: plannerModel,
1647
- maxTokens: opts.maxTokens,
1648
1744
  thinkingLevel: "high",
1649
- system: PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount))
1745
+ system: mergeSystem(opts.system, PLANNER_SYSTEM.replace("{$workerCount}", String(workerCount)))
1746
+ });
1747
+ phases.push({
1748
+ phase: "plan",
1749
+ durationMs: Date.now() - planStart,
1750
+ description: `Generated plan with ${workerCount} workers`,
1751
+ modelUsed: plannerModel
1650
1752
  });
1651
1753
  const subTasks = [];
1652
1754
  const taskLines = planText.split("\n");
@@ -1670,27 +1772,37 @@ async function execute10(pieces, args, opts) {
1670
1772
  `);
1671
1773
  }
1672
1774
  }
1775
+ const planSummary = tasks.length > 0 ? `Execute ${tasks.length} sub-task(s) as planned?
1776
+ ${tasks.map((t, i) => `${i + 1}. ${t.slice(0, 80)}`).join("\n ")}` : `Execute the plan?`;
1777
+ if (!await confirmPhase(planSummary, opts)) {
1778
+ throw new Error("pizx/\u03A9: Execution cancelled by user.");
1779
+ }
1673
1780
  const workerResults = [];
1674
1781
  const concurrency = opts.concurrency ?? 3;
1782
+ const dispatchStart = Date.now();
1675
1783
  for (let i = 0; i < tasks.length; i += concurrency) {
1676
1784
  const batch = tasks.slice(i, i + concurrency);
1677
1785
  const batchResults = await Promise.allSettled(
1678
1786
  batch.map(
1679
- (task) => ask(task, {
1680
- model: workerModel,
1681
- maxTokens: opts.maxTokens,
1682
- thinkingLevel: opts.thinkingLevel,
1683
- system: WORKER_SYSTEM
1684
- }).then((text) => new OrchestratorWorkerResult(task, text, true)).catch((err) => new OrchestratorWorkerResult(task, String(err), false))
1787
+ (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))
1685
1788
  )
1686
1789
  );
1687
1790
  batchResults.forEach((r) => {
1688
1791
  if (r.status === "fulfilled") workerResults.push(r.value);
1689
1792
  });
1690
1793
  }
1794
+ const succeeded = workerResults.filter((w) => w.success).length;
1795
+ phases.push({
1796
+ phase: "dispatch",
1797
+ durationMs: Date.now() - dispatchStart,
1798
+ description: `Executed ${workerResults.length} worker(s), ${succeeded} succeeded`,
1799
+ modelUsed: workerModel,
1800
+ callCount: workerResults.length
1801
+ });
1691
1802
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
1692
1803
  const workerTexts = workerResults.map((wr, i) => `Task ${i + 1}: ${wr.task}
1693
1804
  Result: ${wr.output}`).join("\n\n");
1805
+ const synthStart = Date.now();
1694
1806
  const synthesis = await ask(
1695
1807
  `Original request:
1696
1808
  ${request}
@@ -1703,46 +1815,60 @@ ${workerTexts}
1703
1815
 
1704
1816
  Synthesize a final deliverable.`,
1705
1817
  {
1818
+ ...opts,
1706
1819
  model: plannerModel,
1707
- maxTokens: opts.maxTokens,
1708
1820
  thinkingLevel: "high",
1709
- system: SYNTHESIS_SYSTEM4
1821
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM4)
1710
1822
  }
1711
1823
  );
1824
+ phases.push({
1825
+ phase: "synthesize",
1826
+ durationMs: Date.now() - synthStart,
1827
+ description: "Synthesized worker results into final deliverable",
1828
+ modelUsed: plannerModel
1829
+ });
1830
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1831
+ const qualityStart = Date.now();
1832
+ const qualityReview = await runQualityReview(request, synthesis, opts);
1833
+ if (qualityReview) {
1834
+ phases.push({
1835
+ phase: "quality-review",
1836
+ durationMs: Date.now() - qualityStart,
1837
+ description: `Score: ${qualityReview.score.toFixed(2)} \u2014 ${qualityReview.assessment.slice(0, 60)}`,
1838
+ modelUsed: plannerModel
1839
+ });
1840
+ }
1712
1841
  const t1 = Date.now();
1842
+ const reviewSection = qualityReview ? `
1843
+
1844
+ Quality Review: ${qualityReview.score.toFixed(2)} \u2014 ${qualityReview.assessment}
1845
+ Recommendation: ${qualityReview.recommendation}` : "";
1713
1846
  const summary = `Plan:
1714
1847
  ${planText}
1715
1848
 
1716
1849
  Workers: ${workerResults.filter((w) => w.success).length}/${workerResults.length} succeeded
1717
1850
 
1718
1851
  Synthesis:
1719
- ${synthesis}`;
1720
- return new OrchestratorOutput(summary, planText, synthesis, workerResults, t0, t1);
1721
- }
1722
- function makeOrchestrator(opts = {}) {
1723
- const merged = { ...defaults10, ...opts };
1724
- const fn = ((pieces, ...args) => {
1725
- if (!Array.isArray(pieces)) {
1726
- return makeOrchestrator({ ...merged, ...pieces });
1727
- }
1728
- return new PatternPromise((resolve, reject) => {
1729
- execute10(pieces, args, merged).then(resolve, reject);
1730
- });
1731
- });
1732
- let _quiet;
1733
- Object.defineProperty(fn, "quiet", {
1734
- get() {
1735
- if (!_quiet) _quiet = makeOrchestrator({ ...merged, quiet: true });
1736
- return _quiet;
1737
- },
1738
- enumerable: true,
1739
- configurable: true
1740
- });
1741
- return fn;
1852
+ ${synthesis}${reviewSection}`;
1853
+ const output = new OrchestratorOutput(
1854
+ summary,
1855
+ planText,
1856
+ synthesis,
1857
+ workerResults,
1858
+ t0,
1859
+ t1,
1860
+ qualityReview
1861
+ );
1862
+ output.phaseLog = phases;
1863
+ return output;
1742
1864
  }
1743
- var \u03A9 = makeOrchestrator();
1865
+ var \u03A9 = createPatternTag(defaults10, execute10);
1744
1866
 
1745
1867
  // src/patterns/pipeline.ts
1868
+ function describeStage(stage) {
1869
+ if (typeof stage === "function") return "(composed pattern)";
1870
+ return stage;
1871
+ }
1746
1872
  var defaults11 = {
1747
1873
  maxTokens: 4096,
1748
1874
  thinkingLevel: "medium"
@@ -1758,13 +1884,15 @@ var PipelineStageResult = class {
1758
1884
  index;
1759
1885
  };
1760
1886
  var PipelineOutput = class extends PatternOutput {
1761
- constructor(text, finalOutput, stages, startTime, endTime) {
1887
+ constructor(text, finalOutput, stages, startTime, endTime, qualityReview) {
1762
1888
  super(text, startTime, endTime);
1763
1889
  this.finalOutput = finalOutput;
1764
1890
  this.stages = stages;
1891
+ this.qualityReview = qualityReview;
1765
1892
  }
1766
1893
  finalOutput;
1767
1894
  stages;
1895
+ qualityReview;
1768
1896
  };
1769
1897
  function parseStages(template, explicitStages, separator) {
1770
1898
  if (explicitStages && explicitStages.length > 0) return explicitStages;
@@ -1806,55 +1934,46 @@ async function execute11(pieces, args, opts) {
1806
1934
  `);
1807
1935
  }
1808
1936
  }
1937
+ const stageSummary = `Run ${stages.length} pipeline stage(s)?
1938
+ ${stages.map((s, i) => `${i + 1}. ${describeStage(s)}`).join("\n ")}`;
1939
+ if (!await confirmPhase(stageSummary, opts)) {
1940
+ throw new Error("pizx/\u039B: Execution cancelled by user.");
1941
+ }
1809
1942
  const stageResults = [];
1810
1943
  let currentInput = "";
1811
1944
  for (let i = 0; i < stages.length; i++) {
1812
1945
  const stage = stages[i];
1946
+ const stageLabel = describeStage(stage);
1813
1947
  const customPrompt = opts.stagePrompts?.[i];
1814
1948
  if (!opts.quiet)
1815
- process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stage.slice(0, 50)}...
1949
+ process.stderr.write(` \u2192 Stage ${i + 1}/${stages.length}: ${stageLabel.slice(0, 50)}...
1816
1950
  `);
1817
- const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
1818
- 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.`;
1819
- const output = await ask(prompt, {
1820
- model: workerModel,
1821
- maxTokens: opts.maxTokens,
1822
- thinkingLevel: opts.thinkingLevel,
1823
- system: systemMessage
1824
- });
1825
- stageResults.push(new PipelineStageResult(stage, output, i));
1951
+ let output;
1952
+ if (typeof stage === "function") {
1953
+ output = await stage(currentInput);
1954
+ } else {
1955
+ const prompt = customPrompt ?? generateStagePrompt(stage, currentInput, i === 0);
1956
+ 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.`;
1957
+ output = await ask(prompt, {
1958
+ ...opts,
1959
+ model: workerModel,
1960
+ system: mergeSystem(opts.system, systemMessage)
1961
+ });
1962
+ }
1963
+ stageResults.push(new PipelineStageResult(stageLabel, output, i));
1826
1964
  currentInput = output;
1827
1965
  }
1828
1966
  const t1 = Date.now();
1829
1967
  const finalOutput = currentInput;
1968
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
1969
+ const qualityReview = await runQualityReview(template, finalOutput, opts);
1830
1970
  const summary = stageResults.map(
1831
1971
  (sr) => `Stage ${sr.index + 1} (${sr.stage}):
1832
1972
  ${sr.output.slice(0, 200)}${sr.output.length > 200 ? "..." : ""}`
1833
1973
  ).join("\n\n");
1834
- return new PipelineOutput(summary, finalOutput, stageResults, t0, t1);
1835
- }
1836
- function makePipeline(opts = {}) {
1837
- const merged = { ...defaults11, ...opts };
1838
- const fn = ((pieces, ...args) => {
1839
- if (!Array.isArray(pieces)) {
1840
- return makePipeline({ ...merged, ...pieces });
1841
- }
1842
- return new PatternPromise((resolve, reject) => {
1843
- execute11(pieces, args, merged).then(resolve, reject);
1844
- });
1845
- });
1846
- let _quiet;
1847
- Object.defineProperty(fn, "quiet", {
1848
- get() {
1849
- if (!_quiet) _quiet = makePipeline({ ...merged, quiet: true });
1850
- return _quiet;
1851
- },
1852
- enumerable: true,
1853
- configurable: true
1854
- });
1855
- return fn;
1974
+ return new PipelineOutput(summary, finalOutput, stageResults, t0, t1, qualityReview);
1856
1975
  }
1857
- var \u039B = makePipeline();
1976
+ var \u039B = createPatternTag(defaults11, execute11);
1858
1977
 
1859
1978
  // src/patterns/ralph.ts
1860
1979
  import { createAgentSession } from "@earendil-works/pi-coding-agent";
@@ -1889,17 +2008,17 @@ async function executeWithTools(goal, opts) {
1889
2008
  });
1890
2009
  try {
1891
2010
  await session.sendUserMessage(goal);
1892
- const msgs = session.messages ?? [];
1893
- for (let i = msgs.length - 1; i >= 0; i--) {
1894
- if (msgs[i].role === "assistant") {
1895
- const c = msgs[i].content;
1896
- if (typeof c === "string") return c.trim();
1897
- if (Array.isArray(c)) {
1898
- const texts = c.filter(
1899
- (x) => x.type === "text" && typeof x.text === "string"
1900
- ).map((x) => x.text);
1901
- if (texts.length > 0) return texts.join("").trim();
1902
- }
2011
+ const messages = session.messages;
2012
+ for (let i = messages.length - 1; i >= 0; i--) {
2013
+ const msg = messages[i];
2014
+ if (msg?.role !== "assistant") continue;
2015
+ const c = "content" in msg ? msg.content : void 0;
2016
+ if (typeof c === "string") return c.trim();
2017
+ if (Array.isArray(c)) {
2018
+ const texts = c.filter(
2019
+ (x) => x.type === "text" && typeof x.text === "string"
2020
+ ).map((x) => x.text);
2021
+ if (texts.length > 0) return texts.join("").trim();
1903
2022
  }
1904
2023
  }
1905
2024
  return "(no assistant response)";
@@ -1934,10 +2053,9 @@ async function execute12(pieces, args, opts) {
1934
2053
  }
1935
2054
  if (!opts.quiet) process.stderr.write(" \u2192 Analyzing...\n");
1936
2055
  const analysis = await ask(currentGoal, {
2056
+ ...opts,
1937
2057
  model: plannerModel,
1938
- maxTokens: opts.maxTokens,
1939
- thinkingLevel: opts.thinkingLevel,
1940
- system: ANALYSIS_SYSTEM2
2058
+ system: mergeSystem(opts.system, ANALYSIS_SYSTEM2)
1941
2059
  });
1942
2060
  if (!opts.quiet) process.stderr.write(" \u2192 Planning...\n");
1943
2061
  const plan = await ask(
@@ -1946,12 +2064,7 @@ async function execute12(pieces, args, opts) {
1946
2064
  Analysis: ${analysis}
1947
2065
 
1948
2066
  Generate an implementation plan.`,
1949
- {
1950
- model: plannerModel,
1951
- maxTokens: opts.maxTokens,
1952
- thinkingLevel: opts.thinkingLevel,
1953
- system: PLAN_SYSTEM2
1954
- }
2067
+ { ...opts, model: plannerModel, system: mergeSystem(opts.system, PLAN_SYSTEM2) }
1955
2068
  );
1956
2069
  if (!opts.quiet) process.stderr.write(" \u2192 Executing...\n");
1957
2070
  const result = opts.useTools ? await executeWithTools(`Implement this plan:
@@ -1964,9 +2077,8 @@ Goal: ${currentGoal}`, {
1964
2077
  ${plan}
1965
2078
 
1966
2079
  Goal: ${currentGoal}`, {
1967
- model: workerModel,
1968
- maxTokens: opts.maxTokens,
1969
- thinkingLevel: opts.thinkingLevel
2080
+ ...opts,
2081
+ model: workerModel
1970
2082
  });
1971
2083
  if (!opts.quiet) process.stderr.write(" \u2192 Reviewing...\n");
1972
2084
  const review = await ask(`Plan:
@@ -1976,10 +2088,11 @@ Result:
1976
2088
  ${result}
1977
2089
 
1978
2090
  Review the implementation.`, {
2091
+ ...opts,
1979
2092
  model: plannerModel,
1980
2093
  maxTokens: 1024,
1981
2094
  thinkingLevel: "high",
1982
- system: REVIEW_SYSTEM
2095
+ system: mergeSystem(opts.system, REVIEW_SYSTEM)
1983
2096
  });
1984
2097
  const shouldContinue = review.includes("ITERATE") && !review.includes("DONE");
1985
2098
  iterations.push({
@@ -2015,28 +2128,7 @@ Original goal: ${goal}`;
2015
2128
  t1
2016
2129
  );
2017
2130
  }
2018
- function makeRalph(opts = {}) {
2019
- const merged = { ...defaults12, ...opts };
2020
- const fn = ((pieces, ...args) => {
2021
- if (!Array.isArray(pieces)) {
2022
- return makeRalph({ ...merged, ...pieces });
2023
- }
2024
- return new PatternPromise((resolve, reject) => {
2025
- execute12(pieces, args, merged).then(resolve, reject);
2026
- });
2027
- });
2028
- let _quiet;
2029
- Object.defineProperty(fn, "quiet", {
2030
- get() {
2031
- if (!_quiet) _quiet = makeRalph({ ...merged, quiet: true });
2032
- return _quiet;
2033
- },
2034
- enumerable: true,
2035
- configurable: true
2036
- });
2037
- return fn;
2038
- }
2039
- var \u03A1 = makeRalph();
2131
+ var \u03A1 = createPatternTag(defaults12, execute12);
2040
2132
 
2041
2133
  // src/patterns/subagent.ts
2042
2134
  var defaults13 = {
@@ -2056,13 +2148,15 @@ var SubagentResult = class {
2056
2148
  success;
2057
2149
  };
2058
2150
  var SubagentOutput = class extends PatternOutput {
2059
- constructor(text, synthesis, subResults, startTime, endTime) {
2151
+ constructor(text, synthesis, subResults, startTime, endTime, qualityReview) {
2060
2152
  super(text, startTime, endTime);
2061
2153
  this.synthesis = synthesis;
2062
2154
  this.subResults = subResults;
2155
+ this.qualityReview = qualityReview;
2063
2156
  }
2064
2157
  synthesis;
2065
2158
  subResults;
2159
+ qualityReview;
2066
2160
  };
2067
2161
  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.`;
2068
2162
  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.`;
@@ -2075,10 +2169,11 @@ ${task}
2075
2169
 
2076
2170
  Output a JSON array of strings.`,
2077
2171
  {
2172
+ ...opts,
2078
2173
  model: opts.model,
2079
2174
  maxTokens: 1024,
2080
2175
  thinkingLevel: "medium",
2081
- system: DECOMPOSE_SYSTEM
2176
+ system: mergeSystem(opts.system, DECOMPOSE_SYSTEM)
2082
2177
  }
2083
2178
  );
2084
2179
  try {
@@ -2099,6 +2194,7 @@ var SUBAGENT_SYSTEM = `You are a domain specialist. Complete your assigned sub-t
2099
2194
  async function execute13(pieces, args, opts) {
2100
2195
  const task = build(pieces, args);
2101
2196
  const t0 = Date.now();
2197
+ const phases = [];
2102
2198
  const plannerModel = opts.plannerModel ?? opts.model;
2103
2199
  const workerModel = opts.workerModel ?? opts.model;
2104
2200
  if (!opts.quiet) {
@@ -2108,7 +2204,14 @@ async function execute13(pieces, args, opts) {
2108
2204
  );
2109
2205
  }
2110
2206
  if (!opts.quiet) process.stderr.write(" \u2192 Decomposing task into sub-tasks...\n");
2207
+ const decomposeStart = Date.now();
2111
2208
  const subTasks = await decomposeTask(task, { ...opts, model: plannerModel });
2209
+ phases.push({
2210
+ phase: "decompose",
2211
+ durationMs: Date.now() - decomposeStart,
2212
+ description: `Decomposed into ${subTasks.length} sub-task(s)`,
2213
+ modelUsed: plannerModel
2214
+ });
2112
2215
  if (!opts.quiet) {
2113
2216
  process.stderr.write(` \u2192 ${subTasks.length} sub-task(s) identified:
2114
2217
  `);
@@ -2118,27 +2221,37 @@ async function execute13(pieces, args, opts) {
2118
2221
  `);
2119
2222
  }
2120
2223
  }
2224
+ const subTaskSummary = `Execute ${subTasks.length} sub-task(s)?
2225
+ ${subTasks.map((st, i) => `${i + 1}. ${st.slice(0, 80)}`).join("\n ")}`;
2226
+ if (!await confirmPhase(subTaskSummary, opts)) {
2227
+ throw new Error("pizx/\u03A3: Execution cancelled by user.");
2228
+ }
2121
2229
  const subResults = [];
2122
2230
  const concurrency = opts.concurrency ?? 4;
2231
+ const execStart = Date.now();
2123
2232
  for (let i = 0; i < subTasks.length; i += concurrency) {
2124
2233
  const batch = subTasks.slice(i, i + concurrency);
2125
2234
  const batchResults = await Promise.allSettled(
2126
2235
  batch.map(
2127
- (st) => ask(st, {
2128
- model: workerModel,
2129
- maxTokens: opts.maxTokens,
2130
- thinkingLevel: opts.thinkingLevel,
2131
- system: SUBAGENT_SYSTEM
2132
- }).then((text) => new SubagentResult(st, text, true)).catch((err) => new SubagentResult(st, String(err), false))
2236
+ (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))
2133
2237
  )
2134
2238
  );
2135
2239
  batchResults.forEach((r) => {
2136
2240
  if (r.status === "fulfilled") subResults.push(r.value);
2137
2241
  });
2138
2242
  }
2243
+ const succeeded = subResults.filter((r) => r.success).length;
2244
+ phases.push({
2245
+ phase: "execute",
2246
+ durationMs: Date.now() - execStart,
2247
+ description: `Executed ${subResults.length} sub-task(s), ${succeeded} succeeded`,
2248
+ modelUsed: workerModel,
2249
+ callCount: subResults.length
2250
+ });
2139
2251
  if (!opts.quiet) process.stderr.write(" \u2192 Synthesizing results...\n");
2140
2252
  const subResultsText = subResults.map((sr, i) => `Sub-task ${i + 1}: ${sr.subTask}
2141
2253
  Result: ${sr.text}`).join("\n\n");
2254
+ const synthStart = Date.now();
2142
2255
  const synthesis = await ask(
2143
2256
  `Original task:
2144
2257
  ${task}
@@ -2147,38 +2260,31 @@ Sub-task results:
2147
2260
  ${subResultsText}
2148
2261
 
2149
2262
  Synthesize a comprehensive answer.`,
2150
- {
2151
- model: plannerModel,
2152
- maxTokens: opts.maxTokens,
2153
- thinkingLevel: opts.thinkingLevel,
2154
- system: SYNTHESIS_SYSTEM5
2155
- }
2263
+ { ...opts, model: plannerModel, system: mergeSystem(opts.system, SYNTHESIS_SYSTEM5) }
2156
2264
  );
2157
- const t1 = Date.now();
2158
- return new SubagentOutput(synthesis, synthesis, subResults, t0, t1);
2159
- }
2160
- function makeSubagent(opts = {}) {
2161
- const merged = { ...defaults13, ...opts };
2162
- const fn = ((pieces, ...args) => {
2163
- if (!Array.isArray(pieces)) {
2164
- return makeSubagent({ ...merged, ...pieces });
2165
- }
2166
- return new PatternPromise((resolve, reject) => {
2167
- execute13(pieces, args, merged).then(resolve, reject);
2168
- });
2169
- });
2170
- let _quiet;
2171
- Object.defineProperty(fn, "quiet", {
2172
- get() {
2173
- if (!_quiet) _quiet = makeSubagent({ ...merged, quiet: true });
2174
- return _quiet;
2175
- },
2176
- enumerable: true,
2177
- configurable: true
2265
+ phases.push({
2266
+ phase: "synthesize",
2267
+ durationMs: Date.now() - synthStart,
2268
+ description: "Synthesized sub-agent results",
2269
+ modelUsed: plannerModel
2178
2270
  });
2179
- return fn;
2271
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2272
+ const qStart = Date.now();
2273
+ const qualityReview = await runQualityReview(task, synthesis, opts);
2274
+ if (qualityReview) {
2275
+ phases.push({
2276
+ phase: "quality-review",
2277
+ durationMs: Date.now() - qStart,
2278
+ description: `Score: ${qualityReview.score.toFixed(2)}`,
2279
+ modelUsed: plannerModel
2280
+ });
2281
+ }
2282
+ const t1 = Date.now();
2283
+ const output = new SubagentOutput(synthesis, synthesis, subResults, t0, t1, qualityReview);
2284
+ output.phaseLog = phases;
2285
+ return output;
2180
2286
  }
2181
- var \u03A3 = makeSubagent();
2287
+ var \u03A3 = createPatternTag(defaults13, execute13);
2182
2288
 
2183
2289
  // src/patterns/tau.ts
2184
2290
  var defaults14 = {
@@ -2202,15 +2308,17 @@ var ToolMediatedEntry = class {
2202
2308
  content;
2203
2309
  };
2204
2310
  var TauOutput = class extends PatternOutput {
2205
- constructor(text, entries, finalState, synthesis, startTime, endTime) {
2311
+ constructor(text, entries, finalState, synthesis, startTime, endTime, qualityReview) {
2206
2312
  super(text, startTime, endTime);
2207
2313
  this.entries = entries;
2208
2314
  this.finalState = finalState;
2209
2315
  this.synthesis = synthesis;
2316
+ this.qualityReview = qualityReview;
2210
2317
  }
2211
2318
  entries;
2212
2319
  finalState;
2213
2320
  synthesis;
2321
+ qualityReview;
2214
2322
  };
2215
2323
  var SCHEMA_SYSTEM = `You are a coordination architect. Given a task, design a shared structured context for agent collaboration.
2216
2324
 
@@ -2265,6 +2373,7 @@ async function defineSchema(task, opts) {
2265
2373
  const response = await ask(`Task: ${task}
2266
2374
 
2267
2375
  ${prompt}`, {
2376
+ ...opts,
2268
2377
  model: opts.plannerModel ?? opts.model,
2269
2378
  maxTokens: 1024,
2270
2379
  thinkingLevel: "high"
@@ -2317,10 +2426,9 @@ async function executeRound(roles, assignments, store, round, opts) {
2317
2426
  const systemPrompt = isWrite ? WRITE_SYSTEM(role, keysStr).replace("{store}", storeText) : UPDATE_SYSTEM(role, keysStr).replace("{store}", storeText);
2318
2427
  const task = isWrite ? `Write your initial findings to your assigned keys: ${keysStr}` : `Review the shared context and update your entries for keys: ${keysStr}`;
2319
2428
  const response = await ask(task, {
2429
+ ...opts,
2320
2430
  model: workerModel,
2321
- maxTokens: opts.maxTokens,
2322
- thinkingLevel: opts.thinkingLevel,
2323
- system: systemPrompt
2431
+ system: mergeSystem(opts.system, systemPrompt)
2324
2432
  });
2325
2433
  return { role, response };
2326
2434
  })
@@ -2360,10 +2468,10 @@ ${storeText}
2360
2468
 
2361
2469
  Consolidate into a comprehensive, well-structured synthesis.`,
2362
2470
  {
2471
+ ...opts,
2363
2472
  model: opts.plannerModel ?? opts.model,
2364
- maxTokens: opts.maxTokens,
2365
2473
  thinkingLevel: "high",
2366
- system: CONSOLIDATE_SYSTEM
2474
+ system: mergeSystem(opts.system, CONSOLIDATE_SYSTEM)
2367
2475
  }
2368
2476
  );
2369
2477
  }
@@ -2408,6 +2516,8 @@ async function execute14(pieces, args, opts) {
2408
2516
  }
2409
2517
  if (!opts.quiet) process.stderr.write(" \u2192 Consolidating store...\n");
2410
2518
  const synthesis = await consolidateStore(task, store, { ...opts, plannerModel });
2519
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2520
+ const qualityReview = await runQualityReview(task, synthesis, opts);
2411
2521
  const t1 = Date.now();
2412
2522
  const summary = [
2413
2523
  `Schema keys: ${keys.join(", ")}`,
@@ -2416,30 +2526,9 @@ async function execute14(pieces, args, opts) {
2416
2526
  `Entries: ${allEntries.length}`,
2417
2527
  `Synthesis: ${synthesis}`
2418
2528
  ].join("\n\n");
2419
- return new TauOutput(summary, allEntries, store, synthesis, t0, t1);
2420
- }
2421
- function makeTau(opts = {}) {
2422
- const merged = { ...defaults14, ...opts };
2423
- const fn = ((pieces, ...args) => {
2424
- if (!Array.isArray(pieces)) {
2425
- return makeTau({ ...merged, ...pieces });
2426
- }
2427
- return new PatternPromise((resolve, reject) => {
2428
- execute14(pieces, args, merged).then(resolve, reject);
2429
- });
2430
- });
2431
- let _quiet;
2432
- Object.defineProperty(fn, "quiet", {
2433
- get() {
2434
- if (!_quiet) _quiet = makeTau({ ...merged, quiet: true });
2435
- return _quiet;
2436
- },
2437
- enumerable: true,
2438
- configurable: true
2439
- });
2440
- return fn;
2529
+ return new TauOutput(summary, allEntries, store, synthesis, t0, t1, qualityReview);
2441
2530
  }
2442
- var \u03A4 = makeTau();
2531
+ var \u03A4 = createPatternTag(defaults14, execute14);
2443
2532
 
2444
2533
  // src/patterns/thread.ts
2445
2534
  var defaults15 = {
@@ -2448,27 +2537,6 @@ var defaults15 = {
2448
2537
  agents: 3,
2449
2538
  turns: 3
2450
2539
  };
2451
- var ROLE_SETS4 = {
2452
- 2: ["Proposer \u2014 advocate the best approach", "Critic \u2014 identify weaknesses and gaps"],
2453
- 3: [
2454
- "Proposer \u2014 suggest the best approach",
2455
- "Critic \u2014 identify weaknesses, risks, and missing pieces",
2456
- "Synthesizer \u2014 combine the best ideas into a practical plan"
2457
- ],
2458
- 4: [
2459
- "Proposer \u2014 advocate a bold solution",
2460
- "Critic \u2014 identify risks and weaknesses",
2461
- "Pragmatist \u2014 focus on practical implementation",
2462
- "Innovator \u2014 propose creative alternatives"
2463
- ],
2464
- 5: [
2465
- "Proposer",
2466
- "Critic",
2467
- "Pragmatist",
2468
- "Innovator",
2469
- "Devil's Advocate \u2014 challenge every assumption"
2470
- ]
2471
- };
2472
2540
  var ThreadMessage = class {
2473
2541
  constructor(role, turn, content) {
2474
2542
  this.role = role;
@@ -2480,13 +2548,15 @@ var ThreadMessage = class {
2480
2548
  content;
2481
2549
  };
2482
2550
  var ThreadOutput = class extends PatternOutput {
2483
- constructor(text, conclusion, messages, startTime, endTime) {
2551
+ constructor(text, conclusion, messages, startTime, endTime, qualityReview) {
2484
2552
  super(text, startTime, endTime);
2485
2553
  this.conclusion = conclusion;
2486
2554
  this.messages = messages;
2555
+ this.qualityReview = qualityReview;
2487
2556
  }
2488
2557
  conclusion;
2489
2558
  messages;
2559
+ qualityReview;
2490
2560
  };
2491
2561
  var THREAD_PROMPT = `You are an agent with the role: {role}.
2492
2562
  Engage in a multi-agent conversation about the topic.
@@ -2508,7 +2578,7 @@ async function execute15(pieces, args, opts) {
2508
2578
  const t0 = Date.now();
2509
2579
  const agentCount = opts.agents ?? 3;
2510
2580
  const maxTurns = opts.turns ?? 3;
2511
- const roles = opts.roles ?? ROLE_SETS4[agentCount] ?? ROLE_SETS4[3] ?? [];
2581
+ const roles = opts.roles ?? THREAD_ROLE_SETS[agentCount] ?? THREAD_ROLE_SETS[3] ?? [];
2512
2582
  const plannerModel = opts.plannerModel ?? opts.model;
2513
2583
  const workerModel = opts.workerModel ?? opts.model;
2514
2584
  if (!opts.quiet) {
@@ -2526,11 +2596,7 @@ async function execute15(pieces, args, opts) {
2526
2596
  for (let a = 0; a < roles.length; a++) {
2527
2597
  const role = roles[a] ?? `Agent ${a + 1}`;
2528
2598
  const prompt = buildThreadPrompt(role, thread);
2529
- const response = await ask(prompt, {
2530
- model: workerModel,
2531
- maxTokens: opts.maxTokens,
2532
- thinkingLevel: opts.thinkingLevel
2533
- });
2599
+ const response = await ask(prompt, { ...opts, model: workerModel });
2534
2600
  messages.push(new ThreadMessage(role, turn, response));
2535
2601
  thread += `
2536
2602
  [${role}] (Turn ${turn}): ${response}
@@ -2546,46 +2612,24 @@ ${thread}
2546
2612
 
2547
2613
  Synthesize a clear, actionable conclusion.`,
2548
2614
  {
2615
+ ...opts,
2549
2616
  model: plannerModel,
2550
- maxTokens: opts.maxTokens,
2551
2617
  thinkingLevel: "high",
2552
- system: SYNTHESIS_SYSTEM6
2618
+ system: mergeSystem(opts.system, SYNTHESIS_SYSTEM6)
2553
2619
  }
2554
2620
  );
2621
+ if (!opts.quiet && opts.qualityCheck) process.stderr.write(" \u2192 Quality review...\n");
2622
+ const qualityReview = await runQualityReview(topic, conclusion, opts);
2555
2623
  const t1 = Date.now();
2556
2624
  const summary = messages.map(
2557
2625
  (m) => `[${m.role}] Turn ${m.turn}: ${m.content.slice(0, 150)}${m.content.length > 150 ? "..." : ""}`
2558
2626
  ).join("\n");
2559
- return new ThreadOutput(summary, conclusion, messages, t0, t1);
2560
- }
2561
- function makeThread(opts = {}) {
2562
- const merged = { ...defaults15, ...opts };
2563
- const fn = ((pieces, ...args) => {
2564
- if (!Array.isArray(pieces)) {
2565
- return makeThread({ ...merged, ...pieces });
2566
- }
2567
- return new PatternPromise((resolve, reject) => {
2568
- execute15(pieces, args, merged).then(resolve, reject);
2569
- });
2570
- });
2571
- let _quiet;
2572
- Object.defineProperty(fn, "quiet", {
2573
- get() {
2574
- if (!_quiet) _quiet = makeThread({ ...merged, quiet: true });
2575
- return _quiet;
2576
- },
2577
- enumerable: true,
2578
- configurable: true
2579
- });
2580
- return fn;
2627
+ return new ThreadOutput(summary, conclusion, messages, t0, t1, qualityReview);
2581
2628
  }
2582
- var \u0398 = makeThread();
2629
+ var \u0398 = createPatternTag(defaults15, execute15);
2583
2630
 
2584
2631
  // src/pi.ts
2585
2632
  import {
2586
- getEnvApiKey as getEnvApiKey2,
2587
- getModels as getModels2,
2588
- getProviders as getProviders2,
2589
2633
  streamSimple
2590
2634
  } from "@earendil-works/pi-ai";
2591
2635
 
@@ -2603,10 +2647,28 @@ var PiOutput = class {
2603
2647
  events;
2604
2648
  startTime;
2605
2649
  endTime;
2650
+ /** Trace entry for this single LLM call. Populated by the π tag. */
2651
+ trace = [];
2606
2652
  /** Duration in milliseconds */
2607
2653
  get duration() {
2608
2654
  return this.endTime - this.startTime;
2609
2655
  }
2656
+ /** Total input tokens (convenience accessor) */
2657
+ get inputTokens() {
2658
+ return this.trace[0]?.inputTokens ?? 0;
2659
+ }
2660
+ /** Total output tokens (convenience accessor) */
2661
+ get outputTokens() {
2662
+ return this.trace[0]?.outputTokens ?? 0;
2663
+ }
2664
+ /** Total tokens (convenience accessor) */
2665
+ get totalTokens() {
2666
+ return this.trace[0]?.totalTokens ?? 0;
2667
+ }
2668
+ /** Total cost in USD (convenience accessor) */
2669
+ get totalCost() {
2670
+ return this.trace[0]?.cost ?? 0;
2671
+ }
2610
2672
  toString() {
2611
2673
  return this.text;
2612
2674
  }
@@ -2632,90 +2694,16 @@ var defaults16 = {
2632
2694
  quiet: false,
2633
2695
  maxTokens: 4096
2634
2696
  };
2635
- var _piSettings2;
2636
- function getPiDefaults2() {
2637
- if (_piSettings2 === void 0) {
2638
- _piSettings2 = isPiInstalled() ? loadPiSettings() : {};
2639
- }
2640
- return _piSettings2;
2641
- }
2642
- function allModels2() {
2643
- const result = [];
2644
- for (const p of getProviders2()) {
2645
- const ms = getModels2(p);
2646
- if (ms && ms.length > 0) result.push(...ms);
2647
- }
2648
- return result;
2649
- }
2650
- function getConfiguredProviders2() {
2651
- return getProviders2().filter((p) => getEnvApiKey2(p) !== void 0);
2652
- }
2653
- function configuredModels2() {
2654
- const configured = new Set(getConfiguredProviders2());
2655
- return allModels2().filter((m) => configured.has(m.provider));
2656
- }
2657
- function findModelById2(id) {
2658
- const all = allModels2();
2659
- if (id.includes("/")) {
2660
- const [provider, modelId] = id.split("/", 2);
2661
- return all.find(
2662
- (m) => m.provider === provider && (m.id === modelId || m.id.endsWith(`/${modelId}`))
2663
- );
2664
- }
2665
- return all.find((m) => m.id === id || m.id.endsWith(`/${id}`));
2666
- }
2667
- function pickModel2(preferred) {
2668
- if (preferred) {
2669
- const hit = findModelById2(preferred);
2670
- if (hit) return hit;
2671
- }
2672
- const settings = getPiDefaults2();
2673
- if (settings.defaultModel) {
2674
- const hit = findModelById2(settings.defaultModel);
2675
- if (hit) return hit;
2676
- }
2677
- if (settings.defaultProvider) {
2678
- const providerModels = getModels2(settings.defaultProvider);
2679
- if (providerModels && providerModels.length > 0) {
2680
- const configured = new Set(getConfiguredProviders2());
2681
- if (configured.has(settings.defaultProvider)) {
2682
- return providerModels[0];
2683
- }
2684
- }
2685
- }
2686
- const available = configuredModels2();
2687
- if (available.length > 0) {
2688
- const order2 = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
2689
- for (const id of order2) {
2690
- const m = available.find((m2) => m2.id.includes(id));
2691
- if (m) return m;
2692
- }
2693
- return available[0];
2694
- }
2695
- const models = allModels2();
2696
- if (models.length === 0) return void 0;
2697
- const order = ["claude-sonnet-4-5", "claude-sonnet-4", "gemini-2.5-flash", "gpt-4o-mini"];
2698
- for (const id of order) {
2699
- const m = models.find((m2) => m2.id.includes(id));
2700
- if (m) return m;
2701
- }
2702
- return models[0];
2703
- }
2704
- function build2(pieces, args) {
2705
- let s = "";
2706
- for (let i = 0; i < pieces.length; i++) {
2707
- s += pieces[i];
2708
- if (i < args.length) s += String(args[i]);
2709
- }
2710
- return s;
2711
- }
2712
2697
  function makeContext(pieces, args, opts) {
2698
+ const systemParts = [];
2699
+ if (opts.system) systemParts.push(opts.system);
2700
+ if (opts.appendSystemPrompt) systemParts.push(opts.appendSystemPrompt);
2713
2701
  return {
2714
- systemPrompt: opts.system,
2702
+ systemPrompt: systemParts.length > 0 ? systemParts.join("\n\n") : void 0,
2715
2703
  messages: [
2716
2704
  {
2717
2705
  role: "user",
2718
- content: build2(pieces, args),
2706
+ content: build(pieces, args),
2719
2707
  timestamp: Date.now()
2720
2708
  }
2721
2709
  ]
@@ -2724,25 +2712,52 @@ function makeContext(pieces, args, opts) {
2724
2712
  function makeOpts(opts) {
2725
2713
  return {
2726
2714
  maxTokens: opts.maxTokens,
2727
- reasoning: opts.thinkingLevel
2715
+ reasoning: opts.thinkingLevel,
2716
+ thinkingBudgets: opts.thinkingBudgets,
2717
+ timeoutMs: opts.timeoutMs,
2718
+ maxRetries: opts.maxRetries
2728
2719
  };
2729
2720
  }
2730
2721
  async function run(pieces, args, opts) {
2731
- const model = pickModel2(opts.model);
2722
+ const model = pickModel(opts.model);
2732
2723
  if (!model) throw new Error("pizx/\u03C0: No AI models configured. Run `pi auth login` first.");
2733
2724
  const t0 = Date.now();
2734
2725
  let text = "";
2735
- for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2736
- if (ev.type === "text_delta") {
2737
- text += ev.delta;
2738
- if (!opts.quiet) process.stdout.write(ev.delta);
2726
+ let traceEntry;
2727
+ try {
2728
+ for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2729
+ if (ev.type === "text_delta") {
2730
+ text += ev.delta;
2731
+ if (!opts.quiet) process.stdout.write(ev.delta);
2732
+ } else if (ev.type === "done") {
2733
+ const msg = ev.message;
2734
+ if (msg?.usage) {
2735
+ traceEntry = {
2736
+ call: 1,
2737
+ modelId: model.id,
2738
+ promptPreview: build(pieces, args).slice(0, 200),
2739
+ outputPreview: text.slice(0, 200),
2740
+ inputTokens: msg.usage.input,
2741
+ outputTokens: msg.usage.output,
2742
+ cacheReadTokens: msg.usage.cacheRead,
2743
+ cacheWriteTokens: msg.usage.cacheWrite,
2744
+ totalTokens: msg.usage.totalTokens,
2745
+ cost: msg.usage.cost.total,
2746
+ durationMs: Date.now() - t0
2747
+ };
2748
+ }
2749
+ }
2739
2750
  }
2751
+ } catch (err) {
2752
+ throw new Error(`pizx/\u03C0: AI generation failed: ${getErrorMessage(err)}`);
2740
2753
  }
2741
2754
  if (!opts.quiet && text) process.stdout.write("\n");
2742
- return new PiOutput(text.trim(), model.id, [], t0, Date.now());
2755
+ const output = new PiOutput(text.trim(), model.id, [], t0, Date.now());
2756
+ if (traceEntry) output.trace = [traceEntry];
2757
+ return output;
2743
2758
  }
2744
2759
  async function* runStream(pieces, args, opts) {
2745
- const model = pickModel2(opts.model);
2760
+ const model = pickModel(opts.model);
2746
2761
  if (!model) throw new Error("pizx/\u03C0: No AI models configured");
2747
2762
  for await (const ev of streamSimple(model, makeContext(pieces, args, opts), makeOpts(opts))) {
2748
2763
  if (ev.type === "text_delta") yield ev.delta;
@@ -2788,7 +2803,10 @@ function configurePi(opts) {
2788
2803
  }
2789
2804
 
2790
2805
  // src/pi-agent.ts
2791
- import { createAgentSession as createAgentSession2 } from "@earendil-works/pi-coding-agent";
2806
+ import {
2807
+ createAgentSession as createAgentSession2,
2808
+ DefaultResourceLoader
2809
+ } from "@earendil-works/pi-coding-agent";
2792
2810
  var _agentDefaults = {
2793
2811
  quiet: false,
2794
2812
  maxTurns: 10
@@ -2820,24 +2838,49 @@ var AgentOutput = class {
2820
2838
  var AgentPromise = class extends Promise {
2821
2839
  };
2822
2840
  var _sharedSession = null;
2823
- async function getSession(opts) {
2824
- if (_sharedSession && !opts.model) return _sharedSession;
2825
- const result = await createAgentSession2({
2826
- cwd: opts.cwd,
2827
- thinkingLevel: opts.thinkingLevel,
2828
- tools: opts.tools,
2829
- excludeTools: opts.excludeTools
2841
+ function createLoader(opts) {
2842
+ const hasSystem = opts.system !== void 0;
2843
+ const hasAppend = opts.appendSystemPrompt !== void 0;
2844
+ const hasSkills = opts.skills && opts.skills.length > 0;
2845
+ if (!hasSystem && !hasAppend && !hasSkills) return void 0;
2846
+ return new DefaultResourceLoader({
2847
+ cwd: opts.cwd ?? process.cwd(),
2848
+ agentDir: "",
2849
+ systemPrompt: opts.system,
2850
+ appendSystemPrompt: opts.appendSystemPrompt ? [opts.appendSystemPrompt] : void 0
2830
2851
  });
2831
- _sharedSession = result.session;
2832
- return _sharedSession;
2833
2852
  }
2834
- function build3(pieces, args) {
2835
- let s = "";
2836
- for (let i = 0; i < pieces.length; i++) {
2837
- s += pieces[i];
2838
- if (i < args.length) s += String(args[i]);
2853
+ async function getSession(opts) {
2854
+ if (_sharedSession && !opts.model) return _sharedSession;
2855
+ try {
2856
+ const loader = createLoader(opts);
2857
+ if (opts.skills && opts.skills.length > 0 && loader) {
2858
+ const skillMap = await loadSkillContents(opts.skills);
2859
+ const skillPaths = [];
2860
+ for (const [name] of skillMap) {
2861
+ for (const base of SKILL_PATHS) {
2862
+ skillPaths.push({
2863
+ path: `${base}/${name}`,
2864
+ metadata: { source: "pizx", scope: "project", origin: "top-level" }
2865
+ });
2866
+ }
2867
+ }
2868
+ if (skillPaths.length > 0) {
2869
+ loader.extendResources({ skillPaths });
2870
+ }
2871
+ }
2872
+ const result = await createAgentSession2({
2873
+ cwd: opts.cwd,
2874
+ thinkingLevel: opts.thinkingLevel,
2875
+ tools: opts.tools,
2876
+ excludeTools: opts.excludeTools,
2877
+ resourceLoader: loader
2878
+ });
2879
+ _sharedSession = result.session;
2880
+ return _sharedSession;
2881
+ } catch (err) {
2882
+ throw new Error(`pizx/\u03A0: Failed to create agent session: ${getErrorMessage(err)}`);
2839
2883
  }
2840
- return s.trim();
2841
2884
  }
2842
2885
  function getMessageText(msg) {
2843
2886
  const content = msg.content;
@@ -2859,7 +2902,7 @@ function getLastAssistantText(session) {
2859
2902
  return "";
2860
2903
  }
2861
2904
  async function execute16(pieces, args, opts) {
2862
- const prompt = build3(pieces, args);
2905
+ const prompt = build(pieces, args);
2863
2906
  const session = await getSession(opts);
2864
2907
  const t0 = Date.now();
2865
2908
  if (!opts.quiet) {
@@ -2870,15 +2913,12 @@ async function execute16(pieces, args, opts) {
2870
2913
  await session.sendUserMessage(prompt);
2871
2914
  const t1 = Date.now();
2872
2915
  const text = getLastAssistantText(session);
2873
- const turnCount = session.messages.filter(
2874
- (m) => m.role === "assistant"
2875
- ).length;
2916
+ const turnCount = session.messages.filter((m) => m.role === "assistant").length;
2876
2917
  return new AgentOutput(text || "(no assistant response)", turnCount, t0, t1);
2877
2918
  } catch (err) {
2878
2919
  const t1 = Date.now();
2879
- const msg = err instanceof Error ? err.message : String(err);
2880
2920
  console.error("\u03A0 error:", err);
2881
- return new AgentOutput(`\u03A0 error: ${msg}`, 0, t0, t1);
2921
+ return new AgentOutput(`\u03A0 error: ${getErrorMessage(err)}`, 0, t0, t1);
2882
2922
  }
2883
2923
  }
2884
2924
  function makeAgent(opts = {}) {
@@ -2938,6 +2978,7 @@ export {
2938
2978
  PipelineOutput,
2939
2979
  PipelineStageResult,
2940
2980
  RalphOutput,
2981
+ SKILL_PATHS,
2941
2982
  SubagentOutput,
2942
2983
  SubagentResult,
2943
2984
  ThreadMessage,
@@ -2945,6 +2986,9 @@ export {
2945
2986
  closeAgent,
2946
2987
  configureAgent,
2947
2988
  configurePi,
2989
+ createPatternTag,
2990
+ loadSkillContent,
2991
+ loadSkillContents,
2948
2992
  \u0391,
2949
2993
  \u0392,
2950
2994
  \u0393,