@oh-my-pi/pi-coding-agent 14.6.6 → 14.7.1
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/CHANGELOG.md +50 -0
- package/examples/hooks/handoff.ts +1 -1
- package/examples/hooks/qna.ts +1 -1
- package/examples/sdk/03-custom-prompt.ts +7 -4
- package/examples/sdk/README.md +1 -1
- package/package.json +12 -12
- package/src/autoresearch/index.ts +48 -44
- package/src/cli/grep-cli.ts +1 -1
- package/src/cli/read-cli.ts +58 -0
- package/src/cli.ts +1 -0
- package/src/commands/read.ts +40 -0
- package/src/commit/agentic/agent.ts +1 -1
- package/src/commit/analysis/conventional.ts +1 -1
- package/src/commit/analysis/summary.ts +1 -1
- package/src/commit/changelog/generate.ts +1 -1
- package/src/commit/map-reduce/map-phase.ts +1 -1
- package/src/commit/map-reduce/reduce-phase.ts +1 -1
- package/src/config/settings-schema.ts +49 -0
- package/src/config/settings.ts +71 -1
- package/src/dap/client.ts +1 -0
- package/src/discovery/builtin.ts +34 -9
- package/src/edit/line-hash.ts +34 -4
- package/src/edit/modes/hashline.ts +352 -8
- package/src/edit/streaming.ts +4 -1
- package/src/export/html/index.ts +1 -1
- package/src/extensibility/extensions/runner.ts +3 -3
- package/src/extensibility/extensions/types.ts +4 -4
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/main.ts +13 -18
- package/src/memories/index.ts +1 -1
- package/src/modes/components/agent-dashboard.ts +1 -1
- package/src/modes/components/read-tool-group.ts +4 -9
- package/src/modes/components/tool-execution.ts +4 -0
- package/src/modes/controllers/event-controller.ts +2 -0
- package/src/modes/interactive-mode.ts +19 -12
- package/src/modes/rpc/rpc-types.ts +1 -1
- package/src/modes/utils/context-usage.ts +12 -5
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/system/plan-mode-active.md +7 -3
- package/src/prompts/system/plan-mode-approved.md +5 -0
- package/src/prompts/system/project-prompt.md +36 -0
- package/src/prompts/system/system-prompt.md +0 -29
- package/src/prompts/tools/github.md +1 -0
- package/src/prompts/tools/read.md +15 -14
- package/src/sdk.ts +29 -28
- package/src/session/agent-session.ts +20 -12
- package/src/session/compaction/branch-summarization.ts +1 -1
- package/src/session/compaction/compaction.ts +3 -3
- package/src/session/session-dump-format.ts +10 -5
- package/src/session/streaming-output.ts +1 -1
- package/src/slash-commands/builtin-registry.ts +2 -2
- package/src/system-prompt.ts +35 -3
- package/src/task/executor.ts +4 -3
- package/src/task/isolation-backend.ts +22 -0
- package/src/tools/fetch.ts +4 -4
- package/src/tools/gh.ts +187 -0
- package/src/tools/inspect-image.ts +1 -1
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/path-utils.ts +11 -0
- package/src/tools/read.ts +393 -204
- package/src/tools/search.ts +1 -1
- package/src/tools/sqlite-reader.ts +1 -1
- package/src/utils/commit-message-generator.ts +1 -1
- package/src/utils/title-generator.ts +1 -1
- package/src/web/search/providers/anthropic.ts +1 -1
- package/src/workspace-tree.ts +396 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,56 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [14.7.1] - 2026-05-06
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `pr_create` operation to the GitHub tool to create pull requests with title/body (or `fill`), base/head branch, draft, reviewer, assignee, and label options and return a summarized result including the new PR URL
|
|
10
|
+
- Added `read.summarize.prose` setting to keep Markdown and plain-text reads out of the structural summarizer by default.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Changed the `PI_GREP_WORKERS` environment variable help text to state that it sets filesystem walker workers, defaults to 4, and uses `0` for automatic worker selection
|
|
15
|
+
- Changed hashline replacement and pure-insert auto-absorb to also drop a single duplicated structural-closing line (`}`, `);`, `]`, etc.) on either boundary when keeping it would unbalance brackets. The pure-insert variant fires regardless of `edit.hashlineAutoDropPureInsertDuplicates`, while the existing 2+ line generic absorb stays gated on that setting.
|
|
16
|
+
|
|
17
|
+
## [14.7.0] - 2026-05-04
|
|
18
|
+
### Breaking Changes
|
|
19
|
+
|
|
20
|
+
- Changed session system-prompt APIs to use ordered string block arrays by requiring `buildSystemPrompt`, `CreateAgentSessionOptions.systemPrompt`, `Session.rebuildSystemPrompt`, and extension `before_agent_start`/`getSystemPrompt` hooks to accept and return `systemPrompt: string[]` instead of a plain system-prompt string or separate `projectPrompt` field
|
|
21
|
+
- Changed `buildSystemPrompt` and session `rebuildSystemPrompt` APIs to return `{ systemPrompt, projectPrompt }`, requiring callers expecting a plain system prompt string to update to the new shape
|
|
22
|
+
- Removed the top-level `sel` parameter from the `read` tool schema, requiring callers to migrate to `path`-embedded selectors (for example `path:50-100`, `path:raw`, or `https://...:L1-L40`)
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Added a separate `projectPrompt` artifact containing per-session project context (workstation, context files, AGENTS.md rules, workspace tree, and append prompt) so dynamic context is decoupled from the static system prompt
|
|
27
|
+
- Added `Project prompt` token accounting to context-usage breakdowns and charts
|
|
28
|
+
- Added `tools.elideFileMutationInputs` setting to optionally elide large `write`, `edit`, and `apply_patch` payloads in history after successful mutations
|
|
29
|
+
- Added hashline-style return data for elided `write` calls so tools can include the resulting file content without leaking full input text
|
|
30
|
+
- Added `buildDirectoryTree` and `DirectoryTree` exports to generate configurable directory trees with options for depth, entry limits, hidden-file handling, and truncation caps
|
|
31
|
+
- Added `buildWorkspaceTree` and `WorkspaceTree` exports so callers can precompute and pass a workspace context to prompt generation
|
|
32
|
+
- Added `workspaceTree` support to `buildSystemPrompt` options to reuse a prebuilt directory snapshot
|
|
33
|
+
- Added `read.summarize.enabled`, `read.summarize.minBodyLines`, and `read.summarize.minCommentLines` settings to control whether `read` returns structural summaries and how many multiline body/comment lines are collapsed
|
|
34
|
+
- Added `edit.hashlineAutoDropPureInsertDuplicates` setting to opt into dropping 2+ pure-insert hashline payload lines that duplicate adjacent file context; default is `false`.
|
|
35
|
+
|
|
36
|
+
### Changed
|
|
37
|
+
|
|
38
|
+
- Updated session dump and HTML export output to serialize ordered system-prompt blocks (including project context) and removed the dedicated project-prompt dump section
|
|
39
|
+
- Renamed context-usage system-prompt accounting from a separate `projectPrompt` bucket to `systemContext` to match the new multi-block prompt structure
|
|
40
|
+
- Changed prompt delivery to inject non-empty `projectPrompt` as a leading `developer` message before conversation messages instead of merging it into the base system prompt
|
|
41
|
+
- Added `projectPrompt` to session dumps to expose the injected per-session project context separately
|
|
42
|
+
- Changed write success output and preview rendering to display hashline-formatted written content from captured file text when mutation inputs are elided
|
|
43
|
+
- Changed `read` directory rendering to return a two-level recency-sorted directory tree (including nested folders) instead of a flat alphabetical entry list, while still applying configurable truncation
|
|
44
|
+
- Changed generated system prompts to include a working-directory tree block after directory context, showing recent files/directories (depth ≤ 3) and truncation notices when entries are elided
|
|
45
|
+
- Changed `read` summary rendering to merge opening- and closing-brace boundaries around elided sections into a single `..` line (including closers like `};` or `})`), reducing those segments to one concise anchored summary line
|
|
46
|
+
- Changed default `read` output for parseable code files without an explicit selector to return a structural summary instead of full verbatim lines, while still supporting full output for `:raw` and explicit ranges
|
|
47
|
+
- Changed truncation/pagination hints in read, archive, and SQLite outputs to use colon syntax (`Use :<offset>`) when continuing reads
|
|
48
|
+
- Changed the read tool UI preview title to include summary elision counts when a summary is returned
|
|
49
|
+
- Changed hashline pure-insert duplicate auto-drop to be opt-in through `edit.hashlineAutoDropPureInsertDuplicates` instead of always enabled.
|
|
50
|
+
|
|
51
|
+
### Fixed
|
|
52
|
+
|
|
53
|
+
- Fixed selector parsing for colon-containing paths by only splitting `:<sel>` when the suffix matches a valid line-range or `raw` pattern, preventing paths like `db.sqlite:users:42` from being misread as selectors
|
|
54
|
+
|
|
5
55
|
## [14.6.6] - 2026-05-04
|
|
6
56
|
|
|
7
57
|
### Added
|
|
@@ -94,7 +94,7 @@ export default function (pi: HookAPI) {
|
|
|
94
94
|
|
|
95
95
|
const response = await complete(
|
|
96
96
|
ctx.model!,
|
|
97
|
-
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
|
97
|
+
{ systemPrompt: [SYSTEM_PROMPT], messages: [userMessage] },
|
|
98
98
|
{ apiKey, signal: loader.signal },
|
|
99
99
|
);
|
|
100
100
|
|
package/examples/hooks/qna.ts
CHANGED
|
@@ -85,7 +85,7 @@ export default function (pi: HookAPI) {
|
|
|
85
85
|
|
|
86
86
|
const response = await complete(
|
|
87
87
|
ctx.model!,
|
|
88
|
-
{ systemPrompt: SYSTEM_PROMPT, messages: [userMessage] },
|
|
88
|
+
{ systemPrompt: [SYSTEM_PROMPT], messages: [userMessage] },
|
|
89
89
|
{ apiKey, signal: loader.signal },
|
|
90
90
|
);
|
|
91
91
|
|
|
@@ -7,8 +7,10 @@ import { createAgentSession, SessionManager } from "@oh-my-pi/pi-coding-agent";
|
|
|
7
7
|
|
|
8
8
|
// Option 1: Replace prompt entirely
|
|
9
9
|
const { session: session1 } = await createAgentSession({
|
|
10
|
-
systemPrompt:
|
|
10
|
+
systemPrompt: [
|
|
11
|
+
`You are a helpful assistant that speaks like a pirate.
|
|
11
12
|
Always end responses with "Arrr!"`,
|
|
13
|
+
],
|
|
12
14
|
sessionManager: SessionManager.inMemory(),
|
|
13
15
|
});
|
|
14
16
|
|
|
@@ -24,11 +26,12 @@ console.log("\n");
|
|
|
24
26
|
|
|
25
27
|
// Option 2: Modify default prompt (receives default, returns modified)
|
|
26
28
|
const { session: session2 } = await createAgentSession({
|
|
27
|
-
systemPrompt: defaultPrompt =>
|
|
28
|
-
|
|
29
|
-
|
|
29
|
+
systemPrompt: defaultPrompt => [
|
|
30
|
+
...defaultPrompt,
|
|
31
|
+
`## Additional Instructions
|
|
30
32
|
- Always be concise
|
|
31
33
|
- Use bullet points when listing things`,
|
|
34
|
+
],
|
|
32
35
|
sessionManager: SessionManager.inMemory(),
|
|
33
36
|
});
|
|
34
37
|
|
package/examples/sdk/README.md
CHANGED
|
@@ -87,7 +87,7 @@ const { session } = await createAgentSession({
|
|
|
87
87
|
model,
|
|
88
88
|
authStorage: customAuth,
|
|
89
89
|
modelRegistry: customRegistry,
|
|
90
|
-
systemPrompt: "You are helpful.",
|
|
90
|
+
systemPrompt: ["You are helpful."],
|
|
91
91
|
toolNames: ["read", "bash"],
|
|
92
92
|
customTools: [{ tool: myTool }],
|
|
93
93
|
hooks: [{ factory: myHook }],
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-coding-agent",
|
|
4
|
-
"version": "14.
|
|
4
|
+
"version": "14.7.1",
|
|
5
5
|
"description": "Coding agent CLI with read, bash, edit, write tools and session management",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -44,16 +44,17 @@
|
|
|
44
44
|
"generate-template": "bun scripts/generate-template.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@agentclientprotocol/sdk": "0.
|
|
47
|
+
"@agentclientprotocol/sdk": "0.21.0",
|
|
48
48
|
"@mozilla/readability": "^0.6.0",
|
|
49
|
-
"@oh-my-pi/omp-stats": "14.
|
|
50
|
-
"@oh-my-pi/pi-agent-core": "14.
|
|
51
|
-
"@oh-my-pi/pi-ai": "14.
|
|
52
|
-
"@oh-my-pi/pi-natives": "14.
|
|
53
|
-
"@oh-my-pi/pi-tui": "14.
|
|
54
|
-
"@oh-my-pi/pi-utils": "14.
|
|
49
|
+
"@oh-my-pi/omp-stats": "14.7.1",
|
|
50
|
+
"@oh-my-pi/pi-agent-core": "14.7.1",
|
|
51
|
+
"@oh-my-pi/pi-ai": "14.7.1",
|
|
52
|
+
"@oh-my-pi/pi-natives": "14.7.1",
|
|
53
|
+
"@oh-my-pi/pi-tui": "14.7.1",
|
|
54
|
+
"@oh-my-pi/pi-utils": "14.7.1",
|
|
55
55
|
"@puppeteer/browsers": "^2.13.0",
|
|
56
56
|
"@sinclair/typebox": "^0.34.49",
|
|
57
|
+
"@types/turndown": "5.0.6",
|
|
57
58
|
"@xterm/headless": "^6.0.0",
|
|
58
59
|
"ajv": "^8.20.0",
|
|
59
60
|
"chalk": "^5.6.2",
|
|
@@ -61,16 +62,15 @@
|
|
|
61
62
|
"fflate": "0.8.2",
|
|
62
63
|
"handlebars": "^4.7.9",
|
|
63
64
|
"linkedom": "^0.18.12",
|
|
64
|
-
"lru-cache": "11.3.
|
|
65
|
+
"lru-cache": "11.3.6",
|
|
65
66
|
"markit-ai": "0.5.3",
|
|
66
67
|
"puppeteer-core": "^24.42.0",
|
|
67
68
|
"turndown": "7.2.4",
|
|
68
69
|
"turndown-plugin-gfm": "1.0.2",
|
|
69
|
-
"zod": "4.3
|
|
70
|
+
"zod": "4.4.3"
|
|
70
71
|
},
|
|
71
72
|
"devDependencies": {
|
|
72
|
-
"@types/bun": "^1.3"
|
|
73
|
-
"@types/turndown": "5.0.6"
|
|
73
|
+
"@types/bun": "^1.3.13"
|
|
74
74
|
},
|
|
75
75
|
"engines": {
|
|
76
76
|
"bun": ">=1.3.7"
|
|
@@ -356,54 +356,58 @@ export const createAutoresearchExtension: ExtensionFactory = api => {
|
|
|
356
356
|
? null
|
|
357
357
|
: "Heads up: you are not on a dedicated `autoresearch/*` branch. `log_experiment discard` will only revert run-modified files, not reset to baseline — so harness files written before `init_experiment` may not survive a discard. Clean the worktree and re-run `/autoresearch` if you want full revert safety.";
|
|
358
358
|
return {
|
|
359
|
-
systemPrompt:
|
|
360
|
-
|
|
359
|
+
systemPrompt: [
|
|
360
|
+
prompt.render(setupPromptTemplate, {
|
|
361
|
+
base_system_prompt: event.systemPrompt.join("\n\n"),
|
|
362
|
+
has_goal: goal.trim().length > 0,
|
|
363
|
+
goal,
|
|
364
|
+
working_dir: ctx.cwd,
|
|
365
|
+
has_branch: Boolean(currentBranch),
|
|
366
|
+
branch: currentBranch ?? "",
|
|
367
|
+
has_baseline_warning: baselineWarning !== null,
|
|
368
|
+
baseline_warning: baselineWarning ?? "",
|
|
369
|
+
}),
|
|
370
|
+
],
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
systemPrompt: [
|
|
375
|
+
prompt.render(promptTemplate, {
|
|
376
|
+
base_system_prompt: event.systemPrompt.join("\n\n"),
|
|
361
377
|
has_goal: goal.trim().length > 0,
|
|
362
378
|
goal,
|
|
363
379
|
working_dir: ctx.cwd,
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
380
|
+
default_metric_name: state.metricName,
|
|
381
|
+
metric_name: state.metricName,
|
|
382
|
+
has_branch: Boolean(state.branch),
|
|
383
|
+
branch: state.branch,
|
|
384
|
+
has_baseline_commit: Boolean(state.baselineCommit),
|
|
385
|
+
baseline_commit: state.baselineCommit ? state.baselineCommit.slice(0, 12) : "",
|
|
386
|
+
has_notes: state.notes.trim().length > 0,
|
|
387
|
+
notes: state.notes,
|
|
388
|
+
current_segment: state.currentSegment + 1,
|
|
389
|
+
current_segment_run_count: currentSegmentResults.length,
|
|
390
|
+
has_baseline_metric: baselineMetric !== null,
|
|
391
|
+
baseline_metric_display: formatNum(baselineMetric, state.metricUnit),
|
|
392
|
+
baseline_run_number: baselineRunNumber,
|
|
393
|
+
has_best_result: bestResult !== null && bestMetric !== null,
|
|
394
|
+
best_metric_display: bestMetric !== null ? formatNum(bestMetric, state.metricUnit) : "-",
|
|
395
|
+
best_run_number: bestResult ? (bestResult.runNumber ?? state.results.indexOf(bestResult) + 1) : null,
|
|
396
|
+
has_recent_results: recentResults.length > 0,
|
|
397
|
+
recent_results: recentResults,
|
|
398
|
+
has_unjustified_runs: unjustifiedRuns.length > 0,
|
|
399
|
+
unjustified_runs: unjustifiedRuns,
|
|
400
|
+
has_pending_run: Boolean(pendingRun),
|
|
401
|
+
pending_run_number: pendingRun?.runNumber,
|
|
402
|
+
pending_run_command: pendingRun?.command,
|
|
403
|
+
pending_run_passed: pendingRun?.passed ?? false,
|
|
404
|
+
has_pending_run_metric: pendingRun?.parsedPrimary !== null && pendingRun?.parsedPrimary !== undefined,
|
|
405
|
+
pending_run_metric_display:
|
|
406
|
+
pendingRun?.parsedPrimary !== null && pendingRun?.parsedPrimary !== undefined
|
|
407
|
+
? formatNum(pendingRun.parsedPrimary, state.metricUnit)
|
|
408
|
+
: null,
|
|
368
409
|
}),
|
|
369
|
-
|
|
370
|
-
}
|
|
371
|
-
return {
|
|
372
|
-
systemPrompt: prompt.render(promptTemplate, {
|
|
373
|
-
base_system_prompt: event.systemPrompt,
|
|
374
|
-
has_goal: goal.trim().length > 0,
|
|
375
|
-
goal,
|
|
376
|
-
working_dir: ctx.cwd,
|
|
377
|
-
default_metric_name: state.metricName,
|
|
378
|
-
metric_name: state.metricName,
|
|
379
|
-
has_branch: Boolean(state.branch),
|
|
380
|
-
branch: state.branch,
|
|
381
|
-
has_baseline_commit: Boolean(state.baselineCommit),
|
|
382
|
-
baseline_commit: state.baselineCommit ? state.baselineCommit.slice(0, 12) : "",
|
|
383
|
-
has_notes: state.notes.trim().length > 0,
|
|
384
|
-
notes: state.notes,
|
|
385
|
-
current_segment: state.currentSegment + 1,
|
|
386
|
-
current_segment_run_count: currentSegmentResults.length,
|
|
387
|
-
has_baseline_metric: baselineMetric !== null,
|
|
388
|
-
baseline_metric_display: formatNum(baselineMetric, state.metricUnit),
|
|
389
|
-
baseline_run_number: baselineRunNumber,
|
|
390
|
-
has_best_result: bestResult !== null && bestMetric !== null,
|
|
391
|
-
best_metric_display: bestMetric !== null ? formatNum(bestMetric, state.metricUnit) : "-",
|
|
392
|
-
best_run_number: bestResult ? (bestResult.runNumber ?? state.results.indexOf(bestResult) + 1) : null,
|
|
393
|
-
has_recent_results: recentResults.length > 0,
|
|
394
|
-
recent_results: recentResults,
|
|
395
|
-
has_unjustified_runs: unjustifiedRuns.length > 0,
|
|
396
|
-
unjustified_runs: unjustifiedRuns,
|
|
397
|
-
has_pending_run: Boolean(pendingRun),
|
|
398
|
-
pending_run_number: pendingRun?.runNumber,
|
|
399
|
-
pending_run_command: pendingRun?.command,
|
|
400
|
-
pending_run_passed: pendingRun?.passed ?? false,
|
|
401
|
-
has_pending_run_metric: pendingRun?.parsedPrimary !== null && pendingRun?.parsedPrimary !== undefined,
|
|
402
|
-
pending_run_metric_display:
|
|
403
|
-
pendingRun?.parsedPrimary !== null && pendingRun?.parsedPrimary !== undefined
|
|
404
|
-
? formatNum(pendingRun.parsedPrimary, state.metricUnit)
|
|
405
|
-
: null,
|
|
406
|
-
}),
|
|
410
|
+
],
|
|
407
411
|
};
|
|
408
412
|
});
|
|
409
413
|
|
package/src/cli/grep-cli.ts
CHANGED
|
@@ -150,7 +150,7 @@ ${chalk.bold("Options:")}
|
|
|
150
150
|
--no-gitignore Include files excluded by .gitignore
|
|
151
151
|
|
|
152
152
|
${chalk.bold("Environment:")}
|
|
153
|
-
PI_GREP_WORKERS=
|
|
153
|
+
PI_GREP_WORKERS=N Set filesystem walker workers (default 4, 0 = auto)
|
|
154
154
|
|
|
155
155
|
${chalk.bold("Examples:")}
|
|
156
156
|
${APP_NAME} grep "import" src/
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Read CLI command handler.
|
|
3
|
+
*
|
|
4
|
+
* Handles `omp read` — invokes the `read` agent tool against a path/URL and
|
|
5
|
+
* prints the resulting content blocks exactly as the model would receive them
|
|
6
|
+
* (including truncation/limit notices appended by the meta-notice wrapper).
|
|
7
|
+
*/
|
|
8
|
+
import { getProjectDir } from "@oh-my-pi/pi-utils";
|
|
9
|
+
import chalk from "chalk";
|
|
10
|
+
import { Settings } from "../config/settings";
|
|
11
|
+
import type { ToolSession } from "../tools";
|
|
12
|
+
import { wrapToolWithMetaNotice } from "../tools/output-meta";
|
|
13
|
+
import { ReadTool } from "../tools/read";
|
|
14
|
+
import { renderError } from "../tools/tool-errors";
|
|
15
|
+
|
|
16
|
+
export interface ReadCommandArgs {
|
|
17
|
+
path: string;
|
|
18
|
+
timeout?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function runReadCommand(cmd: ReadCommandArgs): Promise<void> {
|
|
22
|
+
if (!cmd.path) {
|
|
23
|
+
process.stderr.write(chalk.red("error: path is required\n"));
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const cwd = getProjectDir();
|
|
28
|
+
const settings = await Settings.init({ cwd });
|
|
29
|
+
|
|
30
|
+
const session: ToolSession = {
|
|
31
|
+
cwd,
|
|
32
|
+
hasUI: false,
|
|
33
|
+
settings,
|
|
34
|
+
getSessionFile: () => null,
|
|
35
|
+
getSessionSpawns: () => "*",
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const tool = wrapToolWithMetaNotice(new ReadTool(session));
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
const result = await tool.execute("omp-read", { path: cmd.path, timeout: cmd.timeout });
|
|
42
|
+
|
|
43
|
+
for (const block of result.content) {
|
|
44
|
+
if (block.type === "text") {
|
|
45
|
+
process.stdout.write(block.text);
|
|
46
|
+
if (!block.text.endsWith("\n")) process.stdout.write("\n");
|
|
47
|
+
} else if (block.type === "image") {
|
|
48
|
+
const decodedBytes = Buffer.from(block.data, "base64").byteLength;
|
|
49
|
+
process.stdout.write(
|
|
50
|
+
chalk.dim(`[image content: ${block.mimeType}, ${decodedBytes} bytes base64-decoded]\n`),
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
process.stderr.write(`${chalk.red(renderError(err))}\n`);
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
package/src/cli.ts
CHANGED
|
@@ -53,6 +53,7 @@ const commands: CommandEntry[] = [
|
|
|
53
53
|
{ name: "plugin", load: () => import("./commands/plugin").then(m => m.default) },
|
|
54
54
|
{ name: "setup", load: () => import("./commands/setup").then(m => m.default) },
|
|
55
55
|
{ name: "shell", load: () => import("./commands/shell").then(m => m.default) },
|
|
56
|
+
{ name: "read", load: () => import("./commands/read").then(m => m.default) },
|
|
56
57
|
{ name: "ssh", load: () => import("./commands/ssh").then(m => m.default) },
|
|
57
58
|
{ name: "stats", load: () => import("./commands/stats").then(m => m.default) },
|
|
58
59
|
{ name: "update", load: () => import("./commands/update").then(m => m.default) },
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Show what the read tool will return for a given path.
|
|
3
|
+
*/
|
|
4
|
+
import { Args, Command, Flags } from "@oh-my-pi/pi-utils/cli";
|
|
5
|
+
import { type ReadCommandArgs, runReadCommand } from "../cli/read-cli";
|
|
6
|
+
import { initTheme } from "../modes/theme/theme";
|
|
7
|
+
|
|
8
|
+
export default class Read extends Command {
|
|
9
|
+
static description = "Show what the read tool will return for a path or URL";
|
|
10
|
+
|
|
11
|
+
static args = {
|
|
12
|
+
path: Args.string({
|
|
13
|
+
description: "Path or URL to read (append :sel for line ranges or raw mode, e.g. src/foo.ts:50-100)",
|
|
14
|
+
required: true,
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
static flags = {
|
|
19
|
+
timeout: Flags.integer({ description: "Request timeout in seconds (URLs only)" }),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
static examples = [
|
|
23
|
+
"omp read src/foo.ts",
|
|
24
|
+
"omp read src/foo.ts:50-100",
|
|
25
|
+
"omp read src/foo.ts:raw",
|
|
26
|
+
"omp read https://example.com",
|
|
27
|
+
"omp read path/to/archive.zip:dir/file.ts",
|
|
28
|
+
"omp read path/to/db.sqlite:users:42",
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
async run(): Promise<void> {
|
|
32
|
+
const { args, flags } = await this.parse(Read);
|
|
33
|
+
const cmd: ReadCommandArgs = {
|
|
34
|
+
path: args.path ?? "",
|
|
35
|
+
timeout: flags.timeout,
|
|
36
|
+
};
|
|
37
|
+
await initTheme();
|
|
38
|
+
await runReadCommand(cmd);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -60,7 +60,7 @@ export async function runCommitAgentSession(input: CommitAgentInput): Promise<Co
|
|
|
60
60
|
settings: input.settings,
|
|
61
61
|
model: input.model,
|
|
62
62
|
thinkingLevel: input.thinkingLevel,
|
|
63
|
-
systemPrompt,
|
|
63
|
+
systemPrompt: [systemPrompt],
|
|
64
64
|
customTools: tools,
|
|
65
65
|
enableLsp: false,
|
|
66
66
|
enableMCP: false,
|
|
@@ -89,7 +89,7 @@ export async function generateConventionalAnalysis({
|
|
|
89
89
|
const response = await completeSimple(
|
|
90
90
|
model,
|
|
91
91
|
{
|
|
92
|
-
systemPrompt: prompt.render(analysisSystemPrompt),
|
|
92
|
+
systemPrompt: [prompt.render(analysisSystemPrompt)],
|
|
93
93
|
messages: [{ role: "user", content: userContent, timestamp: Date.now() }],
|
|
94
94
|
tools: [ConventionalAnalysisTool],
|
|
95
95
|
},
|
|
@@ -58,7 +58,7 @@ export async function generateChangelogEntries({
|
|
|
58
58
|
const response = await completeSimple(
|
|
59
59
|
model,
|
|
60
60
|
{
|
|
61
|
-
systemPrompt: prompt.render(changelogSystemPrompt),
|
|
61
|
+
systemPrompt: [prompt.render(changelogSystemPrompt)],
|
|
62
62
|
messages: [{ role: "user", content: userContent, timestamp: Date.now() }],
|
|
63
63
|
tools: [changelogTool],
|
|
64
64
|
},
|
|
@@ -76,7 +76,7 @@ export async function runReducePhase({
|
|
|
76
76
|
const response = await completeSimple(
|
|
77
77
|
model,
|
|
78
78
|
{
|
|
79
|
-
systemPrompt: prompt.render(reduceSystemPrompt),
|
|
79
|
+
systemPrompt: [prompt.render(reduceSystemPrompt)],
|
|
80
80
|
messages: [{ role: "user", content: userContent, timestamp: Date.now() }],
|
|
81
81
|
tools: [ReduceTool],
|
|
82
82
|
},
|
|
@@ -1419,6 +1419,15 @@ export const SETTINGS_SCHEMA = {
|
|
|
1419
1419
|
},
|
|
1420
1420
|
},
|
|
1421
1421
|
|
|
1422
|
+
"edit.hashlineAutoDropPureInsertDuplicates": {
|
|
1423
|
+
type: "boolean",
|
|
1424
|
+
default: false,
|
|
1425
|
+
ui: {
|
|
1426
|
+
tab: "editing",
|
|
1427
|
+
label: "Hashline Duplicate Insert Drop",
|
|
1428
|
+
description: "Drop 2+ pure-insert payload lines that duplicate adjacent file context",
|
|
1429
|
+
},
|
|
1430
|
+
},
|
|
1422
1431
|
"edit.blockAutoGenerated": {
|
|
1423
1432
|
type: "boolean",
|
|
1424
1433
|
default: true,
|
|
@@ -1466,6 +1475,46 @@ export const SETTINGS_SCHEMA = {
|
|
|
1466
1475
|
},
|
|
1467
1476
|
},
|
|
1468
1477
|
|
|
1478
|
+
"read.summarize.enabled": {
|
|
1479
|
+
type: "boolean",
|
|
1480
|
+
default: true,
|
|
1481
|
+
ui: {
|
|
1482
|
+
tab: "editing",
|
|
1483
|
+
label: "Read Summaries",
|
|
1484
|
+
description: "Return structural code summaries when read is called without an explicit selector",
|
|
1485
|
+
},
|
|
1486
|
+
},
|
|
1487
|
+
|
|
1488
|
+
"read.summarize.prose": {
|
|
1489
|
+
type: "boolean",
|
|
1490
|
+
default: false,
|
|
1491
|
+
ui: {
|
|
1492
|
+
tab: "editing",
|
|
1493
|
+
label: "Prose Summaries",
|
|
1494
|
+
description: "Return structural summaries for Markdown and plain text reads",
|
|
1495
|
+
},
|
|
1496
|
+
},
|
|
1497
|
+
|
|
1498
|
+
"read.summarize.minBodyLines": {
|
|
1499
|
+
type: "number",
|
|
1500
|
+
default: 4,
|
|
1501
|
+
ui: {
|
|
1502
|
+
tab: "editing",
|
|
1503
|
+
label: "Read Summary Body Lines",
|
|
1504
|
+
description: "Minimum multiline body or literal length before read summaries collapse it",
|
|
1505
|
+
},
|
|
1506
|
+
},
|
|
1507
|
+
|
|
1508
|
+
"read.summarize.minCommentLines": {
|
|
1509
|
+
type: "number",
|
|
1510
|
+
default: 6,
|
|
1511
|
+
ui: {
|
|
1512
|
+
tab: "editing",
|
|
1513
|
+
label: "Read Summary Comment Lines",
|
|
1514
|
+
description: "Minimum multiline block comment length before read summaries collapse it",
|
|
1515
|
+
},
|
|
1516
|
+
},
|
|
1517
|
+
|
|
1469
1518
|
"read.toolResultPreview": {
|
|
1470
1519
|
type: "boolean",
|
|
1471
1520
|
default: false,
|
package/src/config/settings.ts
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import * as fs from "node:fs";
|
|
15
|
+
import * as os from "node:os";
|
|
15
16
|
import * as path from "node:path";
|
|
16
17
|
import {
|
|
17
18
|
getAgentDbPath,
|
|
@@ -98,6 +99,74 @@ function setByPath(obj: RawSettings, segments: string[], value: unknown): void {
|
|
|
98
99
|
current[segments[segments.length - 1]] = value;
|
|
99
100
|
}
|
|
100
101
|
|
|
102
|
+
const PATH_SCOPED_ARRAY_SETTINGS = new Set<SettingPath>(["enabledModels", "disabledProviders"]);
|
|
103
|
+
|
|
104
|
+
type PathScopedStringArrayEntry = {
|
|
105
|
+
path?: unknown;
|
|
106
|
+
paths?: unknown;
|
|
107
|
+
pathPrefix?: unknown;
|
|
108
|
+
pathPrefixes?: unknown;
|
|
109
|
+
values?: unknown;
|
|
110
|
+
items?: unknown;
|
|
111
|
+
models?: unknown;
|
|
112
|
+
providers?: unknown;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
function normalizePathPrefix(prefix: string): string {
|
|
116
|
+
const expanded =
|
|
117
|
+
prefix === "~" ? os.homedir() : prefix.startsWith("~/") ? path.join(os.homedir(), prefix.slice(2)) : prefix;
|
|
118
|
+
return path.resolve(expanded);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function pathMatchesPrefix(cwd: string, prefix: string): boolean {
|
|
122
|
+
const relative = path.relative(normalizePathPrefix(prefix), path.resolve(cwd));
|
|
123
|
+
return relative === "" || (!!relative && !relative.startsWith("..") && !path.isAbsolute(relative));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function stringArrayFromUnknown(value: unknown): string[] {
|
|
127
|
+
if (typeof value === "string") return [value];
|
|
128
|
+
if (Array.isArray(value)) return value.filter((item): item is string => typeof item === "string");
|
|
129
|
+
return [];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function resolvePathScopedStringArray(settingPath: SettingPath, value: unknown, cwd: string): string[] | undefined {
|
|
133
|
+
if (!PATH_SCOPED_ARRAY_SETTINGS.has(settingPath) || !Array.isArray(value)) return undefined;
|
|
134
|
+
|
|
135
|
+
const resolved: string[] = [];
|
|
136
|
+
for (const entry of value) {
|
|
137
|
+
if (typeof entry === "string") {
|
|
138
|
+
resolved.push(entry);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) continue;
|
|
142
|
+
|
|
143
|
+
const scoped = entry as PathScopedStringArrayEntry;
|
|
144
|
+
const prefixes = [
|
|
145
|
+
...stringArrayFromUnknown(scoped.path),
|
|
146
|
+
...stringArrayFromUnknown(scoped.paths),
|
|
147
|
+
...stringArrayFromUnknown(scoped.pathPrefix),
|
|
148
|
+
...stringArrayFromUnknown(scoped.pathPrefixes),
|
|
149
|
+
];
|
|
150
|
+
if (prefixes.length === 0 || !prefixes.some(prefix => pathMatchesPrefix(cwd, prefix))) continue;
|
|
151
|
+
|
|
152
|
+
const values =
|
|
153
|
+
settingPath === "enabledModels"
|
|
154
|
+
? [
|
|
155
|
+
...stringArrayFromUnknown(scoped.values),
|
|
156
|
+
...stringArrayFromUnknown(scoped.items),
|
|
157
|
+
...stringArrayFromUnknown(scoped.models),
|
|
158
|
+
]
|
|
159
|
+
: [
|
|
160
|
+
...stringArrayFromUnknown(scoped.values),
|
|
161
|
+
...stringArrayFromUnknown(scoped.items),
|
|
162
|
+
...stringArrayFromUnknown(scoped.providers),
|
|
163
|
+
];
|
|
164
|
+
resolved.push(...values);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
return resolved;
|
|
168
|
+
}
|
|
169
|
+
|
|
101
170
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
102
171
|
// Settings Class
|
|
103
172
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
@@ -201,7 +270,8 @@ export class Settings {
|
|
|
201
270
|
const segments = path.split(".");
|
|
202
271
|
const value = getByPath(this.#merged, segments);
|
|
203
272
|
if (value !== undefined) {
|
|
204
|
-
|
|
273
|
+
const pathScopedValue = resolvePathScopedStringArray(path, value, this.#cwd);
|
|
274
|
+
return (pathScopedValue ?? value) as SettingValue<P>;
|
|
205
275
|
}
|
|
206
276
|
return getDefault(path);
|
|
207
277
|
}
|