pi-code 1.0.62 → 1.0.64
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 +1 -1
- package/extensions/commands.ts +9 -6
- package/extensions/context-imports.ts +46 -24
- package/extensions/git-checkpoint.ts +141 -21
- package/extensions/hooks/config.ts +16 -10
- package/extensions/hooks/decisions.ts +9 -9
- package/extensions/hooks/index.ts +14 -10
- package/extensions/hooks/matcher.ts +12 -7
- package/extensions/hooks/runners.ts +17 -29
- package/extensions/internal/agent-run.ts +1 -1
- package/extensions/internal/bash-rules.ts +1 -2
- package/extensions/internal/claude-tool-names.ts +4 -7
- package/extensions/internal/command-file.ts +2 -3
- package/extensions/internal/external-imports.ts +4 -5
- package/extensions/internal/goal-evaluator.ts +4 -3
- package/extensions/internal/instruction-events.ts +5 -5
- package/extensions/internal/managed-settings.ts +1 -1
- package/extensions/internal/mcp-oauth.ts +28 -6
- package/extensions/internal/model-complete.ts +7 -1
- package/extensions/internal/model-lookup.ts +10 -3
- package/extensions/internal/path-rules.ts +51 -41
- package/extensions/internal/plugins.ts +5 -5
- package/extensions/internal/process-tree.ts +39 -0
- package/extensions/internal/project-approval.ts +7 -7
- package/extensions/internal/project-root.ts +13 -13
- package/extensions/internal/settings-chain.ts +19 -0
- package/extensions/internal/settings-watch.ts +3 -1
- package/extensions/internal/shell-resolve.ts +19 -6
- package/extensions/internal/tool-target.ts +4 -5
- package/extensions/internal/values.ts +20 -4
- package/extensions/mcp/config.ts +11 -4
- package/extensions/mcp/index.ts +78 -19
- package/extensions/mcp/policy.ts +14 -12
- package/extensions/mcp/transport.ts +12 -8
- package/extensions/memory.ts +4 -4
- package/extensions/notify.ts +2 -7
- package/extensions/output-styles.ts +22 -17
- package/extensions/question.ts +142 -12
- package/extensions/session-title.ts +5 -0
- package/extensions/skills.ts +11 -6
- package/extensions/subagent/agents.ts +4 -4
- package/extensions/subagent/background.ts +35 -41
- package/extensions/subagent/child.ts +19 -4
- package/extensions/subagent/modes.ts +18 -13
- package/extensions/subagent/params.ts +1 -2
- package/extensions/subagent/run.ts +34 -20
- package/extensions/thinking.ts +3 -3
- package/extensions/web.ts +3 -4
- package/package.json +1 -1
|
@@ -152,18 +152,6 @@ export async function runBackgroundMode(params: SubagentParamsStatic, context: B
|
|
|
152
152
|
return backgroundCapResult(makeDetails)
|
|
153
153
|
}
|
|
154
154
|
const runCwd = params.cwd ?? defaultCwd
|
|
155
|
-
// The same isolation boundary as the foreground path: no worktree, no run.
|
|
156
|
-
let worktree: AgentWorktree | undefined
|
|
157
|
-
if (agent.isolation === 'worktree') {
|
|
158
|
-
const created = await createAgentWorktree(runCwd, agent.name)
|
|
159
|
-
if ('error' in created) {
|
|
160
|
-
return {
|
|
161
|
-
content: [{ type: 'text', text: `isolation: worktree could not be created for ${agent.name}: ${created.error}` }],
|
|
162
|
-
details: makeDetails('single')([]),
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
worktree = created
|
|
166
|
-
}
|
|
167
155
|
// Anchor project/local memory at the session project (defaultCwd), which is the one
|
|
168
156
|
// projectApproved gated; see the foreground path for why runCwd must not be used.
|
|
169
157
|
const memorySection = agentMemoryPromptSection(agent, defaultCwd, projectApproved)
|
|
@@ -176,6 +164,21 @@ export async function runBackgroundMode(params: SubagentParamsStatic, context: B
|
|
|
176
164
|
// foreground path).
|
|
177
165
|
args.push('--system-prompt', tmpPrompt.filePath)
|
|
178
166
|
}
|
|
167
|
+
// The same isolation boundary as the foreground path: no worktree, no run. Created
|
|
168
|
+
// after the temp prompt, so a prompt write that throws has nothing to leak; the cap
|
|
169
|
+
// refusal below removes it again.
|
|
170
|
+
let worktree: AgentWorktree | undefined
|
|
171
|
+
if (agent.isolation === 'worktree') {
|
|
172
|
+
const created = await createAgentWorktree(runCwd, agent.name)
|
|
173
|
+
if ('error' in created) {
|
|
174
|
+
removeTmpPrompt(tmpPrompt)
|
|
175
|
+
return {
|
|
176
|
+
content: [{ type: 'text', text: `isolation: worktree could not be created for ${agent.name}: ${created.error}` }],
|
|
177
|
+
details: makeDetails('single')([]),
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
worktree = created
|
|
181
|
+
}
|
|
179
182
|
// The id is preset so SubagentStart hooks run pre-spawn with the id the run
|
|
180
183
|
// will actually carry, and their context lands before the child's first prompt.
|
|
181
184
|
const presetId = `bg-${randomUUID().slice(0, 8)}`
|
|
@@ -216,8 +219,10 @@ export async function runBackgroundMode(params: SubagentParamsStatic, context: B
|
|
|
216
219
|
presetId,
|
|
217
220
|
)
|
|
218
221
|
if (id === null) {
|
|
219
|
-
// Lost the cap race to a parallel batch: the atomic check inside startBackgroundRun
|
|
222
|
+
// Lost the cap race to a parallel batch: the atomic check inside startBackgroundRun
|
|
223
|
+
// refused. A pristine worktree is removed by the same cleanup a finished run uses.
|
|
220
224
|
removeTmpPrompt(tmpPrompt)
|
|
225
|
+
if (worktree) await cleanupAgentWorktree(runCwd, worktree).catch(() => {})
|
|
221
226
|
return backgroundCapResult(makeDetails)
|
|
222
227
|
}
|
|
223
228
|
pi.events.emit(SUBAGENT_CHANNEL, { phase: 'start', agentType: agent.name, agentId: id })
|
|
@@ -23,8 +23,7 @@ export const ChainItem = Type.Object({
|
|
|
23
23
|
})
|
|
24
24
|
|
|
25
25
|
const AgentScopeSchema = StringEnum(['user', 'project', 'both'] as const, {
|
|
26
|
-
description: 'Which agent directories to use.
|
|
27
|
-
default: 'user',
|
|
26
|
+
description: 'Which agent directories to use. Omitted, an approved project resolves to "both" and an unapproved one to "user"; "both" includes project-local agents.',
|
|
28
27
|
})
|
|
29
28
|
|
|
30
29
|
export const SubagentParams = Type.Object({
|
|
@@ -6,16 +6,18 @@
|
|
|
6
6
|
* worktree; everything about how the child was configured lives in child.ts.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { spawn } from 'node:child_process'
|
|
9
|
+
import { type ChildProcessByStdio, spawn } from 'node:child_process'
|
|
10
10
|
import { randomUUID } from 'node:crypto'
|
|
11
11
|
import * as fs from 'node:fs'
|
|
12
12
|
import * as os from 'node:os'
|
|
13
13
|
import * as path from 'node:path'
|
|
14
|
+
import type { Readable } from 'node:stream'
|
|
14
15
|
|
|
15
16
|
import type { AgentToolResult } from '@earendil-works/pi-agent-core'
|
|
16
17
|
import type { Message } from '@earendil-works/pi-ai'
|
|
17
18
|
import { withFileMutationQueue } from '@earendil-works/pi-coding-agent'
|
|
18
19
|
|
|
20
|
+
import { killProcessTree } from '../internal/process-tree.js'
|
|
19
21
|
import { runSubagentStartHooks } from '../internal/subagent-hooks.js'
|
|
20
22
|
import { type AgentConfig, resolveModelAlias } from './agents.js'
|
|
21
23
|
import { agentHooksEnv, agentInvocationArgs, agentMemoryPromptSection, childPromptBody, taskWithStartContext, unresolvedToolsError, withMemoryTools } from './child.js'
|
|
@@ -123,6 +125,29 @@ function appendPartialNote(result: SingleResult): void {
|
|
|
123
125
|
if (result.partial) appendResultNote(result, '[Output is partial: the subagent stopped at its maxTurns limit.]')
|
|
124
126
|
}
|
|
125
127
|
|
|
128
|
+
/** The pi child, or the error spawning it threw synchronously. Node normally
|
|
129
|
+
* reports a spawn failure through the child's async 'error' event, but some
|
|
130
|
+
* failures (confirmed on Linux: posix_spawn detects E2BIG immediately) throw at
|
|
131
|
+
* the spawn() call site instead; catching it here, in one place with an explicit
|
|
132
|
+
* return type, keeps the caller's non-null stdout/stderr narrowing that a bare
|
|
133
|
+
* try/catch around an inline spawn() call loses. */
|
|
134
|
+
export function spawnChild(command: string, args: string[], options: { cwd: string; env: NodeJS.ProcessEnv }): { proc: ChildProcessByStdio<null, Readable, Readable> } | { error: Error } {
|
|
135
|
+
try {
|
|
136
|
+
return {
|
|
137
|
+
proc: spawn(command, args, {
|
|
138
|
+
...options,
|
|
139
|
+
shell: false,
|
|
140
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
141
|
+
// Its own group, so an abort reaches grandchildren too: killing only the
|
|
142
|
+
// direct child orphans a build or dev server the agent started.
|
|
143
|
+
detached: true,
|
|
144
|
+
}),
|
|
145
|
+
}
|
|
146
|
+
} catch (error) {
|
|
147
|
+
return { error: error as Error }
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
126
151
|
export async function runSingleAgent(options: RunAgentOptions): Promise<SingleResult> {
|
|
127
152
|
const agent = options.agents.find((a) => a.name === options.agentName)
|
|
128
153
|
if (!agent) return runSingleAgentInner(options)
|
|
@@ -244,16 +269,17 @@ async function runSingleAgentInner(options: RunAgentOptions): Promise<SingleResu
|
|
|
244
269
|
|
|
245
270
|
const exitCode = await new Promise<number>((resolve) => {
|
|
246
271
|
const invocation = getPiInvocation(args)
|
|
247
|
-
const
|
|
272
|
+
const spawned = spawnChild(invocation.command, invocation.args, {
|
|
248
273
|
cwd: worktree?.dir ?? runCwd,
|
|
249
|
-
shell: false,
|
|
250
|
-
stdio: ['ignore', 'pipe', 'pipe'],
|
|
251
|
-
// Its own group, so an abort reaches grandchildren too: killing only the
|
|
252
|
-
// direct child orphans a build or dev server the agent started.
|
|
253
|
-
detached: true,
|
|
254
274
|
// The marker lets the child's subagent tool refuse to nest further.
|
|
255
275
|
env: { ...process.env, PI_CODE_SUBAGENT: '1', ...agentHooksEnv(agent, options.agentId ?? '') },
|
|
256
276
|
})
|
|
277
|
+
if ('error' in spawned) {
|
|
278
|
+
if (!currentResult.stderr) currentResult.stderr = spawned.error.message
|
|
279
|
+
resolve(1)
|
|
280
|
+
return
|
|
281
|
+
}
|
|
282
|
+
const proc = spawned.proc
|
|
257
283
|
let buffer = ''
|
|
258
284
|
let assistantTurns = 0
|
|
259
285
|
|
|
@@ -323,19 +349,7 @@ async function runSingleAgentInner(options: RunAgentOptions): Promise<SingleResu
|
|
|
323
349
|
resolve(1)
|
|
324
350
|
})
|
|
325
351
|
|
|
326
|
-
const killGroup = (sig: NodeJS.Signals): void =>
|
|
327
|
-
try {
|
|
328
|
-
// A child that never spawned has no pid and no group; the direct kill is all there is.
|
|
329
|
-
if (proc.pid) process.kill(-proc.pid, sig)
|
|
330
|
-
else proc.kill(sig)
|
|
331
|
-
} catch {
|
|
332
|
-
try {
|
|
333
|
-
proc.kill(sig)
|
|
334
|
-
} catch {
|
|
335
|
-
/* already gone */
|
|
336
|
-
}
|
|
337
|
-
}
|
|
338
|
-
}
|
|
352
|
+
const killGroup = (sig: NodeJS.Signals): void => killProcessTree(proc, sig)
|
|
339
353
|
if (signal) {
|
|
340
354
|
onAbort = () => {
|
|
341
355
|
wasAborted = true
|
package/extensions/thinking.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Thinking Keyword Escalation
|
|
3
3
|
*
|
|
4
|
-
* Claude Code raises reasoning effort for a single turn when the prompt carries
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Claude Code raises reasoning effort for a single turn when the prompt carries
|
|
5
|
+
* `ultrathink`, the only keyword: `think`, `think hard` and similar phrases pass
|
|
6
|
+
* through as ordinary prompt text. The word stays in the prompt
|
|
7
7
|
* (the input is observed, never consumed or transformed), the escalation only ever
|
|
8
8
|
* raises the level, and the prior level is restored once the turn settles, mirroring
|
|
9
9
|
* how commands.ts restores a per-command model override on agent_settled.
|
package/extensions/web.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { Type } from 'typebox'
|
|
|
16
16
|
import { htmlToMarkdown, removeTags } from './internal/html-markdown.js'
|
|
17
17
|
import { completeText } from './internal/model-complete.js'
|
|
18
18
|
import { capForContext } from './internal/output-guard.js'
|
|
19
|
+
import { parseNumericEnv } from './internal/values.js'
|
|
19
20
|
import { httpFetch } from './internal/web-transport.js'
|
|
20
21
|
|
|
21
22
|
const SEARCH_ENDPOINT = 'https://html.duckduckgo.com/html/?q='
|
|
@@ -265,10 +266,8 @@ async function fetchText(rawUrl: string, crossHost: CrossHost, transport = httpF
|
|
|
265
266
|
const FETCH_CACHE_TTL_DEFAULT_MS = 15 * 60 * 1000
|
|
266
267
|
|
|
267
268
|
function fetchCacheTtlMs(): number {
|
|
268
|
-
const
|
|
269
|
-
|
|
270
|
-
const parsed = Number(raw)
|
|
271
|
-
return Number.isFinite(parsed) && parsed >= 0 ? parsed : FETCH_CACHE_TTL_DEFAULT_MS
|
|
269
|
+
const parsed = parseNumericEnv(process.env.CLAUDE_CODE_WEBFETCH_CACHE_TTL_MS)
|
|
270
|
+
return parsed !== undefined && parsed >= 0 ? parsed : FETCH_CACHE_TTL_DEFAULT_MS
|
|
272
271
|
}
|
|
273
272
|
const FETCH_CACHE_MAX_ENTRIES = 50
|
|
274
273
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-code",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.64",
|
|
4
4
|
"description": "Claude Code experience for the pi coding agent: reads your .claude config (rules, commands, skills, hooks, output styles, MCP servers, agents) and adds todo, checkpoints, memory, web, subagents, and goals",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi",
|