Skip to content

Add --clean and out-of-repo skill staging - #18

Merged
willwashburn merged 3 commits into
mainfrom
local-mount
Apr 20, 2026
Merged

willwashburn merged 3 commits into
mainfrom
local-mount

Conversation

@willwashburn

@willwashburn willwashburn commented Apr 19, 2026 •

Copy link
Copy Markdown
Member

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//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.


Open in Devin Review

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.

This comment was marked as resolved.

devin-ai-integration[bot]

This comment was marked as resolved.

- 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>

This comment was marked as resolved.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread packages/cli/src/cli.ts
Comment on lines +246 to +249
function generateSessionRoot(personaId: string): string {
const sessionId = `${Date.now().toString(36)}-${randomBytes(4).toString('hex')}`;
return join(homedir(), '.agent-workforce', 'sessions', `${personaId}-${sessionId}`);
}

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment thread packages/cli/src/cli.ts
Comment on lines +229 to +232
function removeSessionRoot(sessionRoot: string | undefined): void {
if (!sessionRoot) return;
spawnSync('rm', ['-rf', sessionRoot], { stdio: 'ignore', shell: false });
}

Copilot AI Apr 20, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
- 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>
@willwashburn
willwashburn merged commit 750920e into main Apr 20, 2026
1 check passed
@willwashburn
willwashburn deleted the local-mount branch April 20, 2026 11:42
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants