@ilya-lesikov/pi-pi 0.8.0 → 0.10.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.
Files changed (114) hide show
  1. package/3p/pi-ask-user/index.ts +71 -124
  2. package/3p/pi-ask-user/single-select-layout.ts +3 -14
  3. package/3p/pi-subagents/package.json +13 -7
  4. package/3p/pi-subagents/src/agent-manager.ts +243 -79
  5. package/3p/pi-subagents/src/agent-runner.ts +501 -365
  6. package/3p/pi-subagents/src/agent-types.ts +46 -57
  7. package/3p/pi-subagents/src/cross-extension-rpc.ts +52 -46
  8. package/3p/pi-subagents/src/custom-agents.ts +30 -6
  9. package/3p/pi-subagents/src/default-agents.ts +25 -43
  10. package/3p/pi-subagents/src/enabled-models.ts +180 -0
  11. package/3p/pi-subagents/src/index.ts +599 -839
  12. package/3p/pi-subagents/src/model-resolver.ts +26 -7
  13. package/3p/pi-subagents/src/output-file.ts +21 -2
  14. package/3p/pi-subagents/src/prompts.ts +17 -3
  15. package/3p/pi-subagents/src/schedule-store.ts +153 -0
  16. package/3p/pi-subagents/src/schedule.ts +365 -0
  17. package/3p/pi-subagents/src/settings.ts +261 -0
  18. package/3p/pi-subagents/src/skill-loader.ts +77 -54
  19. package/3p/pi-subagents/src/status-note.ts +25 -0
  20. package/3p/pi-subagents/src/types.ts +97 -6
  21. package/3p/pi-subagents/src/ui/agent-widget.ts +94 -21
  22. package/3p/pi-subagents/src/ui/conversation-viewer.ts +147 -35
  23. package/3p/pi-subagents/src/ui/schedule-menu.ts +104 -0
  24. package/3p/pi-subagents/src/ui/viewer-keys.ts +39 -0
  25. package/3p/pi-subagents/src/usage.ts +60 -0
  26. package/3p/pi-subagents/src/worktree.ts +49 -20
  27. package/extensions/orchestrator/agents/advisor.ts +13 -8
  28. package/extensions/orchestrator/agents/brainstorm-reviewer.ts +8 -3
  29. package/extensions/orchestrator/agents/code-reviewer.ts +29 -8
  30. package/extensions/orchestrator/agents/constraints.test.ts +65 -1
  31. package/extensions/orchestrator/agents/constraints.ts +73 -2
  32. package/extensions/orchestrator/agents/deep-debugger.ts +13 -8
  33. package/extensions/orchestrator/agents/explore.ts +12 -6
  34. package/extensions/orchestrator/agents/librarian.ts +13 -5
  35. package/extensions/orchestrator/agents/plan-reviewer.ts +8 -3
  36. package/extensions/orchestrator/agents/planner.ts +8 -3
  37. package/extensions/orchestrator/agents/pools.test.ts +56 -0
  38. package/extensions/orchestrator/agents/prompts.test.ts +52 -10
  39. package/extensions/orchestrator/agents/registry.test.ts +245 -0
  40. package/extensions/orchestrator/agents/registry.ts +145 -10
  41. package/extensions/orchestrator/agents/reviewer.ts +14 -8
  42. package/extensions/orchestrator/agents/task.ts +12 -6
  43. package/extensions/orchestrator/agents/tool-routing.ts +213 -69
  44. package/extensions/orchestrator/agents/wait-for-completion.test.ts +171 -0
  45. package/extensions/orchestrator/ai-comment-cleanup.test.ts +89 -0
  46. package/extensions/orchestrator/ai-comment-cleanup.ts +73 -0
  47. package/extensions/orchestrator/ast-search.test.ts +124 -0
  48. package/extensions/orchestrator/billing-spoof.test.ts +67 -0
  49. package/extensions/orchestrator/billing-spoof.ts +95 -0
  50. package/extensions/orchestrator/cbm.more.test.ts +358 -0
  51. package/extensions/orchestrator/command-handlers.test.ts +47 -0
  52. package/extensions/orchestrator/command-handlers.ts +49 -27
  53. package/extensions/orchestrator/commands.test.ts +15 -2
  54. package/extensions/orchestrator/commands.ts +1 -1
  55. package/extensions/orchestrator/config.test.ts +129 -2
  56. package/extensions/orchestrator/config.ts +115 -19
  57. package/extensions/orchestrator/context.test.ts +46 -0
  58. package/extensions/orchestrator/context.ts +34 -5
  59. package/extensions/orchestrator/custom-footer.test.ts +105 -0
  60. package/extensions/orchestrator/custom-footer.ts +22 -20
  61. package/extensions/orchestrator/doctor.more.test.ts +315 -0
  62. package/extensions/orchestrator/doctor.ts +6 -2
  63. package/extensions/orchestrator/event-handlers.more.test.ts +561 -0
  64. package/extensions/orchestrator/event-handlers.test.ts +411 -10
  65. package/extensions/orchestrator/event-handlers.ts +738 -349
  66. package/extensions/orchestrator/exa.more.test.ts +118 -0
  67. package/extensions/orchestrator/flant-infra.more.test.ts +326 -0
  68. package/extensions/orchestrator/flant-infra.test.ts +127 -0
  69. package/extensions/orchestrator/flant-infra.ts +113 -39
  70. package/extensions/orchestrator/index.test.ts +76 -0
  71. package/extensions/orchestrator/index.ts +2 -0
  72. package/extensions/orchestrator/integration.test.ts +488 -74
  73. package/extensions/orchestrator/model-registry.test.ts +2 -1
  74. package/extensions/orchestrator/model-registry.ts +12 -2
  75. package/extensions/orchestrator/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
  76. package/extensions/orchestrator/orchestrator.test.ts +67 -0
  77. package/extensions/orchestrator/orchestrator.ts +165 -85
  78. package/extensions/orchestrator/phases/brainstorm.test.ts +30 -1
  79. package/extensions/orchestrator/phases/brainstorm.ts +53 -12
  80. package/extensions/orchestrator/phases/implementation.ts +2 -0
  81. package/extensions/orchestrator/phases/machine.test.ts +53 -1
  82. package/extensions/orchestrator/phases/machine.ts +15 -2
  83. package/extensions/orchestrator/phases/planning.test.ts +47 -1
  84. package/extensions/orchestrator/phases/planning.ts +18 -3
  85. package/extensions/orchestrator/phases/review-task.test.ts +20 -0
  86. package/extensions/orchestrator/phases/review-task.ts +47 -14
  87. package/extensions/orchestrator/phases/review.test.ts +36 -0
  88. package/extensions/orchestrator/phases/review.ts +62 -10
  89. package/extensions/orchestrator/phases/spawn-blocking.test.ts +1 -0
  90. package/extensions/orchestrator/phases/verdict.ts +6 -5
  91. package/extensions/orchestrator/plannotator-open-failure.test.ts +67 -0
  92. package/extensions/orchestrator/plannotator.test.ts +38 -1
  93. package/extensions/orchestrator/plannotator.ts +50 -3
  94. package/extensions/orchestrator/pp-menu.leaves.test.ts +590 -0
  95. package/extensions/orchestrator/pp-menu.more.test.ts +524 -0
  96. package/extensions/orchestrator/pp-menu.test.ts +314 -1
  97. package/extensions/orchestrator/pp-menu.ts +937 -450
  98. package/extensions/orchestrator/pp-state-tools.test.ts +80 -0
  99. package/extensions/orchestrator/pp-state-tools.ts +65 -0
  100. package/extensions/orchestrator/rate-limit-fallback.more.test.ts +241 -0
  101. package/extensions/orchestrator/rate-limit-fallback.test.ts +20 -1
  102. package/extensions/orchestrator/rate-limit-fallback.ts +12 -0
  103. package/extensions/orchestrator/review-files.test.ts +26 -0
  104. package/extensions/orchestrator/review-files.ts +3 -0
  105. package/extensions/orchestrator/state.test.ts +82 -1
  106. package/extensions/orchestrator/state.ts +110 -23
  107. package/extensions/orchestrator/suppress-pierre-theme-spam.test.ts +56 -0
  108. package/extensions/orchestrator/suppress-pierre-theme-spam.ts +45 -0
  109. package/extensions/orchestrator/transition-controller.test.ts +100 -0
  110. package/extensions/orchestrator/transition-controller.ts +61 -3
  111. package/extensions/orchestrator/validate-artifacts.ts +1 -1
  112. package/package.json +6 -2
  113. package/scripts/test-3p.sh +52 -0
  114. package/AGENTS.md +0 -28
