fix(hindsight): share tags for bare worktrees
Bare-repository worktrees resolve their git common dir to the bare repo itself (for example /repos/foo.git), not to a directory literally named .git. The first fix only collapsed non-bare linked worktrees, so bare worktree layouts still fell back to each individual worktree root. Return the shared commonDir for bare repositories from both repo.primaryRoot and repo.primaryRootSync, update project-label docs, and cover two worktrees attached to one bare repository in the hindsight bank regression test. Fixes #2232
This commit is contained in:
@@ -62,7 +62,7 @@
|
||||
- Fixed pasting into the ask tool's "Other (type your own)" text box (and hook input/editor dialogs) on terminals with OSC 5522 enhanced paste (kitty protocol): the enhanced-paste focus routing only targets components exposing a `pasteText` hook, and the dialog wrappers had none, so the payload was stuffed into the main prompt editor hidden behind the dialog. `HookEditorComponent` and `HookInputComponent` now forward `pasteText` to their inner editor/input (pasting also resets the input dialog's timeout countdown like any keystroke).
|
||||
- Fixed auto-retry giving up after one attempt ("Provider requested Xms wait, exceeds retry.maxDelayMs") on a usage-limit 429 when every sibling account was only momentarily blocked: the retry delay now waits for the earliest sibling unblock when that comes sooner than the provider's multi-hour retry-after, so the next attempt picks up the recovered account instead of failing fast.
|
||||
- Fixed Hindsight `per-project-tagged` mental-model seeding so each project gets its own conventions/decisions models and session context only injects active-project or untagged models ([#2218](https://github.com/can1357/oh-my-pi/issues/2218)).
|
||||
- Fixed Hindsight `per-project-tagged` scoping siloing retains/recalls per linked git worktree: `projectLabel()` now resolves the primary checkout root via the new sync `git.repo.primaryRootSync` helper, so every worktree of one repo shares the same `project:<name>` tag and `per-project` bank id ([#2232](https://github.com/can1357/oh-my-pi/issues/2232)).
|
||||
- Fixed Hindsight `per-project-tagged` scoping siloing retains/recalls per linked git worktree: `projectLabel()` now resolves the primary checkout root (or shared bare-repo common dir) via the new sync `git.repo.primaryRootSync` helper, so every worktree of one repo shares the same `project:<name>` tag and `per-project` bank id ([#2232](https://github.com/can1357/oh-my-pi/issues/2232)).
|
||||
- Fixed Windows stdio MCP `.cmd` commands by wrapping batch shims with `cmd.exe /d /s /c` using the outer command quotes required by `cmd /s`, while preserving literal `%` and quoted JSON arguments for Codegraph MCP ([#2220](https://github.com/can1357/oh-my-pi/issues/2220)).
|
||||
- Fixed the bundled `explore` agent's `thinking-level: med` frontmatter — not a valid effort (`minimal`/`low`/`medium`/`high`/`xhigh`), so it silently parsed to undefined and the agent ran without its intended thinking level
|
||||
- Discovery context-file reads (`~/.claude`, `~/.cursor`, project trees, `@`-imports) now stat-gate to regular files before reading: a FIFO/socket/char device dropped where a context file is expected previously blocked startup forever on a read that can never see EOF.
|
||||
|
||||
@@ -58,8 +58,9 @@ function baseBankId(config: HindsightConfig): string {
|
||||
* Best-effort project label from a working-directory path.
|
||||
*
|
||||
* When `directory` lives inside a git repository we resolve the primary
|
||||
* checkout root via {@link git.repo.primaryRootSync} and basename that, so
|
||||
* every linked worktree of one repo shares the same `project:<name>` tag.
|
||||
* checkout root (or the shared common dir for bare-repo worktrees) via
|
||||
* {@link git.repo.primaryRootSync} and basename that, so every linked
|
||||
* worktree of one repo shares the same `project:<name>` tag.
|
||||
* Outside a repo (or when resolution fails), fall back to the cwd basename.
|
||||
*
|
||||
* Sync only: this runs on the hot path of `computeBankScope`, which is
|
||||
|
||||
@@ -1445,12 +1445,12 @@ export const repo = {
|
||||
return result.stdout.trim() || null;
|
||||
},
|
||||
|
||||
/** Resolve the primary repository root (not a worktree — the main checkout). */
|
||||
/** Resolve the primary checkout root, or the shared common dir for bare-repo worktrees. */
|
||||
async primaryRoot(cwd: string, signal?: AbortSignal): Promise<string | null> {
|
||||
const repository = await resolveRepository(cwd);
|
||||
if (repository) {
|
||||
if (path.basename(repository.commonDir) === ".git") return path.dirname(repository.commonDir);
|
||||
return repository.repoRoot;
|
||||
return repository.commonDir;
|
||||
}
|
||||
const repoRoot = await repo.root(cwd, signal);
|
||||
if (!repoRoot) return null;
|
||||
@@ -1459,20 +1459,21 @@ export const repo = {
|
||||
signal,
|
||||
});
|
||||
if (path.basename(commonDir.trim()) === ".git") return path.dirname(commonDir.trim());
|
||||
return repoRoot;
|
||||
return commonDir.trim();
|
||||
},
|
||||
|
||||
/**
|
||||
* Sync sibling of {@link primaryRoot}. Resolves only via on-disk `.git`/
|
||||
* `commondir` walking — no subprocess fallback — so it stays usable from
|
||||
* paths where async I/O is impractical (e.g. `computeBankScope`). Returns
|
||||
* `null` when `cwd` is outside a repository.
|
||||
* `null` when `cwd` is outside a repository. Bare-repo worktrees resolve to
|
||||
* the shared common dir (`foo.git`) because they have no primary checkout.
|
||||
*/
|
||||
primaryRootSync(cwd: string): string | null {
|
||||
const repository = resolveRepositorySync(cwd);
|
||||
if (!repository) return null;
|
||||
if (path.basename(repository.commonDir) === ".git") return path.dirname(repository.commonDir);
|
||||
return repository.repoRoot;
|
||||
return repository.commonDir;
|
||||
},
|
||||
|
||||
/** Full GitRepository metadata (sync). */
|
||||
|
||||
@@ -151,6 +151,9 @@ describe("computeBankScope", () => {
|
||||
let baseDir: string;
|
||||
let primaryRoot: string;
|
||||
let worktreeRoot: string;
|
||||
let bareRepoRoot: string;
|
||||
let bareWorktreeA: string;
|
||||
let bareWorktreeB: string;
|
||||
|
||||
beforeAll(async () => {
|
||||
baseDir = await fs.mkdtemp(path.join(os.tmpdir(), "hindsight-bank-worktree-"));
|
||||
@@ -164,6 +167,14 @@ describe("computeBankScope", () => {
|
||||
runGit(primaryRoot, ["add", "-A"]);
|
||||
runGit(primaryRoot, ["commit", "-m", "base"]);
|
||||
runGit(primaryRoot, ["worktree", "add", worktreeRoot, "-b", "feature-x"]);
|
||||
bareRepoRoot = path.join(baseDir, "bare-repo.git");
|
||||
bareWorktreeA = path.join(baseDir, "bare-a");
|
||||
bareWorktreeB = path.join(baseDir, "bare-b");
|
||||
runGit(baseDir, ["init", "--bare", bareRepoRoot]);
|
||||
runGit(primaryRoot, ["remote", "add", "bare", bareRepoRoot]);
|
||||
runGit(primaryRoot, ["push", "bare", "main"]);
|
||||
runGit(baseDir, ["--git-dir", bareRepoRoot, "worktree", "add", bareWorktreeA, "-b", "bare-a", "main"]);
|
||||
runGit(baseDir, ["--git-dir", bareRepoRoot, "worktree", "add", bareWorktreeB, "-b", "bare-b", "main"]);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
@@ -184,6 +195,16 @@ describe("computeBankScope", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("emits one shared project label across worktrees attached to a bare repository", () => {
|
||||
const fromA = computeBankScope(baseConfig({ scoping: "per-project-tagged" }), bareWorktreeA);
|
||||
const fromB = computeBankScope(baseConfig({ scoping: "per-project-tagged" }), bareWorktreeB);
|
||||
expect(fromA.retainTags).toEqual(["project:bare-repo.git"]);
|
||||
expect(fromB).toEqual(fromA);
|
||||
expect(computeBankScope(baseConfig({ scoping: "per-project" }), bareWorktreeB)).toEqual({
|
||||
bankId: "omp-bare-repo.git",
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to the cwd basename outside any repository", () => {
|
||||
// The temp parent dir is not itself a repo — it just contains one.
|
||||
expect(computeBankScope(baseConfig({ scoping: "per-project-tagged" }), baseDir).retainTags).toEqual([
|
||||
|
||||
Reference in New Issue
Block a user