Repository navigation
Add --clean and out-of-repo skill staging - #18
Conversation
Introduce interactive Claude session staging and a clean sandbox mode. Features: - New CLI flags: --install-in-repo (legacy) and --clean (mount sandbox). They are mutually exclusive. - By default interactive claude sessions stage skills under ~/.agent-workforce/sessions/<id>/claude/plugin and pass --plugin-dir to Claude so the repo's .claude/ is not touched. --install-in-repo restores legacy in-repo installs. - --clean launches the harness inside a @relayfile/local-mount symlink mount that hides repo-level Claude config (CLAUDE.md, CLAUDE.local.md, .claude, .mcp.json) while preserving persona skills and user-level ~/.claude/. One-shot runs and non-claude harnesses print notes/warnings and ignore --clean. Code changes: - CLI: parseAgentArgs, AgentFlags, session path generation, install root plumbing, decideCleanMode, launchOnMount integration, and wiring of pluginDirs to interactive spec. Added runtime checks and messaging for one-shot and non-claude behavior. - workload-router: add SkillMaterializationOptions.installRoot, materializeSkills/materializeSkillsFor/useSelection/usePersona support for installRoot, scaffold generation, session-mode install/cleanup behavior, and related tests. - harness-kit: support pluginDirs (emit --plugin-dir for claude, warn for others). - Tests added/updated across packages (cli, harness-kit, workload-router) exercising flag parsing, clean behavior, pluginDirs, and materialization. - package.json: add @relayfile/local-mount dependency and update README docs describing skill staging and clean mode. This change isolates interactive Claude sessions from repo artifacts and provides an opt-out legacy path, while leaving one-shot and other harnesses unchanged for v1.
- workload-router: session-mode install always emits the scaffold so `claude --plugin-dir <root>` resolves even for skill-less personas (e.g. posthog). Session cleanup unconditionally drops the stage dir. - cli: after each interactive session, `rm -rf` the whole session root so empty parent dirs don't accumulate under `~/.agent-workforce/sessions/`. - cli: `--clean` launchOnMount now merges `process.env` with persona env so PATH/HOME/etc. aren't stripped from the child. - cli: `isCliEntry` resolves argv[1] to absolute before pathToFileURL, so `node ./dist/cli.js` still detects the CLI entry. - Regenerate pnpm-lock.yaml for @relayfile/local-mount (unblocks CI's `pnpm install --frozen-lockfile`). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 9 out of 10 changed files in this pull request and generated 2 comments.
Files not reviewed (1)
- pnpm-lock.yaml: Language not supported
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| function generateSessionRoot(personaId: string): string { | ||
| const sessionId = `${Date.now().toString(36)}-${randomBytes(4).toString('hex')}`; | ||
| return join(homedir(), '.agent-workforce', 'sessions', `${personaId}-${sessionId}`); | ||
| } |
There was a problem hiding this comment.
personaId is used directly in the session directory name. Local persona overrides (loaded from the repo via the cascade) can set id arbitrarily, including path separators or absolute paths (e.g. "/tmp/x" or "../../.."). Because path.join() normalizes these, sessionRoot can escape ~/.agent-workforce/sessions/, and the subsequent cleanup (rm -rf) can delete an unintended location. Please sanitize/validate personaId for filesystem use (e.g. replace path separators, reject absolute paths / ".." segments) and/or resolve() the computed path and assert it stays within the intended sessions base directory before using it for installs/mounts/cleanup.
| function removeSessionRoot(sessionRoot: string | undefined): void { | ||
| if (!sessionRoot) return; | ||
| spawnSync('rm', ['-rf', sessionRoot], { stdio: 'ignore', shell: false }); | ||
| } |
There was a problem hiding this comment.
removeSessionRoot shells out to the external rm binary and ignores failures. This adds an extra runtime dependency (and is not portable to environments without coreutils), and it makes it harder to surface cleanup errors during debugging. Consider switching to fs.rmSync(sessionRoot, { recursive: true, force: true }) (or async fs.promises.rm) and logging on failure (possibly behind a debug flag).
- cli: the scaffold install step was gated on `install.plan.installs.length > 0`, which skipped running the scaffold for skill-less personas — leaving `--plugin-dir` pointing at a directory that never existed. Gate on `commandString !== ':'` so the scaffold always runs in session mode, with a "Staging session plugin dir" label when there are no actual installs. - workload-router: correct the `installRoot` docstring — the generated install command `mkdir -p`s the root and its parents, so the caller only needs to supply an absolute path. - cli/README: clarify that stage-dir cleanup (workload-router) and session-root cleanup (CLI `removeSessionRoot`) are separate scopes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce interactive Claude session staging and a clean sandbox mode.
Features:
Code changes:
This change isolates interactive Claude sessions from repo artifacts and provides an opt-out legacy path, while leaving one-shot and other harnesses unchanged for v1.