f1c422af49
Closes Stream H Task 10 (H10) that was deferred from the initial Stream H
push. Adds two pure helpers to tools/subagent-prompt-prefix.mjs and wires
them into buildHeader() so subagents spawned inside a linked git worktree
get a SETUP block with vendor symlink + storage/framework mkdir guidance
in their injected prompt.
Two new exports:
1. detectWorktreeMode({cwd, gitDir, gitCommonDir}) — pure detector that
returns {isWorktree, parentRepoRoot}. Worktree is detected when the
per-worktree git-dir differs from the shared git-common-dir; the
parent repo root is derived by stripping the trailing `/.git` segment
from the common dir (separators normalized to forward slashes). Handles
null inputs gracefully and accepts mixed forward/backslash separators.
2. buildSetupBlock({isWorktree, parentRepoRoot, platform}) — pure renderer
that returns the SETUP — worktree bootstrap text block (or '' to omit
when not in a worktree or parentRepoRoot is missing). Picks `mklink /D`
on win32 vs `ln -s` elsewhere. Mentions all four storage/framework
subdirs (cache, sessions, views, testing) per memory
`feedback_subagent_worktree_bootstrap.md` — exactly what Pest 4 needs
to resolve the Eloquent facade and view cache paths inside a worktree.
buildHeader() now resolves --git-dir + --git-common-dir alongside the
existing --show-toplevel, calls detectWorktreeMode to classify the
spawn site, then inserts buildSetupBlock's output between rule 5 and
the END marker. When not in a worktree the block is empty and the header
layout is unchanged.
Regression: vitest tools 1785/1785 GREEN (was 1776; +9 tests across
"detectWorktreeMode (Stream H Task 10)" and "buildSetupBlock (Stream H
Task 10)" describe blocks in the new
tools/subagent-prompt-prefix-h10.test.mjs file). The pre-existing
tools/subagent-prompt-prefix.test.mjs is intentionally excluded from
vitest config (node:test runner used for subprocess-style tests) — H10
helpers are pure and live in the vitest scope so the new test file is
not added to the exclude list.
Stream H Task 10 of 11 — closes the deferred H10. Plan:
docs/superpowers/plans/2026-05-30-router-gate-v4-stream-H.md
89 lines
3.0 KiB
JavaScript
89 lines
3.0 KiB
JavaScript
// tools/subagent-prompt-prefix-h10.test.mjs
|
|
// Stream H Task 10 — worktree-bootstrap helper tests (vitest, separate file
|
|
// because subagent-prompt-prefix.test.mjs is in vitest config exclude list
|
|
// and uses node:test for subprocess-style runs).
|
|
import { describe, it, expect } from 'vitest';
|
|
import { detectWorktreeMode, buildSetupBlock } from './subagent-prompt-prefix.mjs';
|
|
|
|
describe('detectWorktreeMode (Stream H Task 10)', () => {
|
|
it('returns isWorktree=false when cwd .git is the same as common dir', () => {
|
|
const r = detectWorktreeMode({
|
|
cwd: 'c:/repo',
|
|
gitDir: 'c:/repo/.git',
|
|
gitCommonDir: 'c:/repo/.git',
|
|
});
|
|
expect(r.isWorktree).toBe(false);
|
|
expect(r.parentRepoRoot).toBeNull();
|
|
});
|
|
|
|
it('returns isWorktree=true when cwd is a linked worktree', () => {
|
|
const r = detectWorktreeMode({
|
|
cwd: 'c:/parent/v4-stream-A',
|
|
gitDir: 'c:/parent/.git/worktrees/v4-stream-A',
|
|
gitCommonDir: 'c:/parent/.git',
|
|
});
|
|
expect(r.isWorktree).toBe(true);
|
|
expect(r.parentRepoRoot).toBe('c:/parent');
|
|
});
|
|
|
|
it('handles null inputs gracefully', () => {
|
|
expect(detectWorktreeMode({}).isWorktree).toBe(false);
|
|
expect(detectWorktreeMode({ cwd: null, gitDir: null, gitCommonDir: null }).isWorktree).toBe(false);
|
|
});
|
|
|
|
it('handles different separators (backslashes) for parentRepoRoot extraction', () => {
|
|
const r = detectWorktreeMode({
|
|
cwd: 'c:/parent/wt-X',
|
|
gitDir: 'c:\\parent\\.git\\worktrees\\wt-X',
|
|
gitCommonDir: 'c:\\parent\\.git',
|
|
});
|
|
expect(r.isWorktree).toBe(true);
|
|
expect(r.parentRepoRoot).toBe('c:/parent');
|
|
});
|
|
});
|
|
|
|
describe('buildSetupBlock (Stream H Task 10)', () => {
|
|
it('returns empty string when not in a worktree', () => {
|
|
expect(buildSetupBlock({ isWorktree: false, parentRepoRoot: null })).toBe('');
|
|
});
|
|
|
|
it('returns a SETUP — worktree bootstrap block when in a worktree (win32)', () => {
|
|
const s = buildSetupBlock({
|
|
isWorktree: true,
|
|
parentRepoRoot: 'c:/parent',
|
|
platform: 'win32',
|
|
});
|
|
expect(s).toContain('SETUP — worktree bootstrap');
|
|
expect(s).toContain('mklink /D vendor');
|
|
expect(s).toContain('c:/parent/app/vendor');
|
|
expect(s).toContain('storage/framework');
|
|
});
|
|
|
|
it('returns a SETUP block with ln -s on linux/darwin', () => {
|
|
const s = buildSetupBlock({
|
|
isWorktree: true,
|
|
parentRepoRoot: '/home/u/repo',
|
|
platform: 'linux',
|
|
});
|
|
expect(s).toContain('SETUP — worktree bootstrap');
|
|
expect(s).toContain('ln -s');
|
|
expect(s).toContain('/home/u/repo/app/vendor');
|
|
});
|
|
|
|
it('mentions mkdir for cache/sessions/views/testing dirs', () => {
|
|
const s = buildSetupBlock({
|
|
isWorktree: true,
|
|
parentRepoRoot: 'c:/parent',
|
|
platform: 'win32',
|
|
});
|
|
expect(s).toContain('cache');
|
|
expect(s).toContain('sessions');
|
|
expect(s).toContain('views');
|
|
expect(s).toContain('testing');
|
|
});
|
|
|
|
it('returns empty when isWorktree=true but parentRepoRoot missing', () => {
|
|
expect(buildSetupBlock({ isWorktree: true, parentRepoRoot: null })).toBe('');
|
|
});
|
|
});
|