@@ -0,0 +1,180 @@
1
+ /**
2
+ * Reads `enabledModels` from pi's settings (global `<agentDir>/settings.json`
3
+ * + project-local `<cwd>/.pi/settings.json`, project wins) and resolves
4
+ * entries to concrete `provider/modelId` keys for scope validation.
5
+ *
6
+ * **Project overrides global**, mirroring pi's own `SettingsManager`
7
+ * deep-merge behavior and matching the precedence we use for our own
8
+ * `subagents.json` settings (see `src/settings.ts:loadSettings`). If
9
+ * project file has `enabledModels` set, it wholly replaces global's
10
+ * (array fields are replaced, not concatenated).
11
+ *
12
+ * **Limited subset of upstream's resolveModelScope.** We support exact
13
+ * `provider/modelId` matching only. Upstream (pi-coding-agent's
14
+ * `core/model-resolver.ts`) additionally supports glob patterns
15
+ * (`*sonnet*`, `anthropic/*`), bare model IDs without provider, and
16
+ * thinking-level suffixes (`provider/*:high`). Those forms are silently
17
+ * ignored here.
18
+ *
19
+ * In practice, pi's `/scoped-models` picker writes exact `provider/modelId`
20
+ * entries, so the limitation is invisible for users who configure scope
21
+ * through pi's UI. Hand-edited settings using globs or bare IDs will
22
+ * produce an empty allowed set (scope check becomes a no-op).
23
+ *
24
+ * Example:
25
+ * enabledModels = ["anthropic/claude-sonnet-4-6", "anthropic/claude-opus-4-6"]
26
+ * → resolves to { "anthropic/claude-sonnet-4-6", "anthropic/claude-opus-4-6" }
27
+ */
28
+
29
+ import { existsSync, readFileSync, statSync } from "node:fs";
30
+ import { join } from "node:path";
31
+ import { getAgentDir } from "@earendil-works/pi-coding-agent";
32
+ import type { ModelEntry } from "./model-resolver.js";
33
+
34
+ /** Minimal registry shape — only the methods resolveEnabledModels actually calls. */
35
+ export interface ModelRegistryRef {
36
+ getAll(): unknown[];
37
+ getAvailable?(): unknown[];
38
+ }
39
+
40
+ /** Paths to pi's settings.json files: [project, global] (project takes precedence). */
41
+ function settingsPaths(cwd: string): [project: string, global: string] {
42
+ return [
43
+ join(cwd, ".pi", "settings.json"),
44
+ join(getAgentDir(), "settings.json"),
45
+ ];
46
+ }
47
+
48
+ /** Read `enabledModels` from a single settings.json file. Undefined when missing or absent. */
49
+ function readField(path: string): string[] | undefined {
50
+ if (!existsSync(path)) return undefined;
51
+ try {
52
+ const raw = JSON.parse(readFileSync(path, "utf-8"));
53
+ if (Array.isArray(raw?.enabledModels)) return raw.enabledModels as string[];
54
+ } catch {
55
+ /* corrupt file — silent */
56
+ }
57
+ return undefined;
58
+ }
59
+
60
+ /**
61
+ * Read enabledModels from pi's settings — project-local overrides global.
62
+ * Mirrors pi's SettingsManager deep-merge for the `enabledModels` field
63
+ * (and matches our own loadSettings precedence in src/settings.ts).
64
+ * Returns undefined when neither file has the field.
65
+ */
66
+ export function readEnabledModels(cwd: string): string[] | undefined {
67
+ const [project, global] = settingsPaths(cwd);
68
+ return readField(project) ?? readField(global);
69
+ }
70
+
71
+ /**
72
+ * Resolve enabledModels patterns → Set<"provider/modelId"> (lowercase keys).
73
+ *
74
+ * Only exact `provider/modelId` patterns are matched (case-insensitive).
75
+ * Patterns without a slash, with glob characters, or with a `:thinking`
76
+ * suffix are silently dropped. See module-level docstring for rationale.
77
+ *
78
+ * Cache: keyed on JSON.stringify(patterns) + mtime/size of *both*
79
+ * project and global settings.json files. Re-resolves when either file
80
+ * changes or the patterns argument differs.
81
+ *
82
+ * Returns undefined when no patterns are provided or no patterns match
83
+ * (scope check becomes a no-op at the call site).
84
+ */
85
+
86
+ // Module-level cache — invalidated when either settings.json changes or patterns differ.
87
+ let cachedAllowed: Set<string> | undefined;
88
+ let cachedHash = "";
89
+ let cachedPatternsKey = "";
90
+
91
+ /** mtime+size hash of one file, or "missing" if absent. */
92
+ function hashOf(path: string): string {
93
+ try {
94
+ const s = statSync(path);
95
+ return `${s.mtimeMs}-${s.size}`;
96
+ } catch {
97
+ return "missing";
98
+ }
99
+ }
100
+
101
+ export function resolveEnabledModels(
102
+ patterns: string[] | undefined,
103
+ registry: ModelRegistryRef,
104
+ cwd: string = process.cwd(),
105
+ ): Set<string> | undefined {
106
+ // Fast path: check cache (stat both project and global settings.json files)
107
+ const patternsKey = JSON.stringify(patterns);
108
+ const [project, global] = settingsPaths(cwd);
109
+ const fileHash = `${hashOf(project)};${hashOf(global)}`;
110
+
111
+ if (fileHash === cachedHash && patternsKey === cachedPatternsKey) {
112
+ return cachedAllowed;
113
+ }
114
+
115
+ // Cache miss — resolve
116
+ if (!patterns || patterns.length === 0) {
117
+ cachedHash = fileHash;
118
+ cachedPatternsKey = patternsKey;
119
+ cachedAllowed = undefined;
120
+ return undefined;
121
+ }
122
+
123
+ const available = (registry.getAvailable?.() ?? registry.getAll()) as ModelEntry[];
124
+ const allowed = new Set<string>();
125
+
126
+ for (const pattern of patterns) {
127
+ const trimmed = pattern.trim();
128
+ if (!trimmed) continue; // skip empty/whitespace
129
+ resolveExact(trimmed, available, allowed);
130
+ }
131
+
132
+ const result = allowed.size > 0 ? allowed : undefined;
133
+ cachedHash = fileHash;
134
+ cachedPatternsKey = patternsKey;
135
+ cachedAllowed = result;
136
+ return result;
137
+ }
138
+
139
+
140
+
141
+ /**
142
+ * True when `model` is in the allowed set. Centralizes the key format
143
+ * (`provider/id` lowercase) so callers don't have to reproduce it —
144
+ * both set-building (resolveExact) and lookup go through `modelKey`.
145
+ */
146
+ export function isModelInScope(
147
+ model: { provider: string; id: string },
148
+ allowed: Set<string>,
149
+ ): boolean {
150
+ return allowed.has(modelKey(model));
151
+ }
152
+
153
+ /** Canonical lowercase `provider/id` key for the allowed set. */
154
+ function modelKey(model: { provider: string; id: string }): string {
155
+ return `${model.provider}/${model.id}`.toLowerCase();
156
+ }
157
+
158
+ /**
159
+ * Resolve exact model pattern. Example: "google/gemma-4-31b-it".
160
+ */
161
+ function resolveExact(
162
+ pattern: string,
163
+ available: ModelEntry[],
164
+ allowed: Set<string>,
165
+ ): void {
166
+ // "provider/modelId" — exact (colon is part of id, not split)
167
+ const slashIdx = pattern.indexOf("/");
168
+ if (slashIdx === -1) return; // bare modelId not supported
169
+
170
+ const provider = pattern.slice(0, slashIdx).toLowerCase();
171
+ const modelId = pattern.slice(slashIdx + 1).toLowerCase();
172
+ const exact = available.find(
173
+ m => m.provider.toLowerCase() === provider && m.id.toLowerCase() === modelId,
174
+ );
175
+ if (exact) {
176
+ allowed.add(modelKey(exact));
177
+ }
178
+ }
179
+
180
+