Compare commits
No commits in common. "613758e9128446a788299d634c792c7306c2ad67" and "b4cc00e93c7b499cc4183ede557933d4327dba14" have entirely different histories.
613758e912
...
b4cc00e93c
4 changed files with 0 additions and 164 deletions
|
|
@ -163,19 +163,11 @@ export async function runAgent(options: RunAgentOptions): Promise<string> {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Promise<string>((resolve, reject) => {
|
return new Promise<string>((resolve, reject) => {
|
||||||
// IMPORTANT: --bare prevents CLAUDE.md walk-up through parent directories.
|
|
||||||
// Without this flag every agent inherits the DisClaw project CLAUDE.md as
|
|
||||||
// its identity (and any other CLAUDE.md files up the directory tree).
|
|
||||||
// --append-system-prompt-file injects the workspace-specific CLAUDE.md
|
|
||||||
// explicitly so each agent keeps its own isolated identity.
|
|
||||||
const args = [
|
const args = [
|
||||||
"-p",
|
"-p",
|
||||||
prompt,
|
prompt,
|
||||||
"--output-format",
|
"--output-format",
|
||||||
"json",
|
"json",
|
||||||
"--bare",
|
|
||||||
"--append-system-prompt-file",
|
|
||||||
path.join(workspacePath, "CLAUDE.md"),
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const child = spawn(claudeCommand, args, {
|
const child = spawn(claudeCommand, args, {
|
||||||
|
|
|
||||||
2
tests/fixtures/fake-claude.cmd
vendored
2
tests/fixtures/fake-claude.cmd
vendored
|
|
@ -1,2 +0,0 @@
|
||||||
@echo off
|
|
||||||
node "%~dp0fake-claude.mjs" %*
|
|
||||||
8
tests/fixtures/fake-claude.mjs
vendored
8
tests/fixtures/fake-claude.mjs
vendored
|
|
@ -1,8 +0,0 @@
|
||||||
#!/usr/bin/env node
|
|
||||||
const args = process.argv.slice(2);
|
|
||||||
const systemFileIdx = args.indexOf('--append-system-prompt-file');
|
|
||||||
console.log(JSON.stringify({
|
|
||||||
args,
|
|
||||||
hasBare: args.includes('--bare'),
|
|
||||||
systemPromptFile: systemFileIdx >= 0 ? args[systemFileIdx + 1] : null,
|
|
||||||
}));
|
|
||||||
|
|
@ -1,146 +0,0 @@
|
||||||
import { describe, it, expect, vi, beforeAll, afterAll, beforeEach, afterEach } from "vitest";
|
|
||||||
import * as fs from "fs";
|
|
||||||
import * as os from "os";
|
|
||||||
import * as path from "path";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Integration test: verifies that every Claude CLI invocation includes
|
|
||||||
* --bare (prevents CLAUDE.md walk-up through parent directories) and
|
|
||||||
* --append-system-prompt-file pointing to the workspace CLAUDE.md.
|
|
||||||
*
|
|
||||||
* We mock cross-spawn so no real process is launched, and capture the exact
|
|
||||||
* args array that runner.ts would pass to the CLI.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Capture spawned args via a mock before any module imports runner
|
|
||||||
let capturedArgs: string[] = [];
|
|
||||||
|
|
||||||
vi.mock("cross-spawn", () => {
|
|
||||||
// Minimal EventEmitter-like child mock that immediately closes with code 0
|
|
||||||
function makeMockChild(args: string[]) {
|
|
||||||
capturedArgs = args;
|
|
||||||
const listeners: Record<string, Array<(...a: unknown[]) => void>> = {};
|
|
||||||
|
|
||||||
const stdioStream = {
|
|
||||||
setEncoding: () => {},
|
|
||||||
on: (event: string, cb: (...a: unknown[]) => void) => {
|
|
||||||
if (!listeners[event]) listeners[event] = [];
|
|
||||||
listeners[event].push(cb);
|
|
||||||
// emit 'data' with a JSON payload that looks like our fake-claude output
|
|
||||||
if (event === "data") {
|
|
||||||
Promise.resolve().then(() =>
|
|
||||||
cb(
|
|
||||||
JSON.stringify({
|
|
||||||
args,
|
|
||||||
hasBare: args.includes("--bare"),
|
|
||||||
systemPromptFile:
|
|
||||||
args.indexOf("--append-system-prompt-file") >= 0
|
|
||||||
? args[args.indexOf("--append-system-prompt-file") + 1]
|
|
||||||
: null,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const child = {
|
|
||||||
stdout: { ...stdioStream },
|
|
||||||
stderr: {
|
|
||||||
setEncoding: () => {},
|
|
||||||
on: () => {},
|
|
||||||
},
|
|
||||||
stdin: { end: () => {} },
|
|
||||||
on: (event: string, cb: (...a: unknown[]) => void) => {
|
|
||||||
if (!listeners[event]) listeners[event] = [];
|
|
||||||
listeners[event].push(cb);
|
|
||||||
if (event === "close") {
|
|
||||||
// Fire 'close' after stdout data has been emitted
|
|
||||||
Promise.resolve()
|
|
||||||
.then(() => Promise.resolve())
|
|
||||||
.then(() => cb(0));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
kill: () => {},
|
|
||||||
};
|
|
||||||
|
|
||||||
return child;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
default: (_cmd: string, args: string[]) => makeMockChild(args),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
// Import after mock registration
|
|
||||||
import { runAgent } from "../../src/agent/runner";
|
|
||||||
import { _resetResolveClaudeCache } from "../../src/runtime/resolve-claude";
|
|
||||||
|
|
||||||
describe("no-parent-claude-md-leak", () => {
|
|
||||||
let workspaceDir: string;
|
|
||||||
const originalEnv = { ...process.env };
|
|
||||||
|
|
||||||
beforeAll(() => {
|
|
||||||
// Create a minimal temp workspace with agent.yaml and CLAUDE.md
|
|
||||||
workspaceDir = fs.mkdtempSync(path.join(os.tmpdir(), "disclaw-test-"));
|
|
||||||
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(workspaceDir, "agent.yaml"),
|
|
||||||
`name: test-agent\ndisplay_name: Test Agent\nrole: Testing\nchannel_id: "123456789"\n`,
|
|
||||||
"utf-8"
|
|
||||||
);
|
|
||||||
fs.writeFileSync(
|
|
||||||
path.join(workspaceDir, "CLAUDE.md"),
|
|
||||||
"# Test Agent\nYou are a test agent.\n",
|
|
||||||
"utf-8"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
beforeEach(() => {
|
|
||||||
capturedArgs = [];
|
|
||||||
// Reset the resolve-claude cache and set a dummy CLAUDE_PATH
|
|
||||||
_resetResolveClaudeCache();
|
|
||||||
process.env.CLAUDE_PATH = "/fake/claude";
|
|
||||||
});
|
|
||||||
|
|
||||||
afterEach(() => {
|
|
||||||
process.env = { ...originalEnv };
|
|
||||||
_resetResolveClaudeCache();
|
|
||||||
});
|
|
||||||
|
|
||||||
afterAll(() => {
|
|
||||||
try {
|
|
||||||
fs.rmSync(workspaceDir, { recursive: true, force: true });
|
|
||||||
} catch {
|
|
||||||
// Best-effort cleanup
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
it("passes --bare flag to the Claude CLI", async () => {
|
|
||||||
await runAgent({
|
|
||||||
workspacePath: workspaceDir,
|
|
||||||
channelName: "test-channel",
|
|
||||||
userMessage: "hello",
|
|
||||||
conversationHistory: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(capturedArgs).toContain("--bare");
|
|
||||||
});
|
|
||||||
|
|
||||||
it("passes --append-system-prompt-file pointing to workspace CLAUDE.md", async () => {
|
|
||||||
await runAgent({
|
|
||||||
workspacePath: workspaceDir,
|
|
||||||
channelName: "test-channel",
|
|
||||||
userMessage: "hello",
|
|
||||||
conversationHistory: [],
|
|
||||||
});
|
|
||||||
|
|
||||||
const idx = capturedArgs.indexOf("--append-system-prompt-file");
|
|
||||||
expect(idx).toBeGreaterThanOrEqual(0);
|
|
||||||
|
|
||||||
const systemPromptFile = capturedArgs[idx + 1];
|
|
||||||
expect(systemPromptFile).toMatch(/CLAUDE\.md$/);
|
|
||||||
// Must point to the workspace-specific CLAUDE.md, not a parent directory
|
|
||||||
expect(systemPromptFile).toBe(path.join(workspaceDir, "CLAUDE.md"));
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Loading…
Reference in a new issue