@oh-my-pi/pi-coding-agent 13.19.0 → 14.0.2
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 +266 -1
- package/package.json +86 -20
- package/scripts/format-prompts.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +24 -0
- package/src/autoresearch/contract.ts +0 -44
- package/src/autoresearch/dashboard.ts +1 -2
- package/src/autoresearch/git.ts +91 -0
- package/src/autoresearch/helpers.ts +49 -0
- package/src/autoresearch/index.ts +28 -187
- package/src/autoresearch/prompt.md +26 -9
- package/src/autoresearch/state.ts +0 -6
- package/src/autoresearch/tools/init-experiment.ts +202 -117
- package/src/autoresearch/tools/log-experiment.ts +83 -125
- package/src/autoresearch/tools/run-experiment.ts +48 -10
- package/src/autoresearch/types.ts +2 -2
- package/src/capability/index.ts +4 -2
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/grep-cli.ts +8 -8
- package/src/cli/grievances-cli.ts +78 -0
- package/src/cli/read-cli.ts +67 -0
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/update-cli.ts +3 -3
- package/src/cli.ts +2 -0
- package/src/commands/grep.ts +6 -1
- package/src/commands/grievances.ts +20 -0
- package/src/commands/read.ts +33 -0
- package/src/commit/agentic/agent.ts +5 -5
- package/src/commit/agentic/index.ts +3 -4
- package/src/commit/agentic/tools/analyze-file.ts +3 -3
- package/src/commit/agentic/validation.ts +1 -1
- package/src/commit/analysis/conventional.ts +4 -4
- package/src/commit/analysis/summary.ts +3 -3
- package/src/commit/changelog/generate.ts +4 -4
- package/src/commit/map-reduce/map-phase.ts +4 -4
- package/src/commit/map-reduce/reduce-phase.ts +4 -4
- package/src/commit/pipeline.ts +3 -4
- package/src/config/prompt-templates.ts +44 -226
- package/src/config/resolve-config-value.ts +4 -2
- package/src/config/settings-schema.ts +54 -2
- package/src/config/settings.ts +25 -26
- package/src/dap/client.ts +674 -0
- package/src/dap/config.ts +150 -0
- package/src/dap/defaults.json +211 -0
- package/src/dap/index.ts +4 -0
- package/src/dap/session.ts +1255 -0
- package/src/dap/types.ts +600 -0
- package/src/debug/log-viewer.ts +3 -2
- package/src/discovery/builtin.ts +1 -2
- package/src/discovery/codex.ts +2 -2
- package/src/discovery/github.ts +2 -1
- package/src/discovery/helpers.ts +2 -2
- package/src/discovery/opencode.ts +2 -2
- package/src/edit/diff.ts +818 -0
- package/src/edit/index.ts +309 -0
- package/src/edit/line-hash.ts +67 -0
- package/src/edit/modes/chunk.ts +454 -0
- package/src/{patch → edit/modes}/hashline.ts +741 -361
- package/src/{patch/applicator.ts → edit/modes/patch.ts} +420 -117
- package/src/{patch/fuzzy.ts → edit/modes/replace.ts} +519 -197
- package/src/{patch → edit}/normalize.ts +97 -76
- package/src/{patch/shared.ts → edit/renderer.ts} +181 -108
- package/src/exec/bash-executor.ts +4 -2
- package/src/exec/idle-timeout-watchdog.ts +126 -0
- package/src/exec/non-interactive-env.ts +5 -0
- package/src/extensibility/custom-commands/bundled/ci-green/index.ts +2 -2
- package/src/extensibility/custom-commands/bundled/review/index.ts +2 -2
- package/src/extensibility/custom-commands/loader.ts +1 -2
- package/src/extensibility/custom-tools/loader.ts +34 -11
- package/src/extensibility/extensions/loader.ts +9 -4
- package/src/extensibility/extensions/runner.ts +24 -1
- package/src/extensibility/extensions/types.ts +1 -1
- package/src/extensibility/hooks/loader.ts +5 -6
- package/src/extensibility/hooks/types.ts +1 -1
- package/src/extensibility/plugins/doctor.ts +2 -1
- package/src/extensibility/slash-commands.ts +3 -7
- package/src/index.ts +2 -1
- package/src/internal-urls/docs-index.generated.ts +11 -11
- package/src/ipy/executor.ts +58 -17
- package/src/ipy/gateway-coordinator.ts +6 -4
- package/src/ipy/kernel.ts +45 -22
- package/src/ipy/runtime.ts +2 -2
- package/src/lsp/client.ts +7 -4
- package/src/lsp/clients/lsp-linter-client.ts +4 -4
- package/src/lsp/config.ts +2 -2
- package/src/lsp/defaults.json +688 -154
- package/src/lsp/index.ts +234 -45
- package/src/lsp/lspmux.ts +2 -2
- package/src/lsp/startup-events.ts +13 -0
- package/src/lsp/types.ts +12 -1
- package/src/lsp/utils.ts +8 -1
- package/src/main.ts +102 -46
- package/src/memories/index.ts +4 -5
- package/src/modes/acp/acp-agent.ts +563 -163
- package/src/modes/acp/acp-event-mapper.ts +9 -1
- package/src/modes/acp/acp-mode.ts +4 -2
- package/src/modes/components/agent-dashboard.ts +3 -4
- package/src/modes/components/diff.ts +6 -7
- package/src/modes/components/read-tool-group.ts +6 -12
- package/src/modes/components/settings-defs.ts +5 -0
- package/src/modes/components/tool-execution.ts +1 -1
- package/src/modes/components/welcome.ts +1 -1
- package/src/modes/controllers/btw-controller.ts +2 -2
- package/src/modes/controllers/command-controller.ts +3 -2
- package/src/modes/controllers/input-controller.ts +12 -8
- package/src/modes/index.ts +20 -2
- package/src/modes/interactive-mode.ts +94 -37
- package/src/modes/rpc/host-tools.ts +186 -0
- package/src/modes/rpc/rpc-client.ts +178 -13
- package/src/modes/rpc/rpc-mode.ts +73 -3
- package/src/modes/rpc/rpc-types.ts +53 -1
- package/src/modes/theme/theme.ts +80 -8
- package/src/modes/types.ts +2 -2
- package/src/prompts/system/system-prompt.md +2 -1
- package/src/prompts/tools/chunk-edit.md +219 -0
- package/src/prompts/tools/debug.md +43 -0
- package/src/prompts/tools/grep.md +3 -0
- package/src/prompts/tools/lsp.md +5 -5
- package/src/prompts/tools/read-chunk.md +17 -0
- package/src/prompts/tools/read.md +19 -5
- package/src/sdk.ts +190 -154
- package/src/secrets/obfuscator.ts +1 -1
- package/src/session/agent-session.ts +306 -256
- package/src/session/agent-storage.ts +12 -12
- package/src/session/compaction/branch-summarization.ts +3 -3
- package/src/session/compaction/compaction.ts +5 -6
- package/src/session/compaction/utils.ts +3 -3
- package/src/session/history-storage.ts +62 -19
- package/src/session/messages.ts +3 -3
- package/src/session/session-dump-format.ts +203 -0
- package/src/session/session-storage.ts +4 -2
- package/src/session/streaming-output.ts +1 -1
- package/src/session/tool-choice-queue.ts +213 -0
- package/src/slash-commands/builtin-registry.ts +56 -8
- package/src/ssh/connection-manager.ts +2 -2
- package/src/ssh/sshfs-mount.ts +5 -5
- package/src/stt/downloader.ts +4 -4
- package/src/stt/recorder.ts +4 -4
- package/src/stt/transcriber.ts +2 -2
- package/src/system-prompt.ts +21 -13
- package/src/task/agents.ts +5 -6
- package/src/task/commands.ts +2 -5
- package/src/task/executor.ts +4 -4
- package/src/task/index.ts +3 -4
- package/src/task/template.ts +2 -2
- package/src/task/worktree.ts +4 -4
- package/src/tools/ask.ts +2 -3
- package/src/tools/ast-edit.ts +7 -7
- package/src/tools/ast-grep.ts +7 -7
- package/src/tools/auto-generated-guard.ts +36 -41
- package/src/tools/await-tool.ts +2 -2
- package/src/tools/bash.ts +5 -23
- package/src/tools/browser.ts +4 -5
- package/src/tools/calculator.ts +2 -3
- package/src/tools/cancel-job.ts +2 -2
- package/src/tools/checkpoint.ts +3 -3
- package/src/tools/debug.ts +1007 -0
- package/src/tools/exit-plan-mode.ts +2 -3
- package/src/tools/fetch.ts +67 -3
- package/src/tools/find.ts +4 -5
- package/src/tools/fs-cache-invalidation.ts +5 -0
- package/src/tools/gemini-image.ts +13 -5
- package/src/tools/gh.ts +10 -11
- package/src/tools/grep.ts +57 -9
- package/src/tools/index.ts +44 -22
- package/src/tools/inspect-image.ts +4 -4
- package/src/tools/output-meta.ts +1 -1
- package/src/tools/python.ts +19 -6
- package/src/tools/read.ts +198 -67
- package/src/tools/render-mermaid.ts +2 -3
- package/src/tools/render-utils.ts +20 -6
- package/src/tools/renderers.ts +3 -1
- package/src/tools/report-tool-issue.ts +80 -0
- package/src/tools/resolve.ts +70 -39
- package/src/tools/search-tool-bm25.ts +2 -2
- package/src/tools/ssh.ts +2 -2
- package/src/tools/todo-write.ts +2 -2
- package/src/tools/tool-timeouts.ts +1 -0
- package/src/tools/write.ts +5 -6
- package/src/tui/tree-list.ts +3 -1
- package/src/utils/clipboard.ts +80 -0
- package/src/utils/commit-message-generator.ts +2 -3
- package/src/utils/edit-mode.ts +49 -0
- package/src/utils/file-display-mode.ts +6 -5
- package/src/utils/file-mentions.ts +8 -7
- package/src/utils/git.ts +4 -4
- package/src/utils/image-loading.ts +98 -0
- package/src/utils/title-generator.ts +2 -3
- package/src/utils/tools-manager.ts +6 -6
- package/src/web/scrapers/choosealicense.ts +1 -1
- package/src/web/search/index.ts +3 -3
- package/src/autoresearch/command-initialize.md +0 -34
- package/src/patch/diff.ts +0 -433
- package/src/patch/index.ts +0 -888
- package/src/patch/parser.ts +0 -532
- package/src/patch/types.ts +0 -292
- package/src/prompts/agents/oracle.md +0 -77
- package/src/tools/pending-action.ts +0 -49
- package/src/utils/child-process.ts +0 -88
- package/src/utils/frontmatter.ts +0 -117
- package/src/utils/image-input.ts +0 -274
- package/src/utils/mime.ts +0 -53
- package/src/utils/prompt-format.ts +0 -170
|
@@ -8,14 +8,15 @@ import type { ToolDefinition } from "../../extensibility/extensions";
|
|
|
8
8
|
import type { Theme } from "../../modes/theme/theme";
|
|
9
9
|
import { replaceTabs, truncateToWidth } from "../../tools/render-utils";
|
|
10
10
|
import * as git from "../../utils/git";
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
11
|
+
import { applyAutoresearchContractToExperimentState } from "../apply-contract-to-state";
|
|
12
|
+
import { loadAutoresearchScriptSnapshot, pathMatchesContractPath, readAutoresearchContract } from "../contract";
|
|
13
|
+
import { computeRunModifiedPaths, getCurrentAutoresearchBranch, parseWorkDirDirtyPathsWithStatus } from "../git";
|
|
13
14
|
import {
|
|
14
|
-
AUTORESEARCH_COMMITTABLE_FILES,
|
|
15
15
|
formatNum,
|
|
16
16
|
inferMetricUnitFromName,
|
|
17
17
|
isAutoresearchCommittableFile,
|
|
18
18
|
isAutoresearchLocalStatePath,
|
|
19
|
+
isAutoresearchShCommand,
|
|
19
20
|
isBetter,
|
|
20
21
|
mergeAsi,
|
|
21
22
|
readPendingRunSummary,
|
|
@@ -61,7 +62,14 @@ const logExperimentSchema = Type.Object({
|
|
|
61
62
|
),
|
|
62
63
|
force: Type.Optional(
|
|
63
64
|
Type.Boolean({
|
|
64
|
-
description:
|
|
65
|
+
description:
|
|
66
|
+
"When true: skip ASI field requirements and allow keeping a run whose primary metric regressed versus the best kept run.",
|
|
67
|
+
}),
|
|
68
|
+
),
|
|
69
|
+
skip_restore: Type.Optional(
|
|
70
|
+
Type.Boolean({
|
|
71
|
+
description:
|
|
72
|
+
"When true and status is discard/crash/checks_failed: skip reverting the working tree to HEAD. Useful when the experiment did not modify tracked files or you want to preserve the current state.",
|
|
65
73
|
}),
|
|
66
74
|
),
|
|
67
75
|
asi: Type.Optional(
|
|
@@ -71,11 +79,6 @@ const logExperimentSchema = Type.Object({
|
|
|
71
79
|
),
|
|
72
80
|
});
|
|
73
81
|
|
|
74
|
-
interface PreservedFile {
|
|
75
|
-
content: Buffer;
|
|
76
|
-
path: string;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
82
|
interface KeepCommitResult {
|
|
80
83
|
error?: string;
|
|
81
84
|
note?: string;
|
|
@@ -102,10 +105,26 @@ export function createLogExperimentTool(
|
|
|
102
105
|
const runtime = options.getRuntime(ctx);
|
|
103
106
|
const state = runtime.state;
|
|
104
107
|
const workDir = resolveWorkDir(ctx.cwd);
|
|
105
|
-
|
|
106
|
-
|
|
108
|
+
|
|
109
|
+
const contractResult = readAutoresearchContract(workDir);
|
|
110
|
+
const scriptSnapshot = loadAutoresearchScriptSnapshot(workDir);
|
|
111
|
+
const contractErrors = [...contractResult.errors, ...scriptSnapshot.errors];
|
|
112
|
+
if (contractErrors.length > 0) {
|
|
107
113
|
return {
|
|
108
|
-
content: [{ type: "text", text: `Error: ${
|
|
114
|
+
content: [{ type: "text", text: `Error: ${contractErrors.join(" ")}` }],
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
const benchmarkForSync = contractResult.contract.benchmark;
|
|
118
|
+
if (benchmarkForSync.command && !isAutoresearchShCommand(benchmarkForSync.command)) {
|
|
119
|
+
return {
|
|
120
|
+
content: [
|
|
121
|
+
{
|
|
122
|
+
type: "text",
|
|
123
|
+
text:
|
|
124
|
+
"Error: Benchmark.command in autoresearch.md must invoke `autoresearch.sh` directly before logging. " +
|
|
125
|
+
"Fix autoresearch.md or move the workload into autoresearch.sh.",
|
|
126
|
+
},
|
|
127
|
+
],
|
|
109
128
|
};
|
|
110
129
|
}
|
|
111
130
|
|
|
@@ -116,6 +135,10 @@ export function createLogExperimentTool(
|
|
|
116
135
|
content: [{ type: "text", text: "Error: no unlogged run is available. Run run_experiment first." }],
|
|
117
136
|
};
|
|
118
137
|
}
|
|
138
|
+
|
|
139
|
+
applyAutoresearchContractToExperimentState(contractResult.contract, state);
|
|
140
|
+
const logPreamble =
|
|
141
|
+
"Refreshed session fields from autoresearch.md before logging (benchmark, scope, constraints).\n\n";
|
|
119
142
|
runtime.lastRunSummary = pendingRun;
|
|
120
143
|
runtime.lastRunAsi = pendingRun.parsedAsi;
|
|
121
144
|
runtime.lastRunChecks =
|
|
@@ -170,22 +193,20 @@ export function createLogExperimentTool(
|
|
|
170
193
|
};
|
|
171
194
|
}
|
|
172
195
|
|
|
196
|
+
const forceLoose = params.force === true;
|
|
173
197
|
const secondaryMetrics = buildSecondaryMetrics(params.metrics, pendingRun.parsedMetrics, state.metricName);
|
|
174
|
-
const validationError = validateSecondaryMetrics(state, secondaryMetrics, params.force ?? false);
|
|
175
|
-
if (validationError) {
|
|
176
|
-
return {
|
|
177
|
-
content: [{ type: "text", text: `Error: ${validationError}` }],
|
|
178
|
-
};
|
|
179
|
-
}
|
|
180
198
|
|
|
181
199
|
const mergedAsi = mergeAsi(runtime.lastRunAsi, sanitizeAsi(params.asi));
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
200
|
+
if (!forceLoose) {
|
|
201
|
+
const asiValidationError = validateAsiRequirements(mergedAsi, params.status);
|
|
202
|
+
if (asiValidationError) {
|
|
203
|
+
return {
|
|
204
|
+
content: [{ type: "text", text: `Error: ${asiValidationError}` }],
|
|
205
|
+
};
|
|
206
|
+
}
|
|
187
207
|
}
|
|
188
208
|
|
|
209
|
+
const preRunDirtyPaths = pendingRun.preRunDirtyPaths;
|
|
189
210
|
let keepScopeValidation: { committablePaths: string[] } | undefined;
|
|
190
211
|
if (params.status === "keep") {
|
|
191
212
|
const scopeValidation = await validateKeepPaths(options, workDir, state);
|
|
@@ -196,6 +217,7 @@ export function createLogExperimentTool(
|
|
|
196
217
|
}
|
|
197
218
|
const currentBestMetric = findBestKeptMetric(state.results, state.currentSegment, state.bestDirection);
|
|
198
219
|
if (
|
|
220
|
+
!forceLoose &&
|
|
199
221
|
currentBestMetric !== null &&
|
|
200
222
|
params.metric !== currentBestMetric &&
|
|
201
223
|
!isBetter(params.metric, currentBestMetric, state.bestDirection)
|
|
@@ -250,8 +272,8 @@ export function createLogExperimentTool(
|
|
|
250
272
|
};
|
|
251
273
|
}
|
|
252
274
|
gitNote = commitResult.note ?? null;
|
|
253
|
-
} else {
|
|
254
|
-
const revertResult = await revertFailedExperiment(options, workDir);
|
|
275
|
+
} else if (!params.skip_restore) {
|
|
276
|
+
const revertResult = await revertFailedExperiment(options, workDir, preRunDirtyPaths);
|
|
255
277
|
if (revertResult.error) {
|
|
256
278
|
return {
|
|
257
279
|
content: [{ type: "text", text: `Error: ${revertResult.error}` }],
|
|
@@ -309,7 +331,7 @@ export function createLogExperimentTool(
|
|
|
309
331
|
runtime.lastAutoResumePendingRunNumber = null;
|
|
310
332
|
|
|
311
333
|
const currentSegmentRuns = currentResults(state.results, state.currentSegment).length;
|
|
312
|
-
const text = buildLogText(state, experiment, currentSegmentRuns, wallClockSeconds, gitNote);
|
|
334
|
+
const text = logPreamble + buildLogText(state, experiment, currentSegmentRuns, wallClockSeconds, gitNote);
|
|
313
335
|
if (state.maxExperiments !== null && currentSegmentRuns >= state.maxExperiments) {
|
|
314
336
|
runtime.autoresearchMode = false;
|
|
315
337
|
options.pi.appendEntry(
|
|
@@ -432,23 +454,6 @@ export function validateAsiRequirements(asi: ASIData | undefined, status: Experi
|
|
|
432
454
|
return null;
|
|
433
455
|
}
|
|
434
456
|
|
|
435
|
-
function validateSecondaryMetrics(state: ExperimentState, metrics: NumericMetricMap, force: boolean): string | null {
|
|
436
|
-
if (state.secondaryMetrics.length === 0) return null;
|
|
437
|
-
const knownNames = new Set(state.secondaryMetrics.map(metric => metric.name));
|
|
438
|
-
const providedNames = new Set(Object.keys(metrics));
|
|
439
|
-
|
|
440
|
-
const missing = [...knownNames].filter(name => !providedNames.has(name));
|
|
441
|
-
if (missing.length > 0) {
|
|
442
|
-
return `missing secondary metrics: ${missing.join(", ")}`;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
const newMetrics = [...providedNames].filter(name => !knownNames.has(name));
|
|
446
|
-
if (newMetrics.length > 0 && !force) {
|
|
447
|
-
return `new secondary metrics require force=true: ${newMetrics.join(", ")}`;
|
|
448
|
-
}
|
|
449
|
-
return null;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
457
|
function registerSecondaryMetrics(state: ExperimentState, metrics: NumericMetricMap): void {
|
|
453
458
|
for (const name of Object.keys(metrics)) {
|
|
454
459
|
if (state.secondaryMetrics.some(metric => metric.name === name)) continue;
|
|
@@ -547,36 +552,11 @@ async function commitKeptExperiment(
|
|
|
547
552
|
async function revertFailedExperiment(
|
|
548
553
|
options: AutoresearchToolFactoryOptions,
|
|
549
554
|
workDir: string,
|
|
555
|
+
preRunDirtyPaths: string[],
|
|
550
556
|
): Promise<KeepCommitResult> {
|
|
551
|
-
|
|
552
|
-
try {
|
|
553
|
-
await git.restore(workDir, { files: ["."], source: "HEAD", staged: true, worktree: true });
|
|
554
|
-
} catch (err) {
|
|
555
|
-
restoreAutoresearchFiles(preservedFiles);
|
|
556
|
-
return {
|
|
557
|
-
error: `git restore failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
558
|
-
};
|
|
559
|
-
}
|
|
560
|
-
try {
|
|
561
|
-
await git.clean(workDir, { paths: ["."] });
|
|
562
|
-
} catch (err) {
|
|
563
|
-
restoreAutoresearchFiles(preservedFiles);
|
|
564
|
-
return {
|
|
565
|
-
error: `git clean failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
566
|
-
};
|
|
567
|
-
}
|
|
568
|
-
try {
|
|
569
|
-
await git.clean(workDir, { ignoredOnly: true, paths: ["."] });
|
|
570
|
-
} catch (err) {
|
|
571
|
-
restoreAutoresearchFiles(preservedFiles);
|
|
572
|
-
return {
|
|
573
|
-
error: `git clean -X failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
574
|
-
};
|
|
575
|
-
}
|
|
576
|
-
restoreAutoresearchFiles(preservedFiles);
|
|
577
|
-
let dirtyStatus = "";
|
|
557
|
+
let statusText: string;
|
|
578
558
|
try {
|
|
579
|
-
|
|
559
|
+
statusText = await git.status(workDir, {
|
|
580
560
|
pathspecs: ["."],
|
|
581
561
|
porcelainV1: true,
|
|
582
562
|
untrackedFiles: "all",
|
|
@@ -584,45 +564,37 @@ async function revertFailedExperiment(
|
|
|
584
564
|
});
|
|
585
565
|
} catch (err) {
|
|
586
566
|
return {
|
|
587
|
-
error: `git status failed
|
|
567
|
+
error: `git status failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
588
568
|
};
|
|
589
569
|
}
|
|
570
|
+
|
|
590
571
|
const workDirPrefix = await readGitWorkDirPrefix(options, workDir);
|
|
591
|
-
const
|
|
592
|
-
|
|
593
|
-
)
|
|
594
|
-
|
|
595
|
-
return {
|
|
596
|
-
error:
|
|
597
|
-
"Autoresearch cleanup left the worktree dirty. Resolve these paths before continuing: " +
|
|
598
|
-
remainingDirtyPaths.join(", "),
|
|
599
|
-
};
|
|
572
|
+
const { tracked, untracked } = computeRunModifiedPaths(preRunDirtyPaths, statusText, workDirPrefix);
|
|
573
|
+
const totalReverted = tracked.length + untracked.length;
|
|
574
|
+
if (totalReverted === 0) {
|
|
575
|
+
return { note: "nothing to revert" };
|
|
600
576
|
}
|
|
601
|
-
return { note: "reverted changes" };
|
|
602
|
-
}
|
|
603
577
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
});
|
|
613
|
-
}
|
|
614
|
-
const localStateDir = path.join(workDir, ".autoresearch");
|
|
615
|
-
if (fs.existsSync(localStateDir)) {
|
|
616
|
-
collectDirectoryFiles(localStateDir, files);
|
|
578
|
+
if (tracked.length > 0) {
|
|
579
|
+
try {
|
|
580
|
+
await git.restore(workDir, { files: tracked, source: "HEAD", staged: true, worktree: true });
|
|
581
|
+
} catch (err) {
|
|
582
|
+
return {
|
|
583
|
+
error: `git restore failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
584
|
+
};
|
|
585
|
+
}
|
|
617
586
|
}
|
|
618
|
-
return files;
|
|
619
|
-
}
|
|
620
587
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
588
|
+
for (const filePath of untracked) {
|
|
589
|
+
const absolutePath = path.join(workDir, filePath);
|
|
590
|
+
try {
|
|
591
|
+
fs.rmSync(absolutePath, { force: true, recursive: true });
|
|
592
|
+
} catch {
|
|
593
|
+
// Best-effort removal of untracked files
|
|
594
|
+
}
|
|
625
595
|
}
|
|
596
|
+
|
|
597
|
+
return { note: `reverted ${totalReverted} file${totalReverted === 1 ? "" : "s"}` };
|
|
626
598
|
}
|
|
627
599
|
|
|
628
600
|
function mergeStdoutStderr(result: { stderr: string; stdout: string }): string {
|
|
@@ -652,40 +624,26 @@ async function validateKeepPaths(
|
|
|
652
624
|
|
|
653
625
|
const workDirPrefix = await readGitWorkDirPrefix(options, workDir);
|
|
654
626
|
const committablePaths: string[] = [];
|
|
655
|
-
for (const
|
|
656
|
-
if (isAutoresearchLocalStatePath(
|
|
627
|
+
for (const entry of parseWorkDirDirtyPathsWithStatus(statusText, workDirPrefix)) {
|
|
628
|
+
if (isAutoresearchLocalStatePath(entry.path)) {
|
|
657
629
|
continue;
|
|
658
630
|
}
|
|
659
|
-
if (isAutoresearchCommittableFile(
|
|
660
|
-
committablePaths.push(
|
|
631
|
+
if (isAutoresearchCommittableFile(entry.path)) {
|
|
632
|
+
committablePaths.push(entry.path);
|
|
661
633
|
continue;
|
|
662
634
|
}
|
|
663
|
-
if (state.offLimits.some(spec => pathMatchesContractPath(
|
|
664
|
-
return `cannot keep this run because ${
|
|
635
|
+
if (state.offLimits.some(spec => pathMatchesContractPath(entry.path, spec))) {
|
|
636
|
+
return `cannot keep this run because ${entry.path} is listed under Off Limits in autoresearch.md`;
|
|
665
637
|
}
|
|
666
|
-
if (!state.scopePaths.some(spec => pathMatchesContractPath(
|
|
667
|
-
return `cannot keep this run because ${
|
|
638
|
+
if (!state.scopePaths.some(spec => pathMatchesContractPath(entry.path, spec))) {
|
|
639
|
+
return `cannot keep this run because ${entry.path} is outside Files in Scope`;
|
|
668
640
|
}
|
|
669
|
-
committablePaths.push(
|
|
641
|
+
committablePaths.push(entry.path);
|
|
670
642
|
}
|
|
671
643
|
|
|
672
644
|
return { committablePaths };
|
|
673
645
|
}
|
|
674
646
|
|
|
675
|
-
function collectDirectoryFiles(directory: string, files: PreservedFile[]): void {
|
|
676
|
-
for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
|
|
677
|
-
const absolutePath = path.join(directory, entry.name);
|
|
678
|
-
if (entry.isDirectory()) {
|
|
679
|
-
collectDirectoryFiles(absolutePath, files);
|
|
680
|
-
continue;
|
|
681
|
-
}
|
|
682
|
-
files.push({
|
|
683
|
-
content: fs.readFileSync(absolutePath),
|
|
684
|
-
path: absolutePath,
|
|
685
|
-
});
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
|
|
689
647
|
async function updateRunMetadata(
|
|
690
648
|
runDirectory: string | null,
|
|
691
649
|
metadata: {
|
|
@@ -8,7 +8,8 @@ import type { ToolDefinition } from "../../extensibility/extensions";
|
|
|
8
8
|
import type { Theme } from "../../modes/theme/theme";
|
|
9
9
|
import { DEFAULT_MAX_BYTES, DEFAULT_MAX_LINES, truncateTail } from "../../session/streaming-output";
|
|
10
10
|
import { replaceTabs, shortenPath, truncateToWidth } from "../../tools/render-utils";
|
|
11
|
-
import
|
|
11
|
+
import * as git from "../../utils/git";
|
|
12
|
+
import { parseWorkDirDirtyPaths } from "../git";
|
|
12
13
|
import {
|
|
13
14
|
EXPERIMENT_MAX_BYTES,
|
|
14
15
|
EXPERIMENT_MAX_LINES,
|
|
@@ -16,6 +17,7 @@ import {
|
|
|
16
17
|
formatNum,
|
|
17
18
|
getAutoresearchRunDirectory,
|
|
18
19
|
getNextAutoresearchRunNumber,
|
|
20
|
+
isAutoresearchLocalStatePath,
|
|
19
21
|
isAutoresearchShCommand,
|
|
20
22
|
killTree,
|
|
21
23
|
parseAsiLines,
|
|
@@ -40,6 +42,12 @@ const runExperimentSchema = Type.Object({
|
|
|
40
42
|
description: "Timeout in seconds for autoresearch.checks.sh. Defaults to 300.",
|
|
41
43
|
}),
|
|
42
44
|
),
|
|
45
|
+
force: Type.Optional(
|
|
46
|
+
Type.Boolean({
|
|
47
|
+
description:
|
|
48
|
+
"When true, allow a command that differs from the segment benchmark command and skip the rule that autoresearch.sh must be invoked directly when that script exists.",
|
|
49
|
+
}),
|
|
50
|
+
),
|
|
43
51
|
});
|
|
44
52
|
|
|
45
53
|
interface ProcessExecutionResult {
|
|
@@ -87,14 +95,9 @@ export function createRunExperimentTool(
|
|
|
87
95
|
const workDir = resolveWorkDir(ctx.cwd);
|
|
88
96
|
const checksPath = path.join(workDir, "autoresearch.checks.sh");
|
|
89
97
|
const autoresearchScriptPath = path.join(workDir, "autoresearch.sh");
|
|
90
|
-
const fingerprintError = getAutoresearchFingerprintMismatchError(state.segmentFingerprint, workDir);
|
|
91
|
-
if (fingerprintError) {
|
|
92
|
-
return {
|
|
93
|
-
content: [{ type: "text", text: `Error: ${fingerprintError}` }],
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
98
|
|
|
97
|
-
|
|
99
|
+
const forceCommand = params.force === true;
|
|
100
|
+
if (!forceCommand && state.benchmarkCommand && params.command.trim() !== state.benchmarkCommand) {
|
|
98
101
|
return {
|
|
99
102
|
content: [
|
|
100
103
|
{
|
|
@@ -107,7 +110,7 @@ export function createRunExperimentTool(
|
|
|
107
110
|
};
|
|
108
111
|
}
|
|
109
112
|
|
|
110
|
-
if (fs.existsSync(autoresearchScriptPath) && !isAutoresearchShCommand(params.command)) {
|
|
113
|
+
if (!forceCommand && fs.existsSync(autoresearchScriptPath) && !isAutoresearchShCommand(params.command)) {
|
|
111
114
|
return {
|
|
112
115
|
content: [
|
|
113
116
|
{
|
|
@@ -156,6 +159,17 @@ export function createRunExperimentTool(
|
|
|
156
159
|
const checksLogPath = path.join(runDirectory, "checks.log");
|
|
157
160
|
const runJsonPath = path.join(runDirectory, "run.json");
|
|
158
161
|
await fs.promises.mkdir(runDirectory, { recursive: true });
|
|
162
|
+
|
|
163
|
+
const preRunStatus = await git.status(workDir, {
|
|
164
|
+
porcelainV1: true,
|
|
165
|
+
untrackedFiles: "all",
|
|
166
|
+
z: true,
|
|
167
|
+
});
|
|
168
|
+
const workDirPrefix = await git.show.prefix(workDir);
|
|
169
|
+
const preRunDirtyPaths = parseWorkDirDirtyPaths(preRunStatus, workDirPrefix).filter(
|
|
170
|
+
p => !isAutoresearchLocalStatePath(p),
|
|
171
|
+
);
|
|
172
|
+
|
|
159
173
|
runtime.lastRunChecks = null;
|
|
160
174
|
runtime.lastRunDuration = null;
|
|
161
175
|
runtime.lastRunAsi = null;
|
|
@@ -171,6 +185,7 @@ export function createRunExperimentTool(
|
|
|
171
185
|
benchmarkLogPath,
|
|
172
186
|
checksLogPath,
|
|
173
187
|
command: params.command,
|
|
188
|
+
preRunDirtyPaths,
|
|
174
189
|
startedAt: new Date().toISOString(),
|
|
175
190
|
},
|
|
176
191
|
null,
|
|
@@ -287,6 +302,7 @@ export function createRunExperimentTool(
|
|
|
287
302
|
parsedAsi,
|
|
288
303
|
metricName: state.metricName,
|
|
289
304
|
metricUnit: state.metricUnit,
|
|
305
|
+
preRunDirtyPaths,
|
|
290
306
|
truncation: llmTruncation.truncated ? llmTruncation : undefined,
|
|
291
307
|
fullOutputPath: execution.logPath,
|
|
292
308
|
};
|
|
@@ -300,6 +316,7 @@ export function createRunExperimentTool(
|
|
|
300
316
|
parsedMetrics,
|
|
301
317
|
parsedPrimary,
|
|
302
318
|
passed: resultDetails.passed,
|
|
319
|
+
preRunDirtyPaths,
|
|
303
320
|
runDirectory,
|
|
304
321
|
runNumber,
|
|
305
322
|
};
|
|
@@ -329,6 +346,7 @@ export function createRunExperimentTool(
|
|
|
329
346
|
parsedMetrics,
|
|
330
347
|
parsedPrimary,
|
|
331
348
|
parsedAsi,
|
|
349
|
+
preRunDirtyPaths,
|
|
332
350
|
truncation: resultDetails.truncation,
|
|
333
351
|
fullOutputPath: resultDetails.fullOutputPath,
|
|
334
352
|
},
|
|
@@ -337,8 +355,28 @@ export function createRunExperimentTool(
|
|
|
337
355
|
),
|
|
338
356
|
);
|
|
339
357
|
|
|
358
|
+
const commandWarnings: string[] = [];
|
|
359
|
+
if (forceCommand) {
|
|
360
|
+
if (state.benchmarkCommand && params.command.trim() !== state.benchmarkCommand) {
|
|
361
|
+
commandWarnings.push(
|
|
362
|
+
`Warning: command override (force=true). Segment benchmark is ${state.benchmarkCommand}; ran ${params.command}.`,
|
|
363
|
+
);
|
|
364
|
+
}
|
|
365
|
+
if (fs.existsSync(autoresearchScriptPath) && !isAutoresearchShCommand(params.command)) {
|
|
366
|
+
commandWarnings.push(
|
|
367
|
+
"Warning: autoresearch.sh exists but the command was not a direct autoresearch.sh invocation (force=true).",
|
|
368
|
+
);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
const warningPrefix = commandWarnings.length > 0 ? `${commandWarnings.join("\n")}\n\n` : "";
|
|
372
|
+
|
|
340
373
|
return {
|
|
341
|
-
content: [
|
|
374
|
+
content: [
|
|
375
|
+
{
|
|
376
|
+
type: "text",
|
|
377
|
+
text: warningPrefix + buildRunText(resultDetails, llmTruncation.content, state.bestMetric),
|
|
378
|
+
},
|
|
379
|
+
],
|
|
342
380
|
details: resultDetails,
|
|
343
381
|
};
|
|
344
382
|
},
|
|
@@ -64,7 +64,6 @@ export interface ExperimentState {
|
|
|
64
64
|
scopePaths: string[];
|
|
65
65
|
offLimits: string[];
|
|
66
66
|
constraints: string[];
|
|
67
|
-
segmentFingerprint: string | null;
|
|
68
67
|
}
|
|
69
68
|
|
|
70
69
|
export interface RunExperimentProgressDetails {
|
|
@@ -96,6 +95,7 @@ export interface RunDetails {
|
|
|
96
95
|
parsedAsi: ASIData | null;
|
|
97
96
|
metricName: string;
|
|
98
97
|
metricUnit: string;
|
|
98
|
+
preRunDirtyPaths: string[];
|
|
99
99
|
truncation?: TruncationResult;
|
|
100
100
|
fullOutputPath?: string;
|
|
101
101
|
}
|
|
@@ -122,6 +122,7 @@ export interface PendingRunSummary {
|
|
|
122
122
|
parsedMetrics: NumericMetricMap | null;
|
|
123
123
|
parsedPrimary: number | null;
|
|
124
124
|
passed: boolean;
|
|
125
|
+
preRunDirtyPaths: string[];
|
|
125
126
|
runDirectory: string;
|
|
126
127
|
runNumber: number;
|
|
127
128
|
}
|
|
@@ -165,7 +166,6 @@ export interface AutoresearchJsonConfigEntry {
|
|
|
165
166
|
scopePaths?: string[];
|
|
166
167
|
offLimits?: string[];
|
|
167
168
|
constraints?: string[];
|
|
168
|
-
segmentFingerprint?: string;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
export interface AutoresearchJsonRunEntry {
|
package/src/capability/index.ts
CHANGED
|
@@ -114,8 +114,10 @@ async function loadImpl<T>(
|
|
|
114
114
|
const results = await Promise.all(
|
|
115
115
|
providers.map(async provider => {
|
|
116
116
|
try {
|
|
117
|
-
const result = await logger.
|
|
118
|
-
provider.
|
|
117
|
+
const result = await logger.time(
|
|
118
|
+
`capability:${capability.id}:${provider.id}`,
|
|
119
|
+
provider.load.bind(provider),
|
|
120
|
+
ctx,
|
|
119
121
|
);
|
|
120
122
|
return { provider, result };
|
|
121
123
|
} catch (error) {
|
|
@@ -4,12 +4,11 @@
|
|
|
4
4
|
import * as fs from "node:fs";
|
|
5
5
|
import * as path from "node:path";
|
|
6
6
|
import type { ImageContent } from "@oh-my-pi/pi-ai";
|
|
7
|
-
import { getProjectDir, isEnoent } from "@oh-my-pi/pi-utils";
|
|
7
|
+
import { getProjectDir, isEnoent, readImageMetadata } from "@oh-my-pi/pi-utils";
|
|
8
8
|
import chalk from "chalk";
|
|
9
9
|
import { resolveReadPath } from "../tools/path-utils";
|
|
10
10
|
import { formatBytes } from "../tools/render-utils";
|
|
11
11
|
import { formatDimensionNote, resizeImage } from "../utils/image-resize";
|
|
12
|
-
import { detectSupportedImageMimeTypeFromFile } from "../utils/mime";
|
|
13
12
|
|
|
14
13
|
// Keep CLI startup responsive and avoid OOM when users pass huge files.
|
|
15
14
|
// If a file exceeds these limits, we include it as a path-only <file/> block.
|
|
@@ -42,7 +41,8 @@ export async function processFileArguments(fileArgs: string[], options?: Process
|
|
|
42
41
|
process.exit(1);
|
|
43
42
|
}
|
|
44
43
|
|
|
45
|
-
const
|
|
44
|
+
const imageMetadata = await readImageMetadata(absolutePath);
|
|
45
|
+
const mimeType = imageMetadata?.mimeType;
|
|
46
46
|
const maxBytes = mimeType ? MAX_CLI_IMAGE_BYTES : MAX_CLI_TEXT_BYTES;
|
|
47
47
|
if (stat.size > maxBytes) {
|
|
48
48
|
console.error(
|
package/src/cli/grep-cli.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Handles `omp grep` subcommand for testing grep tool on Windows.
|
|
5
5
|
*/
|
|
6
6
|
import * as path from "node:path";
|
|
7
|
-
import { grep } from "@oh-my-pi/pi-natives";
|
|
7
|
+
import { GrepOutputMode, grep } from "@oh-my-pi/pi-natives";
|
|
8
8
|
import { APP_NAME } from "@oh-my-pi/pi-utils";
|
|
9
9
|
import chalk from "chalk";
|
|
10
10
|
|
|
@@ -14,7 +14,7 @@ export interface GrepCommandArgs {
|
|
|
14
14
|
glob?: string;
|
|
15
15
|
limit: number;
|
|
16
16
|
context: number;
|
|
17
|
-
mode:
|
|
17
|
+
mode: GrepOutputMode;
|
|
18
18
|
gitignore: boolean;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -32,7 +32,7 @@ export function parseGrepArgs(args: string[]): GrepCommandArgs | undefined {
|
|
|
32
32
|
path: ".",
|
|
33
33
|
limit: 20,
|
|
34
34
|
context: 2,
|
|
35
|
-
mode:
|
|
35
|
+
mode: GrepOutputMode.Content,
|
|
36
36
|
gitignore: true,
|
|
37
37
|
};
|
|
38
38
|
|
|
@@ -47,9 +47,9 @@ export function parseGrepArgs(args: string[]): GrepCommandArgs | undefined {
|
|
|
47
47
|
} else if (arg === "--context" || arg === "-C") {
|
|
48
48
|
result.context = parseInt(args[++i], 10);
|
|
49
49
|
} else if (arg === "--files" || arg === "-f") {
|
|
50
|
-
result.mode =
|
|
50
|
+
result.mode = GrepOutputMode.FilesWithMatches;
|
|
51
51
|
} else if (arg === "--count" || arg === "-c") {
|
|
52
|
-
result.mode =
|
|
52
|
+
result.mode = GrepOutputMode.Count;
|
|
53
53
|
} else if (arg === "--no-gitignore") {
|
|
54
54
|
result.gitignore = false;
|
|
55
55
|
} else if (!arg.startsWith("-")) {
|
|
@@ -89,7 +89,7 @@ export async function runGrepCommand(cmd: GrepCommandArgs): Promise<void> {
|
|
|
89
89
|
glob: cmd.glob,
|
|
90
90
|
mode: cmd.mode,
|
|
91
91
|
maxCount: cmd.limit,
|
|
92
|
-
context: cmd.mode ===
|
|
92
|
+
context: cmd.mode === GrepOutputMode.Content ? cmd.context : undefined,
|
|
93
93
|
hidden: true,
|
|
94
94
|
gitignore: cmd.gitignore,
|
|
95
95
|
});
|
|
@@ -105,7 +105,7 @@ export async function runGrepCommand(cmd: GrepCommandArgs): Promise<void> {
|
|
|
105
105
|
for (const match of result.matches) {
|
|
106
106
|
const displayPath = match.path.replace(/\\/g, "/");
|
|
107
107
|
|
|
108
|
-
if (cmd.mode ===
|
|
108
|
+
if (cmd.mode === GrepOutputMode.Content) {
|
|
109
109
|
if (match.contextBefore) {
|
|
110
110
|
for (const ctx of match.contextBefore) {
|
|
111
111
|
console.log(chalk.dim(`${displayPath}-${ctx.lineNumber}- ${ctx.line}`));
|
|
@@ -118,7 +118,7 @@ export async function runGrepCommand(cmd: GrepCommandArgs): Promise<void> {
|
|
|
118
118
|
}
|
|
119
119
|
}
|
|
120
120
|
console.log("");
|
|
121
|
-
} else if (cmd.mode ===
|
|
121
|
+
} else if (cmd.mode === GrepOutputMode.Count) {
|
|
122
122
|
console.log(`${chalk.cyan(displayPath)}: ${match.matchCount ?? 0} matches`);
|
|
123
123
|
} else {
|
|
124
124
|
console.log(chalk.cyan(displayPath));
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI handler for `omp grievances` — view reported tool issues from auto-QA.
|
|
3
|
+
*/
|
|
4
|
+
import { Database } from "bun:sqlite";
|
|
5
|
+
import chalk from "chalk";
|
|
6
|
+
import { getAutoQaDbPath } from "../tools/report-tool-issue";
|
|
7
|
+
|
|
8
|
+
interface GrievanceRow {
|
|
9
|
+
id: number;
|
|
10
|
+
model: string;
|
|
11
|
+
version: string;
|
|
12
|
+
tool: string;
|
|
13
|
+
report: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ListGrievancesOptions {
|
|
17
|
+
limit: number;
|
|
18
|
+
tool?: string;
|
|
19
|
+
json: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function openDb(): Database | null {
|
|
23
|
+
try {
|
|
24
|
+
const db = new Database(getAutoQaDbPath(), { readonly: true });
|
|
25
|
+
return db;
|
|
26
|
+
} catch {
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export async function listGrievances(options: ListGrievancesOptions): Promise<void> {
|
|
32
|
+
const db = openDb();
|
|
33
|
+
if (!db) {
|
|
34
|
+
if (options.json) {
|
|
35
|
+
console.log("[]");
|
|
36
|
+
} else {
|
|
37
|
+
console.log(
|
|
38
|
+
chalk.dim("No grievances database found. Enable auto-QA with PI_AUTO_QA=1 or the dev.autoqa setting."),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
let rows: GrievanceRow[];
|
|
46
|
+
if (options.tool) {
|
|
47
|
+
rows = db
|
|
48
|
+
.prepare("SELECT id, model, version, tool, report FROM grievances WHERE tool = ? ORDER BY id DESC LIMIT ?")
|
|
49
|
+
.all(options.tool, options.limit) as GrievanceRow[];
|
|
50
|
+
} else {
|
|
51
|
+
rows = db
|
|
52
|
+
.prepare("SELECT id, model, version, tool, report FROM grievances ORDER BY id DESC LIMIT ?")
|
|
53
|
+
.all(options.limit) as GrievanceRow[];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (options.json) {
|
|
57
|
+
console.log(JSON.stringify(rows, null, 2));
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (rows.length === 0) {
|
|
62
|
+
console.log(chalk.dim("No grievances recorded yet."));
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const row of rows) {
|
|
67
|
+
console.log(
|
|
68
|
+
`${chalk.dim(`#${row.id}`)} ${chalk.cyan(row.tool)} ${chalk.dim(`(${row.model} v${row.version})`)}`,
|
|
69
|
+
);
|
|
70
|
+
console.log(` ${row.report}`);
|
|
71
|
+
console.log();
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
console.log(chalk.dim(`Showing ${rows.length} most recent${options.tool ? ` for ${options.tool}` : ""}`));
|
|
75
|
+
} finally {
|
|
76
|
+
db.close();
|
|
77
|
+
}
|
|
78
|
+
}
|