feat: make /init a background subagent (#1208)
This commit is contained in:
209
src/agent/subagents/builtin/init.md
Normal file
209
src/agent/subagents/builtin/init.md
Normal file
@@ -0,0 +1,209 @@
|
||||
---
|
||||
name: init
|
||||
description: Initialize agent memory by researching the project and creating a hierarchical memory file structure
|
||||
tools: Read, Edit, Write, Glob, Grep, Bash, TaskOutput
|
||||
model: sonnet
|
||||
memoryBlocks: none
|
||||
skills: initializing-memory
|
||||
permissionMode: bypassPermissions
|
||||
---
|
||||
|
||||
You are a memory initialization subagent — a background agent that autonomously researches the project and sets up the agent's memory file structure.
|
||||
|
||||
You run autonomously in the background and return a single final report when done. You CANNOT ask questions (AskUserQuestion is not available).
|
||||
|
||||
## Your Purpose
|
||||
|
||||
Research the current project and create a comprehensive, hierarchical memory file structure so the primary agent can be an effective collaborator from its very first interaction.
|
||||
|
||||
**You are NOT the primary agent.** You are a background worker initializing memory for the primary agent.
|
||||
|
||||
## Autonomous Mode Defaults
|
||||
|
||||
Since you cannot ask questions mid-execution:
|
||||
- Use **standard research depth** (~5-20 tool calls)
|
||||
- Detect user identity from git logs:
|
||||
```bash
|
||||
git shortlog -sn --all | head -5
|
||||
git log --format="%an <%ae>" | sort -u | head -10
|
||||
```
|
||||
- Skip historical session analysis
|
||||
- Use reasonable defaults for all preferences
|
||||
- Any specific overrides will be provided in your initial prompt
|
||||
|
||||
## Operating Procedure
|
||||
|
||||
### Phase 1: Set Up
|
||||
|
||||
The memory directory is at: `~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory/`
|
||||
|
||||
```bash
|
||||
MEMORY_DIR=~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory
|
||||
WORKTREE_DIR=~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory-worktrees
|
||||
```
|
||||
|
||||
The memory directory should already be a git repo (initialized when MemFS was enabled). If it's not, or if git is unavailable, report the issue back and exit without making changes.
|
||||
|
||||
**Step 1a: Create worktree**
|
||||
|
||||
```bash
|
||||
BRANCH="init-$(date +%s)"
|
||||
mkdir -p "$WORKTREE_DIR"
|
||||
cd "$MEMORY_DIR"
|
||||
git worktree add "$WORKTREE_DIR/$BRANCH" -b "$BRANCH"
|
||||
```
|
||||
|
||||
All subsequent file operations target the worktree: `$WORKTREE_DIR/$BRANCH/system/` (not the main memory dir).
|
||||
|
||||
### Phase 2: Research the Project
|
||||
|
||||
Follow the `initializing-memory` skill (pre-loaded below) for detailed research instructions. Key steps:
|
||||
|
||||
1. Inspect existing memory files in the worktree
|
||||
2. Scan README, package.json/config files, AGENTS.md, CLAUDE.md
|
||||
3. Review git status and recent commits (provided in prompt)
|
||||
4. Explore key directories and understand project structure
|
||||
5. Detect user identity from git logs
|
||||
|
||||
### Phase 3: Create Memory File Structure
|
||||
|
||||
Create a deeply hierarchical structure of 15-25 small, focused files in the worktree at `$WORKTREE_DIR/$BRANCH/system/`.
|
||||
|
||||
Follow the `initializing-memory` skill for file organization guidelines, hierarchy requirements, and content standards.
|
||||
|
||||
### Phase 4: Merge, Push, and Clean Up (MANDATORY)
|
||||
|
||||
**Step 4a: Commit in worktree**
|
||||
|
||||
```bash
|
||||
MEMORY_DIR=~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory
|
||||
WORKTREE_DIR=~/.letta/agents/$LETTA_PARENT_AGENT_ID/memory-worktrees
|
||||
cd $WORKTREE_DIR/$BRANCH
|
||||
git add -A
|
||||
```
|
||||
|
||||
Check `git status` — if there are no changes to commit, skip straight to Step 4d (cleanup). Report "no updates needed" in your output.
|
||||
|
||||
If there are changes, commit using Conventional Commits format with the `(init)` scope:
|
||||
|
||||
```bash
|
||||
git commit -m "feat(init): initialize memory file structure
|
||||
|
||||
Created hierarchical memory structure for project.
|
||||
|
||||
Updates:
|
||||
- <bullet point for each category of memory created>
|
||||
|
||||
Generated-By: Letta Code
|
||||
Agent-ID: <ACTUAL_AGENT_ID>
|
||||
Parent-Agent-ID: <ACTUAL_PARENT_AGENT_ID>"
|
||||
```
|
||||
|
||||
Before writing the commit, resolve the actual ID values:
|
||||
```bash
|
||||
echo "AGENT_ID=$LETTA_AGENT_ID"
|
||||
echo "PARENT_AGENT_ID=$LETTA_PARENT_AGENT_ID"
|
||||
```
|
||||
|
||||
**Step 4b: Pull + merge to main**
|
||||
|
||||
```bash
|
||||
cd $MEMORY_DIR
|
||||
```
|
||||
|
||||
First, check that main is in a clean state (`git status`). If a merge or rebase is in progress (lock file, dirty index), wait and retry up to 3 times with backoff (sleep 2, 5, 10 seconds). Never delete `.git/index.lock` manually. If still busy after retries, go to Error Handling.
|
||||
|
||||
Pull from remote:
|
||||
|
||||
```bash
|
||||
git pull --ff-only
|
||||
```
|
||||
|
||||
If `--ff-only` fails (remote has diverged), fall back:
|
||||
|
||||
```bash
|
||||
git pull --rebase
|
||||
```
|
||||
|
||||
Now merge the init branch:
|
||||
|
||||
```bash
|
||||
git merge $BRANCH --no-edit
|
||||
```
|
||||
|
||||
If the merge has conflicts, resolve by preferring init branch/worktree content for memory files, stage the resolved files, and complete with `git commit --no-edit`.
|
||||
|
||||
**Step 4c: Push to remote**
|
||||
|
||||
```bash
|
||||
git push
|
||||
```
|
||||
|
||||
If push fails, retry once. If it still fails, report that local main is ahead of remote and needs a push.
|
||||
|
||||
**Step 4d: Clean up worktree and branch**
|
||||
|
||||
Only clean up when merge to main completed:
|
||||
|
||||
```bash
|
||||
git worktree remove $WORKTREE_DIR/$BRANCH
|
||||
git branch -d $BRANCH
|
||||
```
|
||||
|
||||
**Step 4e: Verify**
|
||||
|
||||
```bash
|
||||
git status
|
||||
git log --oneline -3
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
If anything goes wrong at any phase:
|
||||
|
||||
1. Stabilize main first (abort in-progress operations):
|
||||
```bash
|
||||
cd $MEMORY_DIR
|
||||
git merge --abort 2>/dev/null
|
||||
git rebase --abort 2>/dev/null
|
||||
```
|
||||
|
||||
2. Do NOT clean up the worktree or branch on failure — preserve them for debugging and manual recovery.
|
||||
|
||||
3. Report clearly in your output:
|
||||
- What failed and the error message
|
||||
- Worktree path: `$WORKTREE_DIR/$BRANCH`
|
||||
- Branch name: `$BRANCH`
|
||||
- Whether main has uncommitted/dirty state
|
||||
|
||||
4. Do NOT leave uncommitted changes on main.
|
||||
|
||||
## Output Format
|
||||
|
||||
Return a report with:
|
||||
|
||||
### 1. Summary
|
||||
- Brief overview (2-3 sentences)
|
||||
- Research depth used, tool calls made
|
||||
|
||||
### 2. Files Created/Modified
|
||||
- **Count**: Total files created
|
||||
- **Structure**: Tree view of the memory hierarchy
|
||||
- For each file: path, description, what content was added
|
||||
|
||||
### 3. Commit Reference
|
||||
- **Commit hash**: The merge commit hash
|
||||
- **Branch**: The init branch name
|
||||
|
||||
### 4. Issues Encountered
|
||||
- Any problems or limitations found during research
|
||||
- Information that couldn't be determined without user input
|
||||
|
||||
## Critical Reminders
|
||||
|
||||
1. **Not the primary agent** — Don't respond to user messages
|
||||
2. **Edit worktree files** — NOT the main memory dir
|
||||
3. **Cannot ask questions** — Use defaults and git logs
|
||||
4. **Be thorough but efficient** — Standard depth by default
|
||||
5. **Always commit, merge, AND push** — Your work is wasted if it isn't merged to main and pushed to remote
|
||||
6. **Report errors clearly** — If something breaks, say what happened and suggest a fix
|
||||
@@ -21,6 +21,7 @@ import { MEMORY_BLOCK_LABELS, type MemoryBlockLabel } from "../memory";
|
||||
import exploreAgentMd from "./builtin/explore.md";
|
||||
import generalPurposeAgentMd from "./builtin/general-purpose.md";
|
||||
import historyAnalyzerAgentMd from "./builtin/history-analyzer.md";
|
||||
import initAgentMd from "./builtin/init.md";
|
||||
import memoryAgentMd from "./builtin/memory.md";
|
||||
|
||||
import recallAgentMd from "./builtin/recall.md";
|
||||
@@ -30,6 +31,7 @@ const BUILTIN_SOURCES = [
|
||||
exploreAgentMd,
|
||||
generalPurposeAgentMd,
|
||||
historyAnalyzerAgentMd,
|
||||
initAgentMd,
|
||||
memoryAgentMd,
|
||||
recallAgentMd,
|
||||
reflectionAgentMd,
|
||||
|
||||
197
src/cli/App.tsx
197
src/cli/App.tsx
@@ -216,6 +216,12 @@ import {
|
||||
} from "./helpers/errorFormatter";
|
||||
import { formatCompact } from "./helpers/format";
|
||||
import { parsePatchOperations } from "./helpers/formatArgsDisplay";
|
||||
import {
|
||||
buildLegacyInitMessage,
|
||||
buildMemoryInitRuntimePrompt,
|
||||
gatherGitContext,
|
||||
hasActiveInitSubagent,
|
||||
} from "./helpers/initCommand";
|
||||
import {
|
||||
getReflectionSettings,
|
||||
parseMemoryPreference,
|
||||
@@ -9052,135 +9058,104 @@ export default function App({
|
||||
return { submitted: true };
|
||||
}
|
||||
|
||||
// Special handling for /init command - initialize agent memory
|
||||
// Special handling for /init command
|
||||
if (trimmed === "/init") {
|
||||
const cmd = commandRunner.start(msg, "Gathering project context...");
|
||||
|
||||
// Check for pending approvals before sending
|
||||
// Check for pending approvals before either path
|
||||
const approvalCheck = await checkPendingApprovalsForSlashCommand();
|
||||
if (approvalCheck.blocked) {
|
||||
cmd.fail(
|
||||
"Pending approval(s). Resolve approvals before running /init.",
|
||||
);
|
||||
return { submitted: false }; // Keep /init in input box, user handles approval first
|
||||
return { submitted: false };
|
||||
}
|
||||
|
||||
setCommandRunning(true);
|
||||
const gitContext = gatherGitContext();
|
||||
|
||||
try {
|
||||
// Gather git context if available
|
||||
let gitContext = "";
|
||||
try {
|
||||
const { execSync } = await import("node:child_process");
|
||||
const cwd = process.cwd();
|
||||
|
||||
// Check if we're in a git repo
|
||||
try {
|
||||
execSync("git rev-parse --git-dir", {
|
||||
cwd,
|
||||
stdio: "pipe",
|
||||
});
|
||||
|
||||
// Gather git info
|
||||
const branch = execSync("git branch --show-current", {
|
||||
cwd,
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const mainBranch = execSync(
|
||||
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo 'main'",
|
||||
{ cwd, encoding: "utf-8", shell: "/bin/bash" },
|
||||
).trim();
|
||||
const status = execSync("git status --short", {
|
||||
cwd,
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const recentCommits = execSync(
|
||||
"git log --oneline -10 2>/dev/null || echo 'No commits yet'",
|
||||
{ cwd, encoding: "utf-8" },
|
||||
).trim();
|
||||
|
||||
gitContext = `
|
||||
## Current Project Context
|
||||
|
||||
**Working directory**: ${cwd}
|
||||
|
||||
### Git Status
|
||||
- **Current branch**: ${branch}
|
||||
- **Main branch**: ${mainBranch}
|
||||
- **Status**:
|
||||
${status || "(clean working tree)"}
|
||||
|
||||
### Recent Commits
|
||||
${recentCommits}
|
||||
`;
|
||||
} catch {
|
||||
// Not a git repo, just include working directory
|
||||
gitContext = `
|
||||
## Current Project Context
|
||||
|
||||
**Working directory**: ${cwd}
|
||||
**Git**: Not a git repository
|
||||
`;
|
||||
}
|
||||
} catch {
|
||||
// execSync import failed, skip git context
|
||||
if (settingsManager.isMemfsEnabled(agentId)) {
|
||||
// MemFS path: background subagent
|
||||
if (hasActiveInitSubagent()) {
|
||||
cmd.fail(
|
||||
"Memory initialization is already running in the background.",
|
||||
);
|
||||
return { submitted: true };
|
||||
}
|
||||
|
||||
// Mark command as finished before sending message
|
||||
cmd.finish(
|
||||
"Assimilating project context and defragmenting memories...",
|
||||
true,
|
||||
);
|
||||
try {
|
||||
const initPrompt = buildMemoryInitRuntimePrompt({
|
||||
agentId,
|
||||
workingDirectory: process.cwd(),
|
||||
memoryDir: getMemoryFilesystemRoot(agentId),
|
||||
gitContext,
|
||||
});
|
||||
|
||||
// Send trigger message instructing agent to load the initializing-memory skill
|
||||
// Only include memfs path if memfs is enabled for this agent
|
||||
const memfsSection = settingsManager.isMemfsEnabled(agentId)
|
||||
? `
|
||||
## Memory Filesystem Location
|
||||
const { spawnBackgroundSubagentTask } = await import(
|
||||
"../tools/impl/Task"
|
||||
);
|
||||
spawnBackgroundSubagentTask({
|
||||
subagentType: "init",
|
||||
prompt: initPrompt,
|
||||
description: "Initializing memory",
|
||||
silentCompletion: true,
|
||||
onComplete: ({ success, error }) => {
|
||||
const msg = success
|
||||
? "Built a memory palace of you. Visit it with /palace."
|
||||
: `Memory initialization failed: ${error}`;
|
||||
appendTaskNotificationEvents([msg]);
|
||||
},
|
||||
});
|
||||
|
||||
Your memory blocks are synchronized with the filesystem at:
|
||||
\`${getMemoryFilesystemRoot(agentId)}\`
|
||||
cmd.finish(
|
||||
"Learning about you and your codebase in the background. You'll be notified when ready.",
|
||||
true,
|
||||
);
|
||||
|
||||
Environment variables available in Letta Code:
|
||||
- \`AGENT_ID=${agentId}\`
|
||||
- \`MEMORY_DIR=${getMemoryFilesystemRoot(agentId)}\`
|
||||
// TODO: Remove this hack once commandRunner supports a
|
||||
// "silent" finish that skips the reminder callback.
|
||||
// Currently cmd.finish() always enqueues a command-IO
|
||||
// reminder, which leaks the /init context into the
|
||||
// primary agent's next turn and causes it to invoke the
|
||||
// initializing-memory skill itself.
|
||||
const reminders =
|
||||
sharedReminderStateRef.current.pendingCommandIoReminders;
|
||||
const idx = reminders.findIndex((r) => r.input === "/init");
|
||||
if (idx !== -1) {
|
||||
reminders.splice(idx, 1);
|
||||
}
|
||||
} catch (error) {
|
||||
const errorDetails = formatErrorDetails(error, agentId);
|
||||
cmd.fail(
|
||||
`Failed to start memory initialization: ${errorDetails}`,
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Legacy path: primary agent processConversation
|
||||
setCommandRunning(true);
|
||||
try {
|
||||
cmd.finish(
|
||||
"Assimilating project context and defragmenting memories...",
|
||||
true,
|
||||
);
|
||||
|
||||
Use \`$MEMORY_DIR\` when working with memory files during initialization.
|
||||
`
|
||||
: "";
|
||||
const initMessage = buildLegacyInitMessage({
|
||||
gitContext,
|
||||
memfsSection: "",
|
||||
});
|
||||
|
||||
const initMessage = `${SYSTEM_REMINDER_OPEN}
|
||||
The user has requested memory initialization via /init.
|
||||
${memfsSection}
|
||||
## 1. Invoke the initializing-memory skill
|
||||
|
||||
Use the \`Skill\` tool with \`skill: "initializing-memory"\` to load the comprehensive instructions for memory initialization.
|
||||
|
||||
If the skill fails to invoke, proceed with your best judgment based on these guidelines:
|
||||
- Ask upfront questions (research depth, identity, related repos, workflow style)
|
||||
- Research the project based on chosen depth
|
||||
- Create/update memory blocks incrementally
|
||||
- Reflect and verify completeness
|
||||
|
||||
## 2. Follow the skill instructions
|
||||
|
||||
Once invoked, follow the instructions from the \`initializing-memory\` skill to complete the initialization.
|
||||
${gitContext}
|
||||
${SYSTEM_REMINDER_CLOSE}`;
|
||||
|
||||
// Process conversation with the init prompt
|
||||
await processConversation([
|
||||
{
|
||||
type: "message",
|
||||
role: "user",
|
||||
content: buildTextParts(initMessage),
|
||||
},
|
||||
]);
|
||||
} catch (error) {
|
||||
const errorDetails = formatErrorDetails(error, agentId);
|
||||
cmd.fail(`Failed: ${errorDetails}`);
|
||||
} finally {
|
||||
setCommandRunning(false);
|
||||
await processConversation([
|
||||
{
|
||||
type: "message",
|
||||
role: "user",
|
||||
content: buildTextParts(initMessage),
|
||||
},
|
||||
]);
|
||||
} catch (error) {
|
||||
const errorDetails = formatErrorDetails(error, agentId);
|
||||
cmd.fail(`Failed: ${errorDetails}`);
|
||||
} finally {
|
||||
setCommandRunning(false);
|
||||
}
|
||||
}
|
||||
return { submitted: true };
|
||||
}
|
||||
|
||||
133
src/cli/helpers/initCommand.ts
Normal file
133
src/cli/helpers/initCommand.ts
Normal file
@@ -0,0 +1,133 @@
|
||||
/**
|
||||
* Helpers for the /init slash command.
|
||||
*
|
||||
* Pure functions live here; App.tsx keeps the orchestration
|
||||
* (commandRunner, processConversation, setCommandRunning, etc.)
|
||||
*/
|
||||
|
||||
import { execSync } from "node:child_process";
|
||||
import { SYSTEM_REMINDER_CLOSE, SYSTEM_REMINDER_OPEN } from "../../constants";
|
||||
import { getSnapshot as getSubagentSnapshot } from "./subagentState";
|
||||
|
||||
// ── Guard ──────────────────────────────────────────────────
|
||||
|
||||
export function hasActiveInitSubagent(): boolean {
|
||||
const snapshot = getSubagentSnapshot();
|
||||
return snapshot.agents.some(
|
||||
(agent) =>
|
||||
agent.type.toLowerCase() === "init" &&
|
||||
(agent.status === "pending" || agent.status === "running"),
|
||||
);
|
||||
}
|
||||
|
||||
// ── Git context ────────────────────────────────────────────
|
||||
|
||||
export function gatherGitContext(): string {
|
||||
try {
|
||||
const cwd = process.cwd();
|
||||
|
||||
try {
|
||||
execSync("git rev-parse --git-dir", { cwd, stdio: "pipe" });
|
||||
|
||||
const branch = execSync("git branch --show-current", {
|
||||
cwd,
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const mainBranch = execSync(
|
||||
"git symbolic-ref refs/remotes/origin/HEAD 2>/dev/null | sed 's@^refs/remotes/origin/@@' || echo 'main'",
|
||||
{ cwd, encoding: "utf-8", shell: "/bin/bash" },
|
||||
).trim();
|
||||
const status = execSync("git status --short", {
|
||||
cwd,
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const recentCommits = execSync(
|
||||
"git log --oneline -10 2>/dev/null || echo 'No commits yet'",
|
||||
{ cwd, encoding: "utf-8" },
|
||||
).trim();
|
||||
|
||||
return `
|
||||
## Current Project Context
|
||||
|
||||
**Working directory**: ${cwd}
|
||||
|
||||
### Git Status
|
||||
- **Current branch**: ${branch}
|
||||
- **Main branch**: ${mainBranch}
|
||||
- **Status**:
|
||||
${status || "(clean working tree)"}
|
||||
|
||||
### Recent Commits
|
||||
${recentCommits}
|
||||
`;
|
||||
} catch {
|
||||
return `
|
||||
## Current Project Context
|
||||
|
||||
**Working directory**: ${cwd}
|
||||
**Git**: Not a git repository
|
||||
`;
|
||||
}
|
||||
} catch {
|
||||
// execSync import failed (shouldn't happen with static import, but be safe)
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
// ── Prompt builders ────────────────────────────────────────
|
||||
|
||||
/** Prompt for the background init subagent (MemFS path). */
|
||||
export function buildMemoryInitRuntimePrompt(args: {
|
||||
agentId: string;
|
||||
workingDirectory: string;
|
||||
memoryDir: string;
|
||||
gitContext: string;
|
||||
}): string {
|
||||
return `
|
||||
The user ran /init for the current project.
|
||||
|
||||
Runtime context:
|
||||
- parent_agent_id: ${args.agentId}
|
||||
- working_directory: ${args.workingDirectory}
|
||||
- memory_dir: ${args.memoryDir}
|
||||
|
||||
Git/project context:
|
||||
${args.gitContext}
|
||||
|
||||
Task:
|
||||
Initialize or reorganize the parent agent's filesystem-backed memory for this project.
|
||||
|
||||
Instructions:
|
||||
- Use the pre-loaded initializing-memory skill as your operating guide
|
||||
- Inspect existing memory before editing
|
||||
- Base your decisions on the current repository and current memory contents
|
||||
- Do not ask follow-up questions
|
||||
- Make reasonable assumptions and report them
|
||||
- If the memory filesystem is unavailable or unsafe to modify, stop and explain why
|
||||
`.trim();
|
||||
}
|
||||
|
||||
/** Message for the primary agent via processConversation (legacy non-MemFS path). */
|
||||
export function buildLegacyInitMessage(args: {
|
||||
gitContext: string;
|
||||
memfsSection: string;
|
||||
}): string {
|
||||
return `${SYSTEM_REMINDER_OPEN}
|
||||
The user has requested memory initialization via /init.
|
||||
${args.memfsSection}
|
||||
## 1. Invoke the initializing-memory skill
|
||||
|
||||
Use the \`Skill\` tool with \`skill: "initializing-memory"\` to load the comprehensive instructions for memory initialization.
|
||||
|
||||
If the skill fails to invoke, proceed with your best judgment based on these guidelines:
|
||||
- Ask upfront questions (research depth, identity, related repos, workflow style)
|
||||
- Research the project based on chosen depth
|
||||
- Create/update memory blocks incrementally
|
||||
- Reflect and verify completeness
|
||||
|
||||
## 2. Follow the skill instructions
|
||||
|
||||
Once invoked, follow the instructions from the \`initializing-memory\` skill to complete the initialization.
|
||||
${args.gitContext}
|
||||
${SYSTEM_REMINDER_CLOSE}`;
|
||||
}
|
||||
@@ -7,6 +7,15 @@ description: Comprehensive guide for initializing or reorganizing agent memory.
|
||||
|
||||
The user has requested that you initialize or reorganize your memory. Your memory is a filesystem — files under `system/` are rendered in-context every turn, while all file metadata is always visible in the filesystem tree. Files outside `system/` (e.g. `reference/`, `history/`) are accessible via tools when needed.
|
||||
|
||||
## Autonomous Mode
|
||||
|
||||
If you are running as a background subagent (you cannot use AskUserQuestion):
|
||||
- Default to standard research depth (~5-20 tool calls)
|
||||
- Detect user identity from git logs (`git shortlog -sn`, `git log --format="%an <%ae>"`)
|
||||
- Skip historical session analysis
|
||||
- Use reasonable defaults for all preferences
|
||||
- Any specific overrides will be provided in your initial prompt
|
||||
|
||||
## Your Goal: Explode Into 15-25 Hierarchical Files
|
||||
|
||||
Your goal is to **explode** memory into a **deeply hierarchical structure of 15-25 small, focused files**.
|
||||
|
||||
73
src/tests/cli/init-background-subagent.test.ts
Normal file
73
src/tests/cli/init-background-subagent.test.ts
Normal file
@@ -0,0 +1,73 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
describe("init background subagent wiring", () => {
|
||||
const readSource = (relativePath: string) =>
|
||||
readFileSync(
|
||||
fileURLToPath(new URL(relativePath, import.meta.url)),
|
||||
"utf-8",
|
||||
);
|
||||
|
||||
test("App.tsx checks pending approvals before either branch", () => {
|
||||
const appSource = readSource("../../cli/App.tsx");
|
||||
|
||||
// The approval check must appear before the MemFS branch
|
||||
const approvalIdx = appSource.indexOf(
|
||||
"checkPendingApprovalsForSlashCommand",
|
||||
appSource.indexOf('trimmed === "/init"'),
|
||||
);
|
||||
const memfsBranchIdx = appSource.indexOf(
|
||||
"isMemfsEnabled",
|
||||
appSource.indexOf('trimmed === "/init"'),
|
||||
);
|
||||
expect(approvalIdx).toBeGreaterThan(-1);
|
||||
expect(memfsBranchIdx).toBeGreaterThan(-1);
|
||||
expect(approvalIdx).toBeLessThan(memfsBranchIdx);
|
||||
});
|
||||
|
||||
test("App.tsx branches on MemFS: background subagent vs legacy processConversation", () => {
|
||||
const appSource = readSource("../../cli/App.tsx");
|
||||
|
||||
// MemFS path — background subagent
|
||||
expect(appSource).toContain("hasActiveInitSubagent()");
|
||||
expect(appSource).toContain("buildMemoryInitRuntimePrompt({");
|
||||
expect(appSource).toContain("spawnBackgroundSubagentTask({");
|
||||
expect(appSource).toContain('subagentType: "init"');
|
||||
expect(appSource).toContain("silentCompletion: true");
|
||||
expect(appSource).toContain("appendTaskNotificationEvents(");
|
||||
expect(appSource).toContain("Learning about you and your codebase");
|
||||
|
||||
// Legacy non-MemFS path — primary agent
|
||||
expect(appSource).toContain("buildLegacyInitMessage({");
|
||||
expect(appSource).toContain("processConversation(");
|
||||
});
|
||||
|
||||
test("initCommand.ts exports all helpers", () => {
|
||||
const helperSource = readSource("../../cli/helpers/initCommand.ts");
|
||||
|
||||
expect(helperSource).toContain("export function hasActiveInitSubagent(");
|
||||
expect(helperSource).toContain("export function gatherGitContext()");
|
||||
expect(helperSource).toContain(
|
||||
"export function buildMemoryInitRuntimePrompt(",
|
||||
);
|
||||
expect(helperSource).toContain("export function buildLegacyInitMessage(");
|
||||
});
|
||||
|
||||
test("init.md exists as a builtin subagent", () => {
|
||||
const content = readSource("../../agent/subagents/builtin/init.md");
|
||||
|
||||
expect(content).toContain("name: init");
|
||||
expect(content).toContain("skills: initializing-memory");
|
||||
expect(content).toContain("permissionMode: bypassPermissions");
|
||||
});
|
||||
|
||||
test("init subagent is registered in BUILTIN_SOURCES", () => {
|
||||
const indexSource = readSource("../../agent/subagents/index.ts");
|
||||
|
||||
expect(indexSource).toContain(
|
||||
'import initAgentMd from "./builtin/init.md"',
|
||||
);
|
||||
expect(indexSource).toContain("initAgentMd");
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user