refactor: make bundled agents internal
This commit is contained in:
parent
18aa257ede
commit
5ce9df2e37
29 changed files with 161 additions and 89 deletions
|
|
@ -655,14 +655,34 @@
|
|||
|
||||
### src/resources/agents/
|
||||
|
||||
Bundled autonomous worker-pool subagents. SF is primarily built for autonomous
|
||||
operation, not as a general CLI coder with a persona menu, so these are routing
|
||||
primitives for SF workflows rather than user-facing agent products. Markdown
|
||||
frontmatter is the default format for one-body worker prompts. Use `.agent.yaml`
|
||||
when the agent needs structured runtime metadata such as `promptParts`,
|
||||
tool-policy contracts, or workflow-gate semantics.
|
||||
|
||||
| File | System Label(s) | Description |
|
||||
|------|-----------------|-------------|
|
||||
| critic.md | Internal Subagent | Adversarial pre-implementation review agent used by execution flows |
|
||||
| reviewer.md | Internal Subagent | Structured code/design review agent |
|
||||
| javascript-pro.md | Subagent | JavaScript specialist agent definition |
|
||||
| typescript-pro.md | Subagent | TypeScript specialist agent definition |
|
||||
| worker.md | Subagent | Generic worker agent definition |
|
||||
| researcher.md | Subagent | Research and exploration agent definition |
|
||||
| scout.md | Subagent | Scout/pathfinding agent definition |
|
||||
|
||||
### src/resources/extensions/sf/agents/
|
||||
|
||||
SF-owned workflow agents. YAML is the canonical format here because these
|
||||
agents are loaded as part of SF control flows and may declare named
|
||||
`promptParts`, tool allowlists, and machine-checked output contracts.
|
||||
|
||||
| File | System Label(s) | Description |
|
||||
|------|-----------------|-------------|
|
||||
| review-code.agent.yaml | SF Workflow Agent | Triage apply review gate; emits `review-code: agree` only when the plan can mutate state |
|
||||
| triage-decider.agent.yaml | SF Workflow Agent | Plan-only self-feedback triage decider |
|
||||
|
||||
### src/resources/skills/
|
||||
|
||||
| Skill Directory | System Label(s) | Description |
|
||||
|
|
|
|||
|
|
@ -28,6 +28,25 @@ The names below are separate axes. Do not use one as a synonym for another.
|
|||
|
||||
The flow is the product behavior: how SF captures intent, plans work, applies policy, executes tasks, records evidence, and reports status. Flow behavior must not fork by UI surface. If a TUI run and a non-interactive run receive the same state, run control, and permission profile, they should follow the same control model.
|
||||
|
||||
## Product Modes
|
||||
|
||||
SF's user-facing product modes are about how much intent has been captured and
|
||||
how much control the operator wants to retain. They are not agent personas.
|
||||
|
||||
- **Assisted build** — SF works step by step with the operator. It asks
|
||||
clarifying questions when needed, proposes bounded next actions, and pauses at
|
||||
important gates before continuing.
|
||||
- **Plan/discuss** — the operator explores what they want, constraints, taste,
|
||||
risks, and scope. On exit, SF converts the discussion into structured
|
||||
database-backed planning state: milestones, slices, tasks, requirements,
|
||||
decisions, or follow-up questions.
|
||||
- **Autonomous** — SF attempts the full loop: research missing context, plan the
|
||||
work, execute bounded units, verify, record evidence, update state, and keep
|
||||
going until completion, policy, budget, confidence, or a gate stops it.
|
||||
|
||||
Bundled agents are implementation machinery inside these modes. They should
|
||||
not be presented as the product model.
|
||||
|
||||
## Surface
|
||||
|
||||
A surface is where a person or program drives or observes the flow.
|
||||
|
|
@ -149,11 +168,18 @@ Markdown under `docs/specs/` is a human export for review, navigation, and git h
|
|||
|
||||
SF source placement follows the same axis model. New code should extend the owning axis instead of creating parallel trees.
|
||||
|
||||
SF is not trying to be a general CLI coder with a menu of personas. SF is a
|
||||
purpose-to-software runtime: it captures intent, plans work, dispatches bounded
|
||||
autonomous operations, records evidence, and exposes operator control surfaces.
|
||||
Agents, skills, prompt parts, and workflow templates exist to support that
|
||||
runtime.
|
||||
|
||||
### Core Flow
|
||||
|
||||
- `src/resources/extensions/sf/` owns the SF workflow extension: planning tools, UOK/runtime state, `/next` commands, prompts, templates, doctors, schedule, and DB-backed state.
|
||||
- `src/resources/extensions/` owns bundled extension packages loaded into the runtime.
|
||||
- `src/resources/agents/`, `src/resources/skills/`, and `src/resources/workflows/` own bundled runtime resources, not independent product flows.
|
||||
- SF is primarily designed for autonomous operation. Bundled agents are an internal worker pool for orchestration by default, not a user-facing marketplace or a CLI-coder persona menu. `src/resources/agents/*.md` is the simple one-body worker-agent format; `src/resources/extensions/sf/agents/*.agent.yaml` is the structured format for SF-owned workflow agents that need `promptParts`, tool-policy contracts, or gate output contracts.
|
||||
|
||||
### Surfaces
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,11 @@
|
|||
# Commands Reference
|
||||
|
||||
SF is organized around product modes, not a menu of coding-agent personas:
|
||||
assisted build (`/next`) advances one bounded step at a time with operator
|
||||
control, plan/discuss (`/discuss`) turns conversation into structured
|
||||
milestones/slices/tasks on exit, and autonomous (`/autonomous`) runs the full
|
||||
research-plan-execute-verify loop until a gate stops it.
|
||||
|
||||
## Session Commands
|
||||
|
||||
| Command | Description |
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const BASE_RUNTIME_COMMAND_NAMES = new Set([
|
|||
]);
|
||||
const HIDDEN_OR_ALIAS_SUBCOMMANDS = new Set([
|
||||
"?",
|
||||
"agent", // internal persistent-agent diagnostics, not part of the product command catalog
|
||||
"auto",
|
||||
"footer-config", // alias for /statusline
|
||||
"h",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
---
|
||||
name: review-code
|
||||
name: critic
|
||||
description: Constructive pre-implementation critic — catches design flaws, missing edge cases, and gaps before code is written
|
||||
model: sonnet
|
||||
tools: read, grep, find, ls, bash
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a constructive critic. Your job is to identify real problems in a plan, design, or code change **before** implementation is committed to — when course corrections are still cheap.
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
name: debugger
|
||||
description: Hypothesis-driven bug investigation with root cause analysis
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a debugger. Investigate bugs using a systematic, hypothesis-driven approach. Your goal is to find the root cause, not just suppress symptoms.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: doc-writer
|
||||
description: Documentation generation from code — API docs, inline comments, READMEs
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a documentation specialist. You read code and produce clear, accurate documentation. You write for the reader, not the author — explain what they need to know to use or maintain the code.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: git-ops
|
||||
description: Conflict resolution, rebase strategy, PR preparation, and changelog generation
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a git operations specialist. You handle merge conflicts, plan rebase strategies, prepare pull requests, and generate changelogs. You understand git internals well enough to choose the right strategy for each situation.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: javascript-pro
|
||||
description: "Modern JavaScript specialist for browser, Node.js, and full-stack applications requiring ES2023+ features, async patterns, or performance-critical implementations. Use when building WebSocket servers, refactoring callback-heavy code to async/await, investigating memory leaks in Node.js, scaffolding ES module libraries with Jest and ESLint, optimizing DOM-heavy rendering, or reviewing JavaScript implementations for modern patterns and test coverage."
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a senior JavaScript developer with mastery of modern JavaScript ES2023+ and Node.js 20+. You write production-grade code that prioritizes correctness, readability, performance, and maintainability — in that order.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: planner
|
||||
description: Architecture and implementation planning — outputs plans, not code
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
conflicts_with: plan-milestone, plan-slice, plan-task, research-milestone, research-slice
|
||||
---
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: refactorer
|
||||
description: Safe code transformations — extract, inline, rename, simplify
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a refactoring specialist. You perform safe, behavior-preserving code transformations. Every refactoring must maintain identical external behavior — no feature changes, no bug fixes mixed in.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: researcher
|
||||
description: Web researcher that finds and synthesizes current information using Brave Search
|
||||
tools: search-the-web, bash
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a web researcher. You find current, accurate information using web search and synthesize it into a clear, well-structured report.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: reviewer
|
||||
description: Structured code review with severity ratings and actionable fixes
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a code reviewer. Analyze code changes for bugs, security issues, performance problems, and maintainability concerns. Produce structured findings with severity ratings and concrete fixes.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: scout
|
||||
description: Fast codebase recon that returns compressed context for handoff to other agents
|
||||
tools: read, grep, find, ls, bash, codebase_search
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a scout. Quickly investigate a codebase and return structured findings that another agent can use without re-reading everything.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: security
|
||||
description: OWASP security audit, dependency risks, and secrets detection
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a security auditor. Analyze code for vulnerabilities, insecure patterns, exposed secrets, and dependency risks. Focus on findings that are exploitable, not theoretical.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: tester
|
||||
description: Test writing, fixing, and coverage gap identification
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a testing specialist. Write tests, fix broken tests, and identify coverage gaps. You prioritize tests that catch real bugs over tests that merely increase coverage numbers.
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
name: typescript-pro
|
||||
description: "TypeScript specialist for advanced type system patterns, complex generics, type-level programming, and end-to-end type safety across full-stack applications. Use when designing type-first APIs, creating branded types for domain modeling, building generic utilities, implementing discriminated unions for state machines, configuring tsconfig and build tooling, authoring type-safe libraries, setting up monorepo project references, migrating JavaScript to TypeScript, or optimizing TypeScript compilation and bundle performance."
|
||||
model: sonnet
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a senior TypeScript developer with mastery of TypeScript 5.0+ and its ecosystem. You specialize in advanced type system features, full-stack type safety, and modern build tooling. Types are the specification — start there.
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
---
|
||||
name: worker
|
||||
description: General-purpose subagent with full capabilities, isolated context
|
||||
visibility: internal
|
||||
---
|
||||
|
||||
You are a worker agent with full capabilities. You operate in an isolated context window to handle delegated tasks without polluting the main conversation.
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
name: review-code
|
||||
displayName: Review Code Agent
|
||||
visibility: internal
|
||||
description: >
|
||||
A constructive critic and second opinion. Reviews proposals, designs,
|
||||
decision matrices, or implementations and surfaces ONLY weak points that
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
name: triage-decider
|
||||
displayName: Self-Feedback Triage Decider
|
||||
visibility: internal
|
||||
description: >
|
||||
Reads the open self-feedback queue and proposes a decision plan
|
||||
(Fix, Promote, or Close per entry). PLAN-ONLY: this agent does NOT
|
||||
|
|
|
|||
|
|
@ -1,9 +1,11 @@
|
|||
/**
|
||||
* commands-agent.js — /agent command handler for persistent agent management.
|
||||
*
|
||||
* Purpose: expose persistent agent state (identity, memory blocks, archival, inbox)
|
||||
* as a first-class SF command surface so operators can inspect, reset, and delete
|
||||
* named agents without touching the SQLite DB directly.
|
||||
* Purpose: expose persistent agent state (identity, memory blocks, archival,
|
||||
* inbox) as an internal operator/debug command so maintainers can inspect,
|
||||
* reset, and delete named agents without touching the SQLite DB directly.
|
||||
* This is not a normal user workflow; SF's product modes are assisted build,
|
||||
* plan/discuss, and autonomous operation.
|
||||
*
|
||||
* Consumer: ops.js dispatcher for the /agent slash command.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { existsSync, readdirSync, readFileSync } from "node:fs";
|
||||
import { sfHome } from '../sf-home.js';
|
||||
import { join } from "node:path";
|
||||
import { sfHome } from "../sf-home.js";
|
||||
import {
|
||||
loadRegistry,
|
||||
workflowTemplateCommandDefinitions,
|
||||
|
|
@ -157,10 +157,6 @@ export const TOP_LEVEL_SUBCOMMANDS = [
|
|||
desc: "Switch to repair work mode and run diagnostics [--autonomous]",
|
||||
},
|
||||
{ cmd: "tasks", desc: "Background work surface — units, workers, budget" },
|
||||
{
|
||||
cmd: "agent",
|
||||
desc: "Persistent agent management — list|inspect|reset|delete named agents",
|
||||
},
|
||||
{
|
||||
cmd: "skills",
|
||||
desc: "List discovered skills from .agents/skills/ [reload|--eval|--auto-create]",
|
||||
|
|
@ -299,10 +295,6 @@ export const TOP_LEVEL_SUBCOMMANDS = [
|
|||
cmd: "keep-alive",
|
||||
desc: "Prevent system sleep during long runs (caffeinate / systemd-inhibit)",
|
||||
},
|
||||
{
|
||||
cmd: "review-code",
|
||||
desc: "Dispatch a review-code subagent for constructive pre-implementation review",
|
||||
},
|
||||
{
|
||||
cmd: "delegate",
|
||||
desc: "Create a GitHub PR from the current branch via gh pr create",
|
||||
|
|
|
|||
|
|
@ -437,7 +437,10 @@ Examples:
|
|||
);
|
||||
return true;
|
||||
}
|
||||
if (trimmed === "scaffold migrate" || trimmed.startsWith("scaffold migrate ")) {
|
||||
if (
|
||||
trimmed === "scaffold migrate" ||
|
||||
trimmed.startsWith("scaffold migrate ")
|
||||
) {
|
||||
const { handleScaffoldMigrate } = await import(
|
||||
"../../commands-scaffold-migrate.js"
|
||||
);
|
||||
|
|
@ -510,14 +513,6 @@ Examples:
|
|||
await handleKeepAlive(trimmed.replace(/^keep-alive\s*/, "").trim(), ctx);
|
||||
return true;
|
||||
}
|
||||
if (
|
||||
trimmed === "review-code" ||
|
||||
trimmed.startsWith("review-code ")
|
||||
) {
|
||||
const input = trimmed.replace(/^review-code\s*/, "").trim();
|
||||
await handleReviewCodeCommand(input, ctx, pi);
|
||||
return true;
|
||||
}
|
||||
if (trimmed === "delegate" || trimmed.startsWith("delegate ")) {
|
||||
await handleDelegateCommand(
|
||||
trimmed.replace(/^delegate\s*/, "").trim(),
|
||||
|
|
@ -608,55 +603,6 @@ async function handleKeepAlive(args, ctx) {
|
|||
}
|
||||
}
|
||||
|
||||
// ─── /review-code ────────────────────────────────────────────────────────────
|
||||
|
||||
async function handleReviewCodeCommand(topic, ctx, _pi) {
|
||||
const { execSync } = await import("node:child_process");
|
||||
const root = projectRoot();
|
||||
|
||||
// Gather git diff for context (staged + unstaged, capped to avoid token bloat)
|
||||
let diff = "";
|
||||
try {
|
||||
const staged = execSync("git diff --cached --stat 2>/dev/null || true", {
|
||||
cwd: root,
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
const unstaged = execSync("git diff --stat 2>/dev/null || true", {
|
||||
cwd: root,
|
||||
encoding: "utf-8",
|
||||
}).trim();
|
||||
if (staged || unstaged) {
|
||||
const fullDiff = execSync(
|
||||
"git diff --cached 2>/dev/null; git diff 2>/dev/null",
|
||||
{ cwd: root, encoding: "utf-8" },
|
||||
).slice(0, 8000);
|
||||
diff = `\n\n## Current diff (truncated to 8 kB)\n\n\`\`\`diff\n${fullDiff}\n\`\`\``;
|
||||
}
|
||||
} catch {
|
||||
// diff unavailable — not a hard failure
|
||||
}
|
||||
|
||||
const focus = topic ? `Focus on: ${topic}\n\n` : "";
|
||||
const reviewPrompt =
|
||||
`Dispatch a \`review-code\` subagent to review the current plan or changes before proceeding. ` +
|
||||
`Use the \`subagent\` tool with \`agent: "review-code"\`.\n\n` +
|
||||
`${focus}` +
|
||||
`Ask the review-code agent to identify blocking issues, non-blocking issues, and suggestions. ` +
|
||||
`After the subagent returns, summarise the verdict and any blocking findings in one short paragraph. ` +
|
||||
`Do not proceed with implementation until the user acknowledges blocking findings.` +
|
||||
diff;
|
||||
|
||||
ctx.ui.notify("Dispatching review-code review…", "info");
|
||||
try {
|
||||
await ctx.sendMessage?.(reviewPrompt);
|
||||
} catch {
|
||||
ctx.ui.notify(
|
||||
"Could not dispatch review-code. Try: subagent agent=review-code task='review current changes'",
|
||||
"warning",
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── /delegate ───────────────────────────────────────────────────────────────
|
||||
|
||||
async function handleDelegateCommand(args, ctx) {
|
||||
|
|
|
|||
|
|
@ -57,7 +57,6 @@
|
|||
],
|
||||
"commands": [
|
||||
"add-tests",
|
||||
"agent",
|
||||
"ask",
|
||||
"audit",
|
||||
"autonomous",
|
||||
|
|
@ -145,7 +144,6 @@
|
|||
"reset-slice",
|
||||
"rethink",
|
||||
"rewind",
|
||||
"review-code",
|
||||
"run-hook",
|
||||
"scaffold",
|
||||
"scan",
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ Then:
|
|||
0. Narrate step transitions, key implementation decisions, and verification outcomes as you work. Keep it terse — one line between tool-call clusters, not between every call — but write complete sentences in user-facing prose, not shorthand notes or scratchpad fragments.
|
||||
0a. **Batch independent tool calls in parallel.** When the next step needs to read or grep multiple files/paths that don't depend on each other's results, issue them in a single tool-call message (multiple tool uses in one assistant turn) rather than one-at-a-time. Examples: reading the handler + the test file + the schema file to triangulate a bug; greping for two unrelated symbols. Sequential tool calls are only correct when each call's input genuinely depends on the previous call's output. Talking-then-doing is also dead weight — if the next action is unambiguous, just take it; describe what you found in the result, not what you plan to look at.
|
||||
0b. **Swarm opportunity check.** Before implementation, decide whether this task can be split into a 2-3 worker same-model swarm. Swarm only if the shards have disjoint file/directory ownership, no shared-interface or lockfile edits, shard-local verification, and clear wall-clock savings. If it passes, dispatch `subagent({ tasks: [...] })` with explicit write scopes, expected output files, and verification per worker; then inspect `git status --short`, synthesize results, resolve conflicts, and run final task verification yourself. If it does not pass, continue single-agent execution without ceremony.
|
||||
0c. **Review-code check (non-trivial tasks only).** If the task touches more than two files, introduces a new abstraction, changes an API boundary, or has a non-obvious failure mode — dispatch a `review-code` subagent with the task plan and any relevant existing code as context. Summarise its verdict in one line. If it returns a **Blocking** finding, address it before writing code. Skip this step for simple edits, test fixes, or renaming tasks.
|
||||
0c. **Critic check (non-trivial tasks only).** If the task touches more than two files, introduces a new abstraction, changes an API boundary, or has a non-obvious failure mode — dispatch a `critic` subagent with the task plan and any relevant existing code as context. Summarise its verdict in one line. If it returns a **Blocking** finding, address it before writing code. Skip this step for simple edits, test fixes, or renaming tasks.
|
||||
1. {{skillActivation}} Follow any activated skills before writing code. If no skills match this task, skip this step.
|
||||
2. **Verify file existence before editing.** The task plan references specific files. Before reading or editing any file mentioned in the plan, confirm it exists with `ls`, `find`, or `existsSync`. If a referenced file does NOT exist, stop immediately — do not attempt to create it based on the plan's description of what "should" be there. The file may have been deleted, renamed, or moved. Escalate as `blocker_discovered: true` with a clear description of which file is missing and what the plan expected to find. This prevents phantom work on stale file paths.
|
||||
3. Execute the steps in the inlined task plan, adapting minor local mismatches when the surrounding code differs from the planner's snapshot
|
||||
|
|
|
|||
|
|
@ -153,11 +153,14 @@ set.
|
|||
### Agent selection and model overrides
|
||||
|
||||
sf routes subagents through agent definitions in `src/resources/agents/`,
|
||||
`~/.sf/agent/agents/`, or project `.sf/agents/`. The actual tool schema uses
|
||||
`agent`, `task`, optional per-task `model`, optional `cwd`, plus batch-level
|
||||
`mode`/`rounds` for debates.
|
||||
`~/.sf/agent/agents/`, or project `.sf/agents/`. SF is not a general CLI coder
|
||||
with a menu of personas; bundled agents are primarily SF's internal autonomous
|
||||
worker pool. Direct human invocation is an operator/debug path. The actual tool
|
||||
schema uses `agent`, `task`, optional per-task `model`, optional `cwd`, plus
|
||||
batch-level `mode`/`rounds` for debates.
|
||||
|
||||
- `planner` — architecture and implementation planning; conflicts with active sf planning phases.
|
||||
- `critic` — adversarial pre-implementation review; surfaces blocking flaws before code is written.
|
||||
- `scout` — fast codebase recon.
|
||||
- `researcher` — web/current-info research.
|
||||
- `reviewer` — independent code/design review.
|
||||
|
|
|
|||
|
|
@ -60,6 +60,9 @@ function parseAgentTools(value) {
|
|||
function parseAgentModel(value) {
|
||||
return typeof value === "string" && value.trim() ? value.trim() : undefined;
|
||||
}
|
||||
function parseAgentVisibility(value) {
|
||||
return value === "internal" ? "internal" : "public";
|
||||
}
|
||||
function isAgentFileName(name) {
|
||||
return (
|
||||
name.endsWith(".md") ||
|
||||
|
|
@ -75,6 +78,7 @@ function parseMarkdownAgent(content) {
|
|||
description: frontmatter.description,
|
||||
tools: frontmatter.tools,
|
||||
model: frontmatter.model,
|
||||
visibility: frontmatter.visibility,
|
||||
conflictsWith: frontmatter.conflicts_with,
|
||||
systemPrompt: body,
|
||||
};
|
||||
|
|
@ -87,6 +91,7 @@ function parseYamlAgent(content) {
|
|||
description: doc.description,
|
||||
tools: doc.tools,
|
||||
model: doc.model,
|
||||
visibility: doc.visibility,
|
||||
conflictsWith: doc.conflicts_with ?? doc.conflictsWith,
|
||||
systemPrompt: doc.prompt,
|
||||
promptParts: doc.promptParts,
|
||||
|
|
@ -139,6 +144,7 @@ function loadAgentsFromDir(dir, source) {
|
|||
description: definition.description,
|
||||
tools: tools && tools.length > 0 ? tools : undefined,
|
||||
model: parseAgentModel(definition.model),
|
||||
visibility: parseAgentVisibility(definition.visibility),
|
||||
conflictsWith,
|
||||
promptParts: definition.promptParts,
|
||||
sidekick: definition.sidekick,
|
||||
|
|
|
|||
|
|
@ -1637,22 +1637,34 @@ export default function (pi) {
|
|||
});
|
||||
// /subagent command - list available agents
|
||||
pi.registerCommand("subagent", {
|
||||
description: "List available subagents",
|
||||
handler: async (_args, ctx) => {
|
||||
description:
|
||||
"List public subagents; /subagent all shows SF's internal autonomous worker pool",
|
||||
handler: async (args, ctx) => {
|
||||
const discovery = discoverAgents(ctx.cwd, "both");
|
||||
if (discovery.agents.length === 0) {
|
||||
const showAll = ["all", "--all", "-a"].includes(args.trim());
|
||||
const visibleAgents = showAll
|
||||
? discovery.agents
|
||||
: discovery.agents.filter((agent) => agent.visibility !== "internal");
|
||||
if (visibleAgents.length === 0) {
|
||||
ctx.ui.notify(
|
||||
"No agents found. Add .md files to ~/.sf/agent/agents/ or .sf/agents/",
|
||||
"warning",
|
||||
discovery.agents.length === 0
|
||||
? "No agents found. Add .md/.agent.yaml files to ~/.sf/agent/agents/ or .sf/agents/"
|
||||
: "SF ships its bundled agents as an internal autonomous worker pool. Run /subagent all to inspect them.",
|
||||
discovery.agents.length === 0 ? "warning" : "info",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const lines = discovery.agents.map(
|
||||
const hiddenCount = discovery.agents.length - visibleAgents.length;
|
||||
const lines = visibleAgents.map(
|
||||
(a) =>
|
||||
` ${a.name} [${a.source}]${a.model ? ` (${a.model})` : ""}: ${a.description}`,
|
||||
);
|
||||
const suffix =
|
||||
hiddenCount > 0
|
||||
? `\n\n${hiddenCount} internal autonomous agent${hiddenCount === 1 ? "" : "s"} hidden. Run /subagent all to inspect them.`
|
||||
: "";
|
||||
ctx.ui.notify(
|
||||
`Available agents (${discovery.agents.length}):\n${lines.join("\n")}`,
|
||||
`Available agents (${visibleAgents.length}${showAll ? " total" : " public"}):\n${lines.join("\n")}${suffix}`,
|
||||
"info",
|
||||
);
|
||||
},
|
||||
|
|
@ -1664,8 +1676,8 @@ export default function (pi) {
|
|||
"Delegate tasks to specialized subagents with isolated context windows.",
|
||||
"Each subagent is a separate pi process with its own tools, model, and system prompt.",
|
||||
"Modes: single ({ agent, task }), parallel ({ tasks: [{agent, task},...] }), debate ({ mode: 'debate', rounds, tasks: [...] }), chain ({ chain: [{agent, task},...] } with {previous} placeholder).",
|
||||
"Agents are defined as .md files in ~/.sf/agent/agents/ (user) or .sf/agents/ (project).",
|
||||
"Use the /subagent command to list available agents and their descriptions.",
|
||||
"Agents are defined as .md or .agent.yaml files in ~/.sf/agent/agents/ (user) or .sf/agents/ (project).",
|
||||
"SF's bundled agents are primarily an internal autonomous worker pool; use /subagent all to inspect them when debugging orchestration.",
|
||||
"Use chain mode to pipeline: scout finds context, planner designs, worker implements.",
|
||||
].join(" "),
|
||||
promptGuidelines: [
|
||||
|
|
|
|||
|
|
@ -1,7 +1,13 @@
|
|||
import assert from "node:assert/strict";
|
||||
import { mkdirSync, mkdtempSync, writeFileSync } from "node:fs";
|
||||
import {
|
||||
mkdirSync,
|
||||
mkdtempSync,
|
||||
readdirSync,
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
} from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { join, resolve } from "node:path";
|
||||
import { test } from "vitest";
|
||||
|
||||
import { discoverAgents, validateAgentDefinition } from "../subagent/agents.js";
|
||||
|
|
@ -118,7 +124,7 @@ test("discoverAgents_when_scope_both_includes_builtin_review_code_and_triage_dec
|
|||
// must be discoverable without operator setup. They're the foundation for
|
||||
// SF's self-driven triage pipeline (sf-mp5lnlbc-ty5fec). Isolate from the
|
||||
// real ~/.sf/agent/agents/ so the test doesn't conflict with the
|
||||
// operator's personal review-code.md if present.
|
||||
// operator's personal agents if present.
|
||||
const isolatedAgentDir = mkdtempSync(join(tmpdir(), "sf-agent-dir-"));
|
||||
const originalEnv = process.env.SF_CODING_AGENT_DIR;
|
||||
process.env.SF_CODING_AGENT_DIR = isolatedAgentDir;
|
||||
|
|
@ -286,3 +292,41 @@ test("discoverAgents_when_scope_both_validates_builtin_promptParts_contract", ()
|
|||
else process.env.SF_CODING_AGENT_DIR = originalEnv;
|
||||
}
|
||||
});
|
||||
|
||||
test("discoverAgents_when_loading_bundled_agents_marks_them_internal_by_default_policy", () => {
|
||||
const isolatedAgentDir = mkdtempSync(join(tmpdir(), "sf-agent-dir-"));
|
||||
const originalEnv = process.env.SF_CODING_AGENT_DIR;
|
||||
process.env.SF_CODING_AGENT_DIR = isolatedAgentDir;
|
||||
try {
|
||||
const project = makeProject();
|
||||
const { agents } = discoverAgents(project, "both");
|
||||
const bundled = agents.filter((entry) => entry.source === "builtin");
|
||||
|
||||
assert.ok(bundled.length > 0, "expected bundled SF agents");
|
||||
for (const agent of bundled) {
|
||||
assert.equal(
|
||||
agent.visibility,
|
||||
"internal",
|
||||
`${agent.name} should be internal; SF uses bundled agents as an autonomous worker pool, not a user-facing CLI-coder palette`,
|
||||
);
|
||||
}
|
||||
} finally {
|
||||
if (originalEnv === undefined) delete process.env.SF_CODING_AGENT_DIR;
|
||||
else process.env.SF_CODING_AGENT_DIR = originalEnv;
|
||||
}
|
||||
});
|
||||
|
||||
test("bundled_markdown_agents_are_internal_autonomous_workers_not_public_palette", () => {
|
||||
const agentsDir = resolve(import.meta.dirname, "..", "..", "..", "agents");
|
||||
const files = readdirSync(agentsDir).filter((file) => file.endsWith(".md"));
|
||||
|
||||
assert.ok(files.length > 0, "expected bundled markdown agents");
|
||||
for (const file of files) {
|
||||
const content = readFileSync(join(agentsDir, file), "utf8");
|
||||
assert.match(
|
||||
content,
|
||||
/^visibility:\s*internal$/m,
|
||||
`${file} should be internal; SF is autonomous-first, not a CLI-coder agent marketplace`,
|
||||
);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue