@piagent/platform 1.0.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 (159) hide show
  1. package/.gitattributes +7 -0
  2. package/AGENTS.md +31 -0
  3. package/CHANGELOG.md +296 -0
  4. package/CODE_OF_CONDUCT.md +36 -0
  5. package/CONTRIBUTING.md +66 -0
  6. package/LICENSE +21 -0
  7. package/README.md +588 -0
  8. package/SECURITY.md +38 -0
  9. package/adapters/backend-api/profile.json +88 -0
  10. package/adapters/be-readonly-fe/profile.json +120 -0
  11. package/adapters/data/profile.json +88 -0
  12. package/adapters/devops/profile.json +92 -0
  13. package/adapters/docs/profile.json +74 -0
  14. package/adapters/fullstack/profile.json +94 -0
  15. package/adapters/generic/profile.json +95 -0
  16. package/adapters/mobile/profile.json +87 -0
  17. package/adapters/node-typescript/profile.json +76 -0
  18. package/adapters/python/profile.json +75 -0
  19. package/adapters/web-frontend/profile.json +101 -0
  20. package/catalog/capabilities.json +206 -0
  21. package/docs/architecture.md +64 -0
  22. package/docs/auto-delegation-policy.md +192 -0
  23. package/docs/capability-packs.md +234 -0
  24. package/docs/command-reference-vietnamese.md +484 -0
  25. package/docs/context-window-policy.md +85 -0
  26. package/docs/distribution-standard.md +229 -0
  27. package/docs/herdr-workflow.md +58 -0
  28. package/docs/mcp-and-tools.md +316 -0
  29. package/docs/memory-policy.md +164 -0
  30. package/docs/model-options.md +165 -0
  31. package/docs/oauth-providers.md +53 -0
  32. package/docs/operator-manual-vietnamese.md +1080 -0
  33. package/docs/package-architecture-notes.md +101 -0
  34. package/docs/project-adapters.md +174 -0
  35. package/docs/project-onboarding.md +135 -0
  36. package/docs/publishing-for-teams.md +64 -0
  37. package/docs/quality-benchmark.md +74 -0
  38. package/docs/quickstart-vietnamese.md +350 -0
  39. package/docs/release-install-policy.md +223 -0
  40. package/docs/runtime-harness-standard.md +118 -0
  41. package/docs/runtime-policy-design.md +132 -0
  42. package/docs/runtime-quality-baseline.md +257 -0
  43. package/docs/security-redaction-benchmark.md +56 -0
  44. package/docs/security-threat-model.md +66 -0
  45. package/docs/subagent-orchestration-capabilities.md +104 -0
  46. package/docs/subagents-and-multiagent.md +346 -0
  47. package/docs/task-implementation-contract.md +200 -0
  48. package/docs/team-onboarding.md +333 -0
  49. package/docs/usage-observability.md +130 -0
  50. package/docs/vercel-docs-site.md +115 -0
  51. package/docs/workflow-recipes.md +125 -0
  52. package/docs-site/assets/piagent-logo.svg +35 -0
  53. package/docs-site/favicon.svg +18 -0
  54. package/docs-site/index.html +1613 -0
  55. package/docs-site/vercel.json +24 -0
  56. package/evals/fixtures/action-proposal.invalid.json +34 -0
  57. package/evals/fixtures/action-proposal.valid.json +34 -0
  58. package/evals/fixtures/capability-pack.invalid.json +123 -0
  59. package/evals/fixtures/capability-pack.valid.json +123 -0
  60. package/evals/fixtures/capability-recipe.invalid.json +81 -0
  61. package/evals/fixtures/capability-recipe.valid.json +82 -0
  62. package/evals/fixtures/context-index.invalid.json +25 -0
  63. package/evals/fixtures/context-index.valid.json +25 -0
  64. package/evals/fixtures/eval-scenario.invalid.json +40 -0
  65. package/evals/fixtures/eval-scenario.valid.json +45 -0
  66. package/evals/fixtures/project-profile.invalid.json +88 -0
  67. package/evals/fixtures/project-profile.valid.json +95 -0
  68. package/evals/fixtures/task-contract.invalid.json +73 -0
  69. package/evals/fixtures/task-contract.valid.json +74 -0
  70. package/evals/golden/enforcement-decisions.json +329 -0
  71. package/evals/scenarios/capability-resolution.json +45 -0
  72. package/package.json +132 -0
  73. package/packages/piagent-core/README.md +147 -0
  74. package/packages/piagent-core/capabilities/capability-core.js +1023 -0
  75. package/packages/piagent-core/capabilities/capability-sources.js +265 -0
  76. package/packages/piagent-core/extensions/guard-io.js +45 -0
  77. package/packages/piagent-core/extensions/guard-shell-analysis.ts +558 -0
  78. package/packages/piagent-core/extensions/guard-types.ts +406 -0
  79. package/packages/piagent-core/extensions/piagent-guard.ts +5760 -0
  80. package/packages/piagent-core/extensions/policy-core.js +1070 -0
  81. package/packages/piagent-core/extensions/redaction-core.js +4 -0
  82. package/packages/piagent-core/extensions/runtime-evidence.js +215 -0
  83. package/packages/piagent-core/package.json +46 -0
  84. package/packages/piagent-core/policies/base-policy.json +249 -0
  85. package/packages/piagent-core/prompts/be-to-fe.md +82 -0
  86. package/packages/piagent-core/prompts/commit.md +27 -0
  87. package/packages/piagent-core/prompts/discuss.md +35 -0
  88. package/packages/piagent-core/prompts/memory-policy.md +52 -0
  89. package/packages/piagent-core/prompts/model-options.md +68 -0
  90. package/packages/piagent-core/prompts/onboard-project.md +147 -0
  91. package/packages/piagent-core/prompts/piagent-commands.md +68 -0
  92. package/packages/piagent-core/prompts/plan.md +35 -0
  93. package/packages/piagent-core/prompts/platform-improve.md +69 -0
  94. package/packages/piagent-core/prompts/pr.md +26 -0
  95. package/packages/piagent-core/prompts/review.md +38 -0
  96. package/packages/piagent-core/prompts/scout.md +50 -0
  97. package/packages/piagent-core/prompts/task.md +69 -0
  98. package/packages/piagent-core/security/sensitive-data.js +187 -0
  99. package/packages/piagent-core/skills/piagent-ops/SKILL.md +44 -0
  100. package/packages/piagent-core/skills/piagent-source-cache/SKILL.md +59 -0
  101. package/packages/piagent-core/skills/piagent-source-cache/checkout-source-repo.sh +144 -0
  102. package/packages/piagent-core/subagents/piagent-oracle.md +29 -0
  103. package/packages/piagent-core/subagents/piagent-planner.md +46 -0
  104. package/packages/piagent-core/subagents/piagent-reviewer.md +48 -0
  105. package/packages/piagent-core/subagents/piagent-scout.md +37 -0
  106. package/packages/piagent-core/subagents/piagent-worker.md +44 -0
  107. package/packs/engineering-base/pack.json +123 -0
  108. package/packs/engineering-base/recipes/bounded-change.json +82 -0
  109. package/packs/web-delivery/pack.json +72 -0
  110. package/packs/web-delivery/recipes/verified-web-change.json +57 -0
  111. package/schemas/action-proposal.schema.json +90 -0
  112. package/schemas/capability-pack.schema.json +190 -0
  113. package/schemas/capability-recipe.schema.json +72 -0
  114. package/schemas/context-index.schema.json +258 -0
  115. package/schemas/eval-scenario.schema.json +67 -0
  116. package/schemas/project-profile.schema.json +555 -0
  117. package/schemas/task-contract.schema.json +314 -0
  118. package/scripts/audit-runtime-host.sh +78 -0
  119. package/scripts/bootstrap-local.sh +25 -0
  120. package/scripts/capability-catalog.mjs +290 -0
  121. package/scripts/check-runtime-advisories.mjs +114 -0
  122. package/scripts/configure-mcp.sh +301 -0
  123. package/scripts/configure-model-scope.sh +143 -0
  124. package/scripts/configure-subagents.sh +241 -0
  125. package/scripts/import-agent-instructions.mjs +199 -0
  126. package/scripts/init-project.sh +369 -0
  127. package/scripts/install-global.sh +532 -0
  128. package/scripts/link-project.sh +25 -0
  129. package/scripts/migrate-project-state.mjs +176 -0
  130. package/scripts/pi-auto.sh +79 -0
  131. package/scripts/pi-model-catalog.sh +154 -0
  132. package/scripts/pi-session-stats.sh +135 -0
  133. package/scripts/piagent-cli.mjs +53 -0
  134. package/scripts/profile-doctor.sh +238 -0
  135. package/scripts/quality-benchmark.sh +185 -0
  136. package/scripts/redaction-benchmark.mjs +227 -0
  137. package/scripts/runtime-policy-smoke.sh +48 -0
  138. package/scripts/setup.sh +425 -0
  139. package/scripts/team-doctor.sh +461 -0
  140. package/scripts/verify-local.sh +540 -0
  141. package/scripts/verify-release-identity.mjs +85 -0
  142. package/scripts/verify-vercel-link.mjs +64 -0
  143. package/templates/global/mcp.json +9 -0
  144. package/templates/global/settings.json +37 -0
  145. package/templates/project/.mcp.json +9 -0
  146. package/templates/project/.pi/context-index.json +25 -0
  147. package/templates/project/.pi/gitignore.template +22 -0
  148. package/templates/project/.pi/mcp.json +9 -0
  149. package/templates/project/.pi/memory/MEMORY.md +11 -0
  150. package/templates/project/.pi/memory/memory_summary.md +9 -0
  151. package/templates/project/.pi/piagent-profile.json +107 -0
  152. package/templates/project/.pi/project-context.md +30 -0
  153. package/templates/project/.pi/settings.json +17 -0
  154. package/templates/project/.pi/task-contract.template.json +74 -0
  155. package/templates/project/.pi/tech-context/README.md +7 -0
  156. package/templates/project/.pi/tech-stack.json +10 -0
  157. package/templates/project/AGENTS.md +34 -0
  158. package/templates/project/REVIEW_GUIDELINES.md +23 -0
  159. package/types/pi-runtime-shims.d.ts +13 -0
