feat: Wire --bare mode across headless → pi-coding-agent → resource-loa…

- "src/headless.ts"
- "packages/pi-coding-agent/src/cli/args.ts"
- "packages/pi-coding-agent/src/main.ts"
- "src/tests/headless-cli-surface.test.ts"

GSD-Task: S02/T02
This commit is contained in:
Lex Christopherson 2026-03-26 11:39:25 -06:00
parent d355ab93fb
commit c5b38d69e3
4 changed files with 65 additions and 3 deletions

View file

@ -49,6 +49,8 @@ export interface Args {
fileArgs: string[];
/** Unknown flags (potentially extension flags) - map of flag name to value */
unknownFlags: Map<string, boolean | string>;
/** --bare: suppress CLAUDE.md/AGENTS.md, user skills, prompt templates, themes, project preferences */
bare?: boolean;
}
const VALID_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const;
@ -169,6 +171,8 @@ export function parseArgs(args: string[], extensionFlags?: Map<string, { type: "
}
} else if (arg === "--verbose") {
result.verbose = true;
} else if (arg === "--bare") {
result.bare = true;
} else if (arg === "--offline") {
result.offline = true;
} else if (arg.startsWith("@")) {

View file

@ -419,11 +419,13 @@ export async function main(args: string[]) {
additionalPromptTemplatePaths: firstPass.promptTemplates,
additionalThemePaths: firstPass.themes,
noExtensions: firstPass.noExtensions,
noSkills: firstPass.noSkills,
noPromptTemplates: firstPass.noPromptTemplates,
noThemes: firstPass.noThemes,
noSkills: firstPass.noSkills || firstPass.bare,
noPromptTemplates: firstPass.noPromptTemplates || firstPass.bare,
noThemes: firstPass.noThemes || firstPass.bare,
systemPrompt: firstPass.systemPrompt,
appendSystemPrompt: firstPass.appendSystemPrompt,
// --bare: suppress CLAUDE.md/AGENTS.md ancestor walk
...(firstPass.bare ? { agentsFilesOverride: () => ({ agentsFiles: [] }) } : {}),
});
await resourceLoader.reload();
time("resourceLoader.reload");

View file

@ -71,6 +71,7 @@ export interface HeadlessOptions {
answers?: string // path to answers JSON file
eventFilter?: Set<string> // filter JSONL output to specific event types
resumeSession?: string // session ID to resume (--resume <id>)
bare?: boolean // --bare: suppress CLAUDE.md/AGENTS.md, user skills, project preferences
}
interface TrackedEvent {
@ -158,6 +159,8 @@ export function parseHeadlessArgs(argv: string[]): HeadlessOptions {
}
} else if (arg === '--resume' && i + 1 < args.length) {
options.resumeSession = args[++i]
} else if (arg === '--bare') {
options.bare = true
}
} else if (!positionalStarted) {
positionalStarted = true
@ -306,6 +309,10 @@ async function runHeadlessOnce(options: HeadlessOptions, restartCount: number):
if (injector) {
clientOptions.env = injector.getSecretEnvVars()
}
// Propagate --bare to the child process
if (options.bare) {
clientOptions.args = [...((clientOptions.args as string[]) || []), '--bare']
}
const client = new RpcClient(clientOptions)

View file

@ -41,6 +41,7 @@ interface HeadlessOptions {
answers?: string
eventFilter?: Set<string>
resumeSession?: string
bare?: boolean
}
function parseHeadlessArgs(argv: string[]): HeadlessOptions {
@ -104,6 +105,8 @@ function parseHeadlessArgs(argv: string[]): HeadlessOptions {
options.responseTimeout = parseInt(args[++i], 10)
} else if (arg === '--resume' && i + 1 < args.length) {
options.resumeSession = args[++i]
} else if (arg === '--bare') {
options.bare = true
}
} else if (!positionalStarted) {
positionalStarted = true
@ -336,3 +339,49 @@ test('combined flags parse correctly', () => {
assert.equal(opts.verbose, true)
assert.equal(opts.command, 'auto')
})
// ─── --bare flag ───────────────────────────────────────────────────────────
test('--bare sets bare to true', () => {
const opts = parseHeadlessArgs(['node', 'gsd', 'headless', '--bare', 'auto'])
assert.equal(opts.bare, true)
assert.equal(opts.command, 'auto')
})
test('no --bare means bare is undefined', () => {
const opts = parseHeadlessArgs(['node', 'gsd', 'headless', 'auto'])
assert.equal(opts.bare, undefined)
})
test('--bare is a boolean flag (no value needed)', () => {
const opts = parseHeadlessArgs(['node', 'gsd', 'headless', '--bare', '--json', 'auto'])
assert.equal(opts.bare, true)
assert.equal(opts.json, true)
})
test('--bare combined with --output-format json', () => {
const opts = parseHeadlessArgs([
'node', 'gsd', 'headless',
'--bare',
'--output-format', 'json',
'auto',
])
assert.equal(opts.bare, true)
assert.equal(opts.outputFormat, 'json')
assert.equal(opts.json, true)
assert.equal(opts.command, 'auto')
})
test('--bare does not affect other flags', () => {
const opts = parseHeadlessArgs([
'node', 'gsd', 'headless',
'--bare',
'--timeout', '60000',
'--resume', 'sess-abc',
'auto',
])
assert.equal(opts.bare, true)
assert.equal(opts.timeout, 60000)
assert.equal(opts.resumeSession, 'sess-abc')
assert.equal(opts.command, 'auto')
})