@oh-my-pi/subagents 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,7 +5,7 @@ Task delegation system with specialized subagents for pi.
5
5
  ## Installation
6
6
 
7
7
  ```bash
8
- omp install oh-my-pi/plugins/subagents
8
+ omp install @oh-my-pi/subagents
9
9
  ```
10
10
 
11
11
  ## Contents
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oh-my-pi/subagents",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Task delegation system with specialized subagents (task, planner, explore, reviewer)",
5
5
  "keywords": [
6
6
  "omp-plugin",
@@ -29,7 +29,7 @@
29
29
  import * as fs from "node:fs";
30
30
  import * as os from "node:os";
31
31
  import * as path from "node:path";
32
- import { spawn } from "node:child_process";
32
+ import { spawn, spawnSync } from "node:child_process";
33
33
  import * as readline from "node:readline";
34
34
  import { Type } from "@sinclair/typebox";
35
35
  import { StringEnum, type AgentToolUpdateCallback } from "@mariozechner/pi-ai";
@@ -40,6 +40,76 @@ import type {
40
40
  ToolAPI,
41
41
  } from "@mariozechner/pi-coding-agent";
42
42
 
43
+ /** Cache for available models (refreshed once per tool factory instantiation) */
44
+ let cachedModels: string[] | null = null;
45
+
46
+ /**
47
+ * Get available models from `pi --list-models`.
48
+ * Caches the result for performance.
49
+ */
50
+ function getAvailableModels(): string[] {
51
+ if (cachedModels !== null) return cachedModels;
52
+
53
+ try {
54
+ const result = spawnSync("pi", ["--list-models"], {
55
+ encoding: "utf-8",
56
+ timeout: 5000,
57
+ });
58
+
59
+ if (result.status !== 0 || !result.stdout) {
60
+ cachedModels = [];
61
+ return cachedModels;
62
+ }
63
+
64
+ // Parse output: skip header line, extract model column
65
+ const lines = result.stdout.trim().split("\n");
66
+ cachedModels = lines
67
+ .slice(1) // Skip header
68
+ .map((line) => {
69
+ const parts = line.trim().split(/\s+/);
70
+ return parts[1]; // Model name is second column
71
+ })
72
+ .filter(Boolean);
73
+
74
+ return cachedModels;
75
+ } catch {
76
+ cachedModels = [];
77
+ return cachedModels;
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Resolve a fuzzy model pattern to an actual model name.
83
+ * Supports comma-separated patterns (e.g., "gpt, opus").
84
+ * Returns the first match found, or undefined if no match.
85
+ */
86
+ function resolveModelPattern(
87
+ pattern: string,
88
+ availableModels?: string[],
89
+ ): string | undefined {
90
+ if (!pattern || pattern === "default") return undefined;
91
+
92
+ const models = availableModels ?? getAvailableModels();
93
+ if (models.length === 0) {
94
+ // Fallback: return pattern as-is if we can't get available models
95
+ return pattern;
96
+ }
97
+
98
+ // Split by comma, try each pattern in order
99
+ const patterns = pattern
100
+ .split(",")
101
+ .map((p) => p.trim().toLowerCase())
102
+ .filter(Boolean);
103
+
104
+ for (const p of patterns) {
105
+ const match = models.find((m) => m.toLowerCase().includes(p));
106
+ if (match) return match;
107
+ }
108
+
109
+ // No match found - return first pattern as-is (let pi handle the error)
110
+ return patterns[0];
111
+ }
112
+
43
113
  const MAX_OUTPUT_LINES = 5000;
44
114
  const MAX_OUTPUT_BYTES = 500_000;
45
115
  const MAX_PARALLEL_TASKS = 32;
@@ -448,10 +518,13 @@ async function runSingleAgent(
448
518
 
449
519
  const args: string[] = ["-p", "--no-session", "--mode", "json"];
450
520
 
451
- const modelToUse = options?.model ?? agent.model;
521
+ // "default" means no model override - use pi's configured default
522
+ const modelOverride = options?.model;
523
+ const rawModel =
524
+ modelOverride === "default" ? undefined : (modelOverride ?? agent.model);
525
+ const modelToUse = rawModel ? resolveModelPattern(rawModel) : undefined;
452
526
  if (modelToUse) {
453
- // Use --models for pattern matching (takes first match from available models)
454
- args.push("--models", modelToUse);
527
+ args.push("--model", modelToUse);
455
528
  }
456
529
 
457
530
  if (agent.tools && agent.tools.length > 0) {
@@ -679,7 +752,7 @@ const TaskItem = Type.Object({
679
752
  model: Type.Optional(
680
753
  Type.String({
681
754
  description:
682
- "Override the model for this task (takes precedence over agent's default model)",
755
+ 'Override the model for this task (takes precedence over agent\'s default model), or "default" to use pi\'s default',
683
756
  }),
684
757
  ),
685
758
  });
@@ -803,7 +876,7 @@ function buildDescription(pi: ToolAPI): string {
803
876
  " concurrent)",
804
877
  );
805
878
  lines.push(
806
- ' - model: (optional) Override the agent\'s default model using pattern matching (e.g., "sonnet", "haiku", "gpt-4o")',
879
+ ' - model: (optional) Override the agent\'s default model with fuzzy matching (e.g., "sonnet", "codex", "5.2"). Supports comma-separated fallbacks: "gpt, opus" tries gpt first, then opus. Use "default" for pi\'s default model',
807
880
  );
808
881
  lines.push(
809
882
  "- context: (optional) Shared context string prepended to all task prompts - use this to avoid repeating instructions",