feat(FN-2569): sync fusion skill docs with runtime tools

- Add generated engine-tools reference and refresh extension/capabilities docs from source definitions
- Enhance sync-fusion-skill-tools script to verify and maintain skill documentation consistency
- Expand skill-sync tests and enforce sync:fusion-skill:check in the workspace test pipeline
- Add FN-2569 changeset documenting the published @runfusion/fusion patch update
This commit is contained in:
Fusion
2026-04-25 20:07:49 -07:00
committed by gsxdsm
parent 4addaa8f41
commit 26f9c74d8f
8 changed files with 914 additions and 198 deletions

View File

@@ -21,7 +21,9 @@ Mission → Milestone → Slice → Feature → Task
**Available tools:** Fusion registers tools via the pi extension (prefixed `fn_*`). No CLI commands or Bash needed — use the registered tools directly.
**Naming boundary:** The published skill surface always uses `fn_*` tool names (for example `fn_task_create`, `fn_mission_create`). Internal engine runtime tools like `task_create`, `task_update`, `task_log`, and `task_done` are intentionally unprefixed and not part of this skill.
**Naming boundary:** The published pi-extension skill surface uses `fn_*` tool names (for example `fn_task_create`, `fn_mission_create`). Engine runtime sessions also inject additional `fn_*` tools (for example `fn_review_spec`, `fn_review_step`, `fn_spawn_agent`) that are not user-invokable extension tools.
**Engine runtime tools:** Triage/executor/merger/heartbeat sessions include auto-injected engine tools that do not come from the pi extension registration list. See `references/engine-tools.md` for the canonical runtime-only catalog and usage boundaries.
**Tool categories:**
<!-- BEGIN: tool-categories (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->
@@ -102,6 +104,7 @@ For these operations, guide the user to the dashboard (`/fn`) or CLI commands do
| references/extension-tools.md | All pi extension tools with parameters |
| references/best-practices.md | Tips for effective Fusion usage |
| references/fusion-capabilities.md | Complete feature catalog |
| references/engine-tools.md | Engine session-scoped runtime tools (not extension-invokable) |
| references/skill-patterns.md | Patterns used in this skill's design |
</reference_index>

View File

@@ -0,0 +1,54 @@
# Engine Session-Scoped Tools
These tools are **not** part of the pi extension's user-invokable `extension.ts` surface. They are injected by the engine at runtime for specific agent session types.
- Source files: `packages/engine/src/agent-tools.ts`, `triage.ts`, `executor.ts`, `merger.ts`, `agent-heartbeat.ts`
- Availability: only when the engine creates a session for the matching agent role
- Important: do not tell users to call these directly from the generic pi extension tool list
## Shared runtime tools (`agent-tools.ts`)
| Tool | Agent Types | Purpose | Parameters |
|---|---|---|---|
| `fn_task_create` | triage, executor, heartbeat | Create a follow-up task from within an agent run | `description` (string), `dependencies?` (string[]) |
| `fn_task_log` | executor, heartbeat | Write significant task log entries | `message` (string), `outcome?` (string) |
| `fn_task_document_write` | triage, executor, heartbeat | Save/update a named task document revision | `key` (string), `content` (string), `author?` (string) |
| `fn_task_document_read` | triage, executor, heartbeat | Read one task document or list all | `key?` (string) |
| `fn_memory_search` | triage, executor, heartbeat | Search project/agent memory snippets | `query` (string), `limit?` (number) |
| `fn_memory_get` | triage, executor, heartbeat | Read a bounded memory file window | `path` (string), `startLine?` (number), `lineCount?` (number) |
| `fn_memory_append` | executor, heartbeat (when writable backend enabled) | Append long-term/daily memory notes | `scope?` (`project` \| `agent`), `layer` (`long-term` \| `daily`), `content` (string) |
| `fn_reflect_on_performance` | executor | Generate reflection insights from prior runs | `focus_area?` (string) |
| `fn_list_agents` | triage, executor, heartbeat | List agents (optionally filtered) | `role?` (string), `state?` (string), `includeEphemeral?` (boolean) |
| `fn_delegate_task` | triage, executor, heartbeat | Create and assign a new task to a specific agent | `agent_id` (string), `description` (string), `dependencies?` (string[]) |
| `fn_send_message` | executor, heartbeat | Send inbox messages to agents/users | `to_id` (string), `content` (string), `type?` (`agent-to-agent` \| `agent-to-user`), `reply_to_message_id?` (string) |
| `fn_read_messages` | executor, heartbeat | Read inbox messages | `unread_only?` (boolean), `limit?` (number) |
## Triage-only runtime tools (`triage.ts`)
| Tool | Purpose | Parameters |
|---|---|---|
| `fn_task_list` | List active tasks during specification (duplicate check, discovery) | none |
| `fn_task_get` | Fetch full task detail including PROMPT.md | `id` (string) |
| `fn_review_spec` | Spawn spec reviewer and return `APPROVE`/`REVISE`/`RETHINK`/`UNAVAILABLE` | none |
## Executor-only runtime tools (`executor.ts`)
| Tool | Purpose | Parameters |
|---|---|---|
| `fn_task_update` | Update a spec step status (`pending`/`in-progress`/`done`/`skipped`) | `step` (number), `status` (enum) |
| `fn_task_add_dep` | Add a dependency to current task (confirmation-gated) | `task_id` (string), `confirm?` (boolean) |
| `fn_task_done` | Mark task complete and optionally store summary | `summary?` (string) |
| `fn_review_step` | Spawn step plan/code reviewer | `step` (number), `type` (`plan` \| `code`), `step_name` (string), `baseline?` (string) |
| `fn_spawn_agent` | Spawn child agent in separate worktree | `name` (string), `role` (enum), `task` (string) |
## Merger-only runtime tools (`merger.ts`)
| Tool | Purpose | Parameters |
|---|---|---|
| `fn_report_build_failure` | Explicitly signal merge-time build verification failure | `message` (string) |
## Heartbeat-only runtime tools (`agent-heartbeat.ts`)
| Tool | Purpose | Parameters |
|---|---|---|
| `fn_heartbeat_done` | Signal end of heartbeat run with optional summary | `summary?` (string) |

View File

@@ -2,191 +2,187 @@
All tools are registered via the pi extension. They are available in any pi agent session when the Fusion extension is installed.
> Naming contract: all externally exposed Fusion extension tools are `fn_*` (for example `fn_task_create`). Internal engine/executor runtime tools (`task_create`, `task_update`, `task_log`, `task_done`, etc.) are separate and intentionally out of scope for this skill surface.
> Naming contract: all externally exposed Fusion extension tools are `fn_*` (for example `fn_task_create`). Engine runtime sessions also inject additional `fn_*` tools (for example `fn_review_step`, `fn_spawn_agent`, `fn_task_document_write`) that are separate from this extension surface and documented in `engine-tools.md`.
<!-- BEGIN: extension-tools (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->
## Task Tools
### fn_task_create
Create a new task on the Fusion board. Enters triage for AI specification.
Create a new task on the Fusion task board. The task enters the triage column where the AI triage agent will specify it into a full prompt with steps, file scope, and acceptance criteria.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | ✓ | What needs to be done — be descriptive |
| `depends` | string[] | — | Task IDs this depends on (e.g., ["FN-001"]) |
Returns: task ID, column, dependencies, path
| `depends` | array | — | Task IDs this depends on (e.g. ['FN-001', 'FN-002']) |
| `agentId` | string | — | Agent ID to assign this task to (e.g. 'agent-abc123') |
### fn_task_update
Update fields on an existing task (title, description, dependencies).
Update fields on an existing task. Supports modifying the title, description, dependencies, and assigned agent after task creation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID (e.g., FN-001) |
| `id` | string | ✓ | Task ID (e.g. FN-001) |
| `title` | string | — | New task title |
| `description` | string | — | New task description |
| `depends` | string[] | — | New dependency list — replaces existing |
Returns: task ID, list of updated fields
| `depends` | array | — | New dependency list — replaces existing dependencies (e.g. ['FN-001', 'FN-002']) |
| `agentId` | union | — | Agent ID to assign this task to, or null to clear (e.g. 'agent-abc123') |
### fn_task_list
List all tasks grouped by column.
List all tasks on the Fusion board, grouped by column.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `column` | string | — | Filter to specific column |
| `limit` | number | — | Max tasks per column (default: 10) |
Returns: formatted task list grouped by column
| `column` | string(enum) | — | Filter to a specific column |
| `limit` | number | — | Max tasks to show per column (default: 10) |
### fn_task_show
Show full task details including steps, progress, prompt preview, and log.
Show full details for a task including steps, progress, and log entries.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID (e.g., FN-001) |
Returns: task details with steps, prompt preview (500 chars), last 5 log entries
| `id` | string | ✓ | Task ID (e.g. FN-001) |
### fn_task_attach
Attach a file to a task. Copies file to task's attachments directory.
Attach a file to a task. Supports images (png, jpg, gif, webp) and text files (txt, log, json, yaml, yml, toml, csv, xml).
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID |
| `path` | string | ✓ | Path to file to attach |
Supported formats: png, jpg, jpeg, gif, webp, txt, log, json, yaml, yml, toml, csv, xml
| `id` | string | ✓ | Task ID (e.g. FN-001) |
| `path` | string | ✓ | Path to the file to attach |
### fn_task_pause
Pause automation for a task. Scheduler and executor will skip this task.
Pause a task — stops all automated agent and scheduler interaction for this task.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID |
| `id` | string | ✓ | Task ID (e.g. FN-001) |
### fn_task_unpause
Resume automation for a paused task.
Unpause a task — resumes automated agent and scheduler interaction.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID |
| `id` | string | ✓ | Task ID (e.g. FN-001) |
### fn_task_retry
Retry a failed task. Clears error state, moves to todo for re-execution.
Retry a failed task — clears the error state and moves it back to the todo column for re-execution.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID (must be in failed state) |
| `id` | string | ✓ | Task ID to retry (e.g. FN-001). Must be in 'failed' state. |
### fn_task_duplicate
Duplicate a task. Creates a fresh copy in triage with same title and description.
Duplicate an existing task, creating a fresh copy in triage. Copies the title and description but resets all execution state. The AI triage agent will re-specify the new task.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Source task ID to duplicate |
| `id` | string | ✓ | Source task ID to duplicate (e.g. FN-001) |
### fn_task_refine
Create a follow-up task for a completed task. New task depends on the original.
Request a refinement of a completed or in-review task. Creates a new follow-up task in triage that references the original task as a dependency. Use this when a done or in-review task needs additional work, improvements, or follow-up changes.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID (must be done or in-review) |
| `feedback` | string | ✓ | What needs to be refined (1-2000 chars) |
| `id` | string | ✓ | Task ID to refine (e.g. FN-001). Must be in 'done' or 'in-review' column. |
| `feedback` | string | ✓ | Description of what needs to be refined or improved |
### fn_task_archive
Archive a done task. Moves from done → archived.
Archive a done task (move from done → archived). Archived tasks are preserved for historical reference but moved out of the main board view.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID (must be in done column) |
| `id` | string | ✓ | Task ID to archive (e.g. FN-001). Must be in 'done' column. |
### fn_task_unarchive
Restore an archived task. Moves from archived → done.
Unarchive an archived task (move from archived → done). Restores the task to the done column.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID (must be in archived column) |
| `id` | string | ✓ | Task ID to unarchive (e.g. FN-001). Must be in 'archived' column. |
### fn_task_delete
Permanently delete a task. Cannot be undone.
Permanently delete a task from the Fusion board. Tasks are deleted immediately and cannot be recovered.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Task ID |
| `id` | string | ✓ | Task ID to delete (e.g. FN-001) |
### fn_task_plan
Create a task via AI-guided planning mode. Non-interactive when called from extension.
Create a task via AI-guided planning modeinteractive conversation to refine your idea into a well-specified task.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `description` | string | — | Initial plan description |
| `description` | string | — | Initial plan description (optional) — the AI will ask clarifying questions if not provided |
## GitHub Tools
### fn_task_import_github
Batch import GitHub issues as Fusion tasks.
Import GitHub issues as Fusion tasks. Fetches open issues from a repository and creates tasks in the triage column. Each task includes the issue title and body with a link to the source issue.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `ownerRepo` | string | ✓ | Repository (e.g., "owner/repo") |
| `limit` | number | — | Max issues (default: 30, max: 100) |
| `labels` | string[] | — | Label names to filter by |
| `ownerRepo` | string | ✓ | Repository in owner/repo format (e.g., 'dustinbyrne/fusion') |
| `limit` | number | — | Max issues to import (default: 30, max: 100) |
| `labels` | array | — | Label names to filter by |
### fn_task_import_github_issue
Import a single GitHub issue by number.
Import a specific GitHub issue as a Fusion task. Fetches the issue by number and creates a single task in the triage column with the issue title and body.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `owner` | string | ✓ | Repository owner |
| `repo` | string | ✓ | Repository name |
| `issueNumber` | number | ✓ | GitHub issue number |
| `owner` | string | ✓ | Repository owner (e.g., 'dustinbyrne') |
| `repo` | string | ✓ | Repository name (e.g., 'fusion') |
| `issueNumber` | number | ✓ | GitHub issue number to import |
### fn_task_browse_github_issues
Browse open issues from a repository before importing.
List open GitHub issues from a repository to browse before importing. Returns issue numbers, titles, and URLs for selection. Use with fn_task_import_github_issue to import specific issues by number.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `owner` | string | ✓ | Repository owner |
| `repo` | string | ✓ | Repository name |
| `limit` | number | — | Max issues (default: 30, max: 100) |
| `labels` | string[] | — | Label names to filter by |
| `owner` | string | ✓ | Repository owner (e.g., 'dustinbyrne') |
| `repo` | string | ✓ | Repository name (e.g., 'fusion') |
| `limit` | number | — | Max issues to show (default: 30, max: 100) |
| `labels` | array | — | Label names to filter by |
## Mission Tools
### fn_mission_create
Create a new mission — a high-level objective spanning multiple milestones.
Create a new mission — a high-level objective that can span multiple milestones. Missions contain milestones that break down work into phases.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `title` | string | ✓ | Mission title |
| `description` | string | — | Detailed objectives and context |
| `autoAdvance` | boolean | — | Auto-activate next slice on completion |
| `title` | string | ✓ | Mission title — brief but descriptive |
| `description` | string | — | Detailed mission objectives and context |
| `autoAdvance` | boolean | — | Automatically activate the next pending slice when the current slice completes |
### fn_mission_list
List all missions with current status. No parameters.
List all missions with their current status.
No parameters.
### fn_mission_show
Show mission details with full hierarchy.
Show mission details with full hierarchy: milestones → slices → features.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
@@ -194,63 +190,65 @@ Show mission details with full hierarchy.
### fn_mission_delete
Delete a mission and all children. Tasks are NOT deleted.
Delete a mission and all its milestones, slices, and features. Cannot be undone.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Mission ID |
| `id` | string | ✓ | Mission ID to delete (e.g., M-001) |
### fn_milestone_add
Add a milestone to a mission.
Add a milestone to a mission. Milestones represent phases of work.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `missionId` | string | ✓ | Parent mission ID |
| `missionId` | string | ✓ | Parent mission ID (e.g., M-001) |
| `title` | string | ✓ | Milestone title |
| `description` | string | — | Milestone description |
### fn_slice_add
Add a slice to a milestone.
Add a slice to a milestone. Slices are work units that can be activated for implementation.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `milestoneId` | string | ✓ | Parent milestone ID |
| `milestoneId` | string | ✓ | Parent milestone ID (e.g., MS-001) |
| `title` | string | ✓ | Slice title |
| `description` | string | — | Slice description |
### fn_feature_add
Add a feature to a slice.
Add a feature to a slice. Features are deliverables that can be linked to tasks.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `sliceId` | string | ✓ | Parent slice ID |
| `sliceId` | string | ✓ | Parent slice ID (e.g., SL-001) |
| `title` | string | ✓ | Feature title |
| `description` | string | — | Feature description |
| `acceptanceCriteria` | string | — | Acceptance criteria |
| `acceptanceCriteria` | string | — | Acceptance criteria for completing the feature |
### fn_slice_activate
Activate a pending slice for implementation.
Activate a pending slice for implementation. Sets status to 'active' and enables task linking for its features.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `id` | string | ✓ | Slice ID (must be pending) |
| `id` | string | ✓ | Slice ID to activate (e.g., SL-001) |
### fn_feature_link_task
Link a feature to a Fusion task. Updates feature status to triaged.
Link a feature to a fn task for implementation. Updates the feature status to 'triaged' and associates it with the task.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `featureId` | string | ✓ | Feature ID (e.g., F-001) |
| `taskId` | string | ✓ | Task ID (e.g., FN-001) |
| `featureId` | string | ✓ | Feature ID to link (e.g., F-001) |
| `taskId` | string | ✓ | Task ID to link to (e.g., FN-001) |
## Agent Tools
### fn_agent_stop
Stop (pause) a running agent. Transitions the agent from running/active to paused state.
Stop a running agent — pauses its execution. Transitions the agent from running/active to paused state.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
@@ -258,7 +256,7 @@ Stop (pause) a running agent. Transitions the agent from running/active to pause
### fn_agent_start
Start (resume) a stopped agent. Transitions the agent from paused to active state.
Start a stopped agent — resumes its execution. Transitions the agent from paused to active state.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
@@ -268,22 +266,23 @@ Start (resume) a stopped agent. Transitions the agent from paused to active stat
### fn_skills_search
Search the skills.sh directory for agent skills. Returns matching skills with names, sources, install counts, and install commands.
Search the skills.sh directory for agent skills. Returns matching skills with names, sources (owner/repo), install counts, and install commands. Use fn_skills_install to install a selected skill.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `query` | string | ✓ | Search query — framework, technology, or capability (e.g., "react", "firebase", "testing", "docker") |
| `limit` | number | — | Max results (default: 10, max: 50) |
| `query` | string | ✓ | Search query — framework name, technology, or capability (e.g., 'react', 'firebase', 'testing', 'docker') |
| `limit` | number | — | Max results to return (default: 10, max: 50) |
### fn_skills_install
Install an agent skill from skills.sh into the current project. Downloads skill files into the project's skill directories.
Install an agent skill from skills.sh into the current project. Downloads skill files into the project's skill directories (.fusion/skills/, legacy .pi/skills/, .agents/skills/). The skill becomes available to AI agents in subsequent sessions.
| Parameter | Type | Required | Description |
|-----------|------|----------|-------------|
| `source` | string | ✓ | GitHub source in owner/repo format (e.g., "firebase/agent-skills") |
| `skill` | string | — | Specific skill name to install. Omit to install all skills from the source. |
| `source` | string | ✓ | GitHub source in owner/repo format (e.g., 'firebase/agent-skills') |
| `skill` | string | — | Specific skill name to install (e.g., 'firebase-basics'). Omit to install all skills from the source. |
<!-- END: extension-tools -->
## Dashboard Command
### /fn

View File

@@ -7,40 +7,42 @@ Triage → Todo → In Progress → In Review → Done → Archived
## Pi Extension Tools (Available to Agents)
All skill/extension tool invocations in this catalog use the public `fn_*` namespace. Engine runtime tools (for example `task_create`, `task_update`, `task_log`, `task_done`) are internal and intentionally not listed here.
All skill/extension tool invocations in this catalog use the public `fn_*` namespace. Engine runtime sessions also have additional runtime-only `fn_*` tools that are intentionally not listed here (see `references/engine-tools.md`).
<!-- BEGIN: fusion-capabilities-tool-table (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->
| Tool | Purpose |
|------|---------|
| `fn_task_create` | Create a new task in triage |
| `fn_task_update` | Update task title, description, or dependencies |
| `fn_task_list` | List all tasks grouped by column |
| `fn_task_show` | Show full task details, steps, and log preview |
| `fn_task_attach` | Attach a file to a task |
| `fn_task_pause` | Pause automation for a task |
| `fn_task_unpause` | Resume automation for a task |
| `fn_task_retry` | Retry a failed task (clears error, moves to todo) |
| `fn_task_duplicate` | Duplicate a task (copy to triage) |
| `fn_task_refine` | Create refinement task for follow-up work |
| `fn_task_archive` | Archive a done task |
| `fn_task_unarchive` | Restore an archived task |
| `fn_task_delete` | Permanently delete a task |
| `fn_task_import_github` | Batch import GitHub issues as tasks |
| `fn_task_import_github_issue` | Import a single GitHub issue |
| `fn_task_browse_github_issues` | Browse GitHub issues before importing |
| `fn_task_plan` | Create task via AI-guided planning mode |
| `fn_mission_create` | Create a new mission |
| `fn_mission_list` | List all missions |
| `fn_mission_show` | Show mission hierarchy |
| `fn_mission_delete` | Delete a mission |
| `fn_milestone_add` | Add a milestone to a mission |
| `fn_slice_add` | Add a slice to a milestone |
| `fn_feature_add` | Add a feature to a slice |
| `fn_slice_activate` | Activate a pending slice |
| `fn_feature_link_task` | Link a feature to a task |
| `fn_agent_stop` | Stop (pause) a running agent |
| `fn_agent_start` | Start (resume) a stopped agent |
| `fn_skills_search` | Search skills.sh for agent skills |
| `fn_skills_install` | Install a skill from skills.sh |
| `fn_task_create` | Create a new task on the Fusion task board. The task enters the triage column where the AI triage agent will specify it into a full prompt with steps, file scope, and acceptance criteria. |
| `fn_task_update` | Update fields on an existing task. Supports modifying the title, description, dependencies, and assigned agent after task creation. |
| `fn_task_list` | List all tasks on the Fusion board, grouped by column. |
| `fn_task_show` | Show full details for a task including steps, progress, and log entries. |
| `fn_task_attach` | Attach a file to a task. Supports images (png, jpg, gif, webp) and text files (txt, log, json, yaml, yml, toml, csv, xml). |
| `fn_task_pause` | Pause a task — stops all automated agent and scheduler interaction for this task. |
| `fn_task_unpause` | Unpause a task — resumes automated agent and scheduler interaction. |
| `fn_task_retry` | Retry a failed task clears the error state and moves it back to the todo column for re-execution. |
| `fn_task_duplicate` | Duplicate an existing task, creating a fresh copy in triage. Copies the title and description but resets all execution state. The AI triage agent will re-specify the new task. |
| `fn_task_refine` | Request a refinement of a completed or in-review task. Creates a new follow-up task in triage that references the original task as a dependency. Use this when a done or in-review task needs additional work, improvements, or follow-up changes. |
| `fn_task_archive` | Archive a done task (move from done → archived). Archived tasks are preserved for historical reference but moved out of the main board view. |
| `fn_task_unarchive` | Unarchive an archived task (move from archived → done). Restores the task to the done column. |
| `fn_task_delete` | Permanently delete a task from the Fusion board. Tasks are deleted immediately and cannot be recovered. |
| `fn_task_import_github` | Import GitHub issues as Fusion tasks. Fetches open issues from a repository and creates tasks in the triage column. Each task includes the issue title and body with a link to the source issue. |
| `fn_task_import_github_issue` | Import a specific GitHub issue as a Fusion task. Fetches the issue by number and creates a single task in the triage column with the issue title and body. |
| `fn_task_browse_github_issues` | List open GitHub issues from a repository to browse before importing. Returns issue numbers, titles, and URLs for selection. Use with fn_task_import_github_issue to import specific issues by number. |
| `fn_task_plan` | Create a task via AI-guided planning mode — interactive conversation to refine your idea into a well-specified task. |
| `fn_mission_create` | Create a new mission — a high-level objective that can span multiple milestones. Missions contain milestones that break down work into phases. |
| `fn_mission_list` | List all missions with their current status. |
| `fn_mission_show` | Show mission details with full hierarchy: milestones → slices → features. |
| `fn_mission_delete` | Delete a mission and all its milestones, slices, and features. Cannot be undone. |
| `fn_milestone_add` | Add a milestone to a mission. Milestones represent phases of work. |
| `fn_slice_add` | Add a slice to a milestone. Slices are work units that can be activated for implementation. |
| `fn_feature_add` | Add a feature to a slice. Features are deliverables that can be linked to tasks. |
| `fn_slice_activate` | Activate a pending slice for implementation. Sets status to 'active' and enables task linking for its features. |
| `fn_feature_link_task` | Link a feature to a fn task for implementation. Updates the feature status to 'triaged' and associates it with the task. |
| `fn_agent_stop` | Stop a running agent — pauses its execution. Transitions the agent from running/active to paused state. |
| `fn_agent_start` | Start a stopped agent — resumes its execution. Transitions the agent from paused to active state. |
| `fn_skills_search` | Search the skills.sh directory for agent skills. Returns matching skills with names, sources (owner/repo), install counts, and install commands. Use fn_skills_install to install a selected skill. |
| `fn_skills_install` | Install an agent skill from skills.sh into the current project. Downloads skill files into the project's skill directories (.fusion/skills/, legacy .pi/skills/, .agents/skills/). The skill becomes available to AI agents in subsequent sessions. |
<!-- END: fusion-capabilities-tool-table -->
## CLI Commands (fn)

View File

@@ -6,17 +6,243 @@ import { spawnSync } from "node:child_process";
const __dirname = dirname(fileURLToPath(import.meta.url));
const cliRoot = resolve(__dirname, "../..");
const repoRoot = resolve(cliRoot, "../..");
const skillDir = resolve(cliRoot, "skill/fusion");
const extensionPath = resolve(cliRoot, "src/extension.ts");
const EXT_TOOLS_BEGIN =
"<!-- BEGIN: extension-tools (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->";
const EXT_TOOLS_END = "<!-- END: extension-tools -->";
const CAP_TABLE_BEGIN =
"<!-- BEGIN: fusion-capabilities-tool-table (auto-generated by scripts/sync-fusion-skill-tools.mjs — do not edit by hand) -->";
const CAP_TABLE_END = "<!-- END: fusion-capabilities-tool-table -->";
const engineToolSourceFiles = [
resolve(repoRoot, "packages/engine/src/agent-tools.ts"),
resolve(repoRoot, "packages/engine/src/triage.ts"),
resolve(repoRoot, "packages/engine/src/executor.ts"),
resolve(repoRoot, "packages/engine/src/merger.ts"),
resolve(repoRoot, "packages/engine/src/agent-heartbeat.ts"),
];
function findMatchingBrace(source: string, openIndex: number): number {
let depth = 0;
let inSingle = false;
let inDouble = false;
let inTemplate = false;
let inLineComment = false;
let inBlockComment = false;
let escaped = false;
for (let i = openIndex; i < source.length; i++) {
const char = source[i];
const next = source[i + 1];
if (inLineComment) {
if (char === "\n") inLineComment = false;
continue;
}
if (inBlockComment) {
if (char === "*" && next === "/") {
inBlockComment = false;
i++;
}
continue;
}
if (inSingle || inDouble || inTemplate) {
if (escaped) {
escaped = false;
continue;
}
if (char === "\\") {
escaped = true;
continue;
}
if (inSingle && char === "'") inSingle = false;
else if (inDouble && char === '"') inDouble = false;
else if (inTemplate && char === "`") inTemplate = false;
continue;
}
if (char === "/" && next === "/") {
inLineComment = true;
i++;
continue;
}
if (char === "/" && next === "*") {
inBlockComment = true;
i++;
continue;
}
if (char === "'") {
inSingle = true;
continue;
}
if (char === '"') {
inDouble = true;
continue;
}
if (char === "`") {
inTemplate = true;
continue;
}
if (char === "{") depth++;
if (char === "}") {
depth--;
if (depth === 0) return i;
}
}
throw new Error("Unbalanced braces");
}
function splitTopLevelProperties(objectBody: string): string[] {
const props: string[] = [];
let start = 0;
let depthParen = 0;
let depthBrace = 0;
let depthBracket = 0;
let inSingle = false;
let inDouble = false;
let inTemplate = false;
let inLineComment = false;
let inBlockComment = false;
let escaped = false;
for (let i = 0; i < objectBody.length; i++) {
const ch = objectBody[i];
const next = objectBody[i + 1];
if (inLineComment) {
if (ch === "\n") inLineComment = false;
continue;
}
if (inBlockComment) {
if (ch === "*" && next === "/") {
inBlockComment = false;
i++;
}
continue;
}
if (inSingle || inDouble || inTemplate) {
if (escaped) {
escaped = false;
continue;
}
if (ch === "\\") {
escaped = true;
continue;
}
if (inSingle && ch === "'") inSingle = false;
else if (inDouble && ch === '"') inDouble = false;
else if (inTemplate && ch === "`") inTemplate = false;
continue;
}
if (ch === "/" && next === "/") {
inLineComment = true;
i++;
continue;
}
if (ch === "/" && next === "*") {
inBlockComment = true;
i++;
continue;
}
if (ch === "'") {
inSingle = true;
continue;
}
if (ch === '"') {
inDouble = true;
continue;
}
if (ch === "`") {
inTemplate = true;
continue;
}
if (ch === "(") depthParen++;
else if (ch === ")") depthParen--;
else if (ch === "{") depthBrace++;
else if (ch === "}") depthBrace--;
else if (ch === "[") depthBracket++;
else if (ch === "]") depthBracket--;
if (ch === "," && depthParen === 0 && depthBrace === 0 && depthBracket === 0) {
const prop = objectBody.slice(start, i).trim();
if (prop) props.push(prop);
start = i + 1;
}
}
const tail = objectBody.slice(start).trim();
if (tail) props.push(tail);
return props;
}
function getRegisterToolBlocks(): Array<{ name: string; block: string }> {
const src = readFileSync(extensionPath, "utf-8");
const blocks: Array<{ name: string; block: string }> = [];
const token = "pi.registerTool(";
let from = 0;
while (true) {
const start = src.indexOf(token, from);
if (start === -1) break;
const braceStart = src.indexOf("{", start);
const braceEnd = findMatchingBrace(src, braceStart);
const block = src.slice(braceStart, braceEnd + 1);
const nameMatch = block.match(/name:\s*"(fn_[a-z_]+)"/);
if (nameMatch) {
blocks.push({ name: nameMatch[1], block });
}
from = braceEnd + 1;
}
return blocks;
}
/**
* Extract all tool names registered via pi.registerTool({ name: "..." })
* from the extension source code.
*/
function getExtensionToolNames(): string[] {
const src = readFileSync(extensionPath, "utf-8");
const matches = [...src.matchAll(/name:\s*"(fn_[a-z_]+)"/g)];
return matches.map((m) => m[1]).sort();
return getRegisterToolBlocks()
.map((entry) => entry.name)
.sort();
}
function getExtensionToolParamNames(): Map<string, string[]> {
const result = new Map<string, string[]>();
for (const { name, block } of getRegisterToolBlocks()) {
const paramsStart = block.indexOf("parameters:");
if (paramsStart === -1) {
result.set(name, []);
continue;
}
const objectStart = block.indexOf("Type.Object(", paramsStart);
if (objectStart === -1) {
result.set(name, []);
continue;
}
const braceStart = block.indexOf("{", objectStart);
const braceEnd = findMatchingBrace(block, braceStart);
const body = block.slice(braceStart + 1, braceEnd);
const params = splitTopLevelProperties(body)
.map((prop) => prop.match(/^([A-Za-z_][A-Za-z0-9_]*)\s*:/)?.[1])
.filter((value): value is string => Boolean(value))
.sort();
result.set(name, params);
}
return result;
}
/**
@@ -31,13 +257,28 @@ function getDocumentedToolNames(): string[] {
return matches.map((m) => m[1]).sort();
}
function getDocumentedToolParamNames(): Map<string, string[]> {
const doc = readFileSync(resolve(skillDir, "references/extension-tools.md"), "utf-8");
const map = new Map<string, string[]>();
const toolRegex = /^### (fn_[a-z_]+)\n([\s\S]*?)(?=^### fn_|^## [A-Za-z]|^<!-- END: extension-tools -->)/gm;
for (const match of doc.matchAll(toolRegex)) {
const [, toolName, section] = match;
const params = [...section.matchAll(/\| `([A-Za-z_][A-Za-z0-9_]*)` \|/g)]
.map((m) => m[1])
.sort();
map.set(toolName, params);
}
return map;
}
/**
* Extract tool names listed in SKILL.md under the tool categories.
*/
function getSkillMdToolNames(): string[] {
const doc = readFileSync(resolve(skillDir, "SKILL.md"), "utf-8");
const matches = [...doc.matchAll(/`(fn_[a-z_]+)`/g)];
// Deduplicate
return [...new Set(matches.map((m) => m[1]))].sort();
}
@@ -53,6 +294,22 @@ function getCapabilitiesToolNames(): string[] {
return matches.map((m) => m[1]).sort();
}
function getEngineSessionToolNames(): string[] {
const names = new Set<string>();
for (const path of engineToolSourceFiles) {
const src = readFileSync(path, "utf-8");
for (const match of src.matchAll(/name:\s*"(fn_[a-z_]+)"/g)) {
names.add(match[1]);
}
}
return [...names].sort();
}
function getDocumentedEngineToolNames(): string[] {
const doc = readFileSync(resolve(skillDir, "references/engine-tools.md"), "utf-8");
return [...new Set([...doc.matchAll(/`(fn_[a-z_]+)`/g)].map((m) => m[1]))].sort();
}
function collectMarkdownFiles(dir: string): string[] {
const files: string[] = [];
for (const entry of readdirSync(dir, { withFileTypes: true })) {
@@ -105,6 +362,17 @@ describe("Skill-Extension Sync", () => {
expect(documentedTools).toEqual(extensionTools);
});
it("extension-tools.md tool parameter tables match extension.ts registrations", () => {
const extensionTools = getExtensionToolParamNames();
const documentedTools = getDocumentedToolParamNames();
for (const [toolName, extensionParams] of extensionTools.entries()) {
expect(documentedTools.has(toolName), `missing documented section for ${toolName}`).toBe(true);
const documentedParams = documentedTools.get(toolName) ?? [];
expect(documentedParams).toEqual(extensionParams);
}
});
it("SKILL.md tool listing includes all registered tools", () => {
const extensionTools = getExtensionToolNames();
const skillTools = getSkillMdToolNames();
@@ -123,6 +391,26 @@ describe("Skill-Extension Sync", () => {
(t) => !capTools.includes(t),
);
expect(missingFromCaps).toEqual([]);
expect(capTools).toEqual(extensionTools);
});
it("fusion-capabilities.md tool table is auto-generated with markers", () => {
const doc = readFileSync(resolve(skillDir, "references/fusion-capabilities.md"), "utf-8");
expect(doc).toContain(CAP_TABLE_BEGIN);
expect(doc).toContain(CAP_TABLE_END);
});
it("extension-tools.md has auto-generated markers", () => {
const doc = readFileSync(resolve(skillDir, "references/extension-tools.md"), "utf-8");
expect(doc).toContain(EXT_TOOLS_BEGIN);
expect(doc).toContain(EXT_TOOLS_END);
});
it("engine-tools.md documents all engine session-scoped tools", () => {
const engineTools = getEngineSessionToolNames();
const documented = getDocumentedEngineToolNames();
const missing = engineTools.filter((name) => !documented.includes(name));
expect(missing).toEqual([]);
});
it("covers the full Fusion skill markdown surface", () => {
@@ -134,6 +422,7 @@ describe("Skill-Extension Sync", () => {
"SKILL.md",
"references/best-practices.md",
"references/cli-commands.md",
"references/engine-tools.md",
"references/extension-tools.md",
"references/fusion-capabilities.md",
"references/skill-patterns.md",
@@ -151,8 +440,6 @@ describe("Skill-Extension Sync", () => {
toolName.replace(/^fn_/, ""),
);
// These names are intentionally unprefixed engine/runtime tools and are allowed
// to appear in docs that explain capability boundaries.
const allowedUnprefixedInternalTools = new Set([
"task_create",
"task_update",
@@ -195,8 +482,7 @@ describe("Skill-Extension Sync", () => {
expect(dashboardCli).toContain("/fn");
});
it("SKILL.md tool-categories block matches the sync script output (no drift)", () => {
const repoRoot = resolve(cliRoot, "../..");
it("SKILL.md and reference generated blocks match sync script output (no drift)", () => {
const script = resolve(repoRoot, "scripts/sync-fusion-skill-tools.mjs");
const result = spawnSync("node", [script, "--check"], {
encoding: "utf-8",