@oh-my-pi/pi-coding-agent 14.5.13 → 14.6.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.
- package/CHANGELOG.md +52 -0
- package/package.json +7 -7
- package/src/autoresearch/command-resume.md +5 -8
- package/src/autoresearch/git.ts +41 -51
- package/src/autoresearch/helpers.ts +43 -359
- package/src/autoresearch/index.ts +281 -273
- package/src/autoresearch/prompt-setup.md +43 -0
- package/src/autoresearch/prompt.md +52 -193
- package/src/autoresearch/resume-message.md +2 -8
- package/src/autoresearch/state.ts +59 -166
- package/src/autoresearch/storage.ts +687 -0
- package/src/autoresearch/tools/init-experiment.ts +201 -290
- package/src/autoresearch/tools/log-experiment.ts +304 -517
- package/src/autoresearch/tools/run-experiment.ts +117 -296
- package/src/autoresearch/tools/update-notes.ts +116 -0
- package/src/autoresearch/types.ts +16 -66
- package/src/commit/pipeline.ts +4 -3
- package/src/config/settings-schema.ts +1 -1
- package/src/config/settings.ts +20 -1
- package/src/config.ts +9 -6
- package/src/cursor.ts +1 -1
- package/src/edit/index.ts +9 -31
- package/src/edit/line-hash.ts +70 -43
- package/src/edit/modes/hashline.lark +26 -0
- package/src/edit/modes/hashline.ts +898 -1099
- package/src/edit/modes/patch.ts +0 -7
- package/src/edit/modes/replace.ts +0 -4
- package/src/edit/renderer.ts +22 -20
- package/src/edit/streaming.ts +8 -28
- package/src/eval/eval.lark +24 -30
- package/src/eval/js/context-manager.ts +5 -162
- package/src/eval/js/prelude.txt +0 -12
- package/src/eval/parse.ts +129 -129
- package/src/eval/py/kernel.ts +4 -4
- package/src/eval/py/prelude.py +1 -219
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +2 -2
- package/src/internal-urls/docs-index.generated.ts +1 -1
- package/src/main.ts +10 -0
- package/src/mcp/manager.ts +22 -0
- package/src/modes/components/session-observer-overlay.ts +5 -2
- package/src/modes/components/status-line/segments.ts +1 -1
- package/src/modes/components/status-line.ts +3 -5
- package/src/modes/components/tree-selector.ts +4 -5
- package/src/modes/components/welcome.ts +11 -1
- package/src/modes/controllers/command-controller.ts +2 -6
- package/src/modes/controllers/event-controller.ts +1 -2
- package/src/modes/controllers/extension-ui-controller.ts +3 -15
- package/src/modes/controllers/input-controller.ts +0 -1
- package/src/modes/controllers/selector-controller.ts +1 -1
- package/src/modes/interactive-mode.ts +5 -7
- package/src/modes/rpc/rpc-client.ts +9 -0
- package/src/modes/rpc/rpc-mode.ts +6 -0
- package/src/modes/rpc/rpc-types.ts +9 -0
- package/src/prompts/system/system-prompt.md +14 -38
- package/src/prompts/tools/ast-edit.md +8 -8
- package/src/prompts/tools/ast-grep.md +10 -10
- package/src/prompts/tools/eval.md +13 -31
- package/src/prompts/tools/find.md +2 -1
- package/src/prompts/tools/hashline.md +66 -57
- package/src/prompts/tools/search.md +2 -2
- package/src/sdk.ts +19 -4
- package/src/session/agent-session.ts +110 -4
- package/src/session/session-manager.ts +17 -13
- package/src/task/agents.ts +4 -5
- package/src/tools/archive-reader.ts +9 -3
- package/src/tools/ast-edit.ts +141 -44
- package/src/tools/ast-grep.ts +112 -36
- package/src/tools/browser/readable.ts +11 -6
- package/src/tools/browser/tab-supervisor.ts +2 -2
- package/src/tools/browser.ts +5 -3
- package/src/tools/eval.ts +2 -53
- package/src/tools/find.ts +16 -15
- package/src/tools/image-gen.ts +2 -2
- package/src/tools/path-utils.ts +36 -196
- package/src/tools/search.ts +56 -35
- package/src/tools/write.ts +8 -1
- package/src/utils/edit-mode.ts +2 -11
- package/src/utils/file-display-mode.ts +1 -1
- package/src/utils/git.ts +17 -0
- package/src/utils/session-color.ts +0 -12
- package/src/utils/title-generator.ts +22 -38
- package/src/web/scrapers/crossref.ts +3 -3
- package/src/web/scrapers/devto.ts +1 -1
- package/src/web/scrapers/discourse.ts +5 -5
- package/src/web/scrapers/firefox-addons.ts +1 -1
- package/src/web/scrapers/flathub.ts +2 -2
- package/src/web/scrapers/gitlab.ts +1 -1
- package/src/web/scrapers/go-pkg.ts +2 -2
- package/src/web/scrapers/jetbrains-marketplace.ts +1 -1
- package/src/web/scrapers/mastodon.ts +9 -9
- package/src/web/scrapers/mdn.ts +11 -7
- package/src/web/scrapers/pub-dev.ts +1 -1
- package/src/web/scrapers/rawg.ts +3 -3
- package/src/web/scrapers/readthedocs.ts +1 -1
- package/src/web/scrapers/spdx.ts +1 -1
- package/src/web/scrapers/stackoverflow.ts +2 -2
- package/src/web/scrapers/types.ts +53 -39
- package/src/web/scrapers/w3c.ts +1 -1
- package/src/web/search/providers/gemini.ts +2 -2
- package/src/autoresearch/apply-contract-to-state.ts +0 -24
- package/src/autoresearch/contract.ts +0 -288
- package/src/edit/modes/atom.lark +0 -29
- package/src/edit/modes/atom.ts +0 -1773
- package/src/prompts/tools/atom.md +0 -150
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [14.6.0] - 2026-05-02
|
|
6
|
+
### Breaking Changes
|
|
7
|
+
|
|
8
|
+
- Reworked autoresearch storage and protocol. State now lives in `~/.omp/autoresearch/<project>.db` (SQLite) and per-run logs in `~/.omp/autoresearch/<project>/runs/<id>/benchmark.log`. The repo-side artifacts `autoresearch.md`, `autoresearch.sh`, `autoresearch.checks.sh`, `autoresearch.program.md`, `autoresearch.ideas.md`, `autoresearch.jsonl`, `.autoresearch/`, and `autoresearch.config.json` are no longer read or written; they are deleted by `/autoresearch clear`. Any existing data is not migrated.
|
|
9
|
+
- Removed the autoresearch edit guard. `write`/`edit`/`ast_edit` are no longer blocked based on scope. Scope/off-limits are now post-hoc accountability fields on `log_experiment`.
|
|
10
|
+
- Replaced rigid `init_experiment` contract validation with a simpler schema: `name`, `goal`, `primary_metric`, `metric_unit`, `direction`, `secondary_metrics`, `scope_paths`, `off_limits`, `constraints`, `max_iterations`, `new_segment`. Removed `from_autoresearch_md`, `abandon_unlogged_runs`, `force`, and `preferred_command` flags — the harness `./autoresearch.sh` is the canonical workload, edit it and bump segment when you need to change it.
|
|
11
|
+
- `run_experiment` no longer accepts a `command` parameter. The tool always runs `bash autoresearch.sh`. To change the workload, edit the harness and call `init_experiment new_segment: true`. Removed `force`, `checks_timeout_seconds`, and the legacy `autoresearch.checks.sh` auto-execution; run validation through the regular `bash` tool.
|
|
12
|
+
- Replaced `log_experiment` ASI requirements and `force`/`skip_restore` flags with `justification` (post-hoc explanation for scope deviations) and `flag_runs` (mark earlier runs suspect to exclude them from baseline math). ASI is now opaque metadata.
|
|
13
|
+
- `/autoresearch clear` now resets the worktree to the session's recorded baseline commit (when on an `autoresearch/*` branch or with `--reset-tree`), closes the active session, and deletes any leftover legacy autoresearch repo artifacts.
|
|
14
|
+
- `/autoresearch` now refuses on a dirty worktree with an explicit error instead of silently continuing on the current branch. Commit or stash before invoking — the session needs a clean baseline on a dedicated `autoresearch/*` branch.
|
|
15
|
+
- Split `/autoresearch` into a two-phase protocol. Phase 1 (no session) prompts the agent to build the benchmark harness as `./autoresearch.sh` (must exit 0 and print `METRIC <name>=<value>`). Calling `init_experiment` ends Phase 1: it requires `./autoresearch.sh` to exist, auto-commits any pending harness changes on an `autoresearch/*` branch, then records that commit as the baseline. Phase 2 is the existing iteration loop.
|
|
16
|
+
- Autoresearch sessions are now scoped to the git branch they were created on. Switching off the `autoresearch/*` branch hides the dashboard widget, detaches the experiment tools, and skips the autoresearch system prompt; switching back resumes seamlessly. `/autoresearch` on a fresh branch starts a fresh session instead of resurrecting a session bound to a different branch.
|
|
17
|
+
- `log_experiment discard` no longer rewinds prior `keep` commits. On an `autoresearch/*` branch it now resets the worktree to `HEAD` (and `git clean`s untracked) instead of `git reset --hard $baseline_commit`. Discard reverts only the current iteration's uncommitted edits; previously kept improvements stay on the branch. `/autoresearch clear` continues to reset to the recorded baseline commit when explicitly requested.
|
|
18
|
+
- Autoresearch SQLite storage is now created lazily on first `init_experiment`. Running `omp` in a project that never invokes `/autoresearch` no longer creates a per-folder DB.
|
|
19
|
+
|
|
20
|
+
- Changed `search`, `find`, `ast_grep`, and `ast_edit` to accept `paths: string[]` instead of comma- or whitespace-delimited path strings.
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
|
|
24
|
+
- Added `update_notes` tool with `body` (replace) and `append_idea` (append a bullet under an `## Ideas` section). Notes are injected into the system prompt every iteration and replace the file-based `autoresearch.md` / `.program.md` / `.ideas.md` ecosystem.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- Updated `log_experiment` summary output to include the count of scope deviations detected for a run
|
|
29
|
+
- Used the active session context in autoresearch resume instructions instead of referencing deleted repo-side files
|
|
30
|
+
- Removed `PI_STRICT_EDIT_MODE`; model-specific edit mode fallbacks are no longer disableable by environment flag.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- Atom edit auto-rebase warning now dedupes by `(originalLid, rebasedLine)` pair. Previously, `@Lid` followed by N `+TEXT` lines emitted N identical "Auto-rebased anchor" warnings (one per cloned cursor anchor); now emits exactly one per distinct rebase.
|
|
35
|
+
- Atom/hashline diff preview no longer renders deleted lines with a 2-space hash placeholder (`-20 |old`) that visually mimicked a Lid. Removed lines now use `--` as the placeholder (`-20--|old`), making them unambiguously non-Lid.
|
|
36
|
+
- Atom/hashline diff preview no longer folds size-mismatched `-`/`+` runs into a confusing mix of `*` (paired modification) lines plus surplus `-`/`+` lines. The `*` collapse now applies only to clean 1:1 line replacements (same number of dels and adds); range replaces with N→M (N≠M) render as plain unified-diff `-` then `+` runs.
|
|
37
|
+
- Atom edits now warn when `@Lid` lands on a brace-opening line and the inserted content is at sibling indent (≤ anchor indent) — a foot-gun where the agent meant `^<nextSibling>` but the inserts ended up as the first body element of the `{...}` block.
|
|
38
|
+
- Atom auto-fix warning for adjacent-duplicate cleanup is now formatted as `AUTO-FIX applied — verify the result. Removed ...` instead of the easier-to-miss `Auto-fixed: removed ...`, and explains that `{}/()/[]` balance was the trigger.
|
|
39
|
+
- Fixed multi-target `search`, `ast-grep`, and `ast-edit` path handling by running each resolved target separately under root-level path resolution
|
|
40
|
+
- Fixed pagination and match/replacement summaries for multi-target AST and text searches so totals and affected file counts include all targets
|
|
41
|
+
- Fixed returned file paths for multi-target `search` and `ast-grep` results by normalizing them to the original search scope
|
|
42
|
+
- Fixed `log_experiment keep` silently dropping the iteration's diff on an autoresearch branch. The previous logic filtered out every path that was already dirty when `run_experiment` ran — but in the iteration cycle the agent's edits always land before `run_experiment`, so the entire iteration was filtered away and nothing was committed. On an autoresearch branch, `keep` now treats every currently-dirty path as the iteration's change and commits it.
|
|
43
|
+
|
|
44
|
+
## [14.5.14] - 2026-05-01
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- Changed markdown conversion and archive tooling to defer loading heavy dependencies (Turndown, fflate, and browser agent content) until first use, reducing startup overhead for CLI startup and command initialization
|
|
48
|
+
|
|
49
|
+
### Fixed
|
|
50
|
+
|
|
51
|
+
- Fixed changelog state tracking by flushing `lastChangelogVersion` to settings immediately when showing new entries, so the updated version is persisted across restarts
|
|
52
|
+
|
|
5
53
|
## [14.5.13] - 2026-05-01
|
|
6
54
|
|
|
7
55
|
### Breaking Changes
|
|
@@ -34,6 +82,10 @@
|
|
|
34
82
|
|
|
35
83
|
- Fixed eval startup messaging to report `eval` as unavailable when Python is unreachable and JavaScript backend is disabled
|
|
36
84
|
|
|
85
|
+
### Fixed
|
|
86
|
+
- Stabilized MCP tool ordering so reconnects and refreshes no longer reorder the tools array sent to the model. Anthropic prompt caching is keyed on byte-identical tool definitions; previously, the order depended on connection sequence and a single MCP server reconnect could shuffle tools across servers and invalidate the tools cache breakpoint.
|
|
87
|
+
- Skipped redundant system-prompt rebuilds in `AgentSession.refreshMCPTools` when the active tool set is unchanged. MCP transport flapping (e.g. routine 5-minute SSE reconnects) used to call `rebuildSystemPrompt` on every reconnect even though the resulting prompt was byte-identical, eating CPU and risking cache misses if the rebuild ever became non-deterministic. The applied-tool signature also covers `customWireName` so a wire-name flip with the rest of the tool metadata constant still forces a rebuild.
|
|
88
|
+
|
|
37
89
|
## [14.5.12] - 2026-04-30
|
|
38
90
|
|
|
39
91
|
### Breaking Changes
|
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.6.0",
|
|
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",
|
|
@@ -46,12 +46,12 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@agentclientprotocol/sdk": "0.20.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.6.0",
|
|
50
|
+
"@oh-my-pi/pi-agent-core": "14.6.0",
|
|
51
|
+
"@oh-my-pi/pi-ai": "14.6.0",
|
|
52
|
+
"@oh-my-pi/pi-natives": "14.6.0",
|
|
53
|
+
"@oh-my-pi/pi-tui": "14.6.0",
|
|
54
|
+
"@oh-my-pi/pi-utils": "14.6.0",
|
|
55
55
|
"@puppeteer/browsers": "^2.13.0",
|
|
56
56
|
"@sinclair/typebox": "^0.34.49",
|
|
57
57
|
"@xterm/headless": "^6.0.0",
|
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
Resume autoresearch
|
|
2
|
-
|
|
3
|
-
@{{autoresearch_md_path}}
|
|
1
|
+
Resume autoresearch on the active session.
|
|
4
2
|
|
|
5
3
|
{{branch_status_line}}
|
|
6
4
|
{{#if has_resume_context}}
|
|
@@ -10,8 +8,7 @@ Additional context from the user:
|
|
|
10
8
|
{{resume_context}}
|
|
11
9
|
{{/if}}
|
|
12
10
|
|
|
13
|
-
Use the
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
- keep iterating until interrupted or until the configured iteration cap is reached
|
|
11
|
+
- Use the active session context above as the source of truth for goal, scope, constraints, and run history.
|
|
12
|
+
- Inspect recent git history for context.
|
|
13
|
+
- Continue the most promising unfinished direction.
|
|
14
|
+
- Keep iterating until interrupted or until the configured iteration cap is reached.
|
package/src/autoresearch/git.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { ExtensionAPI } from "../extensibility/extensions";
|
|
2
2
|
import * as git from "../utils/git";
|
|
3
|
-
import {
|
|
3
|
+
import { normalizePathSpec } from "./helpers";
|
|
4
4
|
|
|
5
5
|
const AUTORESEARCH_BRANCH_PREFIX = "autoresearch/";
|
|
6
6
|
const BRANCH_NAME_MAX_LENGTH = 48;
|
|
@@ -11,9 +11,10 @@ export interface EnsureAutoresearchBranchFailure {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
export interface EnsureAutoresearchBranchSuccess {
|
|
14
|
-
branchName: string;
|
|
14
|
+
branchName: string | null;
|
|
15
15
|
created: boolean;
|
|
16
16
|
ok: true;
|
|
17
|
+
warning?: string;
|
|
17
18
|
}
|
|
18
19
|
|
|
19
20
|
export type EnsureAutoresearchBranchResult = EnsureAutoresearchBranchFailure | EnsureAutoresearchBranchSuccess;
|
|
@@ -23,6 +24,14 @@ export async function getCurrentAutoresearchBranch(_api: ExtensionAPI, workDir:
|
|
|
23
24
|
return currentBranch.startsWith(AUTORESEARCH_BRANCH_PREFIX) ? currentBranch : null;
|
|
24
25
|
}
|
|
25
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Ensure the working tree is on an `autoresearch/*` branch when possible.
|
|
29
|
+
*
|
|
30
|
+
* If the worktree is dirty and we're not already on an autoresearch branch, this returns
|
|
31
|
+
* `{ ok: true, branchName: null, warning }` rather than failing. The caller surfaces the
|
|
32
|
+
* warning and continues on the current branch — `keep` will skip auto-commits and `discard`
|
|
33
|
+
* will revert only run-modified paths instead of resetting to baseline.
|
|
34
|
+
*/
|
|
26
35
|
export async function ensureAutoresearchBranch(
|
|
27
36
|
api: ExtensionAPI,
|
|
28
37
|
workDir: string,
|
|
@@ -31,57 +40,48 @@ export async function ensureAutoresearchBranch(
|
|
|
31
40
|
const repoRoot = await git.repo.root(workDir);
|
|
32
41
|
if (!repoRoot) {
|
|
33
42
|
return {
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
ok: true,
|
|
44
|
+
branchName: null,
|
|
45
|
+
created: false,
|
|
46
|
+
warning:
|
|
47
|
+
"Not in a git repository — autoresearch will run without branch isolation, baseline reset, or auto-commits.",
|
|
36
48
|
};
|
|
37
49
|
}
|
|
38
50
|
|
|
39
51
|
let dirtyPathsOutput: string;
|
|
40
52
|
try {
|
|
41
|
-
dirtyPathsOutput = await git.status(repoRoot, {
|
|
42
|
-
porcelainV1: true,
|
|
43
|
-
untrackedFiles: "all",
|
|
44
|
-
z: true,
|
|
45
|
-
});
|
|
53
|
+
dirtyPathsOutput = await git.status(repoRoot, { porcelainV1: true, untrackedFiles: "all", z: true });
|
|
46
54
|
} catch (err) {
|
|
47
55
|
return {
|
|
48
|
-
error: `Unable to inspect git status before starting autoresearch: ${err instanceof Error ? err.message : String(err)}`,
|
|
49
56
|
ok: false,
|
|
57
|
+
error: `Unable to inspect git status before starting autoresearch: ${err instanceof Error ? err.message : String(err)}`,
|
|
50
58
|
};
|
|
51
59
|
}
|
|
52
60
|
|
|
53
61
|
const workDirPrefix = await readGitWorkDirPrefix(api, workDir);
|
|
54
|
-
const
|
|
62
|
+
const dirtyPaths = collectRelativeDirtyPaths(dirtyPathsOutput, workDirPrefix);
|
|
55
63
|
const currentBranch = await getCurrentAutoresearchBranch(api, workDir);
|
|
56
64
|
if (currentBranch) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
65
|
+
return { ok: true, branchName: currentBranch, created: false };
|
|
66
|
+
}
|
|
67
|
+
if (dirtyPaths.length > 0) {
|
|
68
|
+
const preview = formatDirtyPaths(dirtyPaths);
|
|
60
69
|
return {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
ok: true,
|
|
70
|
+
ok: false,
|
|
71
|
+
error: `Worktree is dirty (${preview}). Commit or stash these changes before starting autoresearch — a fresh autoresearch/* branch needs a clean baseline.`,
|
|
64
72
|
};
|
|
65
73
|
}
|
|
66
|
-
if (unsafeDirtyPaths.length > 0) {
|
|
67
|
-
return buildUnsafeDirtyPathsFailure(unsafeDirtyPaths);
|
|
68
|
-
}
|
|
69
74
|
|
|
70
75
|
const branchName = await allocateBranchName(api, workDir, goal);
|
|
71
76
|
try {
|
|
72
77
|
await git.branch.checkoutNew(workDir, branchName);
|
|
73
78
|
} catch (err) {
|
|
74
79
|
return {
|
|
75
|
-
error: `Failed to create autoresearch branch ${branchName}: ${err instanceof Error ? err.message : String(err)}`,
|
|
76
80
|
ok: false,
|
|
81
|
+
error: `Failed to create autoresearch branch ${branchName}: ${err instanceof Error ? err.message : String(err)}`,
|
|
77
82
|
};
|
|
78
83
|
}
|
|
79
|
-
|
|
80
|
-
return {
|
|
81
|
-
branchName,
|
|
82
|
-
created: true,
|
|
83
|
-
ok: true,
|
|
84
|
-
};
|
|
84
|
+
return { ok: true, branchName, created: true };
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
export function parseWorkDirDirtyPaths(statusOutput: string, workDirPrefix: string): string[] {
|
|
@@ -96,7 +96,7 @@ export function parseWorkDirDirtyPaths(statusOutput: string, workDirPrefix: stri
|
|
|
96
96
|
|
|
97
97
|
export function relativizeGitPathToWorkDir(repoRelativePath: string, workDirPrefix: string): string | null {
|
|
98
98
|
const normalizedPath = normalizeStatusPath(repoRelativePath);
|
|
99
|
-
const normalizedPrefix =
|
|
99
|
+
const normalizedPrefix = normalizePathSpec(workDirPrefix);
|
|
100
100
|
if (normalizedPrefix === "" || normalizedPrefix === ".") {
|
|
101
101
|
return normalizedPath;
|
|
102
102
|
}
|
|
@@ -106,7 +106,7 @@ export function relativizeGitPathToWorkDir(repoRelativePath: string, workDirPref
|
|
|
106
106
|
if (!normalizedPath.startsWith(`${normalizedPrefix}/`)) {
|
|
107
107
|
return null;
|
|
108
108
|
}
|
|
109
|
-
return
|
|
109
|
+
return normalizePathSpec(normalizedPath.slice(normalizedPrefix.length + 1));
|
|
110
110
|
}
|
|
111
111
|
|
|
112
112
|
async function readGitWorkDirPrefix(api: ExtensionAPI, workDir: string): Promise<string> {
|
|
@@ -162,12 +162,12 @@ function parseDirtyPathsLines(statusOutput: string): string[] {
|
|
|
162
162
|
return [...unsafePaths];
|
|
163
163
|
}
|
|
164
164
|
|
|
165
|
-
export function normalizeStatusPath(
|
|
166
|
-
let normalized =
|
|
165
|
+
export function normalizeStatusPath(rawPath: string): string {
|
|
166
|
+
let normalized = rawPath.trim();
|
|
167
167
|
if (normalized.startsWith('"') && normalized.endsWith('"')) {
|
|
168
168
|
normalized = normalized.slice(1, -1);
|
|
169
169
|
}
|
|
170
|
-
return
|
|
170
|
+
return normalizePathSpec(normalized);
|
|
171
171
|
}
|
|
172
172
|
|
|
173
173
|
async function allocateBranchName(api: ExtensionAPI, workDir: string, goal: string | null): Promise<string> {
|
|
@@ -209,32 +209,23 @@ function addDirtyPath(paths: Set<string>, rawPath: string): void {
|
|
|
209
209
|
paths.add(normalizedPath);
|
|
210
210
|
}
|
|
211
211
|
|
|
212
|
-
function buildUnsafeDirtyPathsFailure(unsafeDirtyPaths: string[]): EnsureAutoresearchBranchFailure {
|
|
213
|
-
const preview = unsafeDirtyPaths.slice(0, 5).join(", ");
|
|
214
|
-
const suffix = unsafeDirtyPaths.length > 5 ? ` (+${unsafeDirtyPaths.length - 5} more)` : "";
|
|
215
|
-
return {
|
|
216
|
-
error:
|
|
217
|
-
"Autoresearch needs a clean git worktree before it can create or reuse an isolated branch. " +
|
|
218
|
-
`Commit or stash these paths first: ${preview}${suffix}`,
|
|
219
|
-
ok: false,
|
|
220
|
-
};
|
|
221
|
-
}
|
|
222
|
-
|
|
223
212
|
function isRenameOrCopy(statusToken: string): boolean {
|
|
224
213
|
const trimmed = statusToken.trim();
|
|
225
214
|
return trimmed.startsWith("R") || trimmed.startsWith("C");
|
|
226
215
|
}
|
|
227
216
|
|
|
228
|
-
function
|
|
229
|
-
const
|
|
217
|
+
function collectRelativeDirtyPaths(statusOutput: string, workDirPrefix: string): string[] {
|
|
218
|
+
const dirtyPaths: string[] = [];
|
|
230
219
|
for (const dirtyPath of parseDirtyPaths(statusOutput)) {
|
|
231
220
|
const relativePath = relativizeGitPathToWorkDir(dirtyPath, workDirPrefix);
|
|
232
|
-
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
unsafeDirtyPaths.push(relativePath ?? normalizeStatusPath(dirtyPath));
|
|
221
|
+
dirtyPaths.push(relativePath ?? normalizeStatusPath(dirtyPath));
|
|
236
222
|
}
|
|
237
|
-
return
|
|
223
|
+
return dirtyPaths;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
function formatDirtyPaths(paths: string[]): string {
|
|
227
|
+
const preview = paths.slice(0, 5).join(", ");
|
|
228
|
+
return paths.length > 5 ? `${preview} (+${paths.length - 5} more)` : preview;
|
|
238
229
|
}
|
|
239
230
|
|
|
240
231
|
export interface DirtyPathEntry {
|
|
@@ -318,7 +309,6 @@ export function computeRunModifiedPaths(
|
|
|
318
309
|
const untracked: string[] = [];
|
|
319
310
|
for (const entry of parseWorkDirDirtyPathsWithStatus(currentStatusOutput, workDirPrefix)) {
|
|
320
311
|
if (preRunSet.has(entry.path)) continue;
|
|
321
|
-
if (isAutoresearchLocalStatePath(entry.path)) continue;
|
|
322
312
|
if (entry.untracked) {
|
|
323
313
|
untracked.push(entry.path);
|
|
324
314
|
} else {
|