@@ -0,0 +1,301 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'USAGE'
6
+ Usage:
7
+ scripts/configure-mcp.sh [options]
8
+
9
+ Purpose:
10
+ Merge a safe MCP baseline into a standard MCP config file for Pi and other MCP clients.
11
+ Secrets are never written directly; configs reference environment variables.
12
+
13
+ Options:
14
+ --scope <global|pi-global|project|pi-project>
15
+ Config target (default: global)
16
+ --project <path> Project path for project/pi-project scopes (default: current directory)
17
+ --config <path> Explicit config path; overrides --scope/--project
18
+ --preset <minimal|docs|browser|github|design|design-local|web|core|popular|all>
19
+ MCP preset to merge (default: core)
20
+ --replace Replace existing definitions for servers in the preset
21
+ --dry-run Print the merged JSON without writing
22
+ --list Print available presets/servers
23
+ -h, --help
24
+
25
+ Recommended presets:
26
+ core Context7 docs + Chrome DevTools + GitHub
27
+ popular core + Playwright + Figma remote MCP
28
+ all popular + Figma desktop/local MCP
29
+
30
+ Examples:
31
+ piagent-mcp --preset core --scope global --replace
32
+ piagent-mcp --preset popular --scope project --project /path/to/repo
33
+ piagent-mcp --preset design-local --scope project
34
+
35
+ After writing config:
36
+ pi
37
+ /mcp
38
+ /mcp setup
39
+ /mcp reconnect
40
+ USAGE
41
+ }
42
+
43
+ PLATFORM_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
44
+ SCOPE="global"
45
+ PROJECT_PATH="$PWD"
46
+ CONFIG_PATH=""
47
+ PRESET="core"
48
+ REPLACE=false
49
+ DRY_RUN=false
50
+ LIST=false
51
+
52
+ while [[ $# -gt 0 ]]; do
53
+ case "$1" in
54
+ --scope)
55
+ SCOPE="${2:-}"
56
+ shift 2
57
+ ;;
58
+ --project)
59
+ PROJECT_PATH="${2:-}"
60
+ shift 2
61
+ ;;
62
+ --config)
63
+ CONFIG_PATH="${2:-}"
64
+ shift 2
65
+ ;;
66
+ --preset)
67
+ PRESET="${2:-}"
68
+ shift 2
69
+ ;;
70
+ --replace)
71
+ REPLACE=true
72
+ shift
73
+ ;;
74
+ --dry-run)
75
+ DRY_RUN=true
76
+ shift
77
+ ;;
78
+ --list)
79
+ LIST=true
80
+ shift
81
+ ;;
82
+ -h|--help)
83
+ usage
84
+ exit 0
85
+ ;;
86
+ *)
87
+ echo "Unknown option: $1" >&2
88
+ usage >&2
89
+ exit 2
90
+ ;;
91
+ esac
92
+ done
93
+
94
+ if [[ -z "$PRESET" ]]; then
95
+ echo "FAIL: --preset cannot be empty" >&2
96
+ exit 2
97
+ fi
98
+
99
+ if [[ -z "$CONFIG_PATH" ]]; then
100
+ case "$SCOPE" in
101
+ global)
102
+ CONFIG_PATH="${XDG_CONFIG_HOME:-"${HOME}/.config"}/mcp/mcp.json"
103
+ ;;
104
+ pi-global)
105
+ CONFIG_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/mcp.json"
106
+ ;;
107
+ project)
108
+ CONFIG_PATH="$PROJECT_PATH/.mcp.json"
109
+ ;;
110
+ pi-project)
111
+ CONFIG_PATH="$PROJECT_PATH/.pi/mcp.json"
112
+ ;;
113
+ *)
114
+ echo "FAIL: unsupported scope: $SCOPE" >&2
115
+ usage >&2
116
+ exit 2
117
+ ;;
118
+ esac
119
+ fi
120
+
121
+ node --input-type=module - "$CONFIG_PATH" "$PRESET" "$REPLACE" "$DRY_RUN" "$LIST" <<'NODE'
122
+ import fs from "node:fs";
123
+ import path from "node:path";
124
+
125
+ const [configPath, presetName, replaceRaw, dryRunRaw, listRaw] = process.argv.slice(2);
126
+ const replace = replaceRaw === "true";
127
+ const dryRun = dryRunRaw === "true";
128
+ const listOnly = listRaw === "true";
129
+
130
+ const baselineSettings = {
131
+ toolPrefix: "server",
132
+ directTools: false,
133
+ idleTimeout: 10,
134
+ outputGuard: true
135
+ };
136
+
137
+ const servers = {
138
+ context7: {
139
+ description: "Official Context7 MCP server for up-to-date library/framework docs.",
140
+ config: {
141
+ command: "npx",
142
+ args: ["-y", "@upstash/context7-mcp@3.2.4"],
143
+ env: {
144
+ CONTEXT7_API_KEY: "${CONTEXT7_API_KEY}"
145
+ },
146
+ lifecycle: "lazy",
147
+ directTools: false
148
+ }
149
+ },
150
+ "chrome-devtools": {
151
+ description: "Chrome DevTools MCP for runtime browser inspection, console logs, screenshots, and performance checks.",
152
+ config: {
153
+ command: "npx",
154
+ args: ["-y", "chrome-devtools-mcp@1.6.0", "--no-performance-crux"],
155
+ env: {
156
+ CHROME_DEVTOOLS_MCP_NO_USAGE_STATISTICS: "1",
157
+ CHROME_DEVTOOLS_MCP_NO_UPDATE_CHECKS: "1"
158
+ },
159
+ lifecycle: "lazy",
160
+ directTools: false
161
+ }
162
+ },
163
+ playwright: {
164
+ description: "Playwright MCP for browser automation and UI verification workflows.",
165
+ config: {
166
+ command: "npx",
167
+ args: ["-y", "@playwright/mcp@0.0.78"],
168
+ lifecycle: "lazy",
169
+ directTools: false
170
+ }
171
+ },
172
+ github: {
173
+ description: "Official GitHub MCP server via Docker. Requires Docker and GITHUB_PERSONAL_ACCESS_TOKEN when used.",
174
+ config: {
175
+ command: "docker",
176
+ args: [
177
+ "run",
178
+ "-i",
179
+ "--rm",
180
+ "-e",
181
+ "GITHUB_PERSONAL_ACCESS_TOKEN",
182
+ "-e",
183
+ "GITHUB_READ_ONLY=1",
184
+ "-e",
185
+ "GITHUB_LOCKDOWN_MODE=1",
186
+ "ghcr.io/github/github-mcp-server@sha256:2b0c48b070f61e9d3969269ead600f62d00fb237b60ac849ef3d166ee7de9ad3"
187
+ ],
188
+ env: {
189
+ GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_PERSONAL_ACCESS_TOKEN}"
190
+ },
191
+ lifecycle: "lazy",
192
+ directTools: false
193
+ }
194
+ },
195
+ figma: {
196
+ description: "Figma remote MCP server. Requires Figma OAuth from the MCP panel when first used.",
197
+ config: {
198
+ url: "https://mcp.figma.com/mcp",
199
+ auth: "oauth",
200
+ lifecycle: "lazy",
201
+ directTools: false
202
+ }
203
+ },
204
+ "figma-desktop": {
205
+ description: "Figma desktop/local MCP server. Requires Figma desktop Dev Mode MCP enabled.",
206
+ config: {
207
+ url: "http://127.0.0.1:3845/mcp",
208
+ lifecycle: "lazy",
209
+ directTools: false
210
+ }
211
+ }
212
+ };
213
+
214
+ const presets = {
215
+ minimal: [],
216
+ docs: ["context7"],
217
+ browser: ["chrome-devtools", "playwright"],
218
+ github: ["github"],
219
+ design: ["figma"],
220
+ "design-local": ["figma-desktop"],
221
+ web: ["context7", "chrome-devtools", "playwright"],
222
+ core: ["context7", "chrome-devtools", "github"],
223
+ popular: ["context7", "chrome-devtools", "playwright", "github", "figma"],
224
+ all: ["context7", "chrome-devtools", "playwright", "github", "figma", "figma-desktop"]
225
+ };
226
+
227
+ if (listOnly) {
228
+ console.log(JSON.stringify({
229
+ presets,
230
+ servers: Object.fromEntries(Object.entries(servers).map(([name, value]) => [name, value.description]))
231
+ }, null, 2));
232
+ process.exit(0);
233
+ }
234
+
235
+ if (!Object.hasOwn(presets, presetName)) {
236
+ console.error(`FAIL: unknown preset: ${presetName}`);
237
+ console.error(`Available presets: ${Object.keys(presets).join(", ")}`);
238
+ process.exit(2);
239
+ }
240
+
241
+ let current = {};
242
+ if (fs.existsSync(configPath)) {
243
+ try {
244
+ current = JSON.parse(fs.readFileSync(configPath, "utf8"));
245
+ } catch (error) {
246
+ console.error(`FAIL: cannot parse existing MCP config: ${configPath}`);
247
+ console.error(error instanceof Error ? error.message : String(error));
248
+ process.exit(1);
249
+ }
250
+ }
251
+
252
+ if (!current || typeof current !== "object" || Array.isArray(current)) current = {};
253
+ if (!current.settings || typeof current.settings !== "object" || Array.isArray(current.settings)) current.settings = {};
254
+ if (!current.mcpServers || typeof current.mcpServers !== "object" || Array.isArray(current.mcpServers)) current.mcpServers = {};
255
+
256
+ current.settings = {
257
+ ...baselineSettings,
258
+ ...current.settings
259
+ };
260
+
261
+ const added = [];
262
+ const kept = [];
263
+ const replaced = [];
264
+ for (const name of presets[presetName]) {
265
+ if (!Object.hasOwn(servers, name)) {
266
+ throw new Error(`Preset ${presetName} references missing server ${name}`);
267
+ }
268
+ if (Object.hasOwn(current.mcpServers, name)) {
269
+ if (replace) {
270
+ current.mcpServers[name] = servers[name].config;
271
+ replaced.push(name);
272
+ } else {
273
+ kept.push(name);
274
+ }
275
+ } else {
276
+ current.mcpServers[name] = servers[name].config;
277
+ added.push(name);
278
+ }
279
+ }
280
+
281
+ const output = `${JSON.stringify(current, null, 2)}\n`;
282
+ const report = {
283
+ configPath,
284
+ preset: presetName,
285
+ dryRun,
286
+ replace,
287
+ added,
288
+ kept,
289
+ replaced,
290
+ serverCount: Object.keys(current.mcpServers).length
291
+ };
292
+
293
+ if (dryRun) {
294
+ console.log(JSON.stringify(report, null, 2));
295
+ console.log(output);
296
+ } else {
297
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
298
+ fs.writeFileSync(configPath, output);
299
+ console.log(JSON.stringify(report, null, 2));
300
+ }
301
+ NODE
@@ -0,0 +1,143 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'USAGE'
6
+ Usage:
7
+ scripts/configure-model-scope.sh [options]
8
+
9
+ Options:
10
+ --preset <full|codex|claude> Model scope to write (default: full)
11
+ --default-model <provider/model[:thinking]>
12
+ Default model (default: openai-codex/gpt-5.5:xhigh)
13
+ --settings <path> Pi settings file (default: ~/.pi/agent/settings.json)
14
+ --dry-run Print resulting settings JSON without writing
15
+ -h, --help
16
+
17
+ Purpose:
18
+ Configure Pi's native model selector/cycling settings:
19
+ - defaultProvider
20
+ - defaultModel
21
+ - defaultThinkingLevel
22
+ - enabledModels
23
+
24
+ After this, users select models with Pi's built-in UI:
25
+ /model or Ctrl+L selector
26
+ /scoped-models edit cycling scope
27
+ Ctrl+P cycle scoped models
28
+ Shift+Tab cycle thinking level
29
+ USAGE
30
+ }
31
+
32
+ PRESET="full"
33
+ DEFAULT_MODEL="openai-codex/gpt-5.5:xhigh"
34
+ SETTINGS_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/settings.json"
35
+ DRY_RUN=false
36
+
37
+ while [[ $# -gt 0 ]]; do
38
+ case "$1" in
39
+ --preset)
40
+ PRESET="${2:-}"
41
+ shift 2
42
+ ;;
43
+ --default-model)
44
+ DEFAULT_MODEL="${2:-}"
45
+ shift 2
46
+ ;;
47
+ --settings)
48
+ SETTINGS_PATH="${2:-}"
49
+ shift 2
50
+ ;;
51
+ --dry-run)
52
+ DRY_RUN=true
53
+ shift
54
+ ;;
55
+ -h|--help)
56
+ usage
57
+ exit 0
58
+ ;;
59
+ *)
60
+ echo "Unknown argument: $1" >&2
61
+ usage >&2
62
+ exit 2
63
+ ;;
64
+ esac
65
+ done
66
+
67
+ case "$PRESET" in
68
+ full|codex|claude) ;;
69
+ *)
70
+ echo "FAIL: --preset must be full, codex, or claude" >&2
71
+ exit 2
72
+ ;;
73
+ esac
74
+
75
+ node --input-type=module - "$SETTINGS_PATH" "$PRESET" "$DEFAULT_MODEL" "$DRY_RUN" <<'NODE'
76
+ import fs from "node:fs";
77
+ import path from "node:path";
78
+
79
+ const [settingsPath, preset, defaultModelInput, dryRunRaw] = process.argv.slice(2);
80
+ const dryRun = dryRunRaw === "true";
81
+
82
+ const codexModels = [
83
+ "openai-codex/gpt-5.3-codex-spark:minimal",
84
+ "openai-codex/gpt-5.4-mini:minimal",
85
+ "openai-codex/gpt-5.4:xhigh",
86
+ "openai-codex/gpt-5.5:xhigh",
87
+ "openai-codex/gpt-5.6-luna:xhigh",
88
+ "openai-codex/gpt-5.6-sol:xhigh",
89
+ "openai-codex/gpt-5.6-terra:xhigh"
90
+ ];
91
+
92
+ const claudeModels = [
93
+ "anthropic/claude-haiku-4-5:low",
94
+ "anthropic/claude-sonnet-4-5:high",
95
+ "anthropic/claude-sonnet-4-6:max",
96
+ "anthropic/claude-sonnet-5:xhigh",
97
+ "anthropic/claude-opus-4-5:xhigh",
98
+ "anthropic/claude-opus-4-6:max",
99
+ "anthropic/claude-opus-4-7:xhigh",
100
+ "anthropic/claude-opus-4-8:xhigh",
101
+ "anthropic/claude-fable-5:xhigh"
102
+ ];
103
+
104
+ const enabledModels = preset === "codex"
105
+ ? codexModels
106
+ : preset === "claude"
107
+ ? claudeModels
108
+ : [...codexModels, ...claudeModels];
109
+
110
+ function parseDefaultModel(input) {
111
+ const match = input.match(/^([^/]+)\/([^:]+)(?::(.+))?$/);
112
+ if (!match) {
113
+ throw new Error(`--default-model must look like provider/model[:thinking], got: ${input}`);
114
+ }
115
+ return {
116
+ provider: match[1],
117
+ model: match[2],
118
+ thinking: match[3] || "xhigh"
119
+ };
120
+ }
121
+
122
+ const parsedDefault = parseDefaultModel(defaultModelInput);
123
+ const settings = fs.existsSync(settingsPath)
124
+ ? JSON.parse(fs.readFileSync(settingsPath, "utf8"))
125
+ : {};
126
+
127
+ settings.defaultProvider = parsedDefault.provider;
128
+ settings.defaultModel = parsedDefault.model;
129
+ settings.defaultThinkingLevel = parsedDefault.thinking;
130
+ settings.enabledModels = enabledModels;
131
+
132
+ const output = `${JSON.stringify(settings, null, 2)}\n`;
133
+ if (dryRun) {
134
+ process.stdout.write(output);
135
+ } else {
136
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
137
+ fs.writeFileSync(settingsPath, output);
138
+ console.log(`Configured Pi model scope: ${settingsPath}`);
139
+ console.log(` preset: ${preset}`);
140
+ console.log(` default: ${parsedDefault.provider}/${parsedDefault.model}:${parsedDefault.thinking}`);
141
+ console.log(` enabledModels: ${enabledModels.length}`);
142
+ }
143
+ NODE
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+
4
+ usage() {
5
+ cat <<'USAGE'
6
+ Usage:
7
+ scripts/configure-subagents.sh [options]
8
+
9
+ Purpose:
10
+ Configure pi-subagents with a safe, token-conscious baseline.
11
+
12
+ Options:
13
+ --preset <minimal|safe|async|parallel>
14
+ Runtime preset (default: safe)
15
+ --config <path> Subagent config path (default: ~/.pi/agent/extensions/subagent/config.json)
16
+ --settings <path> Pi settings path (default: ~/.pi/agent/settings.json)
17
+ --model-scope <none|piagent|codex|claude>
18
+ Optional subagent model allowlist written to settings (default: none)
19
+ --dry-run Print merged config/settings summary without writing
20
+ --list Print available presets
21
+ -h, --help
22
+
23
+ Recommended:
24
+ piagent-subagents --preset safe
25
+
26
+ After installing/configuring:
27
+ pi
28
+ /subagents-doctor
29
+ /subagents-models
30
+ /run scout "Map the auth flow"
31
+ USAGE
32
+ }
33
+
34
+ PRESET="safe"
35
+ CONFIG_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/extensions/subagent/config.json"
36
+ SETTINGS_PATH="${PI_CODING_AGENT_DIR:-"${HOME}/.pi/agent"}/settings.json"
37
+ MODEL_SCOPE="none"
38
+ DRY_RUN=false
39
+ LIST=false
40
+
41
+ while [[ $# -gt 0 ]]; do
42
+ case "$1" in
43
+ --preset)
44
+ PRESET="${2:-}"
45
+ shift 2
46
+ ;;
47
+ --config)
48
+ CONFIG_PATH="${2:-}"
49
+ shift 2
50
+ ;;
51
+ --settings)
52
+ SETTINGS_PATH="${2:-}"
53
+ shift 2
54
+ ;;
55
+ --model-scope)
56
+ MODEL_SCOPE="${2:-}"
57
+ shift 2
58
+ ;;
59
+ --dry-run)
60
+ DRY_RUN=true
61
+ shift
62
+ ;;
63
+ --list)
64
+ LIST=true
65
+ shift
66
+ ;;
67
+ -h|--help)
68
+ usage
69
+ exit 0
70
+ ;;
71
+ *)
72
+ echo "Unknown option: $1" >&2
73
+ usage >&2
74
+ exit 2
75
+ ;;
76
+ esac
77
+ done
78
+
79
+ node --input-type=module - "$CONFIG_PATH" "$SETTINGS_PATH" "$PRESET" "$MODEL_SCOPE" "$DRY_RUN" "$LIST" <<'NODE'
80
+ import fs from "node:fs";
81
+ import path from "node:path";
82
+
83
+ const [configPath, settingsPath, presetName, modelScopeName, dryRunRaw, listRaw] = process.argv.slice(2);
84
+ const dryRun = dryRunRaw === "true";
85
+ const listOnly = listRaw === "true";
86
+
87
+ const presets = {
88
+ minimal: {
89
+ toolDescriptionMode: "compact",
90
+ asyncByDefault: false,
91
+ asyncWidget: true,
92
+ forceTopLevelAsync: false,
93
+ waitTool: { enabled: true },
94
+ intercomBridge: { mode: "always" },
95
+ globalConcurrencyLimit: 4,
96
+ maxSubagentSpawnsPerSession: 16,
97
+ maxSubagentDepth: 1,
98
+ defaultSessionDir: "~/.pi/agent/sessions/subagent",
99
+ singleRunOutputBaseDir: "~/.pi/agent/subagent-outputs",
100
+ worktreeBaseDir: "~/.pi/agent/worktrees/pi-subagents",
101
+ scheduledRuns: { enabled: false, maxPending: 20, maxLatenessMs: 300000 },
102
+ parallel: { maxTasks: 4, concurrency: 2 },
103
+ completionBatch: { enabled: true, debounceMs: 150, maxWaitMs: 1000 }
104
+ },
105
+ safe: {
106
+ toolDescriptionMode: "compact",
107
+ asyncByDefault: false,
108
+ asyncWidget: true,
109
+ forceTopLevelAsync: false,
110
+ waitTool: { enabled: true },
111
+ intercomBridge: { mode: "always" },
112
+ globalConcurrencyLimit: 8,
113
+ maxSubagentSpawnsPerSession: 32,
114
+ maxSubagentDepth: 1,
115
+ defaultSessionDir: "~/.pi/agent/sessions/subagent",
116
+ singleRunOutputBaseDir: "~/.pi/agent/subagent-outputs",
117
+ worktreeBaseDir: "~/.pi/agent/worktrees/pi-subagents",
118
+ scheduledRuns: { enabled: false, maxPending: 20, maxLatenessMs: 300000 },
119
+ parallel: { maxTasks: 6, concurrency: 3 },
120
+ completionBatch: { enabled: true, debounceMs: 150, maxWaitMs: 1000, stragglerDebounceMs: 75, stragglerMaxWaitMs: 400, stragglerWindowMs: 2000 }
121
+ },
122
+ async: {
123
+ toolDescriptionMode: "compact",
124
+ asyncByDefault: true,
125
+ asyncWidget: true,
126
+ forceTopLevelAsync: false,
127
+ waitTool: { enabled: true },
128
+ intercomBridge: { mode: "always" },
129
+ globalConcurrencyLimit: 8,
130
+ maxSubagentSpawnsPerSession: 32,
131
+ maxSubagentDepth: 1,
132
+ defaultSessionDir: "~/.pi/agent/sessions/subagent",
133
+ singleRunOutputBaseDir: "~/.pi/agent/subagent-outputs",
134
+ worktreeBaseDir: "~/.pi/agent/worktrees/pi-subagents",
135
+ scheduledRuns: { enabled: false, maxPending: 20, maxLatenessMs: 300000 },
136
+ parallel: { maxTasks: 6, concurrency: 3 },
137
+ completionBatch: { enabled: true, debounceMs: 150, maxWaitMs: 1000, stragglerDebounceMs: 75, stragglerMaxWaitMs: 400, stragglerWindowMs: 2000 }
138
+ },
139
+ parallel: {
140
+ toolDescriptionMode: "compact",
141
+ asyncByDefault: false,
142
+ asyncWidget: true,
143
+ forceTopLevelAsync: false,
144
+ waitTool: { enabled: true },
145
+ intercomBridge: { mode: "always" },
146
+ globalConcurrencyLimit: 12,
147
+ maxSubagentSpawnsPerSession: 64,
148
+ maxSubagentDepth: 1,
149
+ defaultSessionDir: "~/.pi/agent/sessions/subagent",
150
+ singleRunOutputBaseDir: "~/.pi/agent/subagent-outputs",
151
+ worktreeBaseDir: "~/.pi/agent/worktrees/pi-subagents",
152
+ scheduledRuns: { enabled: false, maxPending: 20, maxLatenessMs: 300000 },
153
+ parallel: { maxTasks: 10, concurrency: 5 },
154
+ completionBatch: { enabled: true, debounceMs: 150, maxWaitMs: 1000, stragglerDebounceMs: 75, stragglerMaxWaitMs: 400, stragglerWindowMs: 2000 }
155
+ }
156
+ };
157
+
158
+ const modelScopes = {
159
+ none: null,
160
+ piagent: { enforce: true, allow: ["openai-codex/*", "anthropic/*"] },
161
+ codex: { enforce: true, allow: ["openai-codex/*"] },
162
+ claude: { enforce: true, allow: ["anthropic/*"] }
163
+ };
164
+
165
+ if (listOnly) {
166
+ console.log(JSON.stringify({
167
+ presets: Object.keys(presets),
168
+ modelScopes
169
+ }, null, 2));
170
+ process.exit(0);
171
+ }
172
+
173
+ if (!Object.hasOwn(presets, presetName)) {
174
+ console.error(`FAIL: unknown preset: ${presetName}`);
175
+ console.error(`Available presets: ${Object.keys(presets).join(", ")}`);
176
+ process.exit(2);
177
+ }
178
+
179
+ if (!Object.hasOwn(modelScopes, modelScopeName)) {
180
+ console.error(`FAIL: unknown model scope: ${modelScopeName}`);
181
+ console.error(`Available model scopes: ${Object.keys(modelScopes).join(", ")}`);
182
+ process.exit(2);
183
+ }
184
+
185
+ function readJson(file, fallback) {
186
+ if (!fs.existsSync(file)) return structuredClone(fallback);
187
+ try {
188
+ return JSON.parse(fs.readFileSync(file, "utf8"));
189
+ } catch (error) {
190
+ console.error(`FAIL: cannot parse JSON: ${file}`);
191
+ console.error(error instanceof Error ? error.message : String(error));
192
+ process.exit(1);
193
+ }
194
+ }
195
+
196
+ function mergeObject(base, patch) {
197
+ const out = { ...base };
198
+ for (const [key, value] of Object.entries(patch)) {
199
+ if (value && typeof value === "object" && !Array.isArray(value) && base[key] && typeof base[key] === "object" && !Array.isArray(base[key])) {
200
+ out[key] = mergeObject(base[key], value);
201
+ } else {
202
+ out[key] = value;
203
+ }
204
+ }
205
+ return out;
206
+ }
207
+
208
+ const existingConfig = readJson(configPath, {});
209
+ const mergedConfig = mergeObject(existingConfig && typeof existingConfig === "object" && !Array.isArray(existingConfig) ? existingConfig : {}, presets[presetName]);
210
+
211
+ const existingSettings = readJson(settingsPath, {});
212
+ const mergedSettings = existingSettings && typeof existingSettings === "object" && !Array.isArray(existingSettings) ? existingSettings : {};
213
+ if (modelScopes[modelScopeName]) {
214
+ mergedSettings.subagents = mergedSettings.subagents && typeof mergedSettings.subagents === "object" && !Array.isArray(mergedSettings.subagents)
215
+ ? mergedSettings.subagents
216
+ : {};
217
+ mergedSettings.subagents.modelScope = modelScopes[modelScopeName];
218
+ }
219
+
220
+ const report = {
221
+ preset: presetName,
222
+ configPath,
223
+ settingsPath,
224
+ modelScope: modelScopeName,
225
+ dryRun,
226
+ config: mergedConfig,
227
+ settingsSubagents: mergedSettings.subagents ?? null
228
+ };
229
+
230
+ if (dryRun) {
231
+ console.log(JSON.stringify(report, null, 2));
232
+ } else {
233
+ fs.mkdirSync(path.dirname(configPath), { recursive: true });
234
+ fs.writeFileSync(configPath, `${JSON.stringify(mergedConfig, null, 2)}\n`);
235
+ if (modelScopes[modelScopeName]) {
236
+ fs.mkdirSync(path.dirname(settingsPath), { recursive: true });
237
+ fs.writeFileSync(settingsPath, `${JSON.stringify(mergedSettings, null, 2)}\n`);
238
+ }
239
+ console.log(JSON.stringify(report, null, 2));
240
+ }
241
+ NODE