diff --git a/.afx.yaml b/.afx.yaml index 6654c05..1bd5b5e 100644 --- a/.afx.yaml +++ b/.afx.yaml @@ -1,5 +1,5 @@ # AFX dogfooding — using AFX to develop AFX -version: "1.0" +version: "feature/pack" paths: specs: "docs/specs" @@ -37,3 +37,5 @@ ai_attribution: # No source code — these don't apply for a docs/tooling repo require_see_links: [] scan_for_orphans: [] + +packs: diff --git a/.afx.yaml.template b/.afx.yaml.template index 98877a2..d6aca75 100644 --- a/.afx.yaml.template +++ b/.afx.yaml.template @@ -2,7 +2,9 @@ # Copy this file to .afx.yaml in your project root and customize. # See: docs/agenticflowx/agenticflowx.md -version: '1.0' +# AFX version — controls which branch/tag install.sh and pack index are fetched from. +# Accepts: semver (e.g. '1.5.3' → tag v1.5.3), branch name, or 'main'. +version: 'main' # ───────────────────────────────────────────────────────────────────────────── # Paths @@ -97,3 +99,16 @@ scan_for_orphans: templates: location: 'docs/agenticflowx/templates' # Path to AFX templates folder default: 'default' # Template used by /afx:init feature + +# ───────────────────────────────────────────────────────────────────────────── +# Packs — Managed by install.sh, do not edit manually +# ───────────────────────────────────────────────────────────────────────────── +# Packs are installed, enabled, disabled, and removed via install.sh. +# Each entry tracks the pack name, status, installed ref, and disabled items. +# Example: +# packs: +# - name: afx-pack-qa +# status: enabled +# installed_ref: main +# disabled_items: [] +packs: [] diff --git a/.agent/skills/afx-check/SKILL.md b/.agent/skills/afx-check/SKILL.md new file mode 100644 index 0000000..a1da2a6 --- /dev/null +++ b/.agent/skills/afx-check/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-check +description: Quality verification and compliance checking for AgenticFlowX. +--- + +# AFX check + +Use this skill when the user requests: + +- `afx-check` +- `/afx:check` +- AFX check subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-check.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:check` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-context/SKILL.md b/.agent/skills/afx-context/SKILL.md new file mode 100644 index 0000000..f5f873f --- /dev/null +++ b/.agent/skills/afx-context/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-context +description: Session Context protocol for seamless context transfer between AI sessions. +--- + +# AFX context + +Use this skill when the user requests: + +- `afx-context` +- `/afx:context` +- AFX context subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-context.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:context` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-dev/SKILL.md b/.agent/skills/afx-dev/SKILL.md new file mode 100644 index 0000000..d4b7c9f --- /dev/null +++ b/.agent/skills/afx-dev/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-dev +description: Development actions with AFX traceability. +--- + +# AFX dev + +Use this skill when the user requests: + +- `afx-dev` +- `/afx:dev` +- AFX dev subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-dev.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:dev` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-discover/SKILL.md b/.agent/skills/afx-discover/SKILL.md new file mode 100644 index 0000000..6008ee2 --- /dev/null +++ b/.agent/skills/afx-discover/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-discover +description: Discover what exists in your project - infrastructure scripts, automation tools, deployment workflows, and development capabilities. +--- + +# AFX discover + +Use this skill when the user requests: + +- `afx-discover` +- `/afx:discover` +- AFX discover subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-discover.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:discover` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-help/SKILL.md b/.agent/skills/afx-help/SKILL.md new file mode 100644 index 0000000..6bbef26 --- /dev/null +++ b/.agent/skills/afx-help/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-help +description: AFX (AgenticFlowX) command reference. +--- + +# AFX help + +Use this skill when the user requests: + +- `afx-help` +- `/afx:help` +- AFX help subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-help.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:help` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-init/SKILL.md b/.agent/skills/afx-init/SKILL.md new file mode 100644 index 0000000..c064816 --- /dev/null +++ b/.agent/skills/afx-init/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-init +description: Feature spec scaffolding for AgenticFlowX projects. +--- + +# AFX init + +Use this skill when the user requests: + +- `afx-init` +- `/afx:init` +- AFX init subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-init.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:init` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-next/SKILL.md b/.agent/skills/afx-next/SKILL.md new file mode 100644 index 0000000..3bc5ee4 --- /dev/null +++ b/.agent/skills/afx-next/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-next +description: The "Golden Thread" command. Intelligently analyzes your current context (git state, active tasks, session history) and tells you exactly what to do next. +--- + +# AFX next + +Use this skill when the user requests: + +- `afx-next` +- `/afx:next` +- AFX next subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-next.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:next` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-report/SKILL.md b/.agent/skills/afx-report/SKILL.md new file mode 100644 index 0000000..cb8cf01 --- /dev/null +++ b/.agent/skills/afx-report/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-report +description: Traceability metrics and project health reporting for AgenticFlowX. +--- + +# AFX report + +Use this skill when the user requests: + +- `afx-report` +- `/afx:report` +- AFX report subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-report.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:report` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-session/SKILL.md b/.agent/skills/afx-session/SKILL.md new file mode 100644 index 0000000..b375aeb --- /dev/null +++ b/.agent/skills/afx-session/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-session +description: Session discussion capture and recall for multi-agent workflows. +--- + +# AFX session + +Use this skill when the user requests: + +- `afx-session` +- `/afx:session` +- AFX session subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-session.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:session` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-spec/SKILL.md b/.agent/skills/afx-spec/SKILL.md new file mode 100644 index 0000000..4ea18af --- /dev/null +++ b/.agent/skills/afx-spec/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-spec +description: Specification management, navigation, review, and approval for spec-centric workflows. +--- + +# AFX spec + +Use this skill when the user requests: + +- `afx-spec` +- `/afx:spec` +- AFX spec subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-spec.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:spec` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-task/SKILL.md b/.agent/skills/afx-task/SKILL.md new file mode 100644 index 0000000..bc23020 --- /dev/null +++ b/.agent/skills/afx-task/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-task +description: Verify and summarize task implementation status. +--- + +# AFX task + +Use this skill when the user requests: + +- `afx-task` +- `/afx:task` +- AFX task subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-task.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:task` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-update/SKILL.md b/.agent/skills/afx-update/SKILL.md new file mode 100644 index 0000000..304e05d --- /dev/null +++ b/.agent/skills/afx-update/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-update +description: Check for upstream AFX updates and apply them safely. +--- + +# AFX update + +Use this skill when the user requests: + +- `afx-update` +- `/afx:update` +- AFX update subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-update.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:update` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.agent/skills/afx-work/SKILL.md b/.agent/skills/afx-work/SKILL.md new file mode 100644 index 0000000..5e31e28 --- /dev/null +++ b/.agent/skills/afx-work/SKILL.md @@ -0,0 +1,31 @@ +--- +name: afx-work +description: Workflow state management for AgenticFlowX sessions. +--- + +# AFX work + +Use this skill when the user requests: + +- `afx-work` +- `/afx:work` +- AFX work subcommands in natural language + +## Source of Truth + +Follow the canonical command spec in: + +- `.claude/commands/afx-work.md` + +Do not re-invent workflow steps. Read the command file and execute it faithfully using available tools. + +## Execution Rules + +1. Read `.afx.yaml` if present; otherwise use defaults described in the command spec. +2. Execute only the requested subcommand(s). +3. Preserve AFX traceability requirements (`@see`, task/session updates, gates) exactly as defined. +4. End with ranked next-command suggestions matching the command spec. + +## Compatibility Note + +`/afx:work` is a Claude slash command name. In Antigravity, interpret it as an instruction to run the equivalent AFX workflow via this skill. diff --git a/.codex/skills/afx-check/SKILL.md b/.codex/skills/afx-check/SKILL.md index a3ccdc5..c0fb047 100644 --- a/.codex/skills/afx-check/SKILL.md +++ b/.codex/skills/afx-check/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-check -description: Execute the AFX check command workflow in Codex. Trigger when the user asks for afx-check, /afx:check, or the corresponding check subcommands. +description: Quality verification and compliance checking for AgenticFlowX. --- # AFX check diff --git a/.codex/skills/afx-context/SKILL.md b/.codex/skills/afx-context/SKILL.md index ea2a90e..207a0b6 100644 --- a/.codex/skills/afx-context/SKILL.md +++ b/.codex/skills/afx-context/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-context -description: Execute the AFX context command workflow in Codex. Trigger when the user asks for afx-context, /afx:context, or the corresponding context subcommands. +description: Session Context protocol for seamless context transfer between AI sessions. --- # AFX context diff --git a/.codex/skills/afx-dev/SKILL.md b/.codex/skills/afx-dev/SKILL.md index 5607dae..3234686 100644 --- a/.codex/skills/afx-dev/SKILL.md +++ b/.codex/skills/afx-dev/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-dev -description: Execute the AFX dev command workflow in Codex. Trigger when the user asks for afx-dev, /afx:dev, or the corresponding dev subcommands. +description: Development actions with AFX traceability. --- # AFX dev diff --git a/.codex/skills/afx-discover/SKILL.md b/.codex/skills/afx-discover/SKILL.md index 6cfd94b..12d0c65 100644 --- a/.codex/skills/afx-discover/SKILL.md +++ b/.codex/skills/afx-discover/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-discover -description: Execute the AFX discover command workflow in Codex. Trigger when the user asks for afx-discover, /afx:discover, or the corresponding discover subcommands. +description: Discover what exists in your project - infrastructure scripts, automation tools, deployment workflows, and development capabilities. --- # AFX discover diff --git a/.codex/skills/afx-help/SKILL.md b/.codex/skills/afx-help/SKILL.md index ad352c8..7427a77 100644 --- a/.codex/skills/afx-help/SKILL.md +++ b/.codex/skills/afx-help/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-help -description: Execute the AFX help command workflow in Codex. Trigger when the user asks for afx-help, /afx:help, or the corresponding help subcommands. +description: AFX (AgenticFlowX) command reference. --- # AFX help diff --git a/.codex/skills/afx-init/SKILL.md b/.codex/skills/afx-init/SKILL.md index d7992d3..c399023 100644 --- a/.codex/skills/afx-init/SKILL.md +++ b/.codex/skills/afx-init/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-init -description: Execute the AFX init command workflow in Codex. Trigger when the user asks for afx-init, /afx:init, or the corresponding init subcommands. +description: Feature spec scaffolding for AgenticFlowX projects. --- # AFX init diff --git a/.codex/skills/afx-next/SKILL.md b/.codex/skills/afx-next/SKILL.md index 904ed03..6eef26a 100644 --- a/.codex/skills/afx-next/SKILL.md +++ b/.codex/skills/afx-next/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-next -description: Execute the AFX next command workflow in Codex. Trigger when the user asks for afx-next, /afx:next, or the corresponding next subcommands. +description: The "Golden Thread" command. Intelligently analyzes your current context (git state, active tasks, session history) and tells you exactly what to do next. --- # AFX next diff --git a/.codex/skills/afx-report/SKILL.md b/.codex/skills/afx-report/SKILL.md index ab682d2..66baa9b 100644 --- a/.codex/skills/afx-report/SKILL.md +++ b/.codex/skills/afx-report/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-report -description: Execute the AFX report command workflow in Codex. Trigger when the user asks for afx-report, /afx:report, or the corresponding report subcommands. +description: Traceability metrics and project health reporting for AgenticFlowX. --- # AFX report diff --git a/.codex/skills/afx-session/SKILL.md b/.codex/skills/afx-session/SKILL.md index aada894..cf6dac1 100644 --- a/.codex/skills/afx-session/SKILL.md +++ b/.codex/skills/afx-session/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-session -description: Execute the AFX session command workflow in Codex. Trigger when the user asks for afx-session, /afx:session, or the corresponding session subcommands. +description: Session discussion capture and recall for multi-agent workflows. --- # AFX session diff --git a/.codex/skills/afx-spec/SKILL.md b/.codex/skills/afx-spec/SKILL.md index b95d153..7274e3d 100644 --- a/.codex/skills/afx-spec/SKILL.md +++ b/.codex/skills/afx-spec/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-spec -description: Execute the AFX spec command workflow in Codex. Trigger when the user asks for afx-spec, /afx:spec, or the corresponding spec subcommands. +description: Specification management, navigation, review, and approval for spec-centric workflows. --- # AFX spec diff --git a/.codex/skills/afx-task/SKILL.md b/.codex/skills/afx-task/SKILL.md index 2a40a1a..ff12025 100644 --- a/.codex/skills/afx-task/SKILL.md +++ b/.codex/skills/afx-task/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-task -description: Execute the AFX task command workflow in Codex. Trigger when the user asks for afx-task, /afx:task, or the corresponding task subcommands. +description: Verify and summarize task implementation status. --- # AFX task diff --git a/.codex/skills/afx-update/SKILL.md b/.codex/skills/afx-update/SKILL.md index 6b762e5..0600c23 100644 --- a/.codex/skills/afx-update/SKILL.md +++ b/.codex/skills/afx-update/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-update -description: Execute the AFX update command workflow in Codex. Trigger when the user asks for afx-update, /afx:update, or update check/apply operations. +description: Check for upstream AFX updates and apply them safely. --- # AFX update diff --git a/.codex/skills/afx-work/SKILL.md b/.codex/skills/afx-work/SKILL.md index 07e8f0f..f45ba13 100644 --- a/.codex/skills/afx-work/SKILL.md +++ b/.codex/skills/afx-work/SKILL.md @@ -1,6 +1,6 @@ --- name: afx-work -description: Execute the AFX work command workflow in Codex. Trigger when the user asks for afx-work, /afx:work, or the corresponding work subcommands. +description: Workflow state management for AgenticFlowX sessions. --- # AFX work diff --git a/.gemini/commands/afx-check.md b/.gemini/commands/afx-check.md deleted file mode 100644 index 155d26e..0000000 --- a/.gemini/commands/afx-check.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, quality, verification] ---- - -# /afx:check - -Quality verification and compliance checking for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-check.md` - -## Gemini-Specific Guidance - -When performing quality and verification checks, use your specialized tools: - -1. **Path Verification**: Use `grep_search` to find entry points, mock patterns, and Red Flags. Use `codebase_investigator` to trace execution paths across the codebase if the call chain is complex. -2. **Linting**: Use `grep_search` to find orphaned annotations. Use `read_file` to understand the context around annotations and provide high-quality fix suggestions. -3. **Link Integrity**: Use `grep_search` to identify all cross-references in `spec.md`, `design.md`, and `tasks.md`. -4. **Schema Verification**: Use `grep_search` to extract database artifacts from `design.md` and verify consistency between migrations, types, and repository code. - -## Usage - -```bash -/afx:check path -/afx:check lint [path] -/afx:check links -/afx:check all -/afx:check schema -``` diff --git a/.gemini/commands/afx-check.toml b/.gemini/commands/afx-check.toml new file mode 100644 index 0000000..257c357 --- /dev/null +++ b/.gemini/commands/afx-check.toml @@ -0,0 +1,23 @@ +description = "Quality verification and compliance checking for AgenticFlowX." +prompt = """ +You are executing the AFX /afx:check command. This runs quality gates, path verification, lint checks, and link integrity audits. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-check.md} + +## Gemini-Specific Tool Guidance + +When performing quality and verification checks, use your specialized tools: + +1. **Path Verification**: Use `grep_search` to find entry points, mock patterns, and Red Flags. Use `codebase_investigator` to trace execution paths across the codebase if the call chain is complex. +2. **Linting**: Use `grep_search` to find orphaned annotations. Use `read_file` to understand the context around annotations and provide high-quality fix suggestions. +3. **Link Integrity**: Use `grep_search` to identify all cross-references in `spec.md`, `design.md`, and `tasks.md`. +4. **Schema Verification**: Use `grep_search` to extract database artifacts from `design.md` and verify consistency between migrations, types, and repository code. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-context.md b/.gemini/commands/afx-context.md deleted file mode 100644 index 9260daa..0000000 --- a/.gemini/commands/afx-context.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, context, session] ---- - -# /afx:context - -Session Context protocol for seamless context transfer between AI sessions. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-context.md` - -## Gemini-Specific Guidance - -To ensure high-fidelity context preservation: - -1. **Comprehensive Bundle**: Use `grep_search`, `read_file`, and `run_shell_command` to gather all necessary session state (tasks, journal entries, decisions, uncommitted files). -2. **Next Agent Instructions**: When generating the context bundle, be explicit and detailed. Use your ability to synthesize complex information to provide high-quality instructions for the next session. -3. **Traceability Analysis**: For `/afx:context impact`, use `grep_search` to find all `@see` references and `codebase_investigator` to determine the breadth of the impact. - -## Usage - -```bash -/afx:context save [feature] -/afx:context load -/afx:context history [feature] -/afx:context impact -``` diff --git a/.gemini/commands/afx-context.toml b/.gemini/commands/afx-context.toml new file mode 100644 index 0000000..e6b6dcb --- /dev/null +++ b/.gemini/commands/afx-context.toml @@ -0,0 +1,22 @@ +description = "Session context protocol for seamless context transfer between AI sessions." +prompt = """ +You are executing the AFX /afx:context command. This manages session context for seamless transfer between AI sessions. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-context.md} + +## Gemini-Specific Tool Guidance + +To ensure high-fidelity context preservation: + +1. **Comprehensive Bundle**: Use `grep_search`, `read_file`, and `run_shell_command` to gather all necessary session state (tasks, journal entries, decisions, uncommitted files). +2. **Next Agent Instructions**: When generating the context bundle, be explicit and detailed. Use your ability to synthesize complex information to provide high-quality instructions for the next session. +3. **Traceability Analysis**: For `/afx:context impact`, use `grep_search` to find all `@see` references and `codebase_investigator` to determine the breadth of the impact. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-dev.md b/.gemini/commands/afx-dev.md deleted file mode 100644 index 0465927..0000000 --- a/.gemini/commands/afx-dev.md +++ /dev/null @@ -1,34 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, development, traceability] ---- - -# /afx:dev - -Traced development for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-dev.md` - -## Gemini-Specific Guidance - -During development: - -1. **Traced Implementation**: When implementing code, use your ability to generate clean, idiomatic code while ensuring every function includes the mandatory `@see` annotations. -2. **Refactoring**: Use `codebase_investigator` to ensure refactors maintain architectural integrity and that all `@see` links are preserved. -3. **Debugging**: Use `grep_search` to trace errors against specifications and design documents. - -## Usage - -```bash -/afx:dev code -/afx:dev refactor -/afx:dev fix -/afx:dev debug [error] -/afx:dev test [scope] -``` diff --git a/.gemini/commands/afx-dev.toml b/.gemini/commands/afx-dev.toml new file mode 100644 index 0000000..fb616f9 --- /dev/null +++ b/.gemini/commands/afx-dev.toml @@ -0,0 +1,22 @@ +description = "Development actions with AFX traceability - code, refactor, debug, test." +prompt = """ +You are executing the AFX /afx:dev command. This handles development actions with full spec traceability. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-dev.md} + +## Gemini-Specific Tool Guidance + +During development: + +1. **Traced Implementation**: When implementing code, generate clean, idiomatic code while ensuring every function includes the mandatory `@see` annotations. +2. **Refactoring**: Use `codebase_investigator` to ensure refactors maintain architectural integrity and that all `@see` links are preserved. +3. **Debugging**: Use `grep_search` to trace errors against specifications and design documents. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-discover.md b/.gemini/commands/afx-discover.md deleted file mode 100644 index a0d87ff..0000000 --- a/.gemini/commands/afx-discover.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, discovery, tooling] ---- - -# /afx:discover - -Project intelligence for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-discover.md` - -## Gemini-Specific Guidance - -When discovering project capabilities: - -1. **Capability Scan**: Use `codebase_investigator` and `run_shell_command` to identify build systems, test runners, package managers, and development tools. -2. **Infrastructure Discovery**: Use `grep_search` to find provisioning and automation scripts. -3. **Reporting**: Provide a high-level overview of project automation as defined in the canonical command. - -## Usage - -```bash -/afx:discover capabilities -/afx:discover scripts [keyword] -/afx:discover tools -/afx:discover infra [type] -``` diff --git a/.gemini/commands/afx-discover.toml b/.gemini/commands/afx-discover.toml new file mode 100644 index 0000000..ce41d18 --- /dev/null +++ b/.gemini/commands/afx-discover.toml @@ -0,0 +1,22 @@ +description = "Discover project capabilities, infrastructure, scripts, and tools." +prompt = """ +You are executing the AFX /afx:discover command. This discovers what exists in your project: infrastructure scripts, automation tools, deployment workflows, and development capabilities. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-discover.md} + +## Gemini-Specific Tool Guidance + +When discovering project capabilities: + +1. **Capability Scan**: Use `codebase_investigator` and `run_shell_command` to identify build systems, test runners, package managers, and development tools. +2. **Infrastructure Discovery**: Use `grep_search` to find provisioning and automation scripts. +3. **Reporting**: Provide a high-level overview of project automation as defined in the canonical command. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-help.md b/.gemini/commands/afx-help.md deleted file mode 100644 index a3fcbff..0000000 --- a/.gemini/commands/afx-help.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, help, reference] ---- - -# /afx:help - -AFX (AgenticFlowX) command reference. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-help.md` - -## Gemini-Specific Guidance - -When providing help: - -1. **Reference Guidance**: Use `read_file` to access the canonical help and guides. -2. **Contextual Help**: Tailor your help output based on the user's current role or task, as identified in your context analysis. - -## Usage - -```bash -/afx:help -/afx:help guides -``` diff --git a/.gemini/commands/afx-help.toml b/.gemini/commands/afx-help.toml new file mode 100644 index 0000000..5ef623b --- /dev/null +++ b/.gemini/commands/afx-help.toml @@ -0,0 +1,21 @@ +description = "AFX (AgenticFlowX) command reference and help." +prompt = """ +You are executing the AFX /afx:help command. This displays the AFX command reference and guides. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-help.md} + +## Gemini-Specific Tool Guidance + +When providing help: + +1. **Reference Guidance**: Use `read_file` to access the canonical help and guides. +2. **Contextual Help**: Tailor your help output based on the user's current role or task, as identified in your context analysis. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-init.md b/.gemini/commands/afx-init.md deleted file mode 100644 index 47ffd91..0000000 --- a/.gemini/commands/afx-init.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, init, scaffolding] ---- - -# /afx:init - -Feature spec scaffolding for AgenticFlowX projects. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-init.md` - -## Gemini-Specific Guidance - -During the "Smart Init Protocol": - -1. **Context Scan**: Leverage `codebase_investigator` to search the codebase for existing patterns, collisions, and dependencies before scaffolding new features. -2. **Scaffolding**: Use `write_file` to create the feature directory structure and files as defined in the canonical command's execution script. -3. **ADR Creation**: When using `/afx:init adr `, ensure you generate real, meaningful content for each section as instructed in the canonical spec. - -## Usage - -```bash -/afx:init feature <name> -/afx:init feature <name> --from <template> -/afx:init adr <title> -/afx:init template <name> -/afx:init prefix <feature> <prefix> -/afx:init config <action> <key> [value] -``` diff --git a/.gemini/commands/afx-init.toml b/.gemini/commands/afx-init.toml new file mode 100644 index 0000000..9d92daa --- /dev/null +++ b/.gemini/commands/afx-init.toml @@ -0,0 +1,22 @@ +description = "Feature spec scaffolding and ADR creation for AgenticFlowX projects." +prompt = """ +You are executing the AFX /afx:init command. This scaffolds new feature specs and creates Architecture Decision Records. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-init.md} + +## Gemini-Specific Tool Guidance + +During the "Smart Init Protocol": + +1. **Context Scan**: Leverage `codebase_investigator` to search the codebase for existing patterns, collisions, and dependencies before scaffolding new features. +2. **Scaffolding**: Use `write_file` to create the feature directory structure and files as defined in the canonical command's execution script. +3. **ADR Creation**: When using `/afx:init adr <title>`, ensure you generate real, meaningful content for each section as instructed in the canonical spec. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-next.md b/.gemini/commands/afx-next.md deleted file mode 100644 index 70e257d..0000000 --- a/.gemini/commands/afx-next.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, context, guidance] ---- - -# /afx:next - -The "Golden Thread" command. Intelligently analyzes your current context and tells you exactly what to do next. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-next.md` - -## Gemini-Specific Guidance - -To perform the deep context scan required by AFX, you should leverage Gemini's specialized tools: - -1. **Context Analysis**: Use `codebase_investigator` if you need a high-level architectural overview or to understand complex dependencies related to the current task. -2. **State Detection**: - - Use `run_shell_command` with `git status --short` and `git branch --show-current`. - - Use `grep_search` to find active tasks in `tasks.md` and recent entries in `journal.md`. -3. **Refined Suggestions**: Ensure your recommended next steps align perfectly with the AFX priority logic (Plan Mode → Git State → Active Task → Recent Completion → Idle). - -## Usage - -```bash -/afx:next -``` diff --git a/.gemini/commands/afx-next.toml b/.gemini/commands/afx-next.toml new file mode 100644 index 0000000..e860b89 --- /dev/null +++ b/.gemini/commands/afx-next.toml @@ -0,0 +1,24 @@ +description = "Context-aware Golden Thread guidance - tells you exactly what to do next." +prompt = """ +You are executing the AFX /afx:next command. This is the "Golden Thread" - it analyzes your current context and tells you exactly what to do next. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-next.md} + +## Gemini-Specific Tool Guidance + +To perform the deep context scan required by AFX, leverage Gemini's specialized tools: + +1. **Context Analysis**: Use `codebase_investigator` for high-level architectural overview or to understand complex dependencies related to the current task. +2. **State Detection**: + - Use `run_shell_command` with `git status --short` and `git branch --show-current`. + - Use `grep_search` to find active tasks in `tasks.md` and recent entries in `journal.md`. +3. **Refined Suggestions**: Ensure recommended next steps align with the AFX priority logic (Plan Mode -> Git State -> Active Task -> Recent Completion -> Idle). + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-report.md b/.gemini/commands/afx-report.md deleted file mode 100644 index 1702fd0..0000000 --- a/.gemini/commands/afx-report.md +++ /dev/null @@ -1,32 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, report, metrics] ---- - -# /afx:report - -Project metrics and reporting for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-report.md` - -## Gemini-Specific Guidance - -When generating reports: - -1. **Traceability Analysis**: Use `grep_search` to map `@see` links from code back to specifications. -2. **Health Metrics**: Use `grep_search` to calculate spec quality and task completion rates. -3. **Coverage**: Use `grep_search` and `codebase_investigator` to identify which specs have implementation versus those that are documentation-only. - -## Usage - -```bash -/afx:report traceability -/afx:report health -/afx:report coverage -``` diff --git a/.gemini/commands/afx-report.toml b/.gemini/commands/afx-report.toml new file mode 100644 index 0000000..8c96924 --- /dev/null +++ b/.gemini/commands/afx-report.toml @@ -0,0 +1,22 @@ +description = "Traceability metrics and project health reporting for AgenticFlowX." +prompt = """ +You are executing the AFX /afx:report command. This generates traceability metrics and project health reports. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-report.md} + +## Gemini-Specific Tool Guidance + +When generating reports: + +1. **Traceability Analysis**: Use `grep_search` to map `@see` links from code back to specifications. +2. **Health Metrics**: Use `grep_search` to calculate spec quality and task completion rates. +3. **Coverage**: Use `grep_search` and `codebase_investigator` to identify which specs have implementation versus those that are documentation-only. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-session.md b/.gemini/commands/afx-session.md deleted file mode 100644 index 36a18fb..0000000 --- a/.gemini/commands/afx-session.md +++ /dev/null @@ -1,36 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, session, context] ---- - -# /afx:session - -Session management and context preservation for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-session.md` - -## Gemini-Specific Guidance - -To preserve session context: - -1. **Capturing Discussions**: Use `write_file` to append structured metadata and discussion summaries to `journal.md`. -2. **Recall**: Use `read_file` to browse and restore previous session contexts. -3. **Note Taking**: Use `write_file` for smart notes, ensuring they are auto-tagged and linked to the correct feature journal. - -## Usage - -```bash -/afx:session save [feature] -/afx:session recall <session-id> -/afx:session list -/afx:session note "content" [tags] -/afx:session show [feature|all] -/afx:session recap [feature|all] -/afx:session promote <id> -``` diff --git a/.gemini/commands/afx-session.toml b/.gemini/commands/afx-session.toml new file mode 100644 index 0000000..4353e99 --- /dev/null +++ b/.gemini/commands/afx-session.toml @@ -0,0 +1,22 @@ +description = "Session discussion capture and recall for multi-agent workflows." +prompt = """ +You are executing the AFX /afx:session command. This captures and recalls session discussions for multi-agent workflows. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-session.md} + +## Gemini-Specific Tool Guidance + +To preserve session context: + +1. **Capturing Discussions**: Use `write_file` to append structured metadata and discussion summaries to `journal.md`. +2. **Recall**: Use `read_file` to browse and restore previous session contexts. +3. **Note Taking**: Use `write_file` for smart notes, ensuring they are auto-tagged and linked to the correct feature journal. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-spec.md b/.gemini/commands/afx-spec.md deleted file mode 100644 index 3831f63..0000000 --- a/.gemini/commands/afx-spec.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, spec, management] ---- - -# /afx:spec - -Specification management and review for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-spec.md` - -## Gemini-Specific Guidance - -When managing specifications: - -1. **Spec Analysis**: Use `read_file` and `grep_search` to list, show, and validate specifications. -2. **Interactive Discussion**: Use your reasoning capabilities to discuss specs and identify gaps. -3. **Comprehensive Review**: When performing an automated review, use `codebase_investigator` if necessary to check for consistency with existing architectural patterns. -4. **Requirement Extraction**: Use `grep_search` to precisely extract FR/NFR from `spec.md`. - -## Usage - -```bash -/afx:spec list -/afx:spec show <name> -/afx:spec validate <name> -/afx:spec review <name> -/afx:spec approve <name> -/afx:spec requirements <name> -/afx:spec coverage <name> -``` diff --git a/.gemini/commands/afx-spec.toml b/.gemini/commands/afx-spec.toml new file mode 100644 index 0000000..5e6664f --- /dev/null +++ b/.gemini/commands/afx-spec.toml @@ -0,0 +1,23 @@ +description = "Specification management, navigation, review, and approval." +prompt = """ +You are executing the AFX /afx:spec command. This manages specifications: listing, showing, validating, reviewing, and approving. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-spec.md} + +## Gemini-Specific Tool Guidance + +When managing specifications: + +1. **Spec Analysis**: Use `read_file` and `grep_search` to list, show, and validate specifications. +2. **Interactive Discussion**: Use your reasoning capabilities to discuss specs and identify gaps. +3. **Comprehensive Review**: When performing an automated review, use `codebase_investigator` if necessary to check for consistency with existing architectural patterns. +4. **Requirement Extraction**: Use `grep_search` to precisely extract FR/NFR from `spec.md`. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-task.md b/.gemini/commands/afx-task.md deleted file mode 100644 index 5359387..0000000 --- a/.gemini/commands/afx-task.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, task, management] ---- - -# /afx:task - -Task management and verification for AgenticFlowX. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-task.md` - -## Gemini-Specific Guidance - -When managing tasks: - -1. **Verification**: Use `read_file` to compare implementation against task requirements in `tasks.md`. -2. **Audit**: Use `grep_search` to review all tasks for completion criteria. -3. **Closing Tasks**: Use `read_file` to ensure both `[Agent OK]` and `[Human OK]` columns are marked before closing. - -## Usage - -```bash -/afx:task verify <task-id> -/afx:task audit -/afx:task close <task-id> -/afx:task list [phase] -``` diff --git a/.gemini/commands/afx-task.toml b/.gemini/commands/afx-task.toml new file mode 100644 index 0000000..4594a7d --- /dev/null +++ b/.gemini/commands/afx-task.toml @@ -0,0 +1,22 @@ +description = "Verify and summarize task implementation status." +prompt = """ +You are executing the AFX /afx:task command. This verifies, audits, and manages task implementation status. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-task.md} + +## Gemini-Specific Tool Guidance + +When managing tasks: + +1. **Verification**: Use `read_file` to compare implementation against task requirements in `tasks.md`. +2. **Audit**: Use `grep_search` to review all tasks for completion criteria. +3. **Closing Tasks**: Use `read_file` to ensure both `[Agent OK]` and `[Human OK]` columns are marked before closing. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-update.md b/.gemini/commands/afx-update.md deleted file mode 100644 index 4b97dca..0000000 --- a/.gemini/commands/afx-update.md +++ /dev/null @@ -1,30 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, maintenance] ---- - -# /afx:update - -Keep AFX assets current. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-update.md` - -## Gemini-Specific Guidance - -When updating the framework: - -1. **Version Checking**: Use `run_shell_command` with `curl` or `git` to compare the local AFX version to the latest upstream release. -2. **Applying Updates**: Use `run_shell_command` to execute the installer update flow (`install.sh --update`). Always honor safety flags like `--dry-run`. - -## Usage - -```bash -/afx:update check -/afx:update apply -``` diff --git a/.gemini/commands/afx-update.toml b/.gemini/commands/afx-update.toml new file mode 100644 index 0000000..ae74875 --- /dev/null +++ b/.gemini/commands/afx-update.toml @@ -0,0 +1,21 @@ +description = "Check for upstream AFX updates and apply them safely." +prompt = """ +You are executing the AFX /afx:update command. This checks for upstream AFX updates and applies them safely. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-update.md} + +## Gemini-Specific Tool Guidance + +When updating the framework: + +1. **Version Checking**: Use `run_shell_command` with `curl` or `git` to compare the local AFX version to the latest upstream release. +2. **Applying Updates**: Use `run_shell_command` to execute the installer update flow (`install.sh --update`). Always honor safety flags like `--dry-run`. + +## Arguments + +{{args}} +""" diff --git a/.gemini/commands/afx-work.md b/.gemini/commands/afx-work.md deleted file mode 100644 index 788288a..0000000 --- a/.gemini/commands/afx-work.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -afx: true -type: COMMAND -status: Living -tags: [afx, command, workflow, state] ---- - -# /afx:work - -Workflow state management for AgenticFlowX sessions. - -## Source of Truth - -**CRITICAL**: Follow the canonical command logic and output format defined in: - -- `.claude/commands/afx-work.md` - -## Gemini-Specific Guidance - -When managing workflow state, use your specialized tools to ensure accuracy: - -1. **State Verification**: Use `run_shell_command` for all `git` and `gh` (GitHub CLI) operations. -2. **Contextual Analysis**: If `/afx:work status` or `/afx:work plan` requires deep understanding of the codebase, invoke `codebase_investigator` to map out the relevant modules and dependencies. -3. **Task Syncing**: Use `grep_search` and `read_file` to precisely identify and update session log entries in `journal.md` and task states in `tasks.md`. - -## Usage - -```bash -/afx:work status -/afx:work next <spec-path> -/afx:work resume [spec|num] -/afx:work sync [spec] [issue] -/afx:work plan [instruction] -/afx:work approve [feature] <task> "<note>" -/afx:work reopen [feature] <task> "<reason>" -/afx:work close [feature] <issue> "<summary>" -``` diff --git a/.gemini/commands/afx-work.toml b/.gemini/commands/afx-work.toml new file mode 100644 index 0000000..ee2d120 --- /dev/null +++ b/.gemini/commands/afx-work.toml @@ -0,0 +1,22 @@ +description = "Workflow state management - status, next task, resume, sync, plan." +prompt = """ +You are executing the AFX /afx:work command. This manages workflow state: checking status, picking next tasks, resuming work, syncing with GitHub, and planning. + +## Canonical Command Logic + +Follow the full command logic defined in the canonical source: + +@{.claude/commands/afx-work.md} + +## Gemini-Specific Tool Guidance + +When managing workflow state, use your specialized tools to ensure accuracy: + +1. **State Verification**: Use `run_shell_command` for all `git` and `gh` (GitHub CLI) operations. +2. **Contextual Analysis**: If `/afx:work status` or `/afx:work plan` requires deep understanding of the codebase, invoke `codebase_investigator` to map out the relevant modules and dependencies. +3. **Task Syncing**: Use `grep_search` and `read_file` to precisely identify and update session log entries in `journal.md` and task states in `tasks.md`. + +## Arguments + +{{args}} +""" diff --git a/.github/prompts/afx-check.prompt.md b/.github/prompts/afx-check.prompt.md index 5fd7b07..d15fa10 100644 --- a/.github/prompts/afx-check.prompt.md +++ b/.github/prompts/afx-check.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX check commands for path, lint, links, and gate validation. +description: Quality verification and compliance checking for AgenticFlowX. --- # AFX check diff --git a/.github/prompts/afx-context.prompt.md b/.github/prompts/afx-context.prompt.md index 78d802e..212d975 100644 --- a/.github/prompts/afx-context.prompt.md +++ b/.github/prompts/afx-context.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX context commands for saving and loading project context. +description: Session Context protocol for seamless context transfer between AI sessions. --- # AFX context diff --git a/.github/prompts/afx-dev.prompt.md b/.github/prompts/afx-dev.prompt.md index 2123912..4c27100 100644 --- a/.github/prompts/afx-dev.prompt.md +++ b/.github/prompts/afx-dev.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX dev commands for implementation, debugging, refactoring, and review. +description: Development actions with AFX traceability. --- # AFX dev diff --git a/.github/prompts/afx-discover.prompt.md b/.github/prompts/afx-discover.prompt.md index bc30b55..e9f9b7e 100644 --- a/.github/prompts/afx-discover.prompt.md +++ b/.github/prompts/afx-discover.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX discover commands for capabilities, infrastructure, scripts, and tools. +description: Discover what exists in your project - infrastructure scripts, automation tools, deployment workflows, and development capabilities. --- # AFX discover diff --git a/.github/prompts/afx-help.prompt.md b/.github/prompts/afx-help.prompt.md index ded63d1..6a5b724 100644 --- a/.github/prompts/afx-help.prompt.md +++ b/.github/prompts/afx-help.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX command reference and workflow guidance. +description: AFX (AgenticFlowX) command reference. --- # AFX help diff --git a/.github/prompts/afx-init.prompt.md b/.github/prompts/afx-init.prompt.md index d010ef3..6571807 100644 --- a/.github/prompts/afx-init.prompt.md +++ b/.github/prompts/afx-init.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX init commands for setting up features and ADR artifacts. +description: Feature spec scaffolding for AgenticFlowX projects. --- # AFX init diff --git a/.github/prompts/afx-next.prompt.md b/.github/prompts/afx-next.prompt.md index df8f362..5da15ba 100644 --- a/.github/prompts/afx-next.prompt.md +++ b/.github/prompts/afx-next.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX next-step guidance and priority recommendations. +description: The "Golden Thread" command. Intelligently analyzes your current context (git state, active tasks, session history) and tells you exactly what to do next. --- # AFX next diff --git a/.github/prompts/afx-report.prompt.md b/.github/prompts/afx-report.prompt.md index 47f5463..ad83ecc 100644 --- a/.github/prompts/afx-report.prompt.md +++ b/.github/prompts/afx-report.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX report commands for health, coverage, and orphan analysis. +description: Traceability metrics and project health reporting for AgenticFlowX. --- # AFX report diff --git a/.github/prompts/afx-session.prompt.md b/.github/prompts/afx-session.prompt.md index 0393931..d812009 100644 --- a/.github/prompts/afx-session.prompt.md +++ b/.github/prompts/afx-session.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX session commands for notes, recaps, and journal continuity. +description: Session discussion capture and recall for multi-agent workflows. --- # AFX session diff --git a/.github/prompts/afx-spec.prompt.md b/.github/prompts/afx-spec.prompt.md index abccdef..ab7ae3e 100644 --- a/.github/prompts/afx-spec.prompt.md +++ b/.github/prompts/afx-spec.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX spec commands for drafting and refining specs, design, and tasks. +description: Specification management, navigation, review, and approval for spec-centric workflows. --- # AFX spec diff --git a/.github/prompts/afx-task.prompt.md b/.github/prompts/afx-task.prompt.md index 6e0bc0a..cca02dd 100644 --- a/.github/prompts/afx-task.prompt.md +++ b/.github/prompts/afx-task.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX task commands for audit, summary, listing, and progress checks. +description: Verify and summarize task implementation status. --- # AFX task diff --git a/.github/prompts/afx-update.prompt.md b/.github/prompts/afx-update.prompt.md index 8fe5a5d..2301c69 100644 --- a/.github/prompts/afx-update.prompt.md +++ b/.github/prompts/afx-update.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX update commands for version checks and framework upgrades. +description: Check for upstream AFX updates and apply them safely. --- # AFX update diff --git a/.github/prompts/afx-work.prompt.md b/.github/prompts/afx-work.prompt.md index 9e50b54..51e72da 100644 --- a/.github/prompts/afx-work.prompt.md +++ b/.github/prompts/afx-work.prompt.md @@ -1,6 +1,6 @@ --- mode: agent -description: Help with AFX work commands for status, next tasks, resume, sync, and planning. +description: Workflow state management for AgenticFlowX sessions. --- # AFX work diff --git a/.gitignore b/.gitignore index 4058d35..be500b5 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ tmp .codex/* !.codex/skills/ !.codex/skills/** +.afx/ diff --git a/CLAUDE.md b/CLAUDE.md index a7f3377..78058a3 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -20,10 +20,13 @@ This is a **documentation and tooling repository** - it contains: ``` afx/ +├── .agent/skills/ # AFX Antigravity skills (afx-*) ├── .claude/commands/ # AFX slash commands (/afx:*) ├── .codex/skills/ # AFX Codex skills (afx-*) ├── .gemini/commands/ # AFX Gemini CLI commands (/afx:*) ├── .github/prompts/ # AFX GitHub Copilot prompts +├── packs/ # Pack manifests + index (afx-pack-*.yaml) +├── skills/ # AFX-built skills (guardrails baked in) ├── scripts/ # Utility scripts (sync, install support) ├── docs/ │ ├── adr/ # Global Architecture Decision Records @@ -44,9 +47,10 @@ This is a documentation-only repo with no build or test commands. Changes are ve 1. Testing commands in a real project (copy to `.claude/commands/`) 2. Testing Codex skills in a real project (copy to `.codex/skills/`) -3. Testing Copilot prompts in a real project (copy to `.github/prompts/`) -4. Reviewing markdown rendering -5. Verifying YAML frontmatter syntax +3. Testing Antigravity skills in a real project (copy to `.agent/skills/`) +4. Testing Copilot prompts in a real project (copy to `.github/prompts/`) +5. Reviewing markdown rendering +6. Verifying YAML frontmatter syntax ## Key Concepts diff --git a/docs/agenticflowx/agenticflowx.md b/docs/agenticflowx/agenticflowx.md index 34d186c..37485e4 100644 --- a/docs/agenticflowx/agenticflowx.md +++ b/docs/agenticflowx/agenticflowx.md @@ -7,6 +7,10 @@ version: 1.0 tags: [framework, specification, afx] --- +<p align="center"> + <img src="https://raw.githubusercontent.com/rixrix/afx/main/assets/agenticflow_logo_light.svg" alt="AgenticFlowX Logo" width="600"/> +</p> + # AgenticFlowX (AFX) > **Agentic workflow—from spec to ship.** diff --git a/docs/specs/afx-packs/design.md b/docs/specs/afx-packs/design.md new file mode 100644 index 0000000..f401e69 --- /dev/null +++ b/docs/specs/afx-packs/design.md @@ -0,0 +1,1977 @@ +--- +afx: true +type: DESIGN +status: Approved +owner: "@rix" +version: 1.0 +created: "2026-02-28" +last_verified: "2026-02-28" +tags: [packs, install, skills, ecosystem] +--- + +# AFX Pack System - Technical Design + +**Version:** 1.0 +**Date:** 2026-02-28 +**Status:** Approved +**Author:** Richard Sentino +**Spec:** [spec.md](./spec.md) + +--- + +## Overview + +This design defines the architecture for the AFX Pack System, enabling the installation, management, and composition of skill packs across Claude Code, Codex CLI, Google Antigravity, and GitHub Copilot. It centers on `install.sh` as the single driver for state mutations, using `.afx/` as the master storage for pristine external skills and `.afx.yaml` for project state. + +### Core Principles + +1. **Single Driver**: `install.sh` handles all file operations. Extensions (VSCode) delegate to it. +2. **Pristine Storage**: External skills are downloaded once to `.afx/packs/{pack}/{provider}/` and never modified. +3. **Derived State**: Provider directories (`.claude/`, `.agents/`, `.agent/`, `.github/`) are ephemeral copies of the master state. +4. **No Registry Auth**: All metadata and public packs are fetched from public URLs (`raw.githubusercontent.com`, `codeload.github.com`). +5. **Minimal Dependencies**: Only `curl`, `tar`, and `bash` — no git, node, python, or package managers. + +--- + +## 1. Data Models + +### 1.1 Pack Manifests + +Pack manifests live in the AFX repo under `packs/`. Each manifest is a self-contained YAML file defining the pack's contents, sources, and platform support. + +#### `packs/afx-pack-qa.yaml` + +```yaml +name: afx-pack-qa +description: QA Engineer role pack — testing, review, and quality assurance +category: role + +platforms: + claude: true + codex: true + antigravity: true + copilot: partial # Only AFX-built skills — no external SKILL.md conversion + +includes: + # External skills from Antigravity (pristine — SKILL.md format → Claude + Codex + Antigravity) + - repo: anthropics/antigravity-awesome-skills + path: skills/ + items: + - test-driven-development + - tdd-workflow + - playwright-skill + - e2e-testing-patterns + - unit-testing-test-generate + - systematic-debugging + - performance-testing-review + + # External skills from OpenAI (pristine — SKILL.md + openai.yaml → Codex only) + - repo: openai/skills + path: skills/.curated/ + items: + - playwright + + # Claude Code plugins (pristine — .claude-plugin/ format → Claude only) + - repo: anthropics/claude-code + path: plugins/ + items: + - code-review + - pr-review-toolkit + + # AFX-built skills (guardrails baked in, hosted in AFX repo) + - repo: rixrix/afx + path: skills/ + items: + - afx-qa-methodology # QA workflow with @see tracing + two-stage verify + - afx-spec-test-planning # Test plans linked to spec tasks +``` + +**What each provider gets from `afx-pack-qa`:** + +| Provider | External skills (pristine) | AFX-built | +| --------------- | ----------------------------------------------------------------- | ----------------------------------------------------------------- | +| **Claude** | 7 Antigravity SKILL.md skills + 2 Claude plugins | `afx-qa-methodology` + `afx-spec-test-planning` | +| **Codex** | 7 Antigravity SKILL.md skills + 1 OpenAI skill (with openai.yaml) | `afx-qa-methodology` + `afx-spec-test-planning` | +| **Antigravity** | 7 Antigravity SKILL.md skills | `afx-qa-methodology` + `afx-spec-test-planning` | +| **Copilot** | None (no conversion of external skills) | `afx-qa-methodology.agent.md` + `afx-spec-test-planning.agent.md` | + +#### `packs/afx-pack-security.yaml` + +```yaml +name: afx-pack-security +description: Security review and audit pack — OWASP, dependency scanning, code hardening +category: role + +platforms: + claude: true + codex: true + antigravity: true + copilot: partial + +includes: + # External skills from Antigravity (pristine) + - repo: anthropics/antigravity-awesome-skills + path: skills/ + items: + - security-review + - dependency-vulnerability-check + - code-hardening + + # Claude Code plugins (pristine) + - repo: anthropics/claude-code + path: plugins/ + items: + - security-scanner + + # AFX-built skills (guardrails baked in) + - repo: rixrix/afx + path: skills/ + items: + - afx-owasp-top-10 # OWASP top 10 checklist with @see tracing + - afx-security-audit # Security audit workflow +``` + +### 1.2 Pack Index (`packs/index.json`) + +Located in the AFX repo. Single aggregated metadata file for all packs and upstream providers. Fetched by vscode-afx via `raw.githubusercontent.com` — no auth required. + +```jsonc +{ + "packs": { + "afx-pack-qa": { + "description": "QA Engineer role pack — testing, review, and quality assurance", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"], + }, + "afx-pack-security": { + "description": "Security review and audit pack — OWASP, dependency scanning, code hardening", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"], + }, + }, + "upstream": { + "anthropics/claude-plugins-official": { + "featured": ["playwright-e2e", "security-scanner", "code-architect"], + }, + "openai/skills": {}, + "anthropics/antigravity-awesome-skills": { + "featured": ["code-architect"], + }, + }, +} +``` + +### 1.3 Project State (`.afx.yaml`) + +Located in the user's project root. Committed to git (team-shared). + +```yaml +# .afx.yaml (committed — team-shared) +packs: + - name: afx-pack-qa + status: enabled + installed_ref: v1.5.3 # git ref used at install time (from --version, --branch, or default main) + disabled_items: [] + + - name: afx-pack-security + status: disabled + installed_ref: main + disabled_items: [] + +custom_skills: + - repo: anthropics/antigravity-awesome-skills + path: skills/some-niche-skill +``` + +--- + +## 2. Directory Structure + +### 2.1 AFX Repo — New Directories + +Two new top-level directories in `rixrix/afx`: + +``` +afx/ # AFX repo (rixrix/afx) +├── packs/ # NEW — pack manifests + index +│ ├── index.json # Aggregated metadata for discovery +│ ├── afx-pack-qa.yaml # QA pack manifest +│ └── afx-pack-security.yaml # Security pack manifest +│ +├── skills/ # NEW — AFX-built skills (guardrails baked in) +│ ├── afx-qa-methodology/ # Mirrors .afx/packs/{pack}/ structure +│ │ ├── claude/ +│ │ │ └── skills/ +│ │ │ └── afx-qa-methodology/ +│ │ │ └── SKILL.md +│ │ ├── codex/ +│ │ │ └── skills/ +│ │ │ └── afx-qa-methodology/ +│ │ │ └── SKILL.md +│ │ ├── antigravity/ +│ │ │ └── skills/ +│ │ │ └── afx-qa-methodology/ +│ │ │ └── SKILL.md +│ │ └── copilot/ +│ │ └── agents/ +│ │ └── afx-qa-methodology.agent.md +│ ├── afx-spec-test-planning/ +│ │ ├── claude/ +│ │ │ └── skills/ +│ │ │ └── afx-spec-test-planning/ +│ │ │ └── SKILL.md +│ │ ├── codex/ +│ │ │ └── skills/ +│ │ │ └── afx-spec-test-planning/ +│ │ │ └── SKILL.md +│ │ ├── antigravity/ +│ │ │ └── skills/ +│ │ │ └── afx-spec-test-planning/ +│ │ │ └── SKILL.md +│ │ └── copilot/ +│ │ └── agents/ +│ │ └── afx-spec-test-planning.agent.md +│ ├── afx-owasp-top-10/ +│ │ ├── claude/ +│ │ │ └── skills/ +│ │ │ └── afx-owasp-top-10/ +│ │ │ └── SKILL.md +│ │ ├── codex/ +│ │ │ └── skills/ +│ │ │ └── afx-owasp-top-10/ +│ │ │ └── SKILL.md +│ │ ├── antigravity/ +│ │ │ └── skills/ +│ │ │ └── afx-owasp-top-10/ +│ │ │ └── SKILL.md +│ │ └── copilot/ +│ │ └── agents/ +│ │ └── afx-owasp-top-10.agent.md +│ └── afx-security-audit/ +│ ├── claude/ +│ │ └── skills/ +│ │ └── afx-security-audit/ +│ │ └── SKILL.md +│ ├── codex/ +│ │ └── skills/ +│ │ └── afx-security-audit/ +│ │ └── SKILL.md +│ ├── antigravity/ +│ │ └── skills/ +│ │ └── afx-security-audit/ +│ │ └── SKILL.md +│ └── copilot/ +│ └── agents/ +│ └── afx-security-audit.agent.md +│ +├── install.sh # MODIFIED — pack management added +├── .claude/commands/ # Existing +├── .codex/skills/ # Existing +├── .gemini/commands/ # Existing +├── .github/prompts/ # Existing +├── templates/ # Existing +└── docs/ # Existing +``` + +### 2.2 User Project — Master Storage (`.afx/`) + +The `.afx/` directory acts as the local package cache in the user's project. It is gitignored. + +``` +.afx/ # Gitignored +├── .cache/ +│ └── lastIndex.json # Cached index + timestamp +│ +└── packs/ + ├── afx-pack-qa/ # Installed pack (enabled) + │ ├── claude/ + │ │ ├── skills/ + │ │ │ ├── test-driven-development/ # PRISTINE from Antigravity + │ │ │ │ ├── SKILL.md + │ │ │ │ └── resources/ + │ │ │ ├── tdd-workflow/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── playwright-skill/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── e2e-testing-patterns/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── unit-testing-test-generate/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── systematic-debugging/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── performance-testing-review/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── afx-qa-methodology/ # AFX-BUILT + │ │ │ │ └── SKILL.md + │ │ │ └── afx-spec-test-planning/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── plugins/ + │ │ ├── code-review/ # PRISTINE from Claude Code + │ │ │ ├── .claude-plugin/plugin.json + │ │ │ ├── commands/review.md + │ │ │ └── hooks/hooks.json + │ │ └── pr-review-toolkit/ # PRISTINE from Claude Code + │ │ ├── .claude-plugin/plugin.json + │ │ └── commands/ + │ ├── codex/ + │ │ └── skills/ + │ │ ├── test-driven-development/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── tdd-workflow/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── playwright-skill/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── e2e-testing-patterns/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── unit-testing-test-generate/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── systematic-debugging/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── performance-testing-review/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── playwright/ # PRISTINE from OpenAI + │ │ │ ├── SKILL.md + │ │ │ ├── agents/openai.yaml + │ │ │ └── scripts/run-tests.py + │ │ ├── afx-qa-methodology/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── afx-spec-test-planning/ # AFX-BUILT + │ │ └── SKILL.md + │ ├── antigravity/ + │ │ └── skills/ + │ │ ├── test-driven-development/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── tdd-workflow/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── playwright-skill/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── e2e-testing-patterns/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── unit-testing-test-generate/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── systematic-debugging/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── performance-testing-review/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── afx-qa-methodology/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── afx-spec-test-planning/ # AFX-BUILT + │ │ └── SKILL.md + │ └── copilot/ + │ └── agents/ + │ ├── afx-qa-methodology.agent.md # AFX-BUILT (only AFX skills) + │ └── afx-spec-test-planning.agent.md # AFX-BUILT + │ + └── afx-pack-security/ # Installed pack (disabled — master preserved) + ├── claude/ + │ ├── skills/ + │ │ ├── security-review/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── dependency-vulnerability-check/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── code-hardening/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── afx-owasp-top-10/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── afx-security-audit/ # AFX-BUILT + │ │ └── SKILL.md + │ └── plugins/ + │ └── security-scanner/ # PRISTINE from Claude Code + │ ├── .claude-plugin/plugin.json + │ └── commands/ + ├── codex/ + │ └── skills/ + │ ├── security-review/ # PRISTINE from Antigravity + │ │ └── SKILL.md + │ ├── dependency-vulnerability-check/ # PRISTINE from Antigravity + │ │ └── SKILL.md + │ ├── code-hardening/ # PRISTINE from Antigravity + │ │ └── SKILL.md + │ ├── afx-owasp-top-10/ # AFX-BUILT + │ │ └── SKILL.md + │ └── afx-security-audit/ # AFX-BUILT + │ └── SKILL.md + ├── antigravity/ + │ └── skills/ + │ ├── security-review/ # PRISTINE from Antigravity + │ │ └── SKILL.md + │ ├── dependency-vulnerability-check/ # PRISTINE from Antigravity + │ │ └── SKILL.md + │ ├── code-hardening/ # PRISTINE from Antigravity + │ │ └── SKILL.md + │ ├── afx-owasp-top-10/ # AFX-BUILT + │ │ └── SKILL.md + │ └── afx-security-audit/ # AFX-BUILT + │ └── SKILL.md + └── copilot/ + └── agents/ + ├── afx-owasp-top-10.agent.md # AFX-BUILT + └── afx-security-audit.agent.md # AFX-BUILT +``` + +### 2.3 User Project — Provider Directories (Derived) + +`install.sh` copies files from `.afx/packs/` to these locations based on `.afx.yaml` state. These are ephemeral — they can be rebuilt from `.afx/packs/` master at any time. + +``` +project/ +├── .claude/ +│ ├── commands/ # Existing AFX slash commands (unchanged) +│ │ └── afx-*.md +│ ├── skills/ # ← pack skills copied here +│ │ ├── test-driven-development/ +│ │ ├── tdd-workflow/ +│ │ ├── playwright-skill/ +│ │ ├── e2e-testing-patterns/ +│ │ ├── unit-testing-test-generate/ +│ │ ├── systematic-debugging/ +│ │ ├── performance-testing-review/ +│ │ ├── afx-qa-methodology/ +│ │ └── afx-spec-test-planning/ +│ └── plugins/ # ← pack plugins copied here +│ ├── code-review/ +│ └── pr-review-toolkit/ +│ +├── .agents/ +│ └── skills/ # ← pack skills copied here (Codex CLI) +│ ├── test-driven-development/ +│ ├── tdd-workflow/ +│ ├── playwright-skill/ +│ ├── e2e-testing-patterns/ +│ ├── unit-testing-test-generate/ +│ ├── systematic-debugging/ +│ ├── performance-testing-review/ +│ ├── playwright/ # OpenAI skill (Codex only) +│ ├── afx-qa-methodology/ +│ └── afx-spec-test-planning/ +│ +├── .agent/ +│ └── skills/ # ← pack skills copied here (Google Antigravity) +│ ├── test-driven-development/ +│ ├── tdd-workflow/ +│ ├── playwright-skill/ +│ ├── e2e-testing-patterns/ +│ ├── unit-testing-test-generate/ +│ ├── systematic-debugging/ +│ ├── performance-testing-review/ +│ ├── afx-qa-methodology/ +│ └── afx-spec-test-planning/ +│ +├── .github/ +│ ├── prompts/ # Existing AFX Copilot prompts (unchanged) +│ │ └── afx-*.prompt.md +│ └── agents/ # ← AFX-built agents copied here +│ ├── afx-qa-methodology.agent.md +│ └── afx-spec-test-planning.agent.md +│ +├── .afx/ # Master storage (gitignored) +├── .afx.yaml # Pack state (committed) +└── ... +``` + +### 2.4 Provider Directory Conventions + +Each provider has a fixed directory layout that it scans for auto-discovery. + +**Claude Code** — scans `.claude/` for components: + +``` +.claude/ +├── commands/*.md # Slash commands — auto-discovered by filename +├── agents/*.md # Subagents — auto-discovered by filename +├── skills/{name}/ # Auto-activating skills — one dir per skill +│ ├── SKILL.md # REQUIRED +│ ├── references/ # Loaded on demand +│ └── examples/ # Working code samples +├── plugins/{name}/ # Full plugins — auto-discovered as a unit +│ ├── .claude-plugin/ +│ │ └── plugin.json # REQUIRED (name, version, description) +│ ├── commands/*.md +│ ├── agents/*.md +│ ├── skills/{name}/SKILL.md +│ ├── hooks/hooks.json +│ └── [core/, utils/, ...] +└── hooks.json # Project-level hooks +``` + +**Codex CLI** — scans `.agents/skills/` for skills: + +``` +.agents/ +└── skills/{name}/ # One dir per skill + ├── SKILL.md # REQUIRED + ├── agents/ + │ └── openai.yaml # Agent interface (display_name, icon, default_prompt) + ├── scripts/*.py # Executable scripts + ├── references/ # Supporting docs + └── assets/ # Icons, templates +``` + +**Google Antigravity** — scans `.agent/skills/` for skills: + +``` +.agent/ +└── skills/{name}/ # One dir per skill + ├── SKILL.md # REQUIRED (agentskills.io standard) + ├── references/ # Supporting docs + └── examples/ # Working code samples +``` + +**Copilot** — scans `.github/agents/` for agent files: + +``` +.github/ +└── agents/ + └── {name}.agent.md # One file per agent (frontmatter + body) +``` + +--- + +## 3. install.sh Architecture + +### 3.1 Prerequisites + +`install.sh` requires only three standard POSIX-available tools: + +| Tool | Purpose | Availability | +| :----- | :---------------------------------------------- | :-------------------------------- | +| `bash` | Script runtime (≥ 4.0) | macOS, Linux, WSL — pre-installed | +| `curl` | Download tarballs and raw files from GitHub | macOS, Linux, WSL — pre-installed | +| `tar` | Extract specific paths from downloaded tarballs | macOS, Linux, WSL — pre-installed | + +**Not required**: `git`, `node`, `python`, `npm`, `pip`, or any package manager. This is intentional — the script must work on a fresh machine with no dev tooling beyond a shell. + +**Platform support**: macOS, Linux, and WSL (Windows Subsystem for Linux). Native Windows (cmd/PowerShell) is not supported. + +### 3.2 Current install.sh Structure + +The existing `install.sh` (767 lines) handles core AFX installation only. No pack support exists today. + +**Current flow (11 steps):** + +``` +1. Parse arguments (--update, --commands-only, --force, --dry-run, etc.) +2. Validate target directory +3. Determine AFX source (local clone or git clone to temp) +4. [1/11] Install Claude slash commands (.claude/commands/) +5. [2/11] Install Codex skills (.codex/skills/) +6. [3/11] Install Gemini CLI commands (.gemini/commands/) +7. [4/11] Install GitHub Copilot prompts (.github/prompts/) +8. [5/11] Install templates +9. [6/11] Create/update .afx.yaml +10. [7/11]-[10/11] Update CLAUDE.md, AGENTS.md, GEMINI.md, copilot-instructions.md +11. [11/11] Install AFX documentation +→ Print summary +``` + +**Current argument parser** (line 62-136): + +```bash +while [[ $# -gt 0 ]]; do + case $1 in + --update) UPDATE_MODE=true; shift ;; + --commands-only) COMMANDS_ONLY=true; shift ;; + --no-claude-md) NO_CLAUDE_MD=true; shift ;; + --no-agents-md) NO_AGENTS_MD=true; shift ;; + --no-gemini-md) NO_GEMINI_MD=true; shift ;; + --no-copilot-md) NO_COPILOT_MD=true; shift ;; + --no-docs) NO_DOCS=true; shift ;; + --force) FORCE=true; shift ;; + --dry-run) DRY_RUN=true; shift ;; + -h|--help) # print help; exit 0 ;; + *) TARGET_DIR="$1"; shift ;; + esac +done +``` + +**Current AFX source resolution** (line 165-182): Uses `git clone --depth 1` when running remotely. This must change to `curl` + `tar` for pack operations (no git dependency). + +**Existing helpers** (used by both core and pack operations): + +- `install_file()` — copy single file with dry-run/update/force logic +- `install_directory()` — copy directory with rm+cp for updates + +### 3.3 New Arguments + +Pack management adds these flags to the argument parser: + +```bash +# New variables (defaults) +PACK_NAMES=() # Array — supports --pack qa --pack security +PACK_DISABLE="" # Single pack name +PACK_ENABLE="" # Single pack name +PACK_REMOVE="" # Single pack name +PACK_LIST=false +SKILL_DISABLE="" # Skill name (requires --pack) +SKILL_ENABLE="" # Skill name (requires --pack) +UPDATE_PACKS=false # --update --packs +ADD_SKILL="" # repo:path/skill format +BRANCH="" # Branch name (e.g., dev, feature/packs) +VERSION="" # Version tag (e.g., v1.5.3, 1.5.3) + +# New case entries +--pack) PACK_NAMES+=("$2"); shift 2 ;; +--pack-disable) PACK_DISABLE="$2"; shift 2 ;; +--pack-enable) PACK_ENABLE="$2"; shift 2 ;; +--pack-remove) PACK_REMOVE="$2"; shift 2 ;; +--pack-list) PACK_LIST=true; shift ;; +--skill-disable) SKILL_DISABLE="$2"; shift 2 ;; +--skill-enable) SKILL_ENABLE="$2"; shift 2 ;; +--packs) UPDATE_PACKS=true; shift ;; +--add-skill) ADD_SKILL="$2"; shift 2 ;; +--branch) BRANCH="$2"; shift 2 ;; +--version) VERSION="$2"; shift 2 ;; +``` + +**Validation rules:** + +- `--skill-disable` and `--skill-enable` require `--pack` to be set +- `--packs` only valid with `--update` +- `--branch` and `--version` are mutually exclusive +- `--branch` and `--version` only affect AFX repo fetches, not upstream +- `--dry-run` applies to all pack operations +- Pack flags are mutually exclusive with each other (can't `--pack-disable` and `--pack-enable` in one call) + +### 3.4 Download Strategy + +To avoid full git clones, we use `curl` + `tar` against GitHub's codeload service. + +#### Ref Resolution + +The `{ref}` used for AFX repo fetches is resolved from three sources in priority order: + +1. `--version {tag}` — a release version tag (e.g., `v1.5.3` or `1.5.3`). Auto-prefixes `v` if missing. +2. `--branch {name}` — a branch name (e.g., `dev`, `feature/packs`). +3. Default: `main`. + +`--branch` and `--version` are mutually exclusive — the script exits with an error if both are provided. + +```bash +# Resolve the git ref for AFX repo fetches +resolve_ref() { + if [[ -n "$VERSION" && -n "$BRANCH" ]]; then + echo "Error: --version and --branch are mutually exclusive" >&2 + exit 1 + fi + if [[ -n "$VERSION" ]]; then + # Auto-prefix v if missing (1.5.3 → v1.5.3) + [[ "$VERSION" == v* ]] && echo "$VERSION" || echo "v$VERSION" + elif [[ -n "$BRANCH" ]]; then + echo "$BRANCH" + else + echo "main" + fi +} +``` + +| Invocation | `{ref}` used | +| :------------------------------------------ | :----------- | +| `./install.sh --pack qa .` | `main` | +| `./install.sh --branch dev --pack qa .` | `dev` | +| `./install.sh --version 1.5.3 --pack qa .` | `v1.5.3` | +| `./install.sh --version v1.5.3 --pack qa .` | `v1.5.3` | + +#### URL Patterns + +| Purpose | URL | Notes | +| :--------------- | :---------------------------------------------------------------- | :------------------------------------------ | +| AFX repo tarball | `https://codeload.github.com/rixrix/afx/tar.gz/{ref}` | `{ref}` resolved by `resolve_ref()` | +| Upstream tarball | `https://codeload.github.com/{owner}/{repo}/tar.gz/main` | Always `main` — branch/version flag ignored | +| Raw file fetch | `https://raw.githubusercontent.com/rixrix/afx/{ref}/path/to/file` | For single files (index.json, manifests) | + +This applies to: + +1. **AFX repo fetches** — pack manifests, index, AFX-built skills all come from `https://codeload.github.com/rixrix/afx/tar.gz/{ref}` +2. **Upstream repo fetches** — external skills are always fetched from `main` (`--branch` and `--version` only control the AFX repo ref, not upstream repos) + +**Download function:** + +```bash +# Download and extract specific paths from a GitHub repo tarball +# Usage: download_items <owner/repo> <ref> <base_path> <items...> +download_items() { + local repo="$1" + local ref="$2" + local base_path="$3" + shift 3 + local items=("$@") + + local url="https://codeload.github.com/${repo}/tar.gz/${ref}" + local temp_dir=$(mktemp -d) + + # Build tar extraction patterns + # Tarball root is {repo-name}-{ref}/, so strip-components=1 + local patterns=() + local repo_name="${repo##*/}" + for item in "${items[@]}"; do + patterns+=("${repo_name}-${ref}/${base_path}${item}") + done + + # Download and extract + curl -sL "$url" | tar xz -C "$temp_dir" --strip-components=1 "${patterns[@]}" 2>/dev/null + + echo "$temp_dir" +} +``` + +**Extraction logic:** + +1. Resolve `ref` — call `resolve_ref()` to get the effective git ref from `--version`, `--branch`, or default `main`. +2. Download tarball to temp dir via `curl -sL | tar xz`. +3. Extract specific paths defined in manifest `includes` using tar path patterns. +4. Move extracted items to `.afx/packs/{pack}/{temp_staging}`. +5. Clean up temp dir. + +**Manifest fetch** (single file — use raw URL): + +```bash +# Fetch a pack manifest YAML +fetch_manifest() { + local pack_name="$1" + local ref="$2" + curl -sL "https://raw.githubusercontent.com/rixrix/afx/${ref}/packs/${pack_name}.yaml" +} +``` + +### 3.5 YAML Parsing + +Pack manifests are YAML. Since we can't depend on external tools, we use `grep`/`sed`/`awk` for the simple flat structure. + +**Fields to extract from manifest:** + +```bash +# Parse pack manifest — extract key fields +parse_manifest() { + local manifest="$1" + + # Simple fields (line-level grep — safe for flat YAML) + PACK_NAME=$(grep '^name:' "$manifest" | awk '{print $2}') + PACK_DESC=$(grep '^description:' "$manifest" | sed 's/^description: //') + PACK_CATEGORY=$(grep '^category:' "$manifest" | awk '{print $2}') + + # platforms: — 2-space indent, key:value pairs + # claude: true + # codex: true + # copilot: partial +} + +# Parse includes[] — state machine for repo/path/items blocks +# Outputs one line per include: "repo path item1 item2 ..." +for_each_include() { + local manifest="$1" + local in_includes=false + local current_repo="" current_path="" + local items=() + + while IFS= read -r line; do + # Detect sections + if [[ "$line" == "includes:" ]]; then + in_includes=true; continue + fi + # Exit includes on next top-level key + if $in_includes && [[ "$line" =~ ^[a-z] ]]; then + # Flush last block + [[ -n "$current_repo" ]] && echo "$current_repo $current_path ${items[*]}" + break + fi + + if $in_includes; then + if [[ "$line" =~ ^\ \ -\ repo:\ (.+) ]]; then + # Flush previous block + [[ -n "$current_repo" ]] && echo "$current_repo $current_path ${items[*]}" + current_repo="${BASH_REMATCH[1]}" + current_path="" ; items=() + elif [[ "$line" =~ ^\ \ \ \ path:\ (.+) ]]; then + current_path="${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^\ \ \ \ \ \ -\ (.+) ]]; then + items+=("${BASH_REMATCH[1]}") + fi + fi + done < "$manifest" + + # Flush final block + [[ -n "$current_repo" ]] && echo "$current_repo $current_path ${items[*]}" +} +``` + +#### Manifest YAML Subset Rules + +To keep bash parsing tractable, manifests MUST follow these strict formatting rules: + +| Rule | Constraint | +| :----------------------------- | :------------------------------------------------------------ | +| Top-level keys | No indent, `key: value` or `key:` (for maps/arrays) | +| `platforms:` entries | 2-space indent, `key: value` | +| `includes:` entries | `- repo:` at 2-space indent | +| Include fields | `path:` and `items:` at 4-space indent | +| Item entries | `- name` at 6-space indent | +| No comments inline with values | `# comments` only on their own line or after value with space | +| No multi-line strings | All values on a single line | +| No anchors/aliases | No `&` or `*` YAML references | + +Manifests that violate these rules will fail to parse. This is intentional — the format is designed for bash, not for general YAML flexibility. + +### 3.6 Type Detection & Routing + +After download, `install.sh` inspects each item to determine where it belongs. + +| Signature | Type | Target Providers | Modification | +| :--------------------------------------- | :------------ | :----------------------------------------------------- | :----------- | +| `SKILL.md` at root, no `.claude-plugin/` | Simple Skill | `.claude/skills/`, `.agents/skills/`, `.agent/skills/` | None | +| `.claude-plugin/plugin.json` exists | Claude Plugin | `.claude/plugins/` | None | +| `SKILL.md` + `agents/openai.yaml` | OpenAI Skill | `.agents/skills/` only | None | +| From `rixrix/afx` repo | AFX Skill | All (transformed per provider from canonical SKILL.md) | N/A | + +**Detection function:** + +```bash +# Detect skill type and return target providers +# Usage: detect_type <item_dir> <source_repo> +detect_type() { + local item_dir="$1" + local source_repo="$2" + + if [[ "$source_repo" == "rixrix/afx" ]]; then + echo "afx" # All providers — canonical SKILL.md, transformed per provider + elif [[ -d "$item_dir/.claude-plugin" ]]; then + echo "plugin" # Claude only + elif [[ -f "$item_dir/agents/openai.yaml" ]]; then + echo "openai" # Codex only + elif [[ -f "$item_dir/SKILL.md" ]]; then + echo "skill" # Claude + Codex + else + echo "unknown" + fi +} +``` + +**Routing logic by type:** + +```bash +route_item() { + local item_dir="$1" + local item_name="$2" + local type="$3" + local pack_dir="$4" # .afx/packs/afx-pack-{name} + local platforms="$5" # Manifest platforms string (e.g., "claude:true codex:true copilot:partial") + + case "$type" in + skill) + # Simple Skill → Claude + Codex + Antigravity (gated by manifest platforms) + platform_enabled "$platforms" "claude" && \ + cp -r "$item_dir" "$pack_dir/claude/skills/$item_name" + platform_enabled "$platforms" "codex" && \ + cp -r "$item_dir" "$pack_dir/codex/skills/$item_name" + platform_enabled "$platforms" "antigravity" && \ + cp -r "$item_dir" "$pack_dir/antigravity/skills/$item_name" + ;; + plugin) + # Claude Plugin → Claude only + platform_enabled "$platforms" "claude" && \ + cp -r "$item_dir" "$pack_dir/claude/plugins/$item_name" + ;; + openai) + # OpenAI Skill → Codex only + platform_enabled "$platforms" "codex" && \ + cp -r "$item_dir" "$pack_dir/codex/skills/$item_name" + ;; + afx) + # AFX-built → canonical SKILL.md, transformed per provider + local canonical="$item_dir/SKILL.md" + for provider in claude codex antigravity; do + platform_enabled "$platforms" "$provider" && \ + transform_for_provider "$canonical" \ + "$pack_dir/$provider/skills/$item_name/SKILL.md" "$provider" + done + platform_enabled "$platforms" "copilot" && \ + generate_copilot_agent "$canonical" \ + "$pack_dir/copilot/agents/${item_name}.agent.md" "$item_name" + ;; + esac +} + +# Check if a platform is enabled in the manifest (true or partial = enabled, false/missing = disabled) +platform_enabled() { + local platforms="$1" + local provider="$2" + # Extract value for provider from platforms string + local val=$(echo "$platforms" | grep -oP "${provider}:\K\w+") + [[ "$val" == "true" || "$val" == "partial" ]] +} +``` + +**Platform gating:** The manifest's `platforms:` map gates which provider directories receive items. A platform value of `true` or `partial` enables routing; `false` or missing disables it. This prevents installing skills for providers the pack author didn't intend to support (e.g., a pack with `codex: false` won't copy SKILL.md files to `.agents/skills/` even if the file format is compatible). + +### 3.7 AFX-Built Skills + +AFX-built skills are hosted in the `rixrix/afx` repo under `skills/`. Each skill ships as a **single canonical SKILL.md** using Claude command syntax. At install time, `install.sh` transforms this file per provider — eliminating 4× file duplication in the source repo. + +``` +skills/afx-qa-methodology/ +└── SKILL.md # Single canonical file (Claude format) +``` + +The canonical file uses HTML comment markers to delineate provider-specific command references: + +```markdown +### AFX Integration + +<!-- @afx:provider-commands --> + +- Use `/afx:check path` to verify execution flow from UI to DB +- Use `/afx:task audit` to verify test coverage against spec +<!-- @afx:/provider-commands --> +- Follow the spec → design → tasks → code traceability chain +``` + +**Transform rules per provider:** + +| Provider | Action | +| ----------- | -------------------------------------------------------------- | +| Claude | Strip markers, keep content as-is (canonical format) | +| Codex | Strip markers, sed `/afx:cmd sub` → `afx-cmd-sub` | +| Antigravity | Remove markers AND content between them (generic lines remain) | +| Copilot | Auto-generate condensed `agent.md` from SKILL.md structure | + +**Sed patterns used:** + +```bash +# Claude — strip markers only +sed -e '/<!-- @afx:provider-commands -->/d' \ + -e '/<!-- @afx:\/provider-commands -->/d' + +# Codex — strip markers + convert command syntax to kebab-case +sed -e '/<!-- @afx:provider-commands -->/d' \ + -e '/<!-- @afx:\/provider-commands -->/d' \ + -e 's|`/afx:\([a-z]*\) \([a-z]*\)`|`afx-\1-\2`|g' + +# Antigravity — remove entire marked block (markers + content between) +sed '/<!-- @afx:provider-commands -->/,/<!-- @afx:\/provider-commands -->/d' +``` + +**Copilot generation** extracts the title, description, and instruction items from the canonical SKILL.md and produces a condensed `agent.md` with YAML frontmatter. + +`install.sh` routing for AFX-built skills (type `afx`): + +``` +skills/{name}/SKILL.md → transform → .afx/packs/{pack}/claude/skills/{name}/SKILL.md +skills/{name}/SKILL.md → transform → .afx/packs/{pack}/codex/skills/{name}/SKILL.md +skills/{name}/SKILL.md → transform → .afx/packs/{pack}/antigravity/skills/{name}/SKILL.md +skills/{name}/SKILL.md → generate → .afx/packs/{pack}/copilot/agents/{name}.agent.md +``` + +The only differentiator from external skills is the **guardrails baked in** — AFX-built skills include `@see` traceability, two-stage verification, and spec-driven patterns. + +### 3.8 Name Collision Detection + +Before copying items to provider directories, `install.sh` checks for name collisions. + +```bash +# Check if a skill/plugin already exists in a provider dir from a DIFFERENT pack +# Usage: check_collision <item_name> <provider_dir> <current_pack> +check_collision() { + local item_name="$1" + local provider_dir="$2" + local current_pack="$3" + + if [[ -d "$provider_dir/$item_name" ]]; then + # Check all installed packs to see who owns this item + for pack_dir in "$TARGET_DIR/.afx/packs"/afx-pack-*/; do + local pack_name=$(basename "$pack_dir") + if [[ "$pack_name" != "$current_pack" ]]; then + # Check if this other pack has this item in its master + if [[ -d "$pack_dir"/*/"skills/$item_name" ]] || \ + [[ -d "$pack_dir"/*/"plugins/$item_name" ]]; then + echo -e "${RED}Error: '$item_name' already installed by pack '$pack_name'${NC}" + echo "Use --force to overwrite." + return 1 + fi + fi + done + fi + return 0 +} +``` + +### 3.9 State Transitions + +#### Install + Enable (`--pack {name}` [--branch {ref} | --version {tag}]) + +```bash +pack_install() { + local pack_name="afx-pack-$1" + local ref=$(resolve_ref) + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + # 1. Ensure .afx/ and .gitignore + mkdir -p "$TARGET_DIR/.afx/.cache" + ensure_gitignore ".afx/" + + # 2. Fetch manifest + local manifest=$(fetch_manifest "$pack_name" "$ref") + + # 3. For each includes entry, download items + for_each_include "$manifest" | while read repo path items; do + local item_ref="main" + if [[ "$repo" == "rixrix/afx" ]]; then + item_ref="$ref" # Use --branch for AFX repo only + fi + + local temp=$(download_items "$repo" "$item_ref" "$path" $items) + + # 4. Detect type and route to .afx/packs/{pack}/{provider}/ + local platforms=$(parse_platforms "$manifest") + for item_dir in "$temp"/*/; do + local item_name=$(basename "$item_dir") + local type=$(detect_type "$item_dir" "$repo") + route_item "$item_dir" "$item_name" "$type" "$pack_dir" "$platforms" + done + + rm -rf "$temp" + done + + # 5. Copy from master to provider dirs + pack_copy_to_providers "$pack_name" + + # 6. Update .afx.yaml (store ref for staleness checks / sync) + afx_yaml_set_pack "$pack_name" "enabled" "$ref" + + echo -e "${GREEN}Pack '$1' installed and enabled (ref: $ref).${NC}" +} +``` + +#### Enable (`--pack-enable {name}`) + +```bash +pack_enable() { + local pack_name="afx-pack-$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + if [[ ! -d "$pack_dir" ]]; then + echo -e "${RED}Error: Pack '$1' not found. Install it first with --pack $1${NC}" + exit 1 + fi + + # Copy from .afx/ master to provider dirs (no download) + pack_copy_to_providers "$pack_name" + + # Update .afx.yaml + afx_yaml_set_pack "$pack_name" "enabled" + + echo -e "${GREEN}Pack '$1' enabled.${NC}" +} +``` + +#### Disable (`--pack-disable {name}`) + +```bash +pack_disable() { + local pack_name="afx-pack-$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + if [[ ! -d "$pack_dir" ]]; then + echo -e "${RED}Error: Pack '$1' not installed.${NC}" + exit 1 + fi + + # Delete provider copies for all items in this pack + pack_remove_from_providers "$pack_name" + + # Master stays in .afx/ + # Update .afx.yaml + afx_yaml_set_pack "$pack_name" "disabled" + + echo -e "${YELLOW}Pack '$1' disabled. Master preserved in .afx/${NC}" +} +``` + +#### Remove (`--pack-remove {name}`) + +```bash +pack_remove() { + local pack_name="afx-pack-$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + # 1. Remove provider copies + pack_remove_from_providers "$pack_name" + + # 2. Delete master + rm -rf "$pack_dir" + + # 3. Remove from .afx.yaml + afx_yaml_remove_pack "$pack_name" + + echo -e "${YELLOW}Pack '$1' removed entirely.${NC}" +} +``` + +#### Update Packs (`--update --packs`) + +```bash +pack_update_all() { + local ref=$(resolve_ref) + + # Fetch latest index + curl -sL "https://raw.githubusercontent.com/rixrix/afx/${ref}/packs/index.json" \ + > "$TARGET_DIR/.afx/.cache/lastIndex.json" + + # For each enabled pack in .afx.yaml + for pack_name in $(afx_yaml_enabled_packs); do + echo -e "${BLUE}Updating $pack_name...${NC}" + + # Re-download to temp + # Replace .afx/packs/{pack}/ + # Re-copy to providers + pack_remove_from_providers "$pack_name" + rm -rf "$TARGET_DIR/.afx/packs/$pack_name" + + # Re-install (reuses pack_install logic) + local short_name="${pack_name#afx-pack-}" + pack_install "$short_name" + done +} +``` + +#### Skill Toggle (`--skill-disable / --skill-enable`) + +```bash +skill_disable() { + local skill_name="$1" + local pack_name="afx-pack-$2" + + # Remove this specific skill from provider dirs + rm -rf "$TARGET_DIR/.claude/skills/$skill_name" + rm -rf "$TARGET_DIR/.claude/plugins/$skill_name" + rm -rf "$TARGET_DIR/.agents/skills/$skill_name" + rm -rf "$TARGET_DIR/.agent/skills/$skill_name" + rm -f "$TARGET_DIR/.github/agents/${skill_name}.agent.md" + + # Add to disabled_items in .afx.yaml + afx_yaml_disable_item "$pack_name" "$skill_name" + + echo -e "${YELLOW}Skill '$skill_name' disabled in pack '${pack_name}'.${NC}" +} + +skill_enable() { + local skill_name="$1" + local pack_name="afx-pack-$2" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + # Check for collisions before restoring + check_collision "$skill_name" "$TARGET_DIR/.claude/skills" "$pack_name" || exit 1 + + # Copy from master for this specific skill + for provider in claude codex antigravity copilot; do + if [[ -d "$pack_dir/$provider/skills/$skill_name" ]]; then + local target=$(provider_target_dir "$provider" "skills") + cp -r "$pack_dir/$provider/skills/$skill_name" "$target/$skill_name" + fi + if [[ -d "$pack_dir/$provider/plugins/$skill_name" ]]; then + local target=$(provider_target_dir "$provider" "plugins") + cp -r "$pack_dir/$provider/plugins/$skill_name" "$target/$skill_name" + fi + if [[ -f "$pack_dir/$provider/agents/${skill_name}.agent.md" ]]; then + cp "$pack_dir/$provider/agents/${skill_name}.agent.md" \ + "$TARGET_DIR/.github/agents/${skill_name}.agent.md" + fi + done + + # Remove from disabled_items in .afx.yaml + afx_yaml_enable_item "$pack_name" "$skill_name" + + echo -e "${GREEN}Skill '$skill_name' re-enabled in pack '${pack_name}'.${NC}" +} +``` + +#### Pack List (`--pack-list`) + +```bash +pack_list() { + echo -e "${BLUE}Installed packs:${NC}" + echo "" + + # Read from .afx.yaml + for entry in $(afx_yaml_all_packs); do + local name=$(echo "$entry" | cut -d: -f1) + local status=$(echo "$entry" | cut -d: -f2) + local disabled_count=$(echo "$entry" | cut -d: -f3) + + if [[ "$status" == "enabled" ]]; then + echo -e " ${GREEN}●${NC} $name (enabled)" + else + echo -e " ${YELLOW}○${NC} $name (disabled)" + fi + + if [[ "$disabled_count" -gt 0 ]]; then + echo " $disabled_count items disabled" + fi + done +} +``` + +#### Dry Run + +All pack operations respect the existing `$DRY_RUN` flag. When `DRY_RUN=true`: + +- Downloads still happen (to show what would be fetched) +- No files are written to `.afx/`, provider dirs, or `.afx.yaml` +- Output shows `(would create)`, `(would update)`, `(would delete)` annotations +- Uses the existing `INSTALLED[]`, `UPDATED[]`, `SKIPPED[]` tracking arrays + +#### Add Skill (`--add-skill {repo}:{path}/{skill}`) + +One-off skill install outside any pack. The skill is downloaded, type-detected, routed to provider dirs, and tracked in `.afx.yaml` under `custom_skills:`. + +```bash +add_skill() { + local spec="$1" # e.g., "anthropics/antigravity-awesome-skills:skills/some-niche-skill" + local repo="${spec%%:*}" + local full_path="${spec#*:}" + local skill_name=$(basename "$full_path") + local base_path=$(dirname "$full_path") + + # 1. Download the single skill + local temp=$(download_items "$repo" "main" "$base_path" "$skill_name") + + # 2. Detect type and copy to provider dirs directly (no .afx/packs/ master) + local item_dir="$temp/$skill_name" + local type=$(detect_type "$item_dir" "$repo") + + case "$type" in + skill) + cp -r "$item_dir" "$TARGET_DIR/.claude/skills/$skill_name" + cp -r "$item_dir" "$TARGET_DIR/.agents/skills/$skill_name" + cp -r "$item_dir" "$TARGET_DIR/.agent/skills/$skill_name" + ;; + plugin) + cp -r "$item_dir" "$TARGET_DIR/.claude/plugins/$skill_name" + ;; + openai) + cp -r "$item_dir" "$TARGET_DIR/.agents/skills/$skill_name" + ;; + esac + + rm -rf "$temp" + + # 3. Track in .afx.yaml + afx_yaml_add_custom_skill "$repo" "$full_path" + + echo -e "${GREEN}Skill '$skill_name' installed from $repo.${NC}" +} +``` + +**Custom skills during `--update --packs`:** Custom skills are NOT updated by `--update --packs`. They are one-off installs — the user can re-run `--add-skill` to refresh them manually. The `custom_skills:` list in `.afx.yaml` serves as a team-shared record of what was installed, not as an update target. + +### 3.10 Helper Functions + +```bash +# Copy all items from .afx/packs/{pack}/{provider}/ to provider dirs +pack_copy_to_providers() { + local pack_name="$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + local disabled_items=$(afx_yaml_disabled_items "$pack_name") + + # Claude skills + if [[ -d "$pack_dir/claude/skills" ]]; then + for skill in "$pack_dir"/claude/skills/*/; do + local name=$(basename "$skill") + [[ " $disabled_items " =~ " $name " ]] && continue + check_collision "$name" "$TARGET_DIR/.claude/skills" "$pack_name" || continue + cp -r "$skill" "$TARGET_DIR/.claude/skills/$name" + done + fi + + # Claude plugins + if [[ -d "$pack_dir/claude/plugins" ]]; then + for plugin in "$pack_dir"/claude/plugins/*/; do + local name=$(basename "$plugin") + [[ " $disabled_items " =~ " $name " ]] && continue + check_collision "$name" "$TARGET_DIR/.claude/plugins" "$pack_name" || continue + cp -r "$plugin" "$TARGET_DIR/.claude/plugins/$name" + done + fi + + # Codex skills + if [[ -d "$pack_dir/codex/skills" ]]; then + for skill in "$pack_dir"/codex/skills/*/; do + local name=$(basename "$skill") + [[ " $disabled_items " =~ " $name " ]] && continue + cp -r "$skill" "$TARGET_DIR/.agents/skills/$name" + done + fi + + # Antigravity skills + if [[ -d "$pack_dir/antigravity/skills" ]]; then + for skill in "$pack_dir"/antigravity/skills/*/; do + local name=$(basename "$skill") + [[ " $disabled_items " =~ " $name " ]] && continue + cp -r "$skill" "$TARGET_DIR/.agent/skills/$name" + done + fi + + # Copilot agents + if [[ -d "$pack_dir/copilot/agents" ]]; then + for agent in "$pack_dir"/copilot/agents/*.agent.md; do + local name=$(basename "$agent" .agent.md) + [[ " $disabled_items " =~ " $name " ]] && continue + cp "$agent" "$TARGET_DIR/.github/agents/$(basename "$agent")" + done + fi +} + +# Remove all items belonging to a pack from provider dirs +pack_remove_from_providers() { + local pack_name="$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + # Walk the master and remove matching items from provider dirs + for skill in "$pack_dir"/claude/skills/*/; do + [[ -d "$skill" ]] && rm -rf "$TARGET_DIR/.claude/skills/$(basename "$skill")" + done + for plugin in "$pack_dir"/claude/plugins/*/; do + [[ -d "$plugin" ]] && rm -rf "$TARGET_DIR/.claude/plugins/$(basename "$plugin")" + done + for skill in "$pack_dir"/codex/skills/*/; do + [[ -d "$skill" ]] && rm -rf "$TARGET_DIR/.agents/skills/$(basename "$skill")" + done + for skill in "$pack_dir"/antigravity/skills/*/; do + [[ -d "$skill" ]] && rm -rf "$TARGET_DIR/.agent/skills/$(basename "$skill")" + done + for agent in "$pack_dir"/copilot/agents/*.agent.md; do + [[ -f "$agent" ]] && rm -f "$TARGET_DIR/.github/agents/$(basename "$agent")" + done +} + +# Map provider name to target directory +provider_target_dir() { + local provider="$1" + local subdir="$2" # "skills" or "plugins" + + case "$provider" in + claude) echo "$TARGET_DIR/.claude/$subdir" ;; + codex) echo "$TARGET_DIR/.agents/$subdir" ;; + antigravity) echo "$TARGET_DIR/.agent/$subdir" ;; + copilot) echo "$TARGET_DIR/.github/agents" ;; + esac +} + +# Ensure .afx/ is in .gitignore +ensure_gitignore() { + local pattern="$1" + local gitignore="$TARGET_DIR/.gitignore" + + if [[ -f "$gitignore" ]]; then + grep -qF "$pattern" "$gitignore" || echo "$pattern" >> "$gitignore" + else + echo "$pattern" > "$gitignore" + fi +} +``` + +### 3.11 .afx.yaml Read/Write + +Since we can't use a YAML library in bash, `.afx.yaml` operations use `grep`/`sed`/`awk`. + +```bash +# Read all enabled pack names from .afx.yaml +afx_yaml_enabled_packs() { + # Parse packs section, find entries with status: enabled + awk '/^packs:/,/^[^ ]/' "$TARGET_DIR/.afx.yaml" | \ + grep -B1 'status: enabled' | \ + grep 'name:' | \ + awk '{print $3}' +} + +# Set pack status in .afx.yaml (add if missing, update if exists) +afx_yaml_set_pack() { + local pack_name="$1" + local status="$2" + local ref="${3:-}" # optional — written on install, preserved on enable/disable + # Implementation: check if pack exists in file, update or append + # If ref is non-empty, set installed_ref: $ref +} + +# Remove pack entry from .afx.yaml +afx_yaml_remove_pack() { + local pack_name="$1" + # Implementation: remove the multi-line block for this pack +} + +# Get disabled_items for a pack +afx_yaml_disabled_items() { + local pack_name="$1" + # Implementation: parse disabled_items array for this pack +} + +# Add item to disabled_items +afx_yaml_disable_item() { + local pack_name="$1" + local item_name="$2" + # Implementation: append to disabled_items array +} + +# Remove item from disabled_items +afx_yaml_enable_item() { + local pack_name="$1" + local item_name="$2" + # Implementation: remove from disabled_items array +} +``` + +### 3.12 Remote Execution (curl pipe) + +The existing remote install pattern must work for pack operations too: + +```bash +# Core install (existing — unchanged) +curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- . + +# Pack install (new) +curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- --pack qa . + +# Pack install from branch (new) +curl -sL https://raw.githubusercontent.com/rixrix/afx/dev/install.sh | bash -s -- --branch dev --pack qa . + +# Pack install from version (new) +curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- --version 1.5.3 --pack qa . + +# Update packs (new) +curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- --update --packs . +``` + +**Key change**: The current remote mode uses `git clone --depth 1` to get AFX source (line 173). Pack mode replaces this with `curl` + `tar` from codeload, removing the git dependency entirely. The current `git clone` path remains as a fallback for core-only installs but the preferred path becomes tarball-based. + +### 3.13 Main Dispatch Logic + +After argument parsing, the script dispatches to the appropriate handler: + +```bash +# Pack operations take priority — if any pack flag is set, skip core install +if [[ ${#PACK_NAMES[@]} -gt 0 ]]; then + for name in "${PACK_NAMES[@]}"; do + pack_install "$name" + done + exit 0 +fi + +if [[ -n "$PACK_DISABLE" ]]; then + pack_disable "$PACK_DISABLE" + exit 0 +fi + +if [[ -n "$PACK_ENABLE" ]]; then + pack_enable "$PACK_ENABLE" + exit 0 +fi + +if [[ -n "$PACK_REMOVE" ]]; then + pack_remove "$PACK_REMOVE" + exit 0 +fi + +if [[ "$PACK_LIST" == "true" ]]; then + pack_list + exit 0 +fi + +if [[ -n "$SKILL_DISABLE" ]]; then + skill_disable "$SKILL_DISABLE" "${PACK_NAMES[0]}" + exit 0 +fi + +if [[ -n "$SKILL_ENABLE" ]]; then + skill_enable "$SKILL_ENABLE" "${PACK_NAMES[0]}" + exit 0 +fi + +if [[ "$UPDATE_MODE" == "true" && "$UPDATE_PACKS" == "true" ]]; then + pack_update_all + exit 0 +fi + +# ... existing core install flow (unchanged) ... +``` + +--- + +## 4. AFX-Built Skills — Content + +### 4.1 `afx-qa-methodology` + +**Purpose**: QA workflow skill with AFX guardrails (SDD traceability, two-stage verification) baked in. + +**`skills/afx-qa-methodology/claude/skills/afx-qa-methodology/SKILL.md`**: + +```markdown +# AFX QA Methodology + +A structured quality assurance workflow that integrates with AFX spec-driven development. + +## Activation + +This skill activates when the user asks about: + +- Testing strategy or methodology +- Quality assurance workflows +- Test coverage analysis +- Bug triaging or classification + +## Instructions + +### Test Strategy + +1. **Read the spec first** — always check `docs/specs/{feature}/spec.md` for acceptance criteria +2. **Link tests to requirements** — every test file must include `@see` annotations: +``` + +@see docs/specs/{feature}/spec.md#FR-{n} +@see docs/specs/{feature}/tasks.md#2.1-task-slug + +``` +3. **Two-stage verification** — mark tasks `[OK]` in agent column, leave human column for reviewer +4. **Coverage mapping** — identify untested requirements and flag gaps + +### Bug Triage + +- Link bugs to spec requirements where possible +- Classify by severity: Critical (data loss), Major (broken workflow), Minor (cosmetic) +- Include reproduction steps tied to acceptance criteria + +### AFX Integration + +- Use `/afx:check path` to verify execution flow from UI to DB +- Use `/afx:task audit` to verify test coverage against spec +- Follow the spec → design → tasks → code traceability chain +``` + +**`skills/afx-qa-methodology/codex/skills/afx-qa-methodology/SKILL.md`**: + +```markdown +# AFX QA Methodology + +A structured quality assurance workflow that integrates with AFX spec-driven development. + +## Activation + +This skill activates when the user asks about: + +- Testing strategy or methodology +- Quality assurance workflows +- Test coverage analysis +- Bug triaging or classification + +## Instructions + +### Test Strategy + +1. **Read the spec first** — always check `docs/specs/{feature}/spec.md` for acceptance criteria +2. **Link tests to requirements** — every test file must include `@see` annotations: +``` + +@see docs/specs/{feature}/spec.md#FR-{n} +@see docs/specs/{feature}/tasks.md#2.1-task-slug + +``` +3. **Two-stage verification** — mark tasks `[OK]` in agent column, leave human column for reviewer +4. **Coverage mapping** — identify untested requirements and flag gaps + +### Bug Triage + +- Link bugs to spec requirements where possible +- Classify by severity: Critical (data loss), Major (broken workflow), Minor (cosmetic) +- Include reproduction steps tied to acceptance criteria + +### AFX Integration + +- Use `afx-check-path` to verify execution flow from UI to DB +- Use `afx-task-audit` to verify test coverage against spec +- Follow the spec → design → tasks → code traceability chain +``` + +**`skills/afx-qa-methodology/antigravity/skills/afx-qa-methodology/SKILL.md`**: + +```markdown +# AFX QA Methodology + +A structured quality assurance workflow that integrates with AFX spec-driven development. + +## Activation + +This skill activates when the user asks about: + +- Testing strategy or methodology +- Quality assurance workflows +- Test coverage analysis +- Bug triaging or classification + +## Instructions + +### Test Strategy + +1. **Read the spec first** — always check `docs/specs/{feature}/spec.md` for acceptance criteria +2. **Link tests to requirements** — every test file must include `@see` annotations: +``` + +@see docs/specs/{feature}/spec.md#FR-{n} +@see docs/specs/{feature}/tasks.md#2.1-task-slug + +``` +3. **Two-stage verification** — mark tasks `[OK]` in agent column, leave human column for reviewer +4. **Coverage mapping** — identify untested requirements and flag gaps + +### Bug Triage + +- Link bugs to spec requirements where possible +- Classify by severity: Critical (data loss), Major (broken workflow), Minor (cosmetic) +- Include reproduction steps tied to acceptance criteria + +### AFX Integration + +- Follow the spec → design → tasks → code traceability chain +- Use @see annotations to link implementations back to specs +``` + +**`skills/afx-qa-methodology/copilot/agents/afx-qa-methodology.agent.md`**: + +```markdown +--- +name: afx-qa-methodology +description: QA workflow with AFX spec-driven traceability +--- + +# AFX QA Methodology + +Quality assurance workflow integrated with spec-driven development. + +When assisting with testing or QA: + +1. Check `docs/specs/{feature}/spec.md` for acceptance criteria +2. Link test files to specs with `@see` annotations +3. Map test coverage to functional requirements (FR-{n}) +4. Flag untested requirements as gaps +5. Follow two-stage verification: agent marks [OK], human reviews separately +``` + +### 4.2 `afx-spec-test-planning` + +**Purpose**: Test planning skill that links test plans to spec tasks and requirements. + +**`skills/afx-spec-test-planning/claude/skills/afx-spec-test-planning/SKILL.md`**: + +````markdown +# AFX Spec Test Planning + +Plan tests by deriving them directly from spec requirements and acceptance criteria. + +## Activation + +This skill activates when the user asks about: + +- Test planning or test plan creation +- Deriving tests from specifications +- Mapping tests to requirements + +## Instructions + +### Derive Tests from Spec + +1. Read `docs/specs/{feature}/spec.md` +2. For each FR-{n} and NFR-{n}, derive one or more test cases +3. For each acceptance criteria item, derive at least one assertion +4. Document the mapping: + + | Requirement | Test Case | Type | Status | + | ----------- | ---------------------- | ----------- | ------- | + | FR-1 | test_user_login | Integration | Pending | + | NFR-1 | test_login_latency_p95 | Performance | Pending | + +### Test File Structure + +Every test file must include traceability: + +```typescript +/** + * @see docs/specs/{feature}/spec.md#FR-1 + * @see docs/specs/{feature}/tasks.md#3.1-write-login-tests + */ +describe('User Login', () => { ... }); +``` +```` + +### Gap Detection + +After planning, check for: + +- Requirements without test cases +- Test cases without requirement links +- Acceptance criteria without assertions + +```` + +**`skills/afx-spec-test-planning/codex/skills/afx-spec-test-planning/SKILL.md`**: + +```markdown +# AFX Spec Test Planning + +Plan tests by deriving them directly from spec requirements and acceptance criteria. + +## Activation + +This skill activates when the user asks about: +- Test planning or test plan creation +- Deriving tests from specifications +- Mapping tests to requirements + +## Instructions + +### Derive Tests from Spec + +1. Read `docs/specs/{feature}/spec.md` +2. For each FR-{n} and NFR-{n}, derive one or more test cases +3. For each acceptance criteria item, derive at least one assertion +4. Document the mapping: + + | Requirement | Test Case | Type | Status | + |-------------|-----------|------|--------| + | FR-1 | test_user_login | Integration | Pending | + | NFR-1 | test_login_latency_p95 | Performance | Pending | + +### Test File Structure + +Every test file must include traceability: + +```typescript +/** + * @see docs/specs/{feature}/spec.md#FR-1 + * @see docs/specs/{feature}/tasks.md#3.1-write-login-tests + */ +describe('User Login', () => { ... }); +```` + +### Gap Detection + +After planning, check for: + +- Requirements without test cases +- Test cases without requirement links +- Acceptance criteria without assertions + +```` + +**`skills/afx-spec-test-planning/antigravity/skills/afx-spec-test-planning/SKILL.md`**: + +```markdown +# AFX Spec Test Planning + +Plan tests by deriving them directly from spec requirements and acceptance criteria. + +## Activation + +This skill activates when the user asks about: +- Test planning or test plan creation +- Deriving tests from specifications +- Mapping tests to requirements + +## Instructions + +### Derive Tests from Spec + +1. Read `docs/specs/{feature}/spec.md` +2. For each FR-{n} and NFR-{n}, derive one or more test cases +3. For each acceptance criteria item, derive at least one assertion +4. Document the mapping: + + | Requirement | Test Case | Type | Status | + |-------------|-----------|------|--------| + | FR-1 | test_user_login | Integration | Pending | + | NFR-1 | test_login_latency_p95 | Performance | Pending | + +### Test File Structure + +Every test file must include traceability: + +```typescript +/** + * @see docs/specs/{feature}/spec.md#FR-1 + * @see docs/specs/{feature}/tasks.md#3.1-write-login-tests + */ +describe('User Login', () => { ... }); +``` + +### Gap Detection + +After planning, check for: + +- Requirements without test cases +- Test cases without requirement links +- Acceptance criteria without assertions +``` + +**`skills/afx-spec-test-planning/copilot/agents/afx-spec-test-planning.agent.md`**: + +```markdown +--- +name: afx-spec-test-planning +description: Test planning linked to spec requirements and acceptance criteria +--- + +# AFX Spec Test Planning + +Derive test plans from spec requirements. + +When planning tests: + +1. Read the feature spec at `docs/specs/{feature}/spec.md` +2. Map each FR-{n} and NFR-{n} to test cases +3. Ensure each acceptance criteria has at least one test assertion +4. Add `@see` annotations linking tests to spec sections +5. Flag requirements without test coverage +```` + +--- + +## 5. Backward Compatibility + +### 5.1 Core Install Unchanged + +When no pack flags are provided, `install.sh` behaves exactly as before: + +```bash +# These work identically to today — zero changes +./install.sh /path/to/project +./install.sh --update . +./install.sh --commands-only . +./install.sh --no-claude-md --no-docs . +./install.sh --force . +./install.sh --dry-run . +curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- . +curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- --update . +``` + +### 5.2 Existing Flags Preserved + +All current flags continue to work: + +| Flag | Behavior | Pack interaction | +| :---------------- | :----------------------------------- | :------------------------------ | +| `--update` | Update core AFX commands | Can combine with `--packs` | +| `--commands-only` | Only install command assets | Ignored when pack flags present | +| `--no-claude-md` | Skip CLAUDE.md snippet | N/A for pack ops | +| `--no-agents-md` | Skip AGENTS.md snippet | N/A for pack ops | +| `--no-gemini-md` | Skip GEMINI.md snippet | N/A for pack ops | +| `--no-copilot-md` | Skip copilot-instructions.md snippet | N/A for pack ops | +| `--no-docs` | Skip docs copy | N/A for pack ops | +| `--force` | Overwrite all files | Also forces pack overwrites | +| `--dry-run` | Preview without changes | Works for all pack ops | + +### 5.3 Help Text Update + +The `--help` output must be updated to include pack commands: + +``` +AFX Installer v{VERSION} + +Usage: ./install.sh [OPTIONS] <target-project-path> + +Core Options: + --update Update existing AFX installation + --commands-only Only install/update command assets + --no-claude-md Skip CLAUDE.md snippet integration + --no-agents-md Skip AGENTS.md snippet integration + --no-gemini-md Skip GEMINI.md snippet integration + --no-copilot-md Skip copilot-instructions.md snippet integration + --no-docs Skip copying AFX documentation + --force Overwrite all files (fresh install) + --dry-run Preview changes without applying + --branch NAME Use a specific branch (default: main) + --version TAG Use a specific version tag (e.g., 1.5.3 or v1.5.3) + +Pack Management: + --pack NAME Install and enable a pack + --pack-disable NAME Disable a pack (keep master) + --pack-enable NAME Re-enable a disabled pack + --pack-remove NAME Remove a pack entirely + --pack-list List installed packs + --skill-disable NAME --pack P Disable a skill within a pack + --skill-enable NAME --pack P Re-enable a skill within a pack + --update --packs Update all enabled packs + --add-skill REPO:PATH/SKILL Install a single skill (no pack) + +Examples: + # Core install + ./install.sh . + + # Install QA pack + ./install.sh --pack qa . + + # Install from dev branch + ./install.sh --branch dev --pack qa . + + # Install from version + ./install.sh --version 1.5.3 --pack qa . + + # Multiple packs + ./install.sh --pack qa --pack security . + + # Manage packs + ./install.sh --pack-disable qa . + ./install.sh --pack-enable qa . + ./install.sh --pack-remove qa . + ./install.sh --pack-list . + + # Update all packs + ./install.sh --update --packs . +``` + +--- + +## 6. Security & Safety + +- **No Overwrites**: If a user has a manually installed skill with the same name as a pack skill, `install.sh` aborts with an error unless `--force` is used. Name collision detection (Section 3.8) prevents one pack from overwriting another pack's items. +- **Pristine Source**: We do not modify external code. We only place it. Downloaded files are byte-identical to the source repository. +- **Gitignore**: `.afx/` is added to `.gitignore` on first pack install to prevent committing external code to the user's repo. +- **No Auth**: All fetches use public URLs. No credentials, tokens, or API keys are stored or transmitted. +- **Temp Cleanup**: All temp directories created during download are cleaned up via `trap` handlers, even on failure. +- **Idempotent**: Running the same pack install twice produces the same result. Existing items are overwritten with identical content. + +--- + +## 7. Implementation Plan + +### Phase 1: Manifests & Index + +1. Create `packs/` directory in AFX repo. +2. Author `packs/afx-pack-qa.yaml` (full manifest as shown in Section 1.1). +3. Author `packs/afx-pack-security.yaml` (full manifest as shown in Section 1.1). +4. Create `packs/index.json` (full index as shown in Section 1.2). + +### Phase 2: AFX-Built Skills + +1. Create `skills/` directory in AFX repo. +2. Author `skills/afx-qa-methodology/SKILL.md` (single canonical, with `@afx:provider-commands` markers). +3. Author `skills/afx-spec-test-planning/SKILL.md` (single canonical). +4. Author `skills/afx-owasp-top-10/SKILL.md` (single canonical, with `@afx:provider-commands` markers). +5. Author `skills/afx-security-audit/SKILL.md` (single canonical, with `@afx:provider-commands` markers). + +### Phase 3: install.sh — Download & Detection + +1. Add new argument parsing (Section 3.3). +2. Implement `download_items()` — curl + tar extraction. +3. Implement `fetch_manifest()` — raw URL fetch. +4. Implement `detect_type()` — skill type detection. +5. Implement `route_item()` — type-based routing to `.afx/`. +6. Implement `check_collision()` — name collision detection. +7. Implement `ensure_gitignore()` — auto-add `.afx/` to `.gitignore`. + +### Phase 4: install.sh — State Management + +1. Implement YAML read/write helpers for `.afx.yaml` (Section 3.11). +2. Implement `pack_install()` — full install + enable flow. +3. Implement `pack_enable()` / `pack_disable()` / `pack_remove()`. +4. Implement `skill_disable()` / `skill_enable()`. +5. Implement `pack_list()`. +6. Implement `pack_update_all()`. +7. Implement `pack_copy_to_providers()` / `pack_remove_from_providers()`. +8. Add main dispatch logic (Section 3.13). +9. Update `--help` output (Section 5.3). +10. Replace `git clone` fallback with `curl` + `tar` for remote execution. diff --git a/docs/specs/afx-packs/journal.md b/docs/specs/afx-packs/journal.md new file mode 100644 index 0000000..4cf4312 --- /dev/null +++ b/docs/specs/afx-packs/journal.md @@ -0,0 +1,190 @@ +--- +afx: true +type: JOURNAL +status: Living +owner: "@rix" +tags: [packs, install, skills, ecosystem, journal] +--- + +# Journal - AFX Pack System + +<!-- prefix: PK --> + +> Quick captures and discussion history for AI-assisted development sessions. +> See [agenticflowx.md](../../agenticflowx/agenticflowx.md) for workflow. + +## Captures + +<!-- Quick notes during active chat - cleared when recorded --> + +--- + +## Discussions + +<!-- Recorded discussions with IDs: PK-D001, PK-D002, etc. --> +<!-- Chronological order: oldest first, newest last --> + +### PK-D001 - 2026-02-28 - Spec Promotion from Research + +`status:active` `[product, planning, research]` + +**Context**: The pack system design in `res-skills-ecosystem-index.md` (Section 8) and the pack management research in `res-vscode-pack-management.md` reached sufficient maturity. The AFX-side infrastructure (install.sh, pack manifests, index, skills directory) was split into its own spec as the counterpart to the vscode-toolbox UI spec. + +**Summary**: Created spec covering 43 functional requirements across 7 areas: pack manifests, pack index, install.sh CLI commands, skill type detection, .afx/ directory structure, .afx.yaml state management, AFX-built skills, and provider copy routing. This is purely the AFX repo infrastructure — the UI consumer is in the vscode-toolbox spec. + +**Decisions**: + +- Separate spec from vscode-toolbox — this covers AFX repo changes only +- 7 requirement areas: manifests, index, install.sh, type detection, .afx/ structure, .afx.yaml state, AFX-built skills, provider routing +- Current install.sh has zero pack support — all pack commands are net new +- `packs/` and `skills/` directories don't exist yet — both need to be created +- Index schema includes version and changelog (richer than the lean research version) per user edits to toolbox spec +- Open questions: download method (sparse checkout vs tarball), offline install, one-off skill tracking +- Deferred: quality scoring, version pinning + +**Notes**: + +- **[PK-D001.N1]** **[2026-02-28]** Split from vscode-toolbox spec. AFX-side = install.sh + manifests + index + skills. VSCode-side = Toolbox UI. Both share the same constraints from research. `[product, spec]` +- **[PK-D001.N2]** **[2026-02-28]** User edited vscode-toolbox spec to add version + changelog to index.json schema, resolve OQ#2 (install.sh not available → "Setup AFX" button), remove .afx.local.yaml references from FR-20/AC, add right-click "Preview Changes" to interaction map. These changes reflected in this spec's index schema. `[sync]` + +**Related Files**: docs/research/res-skills-ecosystem-index.md, docs/research/res-vscode-pack-management.md, docs/specs/vscode-toolbox/spec.md, install.sh +**Participants**: @rix, claude + +### PK-D002 - 2026-02-28 - Phase 1 & 2 Implementation + Antigravity Core Skills + +`status:completed` `[implementation, antigravity]` + +**Context**: Implementing Phase 1 (Manifests & Index) and Phase 2 (AFX-Built Skills) from the approved spec. During implementation, discovered that `.agent/skills/` (Google Antigravity core skills) was missing from the AFX repo — we had `.claude/commands/`, `.codex/skills/`, `.gemini/commands/`, `.github/prompts/` but no Antigravity equivalent. + +**Summary**: Created 3 pack files (2 manifests + 1 index), 16 AFX-built skill files (4 skills × 4 provider variants), 13 Antigravity core skill files (mirroring `.codex/skills/`), and updated `install.sh` to copy `.agent/skills/` as step 3/12. + +**Decisions**: + +- Antigravity core skills follow same pattern as Codex skills: lightweight SKILL.md wrappers pointing to `.claude/commands/` as source of truth +- No `openai.yaml` equivalent needed for Antigravity (only Codex has that) +- `install.sh` step count: 11 → 12 (added Antigravity between Codex and Gemini) +- `--commands-only` help text updated to include `.agent` +- CLAUDE.md repo structure updated with `.agent/`, `packs/`, `skills/` + +**Participants**: @rix, claude + +### PK-D003 - 2026-02-28 - Phase 3 & 4 Implementation (install.sh Pack Management) + +`status:completed` `[implementation, install.sh, pack-management]` + +**Context**: Implementing Phase 3 (Download & Detection) and Phase 4 (State Management) — the full pack management system in install.sh. This is the core CLI infrastructure that the VSCode Toolbox UI will consume. + +**Summary**: Added ~1100 lines to install.sh (767 → 1865 lines), implementing 30+ functions covering: argument parsing (14 new flags), manifest fetch via raw.githubusercontent.com, tarball download via codeload.github.com, skill type detection (5 types), item routing to `.afx/packs/{pack}/{provider}/`, name collision detection, gitignore management, bash-only YAML read/write for `.afx.yaml`, pack lifecycle (install/enable/disable/remove), individual skill enable/disable, pack list, bulk update, one-off skill install, and dry-run mode. All verified with `bash -n` — no syntax errors. + +**Decisions**: + +- Bash-only YAML parsing (grep/sed/awk) — no external dependencies required +- codeload.github.com tarballs for downloads — avoids git dependency +- `.afx/packs/{pack}/{provider}/` as master storage, provider dirs are derived copies +- Dry-run mode threads through all operations via `$DRY_RUN` flag +- Pack operations dispatch before core install logic and `exit 0` — mutually exclusive paths +- Preserved existing `git clone` fallback for remote execution (task 4.10 deferred item) +- `resolve_ref()` handles --version (auto-prefix v), --branch, default main with mutual exclusion + +**Notes**: + +- **[PK-D003.N1]** **[2026-02-28]** One task item deferred: "Replace git clone fallback with curl + tar for remote execution" (task 4.10, last checkbox). The existing git clone path works and was not touched. `[deferred]` + +**Participants**: @rix, claude + +### PK-D004 - 2026-02-28 - Canonical SKILL.md Refactor (4× dedup) + +`status:completed` `[refactor, skills, install.sh]` + +**Context**: User observed that Claude, Codex, Antigravity, and Copilot skill variants were 95% identical — only provider-specific command syntax differed. The 4× file duplication made maintenance harder. + +**Summary**: Flattened each skill from 4 provider subdirectories to 1 canonical `SKILL.md` (16 files → 4). Added `transform_for_provider()` and `generate_copilot_agent()` to install.sh with full documentation of sed patterns. Canonical files use `<!-- @afx:provider-commands -->` HTML comment markers to delineate provider-specific command lines. Updated design.md Section 3.7 with transform rules table and sed pattern reference. + +**Decisions**: + +- Claude format is canonical (uses `/afx:cmd sub` syntax) +- Codex: sed converts `/afx:cmd sub` → `afx-cmd-sub` (kebab-case) +- Antigravity: sed removes entire marked block (generic traceability lines remain) +- Copilot: auto-generated condensed `agent.md` from SKILL.md structure (extracts title, description, instruction items) +- Skills without `/afx:` commands (e.g., afx-spec-test-planning) have no markers — identical across Claude/Codex/Antigravity + +**Participants**: @rix, claude + +### PK-D005 - 2026-02-28 - Full Test Suite + Bug Fixes + +`status:completed` `[testing, bugs, install.sh]` + +**Context**: Comprehensive testing of install.sh in `tmp/` covering all scenarios: fresh install, update, commands-only, transform functions, pack management, dry-run, help, and argument parsing. + +**Summary**: Ran 8 test suites with 27 transform assertions (all passing). Found and fixed 3 bugs: + +1. **Dispatch bug**: `--skill-disable NAME --pack PACK` was triggering `pack_install` because `--pack` was treated as an install target. Fixed by gating pack install on absence of `SKILL_DISABLE`/`SKILL_ENABLE`. +2. **resolve_ref exit propagation**: `local ref=$(resolve_ref)` masked the exit code because `local` always returns 0 (bash gotcha). Fixed by separating declaration from assignment: `local ref; ref=$(resolve_ref) || exit 1`. +3. **generate_copilot_agent description extraction**: `sed` range `/^# /,/^$/` captured heading-to-blank but description is AFTER the blank. Fixed with `awk 'NR>1 && /^[^#]/ && !/^$/ { print; exit }'`. + +**Notes**: + +- **[PK-D005.N1]** **[2026-02-28]** Pack install fails on network (expected — manifests not pushed to GitHub yet). Tested with simulated `.afx/` structure for enable/disable/remove/list. `[expected]` +- **[PK-D005.N2]** **[2026-02-28]** `.codex/skills/` is for AFX command wrappers only. Pack skills go to `.agents/skills/` (Codex/OpenAI runtime convention). Test fixture initially used wrong dir. `[clarification]` + +**Participants**: @rix, claude + +--- + +## Approval: Spec Approved (2026-02-28 04:35 UTC) + +Spec approved and frozen. Further changes require version bump. + +Approved by: Gemini Code Assist (automated validation) +Review score: 100% compliant + +Validation Summary: + +- Structure: All required sections present +- Frontmatter: Valid +- Quality: 0 Critical issues + +Next step: Create design PRD. + +--- + +## Work Sessions + +<!-- Task execution log - updated by /afx:work next, /afx:dev code --> + +| Date | Task | Action | Files Modified | Agent | Human | +| ---- | ---- | ------ | -------------- | ----- | ----- | +| 2026-02-28 | 1.1 | /afx:dev code | packs/afx-pack-qa.yaml | [OK] | | +| 2026-02-28 | 1.2 | /afx:dev code | packs/afx-pack-security.yaml | [OK] | | +| 2026-02-28 | 1.3 | /afx:dev code | packs/index.json | [OK] | | +| 2026-02-28 | 2.1 | /afx:dev code | skills/ (4 skill dirs × 4 providers) | [OK] | | +| 2026-02-28 | 2.2 | /afx:dev code | skills/afx-qa-methodology/ (4 variants) | [OK] | | +| 2026-02-28 | 2.3 | /afx:dev code | skills/afx-spec-test-planning/ (4 variants) | [OK] | | +| 2026-02-28 | 2.4 | /afx:dev code | skills/afx-owasp-top-10/ (4 variants) | [OK] | | +| 2026-02-28 | 2.5 | /afx:dev code | skills/afx-security-audit/ (4 variants) | [OK] | | +| 2026-02-28 | — | /afx:dev code | .agent/skills/ (13 core AFX skills) | [OK] | | +| 2026-02-28 | — | /afx:dev code | install.sh (add Antigravity step 3/12) | [OK] | | +| 2026-02-28 | — | /afx:dev code | CLAUDE.md (.agent/ + packs/ + skills/) | [OK] | | +| 2026-02-28 | 3.1 | /afx:dev code | install.sh (14 new case entries, help text) | [OK] | | +| 2026-02-28 | 3.2 | /afx:dev code | install.sh (resolve_ref, fetch_manifest) | [OK] | | +| 2026-02-28 | 3.3 | /afx:dev code | install.sh (download_items, for_each_include) | [OK] | | +| 2026-02-28 | 3.4 | /afx:dev code | install.sh (detect_type — 5 type rules) | [OK] | | +| 2026-02-28 | 3.5 | /afx:dev code | install.sh (route_item, platform_enabled) | [OK] | | +| 2026-02-28 | 3.6 | /afx:dev code | install.sh (check_collision) | [OK] | | +| 2026-02-28 | 3.7 | /afx:dev code | install.sh (ensure_gitignore) | [OK] | | +| 2026-02-28 | 4.1 | /afx:dev code | install.sh (6 afx_yaml_* helpers) | [OK] | | +| 2026-02-28 | 4.2 | /afx:dev code | install.sh (pack_install orchestrator) | [OK] | | +| 2026-02-28 | 4.3 | /afx:dev code | install.sh (pack_copy/remove_from_providers) | [OK] | | +| 2026-02-28 | 4.4 | /afx:dev code | install.sh (pack_enable, pack_disable, pack_remove) | [OK] | | +| 2026-02-28 | 4.5 | /afx:dev code | install.sh (skill_disable, skill_enable) | [OK] | | +| 2026-02-28 | 4.6 | /afx:dev code | install.sh (pack_list) | [OK] | | +| 2026-02-28 | 4.7 | /afx:dev code | install.sh (pack_update_all) | [OK] | | +| 2026-02-28 | 4.8 | /afx:dev code | install.sh (add_skill — one-off install) | [OK] | | +| 2026-02-28 | 4.9 | /afx:dev code | install.sh (dry-run for all pack ops) | [OK] | | +| 2026-02-28 | 4.10 | /afx:dev code | install.sh (main dispatch, help, bash -n pass) | [OK] | | +| 2026-02-28 | 2.1–2.5 | /afx:dev refactor | skills/ (4×4 provider dirs → 4 canonical SKILL.md) | [OK] | | +| 2026-02-28 | 3.5 | /afx:dev refactor | install.sh (transform_for_provider, generate_copilot_agent, route_item afx case) | [OK] | | +| 2026-02-28 | — | /afx:dev code | design.md (Section 3.7 rewrite: canonical + transform docs) | [OK] | | +| 2026-02-28 | — | /afx:dev test | install.sh (8 test suites, 27 transform tests, 3 bugs fixed) | [OK] | | + +--- diff --git a/docs/specs/afx-packs/spec.md b/docs/specs/afx-packs/spec.md new file mode 100644 index 0000000..417d6cc --- /dev/null +++ b/docs/specs/afx-packs/spec.md @@ -0,0 +1,524 @@ +--- +<!-- APPROVED: 2026-02-28 - Do not edit without version bump --> +afx: true +type: SPEC +status: Approved +owner: "@rix" +priority: High +version: 1.0 +approved_at: "2026-02-28T04:35:00Z" +created: "2026-02-28" +last_verified: "2026-02-28" +tags: [packs, install, skills, ecosystem] + +--- + +# AFX Pack System - Product Specification + +**Version:** 1.0 +**Date:** 2026-02-28 +**Status:** Approved +**Author:** Richard Sentino + +## References + +- **Research**: [res-skills-ecosystem-index.md](../../research/res-skills-ecosystem-index.md) — Pack system design, provider conventions, ecosystem inventory (Section 8 is source of truth) +- **Research**: [res-vscode-pack-management.md](../../research/res-vscode-pack-management.md) — Pack management UI, index strategy +- **ADR**: [ADR-0003](../../adr/ADR-0003-skill-management-architecture.md) — V1 rename toggle (superseded by pack system) +- **Counterpart**: [vscode-toolbox/spec.md](../vscode-toolbox/spec.md) — VSCode extension UI that consumes this infrastructure + +--- + +## Problem Statement + +AFX currently installs only core commands (slash commands, Codex skills, Copilot prompts) via `install.sh`. There is no way to install curated bundles of third-party skills, no pack management CLI, no index file for discovery, and no directory structure for managing skill state across providers. + +The ecosystem has ~1,100 unique skills across Antigravity, OpenAI, Claude Plugins, and Agentic-Flow. AFX's value is in **composing** these — curating packs by role/domain, adding guardrails via AFX-built skills, and managing the lifecycle (install, enable, disable, remove, update) across Claude Code, Codex CLI, and GitHub Copilot. + +This spec covers what needs to be built **in the AFX repo itself**: pack manifests, `install.sh` pack management commands, `packs/index.json`, AFX-built skills, `.afx/` directory structure, and `.afx.yaml` pack state tracking. + +--- + +## User Stories + +### Primary Users + +Developers using AFX who want to extend their AI coding assistants with curated skill packs. + +### Stories + +**As a** developer +**I want** to install a curated skill pack with a single command (`install.sh --pack qa .`) +**So that** I get a vetted set of skills across all my AI assistants without picking them one by one + +**As a** developer +**I want** to disable a pack without deleting it +**So that** I can reduce token consumption and re-enable it later without re-downloading + +**As a** developer +**I want** to disable individual skills within an enabled pack +**So that** I have fine-grained control over which skills are active + +**As a** developer +**I want** to update all installed packs to their latest versions +**So that** I get new skills and fixes without manual tracking + +**As a** developer +**I want** to preview what a pack install will do before committing +**So that** I can review the changes and avoid surprises + +**As a** team lead +**I want** to commit `.afx.yaml` with our team's packs so all developers get the same skill baseline +**So that** the team has consistent AI assistant capabilities + +--- + +## Requirements + +### Functional Requirements + +#### Pack Manifests (`packs/`) + +| ID | Requirement | Priority | +| ---- | --------------------------------------------------------------------------------------------------------- | --------- | +| FR-1 | Create `packs/` directory in AFX repo to hold pack manifest files | Must Have | +| FR-2 | Each pack manifest is a YAML file: `packs/afx-pack-{name}.yaml` | Must Have | +| FR-3 | Manifest schema: `name`, `description`, `category`, `platforms`, `includes[]` | Must Have | +| FR-4 | Each `includes` entry specifies: `repo`, `path`, `items[]` (repo URLs inline, no registry indirection) | Must Have | +| FR-5 | `platforms` field declares provider support: `claude`, `codex`, `antigravity`, `copilot` (best-effort, no forced parity) | Must Have | +| FR-7 | Create at least one pack manifest (`afx-pack-qa`) as reference implementation | Must Have | + +#### Pack Index (`packs/index.json`) + +| ID | Requirement | Priority | +| ----- | ------------------------------------------------------------------------------------------ | --------- | +| FR-8 | Create `packs/index.json` — single aggregated metadata file for all packs and upstream | Must Have | +| FR-9 | Index schema for packs: `description`, `category`, `providers[]` | Must Have | +| FR-10 | Index schema for upstream: provider repos with `featured[]` item lists | Must Have | +| FR-11 | Index served via `raw.githubusercontent.com` — no auth, no API key | Must Have | +| FR-12 | Index maintained manually alongside pack manifests (updated when packs or upstream change) | Must Have | + +#### `install.sh` Pack Management + +| ID | Requirement | Priority | +| ------ | ----------------------------------------------------------------------------------------------------------------------- | ----------- | +| FR-13 | `--pack {name}` — install and enable a pack (download items, detect types, store in `.afx/`, copy to providers) | Must Have | +| FR-14 | `--pack {a} --pack {b}` — install multiple packs in one command | Must Have | +| FR-15 | `--pack-disable {name}` — delete provider copies, keep master in `.afx/`, update `.afx.yaml` status | Must Have | +| FR-16 | `--pack-enable {name}` — `cp -r` from `.afx/` master to provider dirs, update `.afx.yaml` status | Must Have | +| FR-17 | `--pack-remove {name}` — delete both provider copies and `.afx/packs/{pack}/`, remove from `.afx.yaml` | Must Have | +| FR-18 | `--pack-list` — list installed packs with status (enabled/disabled) | Must Have | +| FR-19 | `--skill-disable {name} --pack {pack}` — disable single skill within an enabled pack | Must Have | +| FR-20 | `--skill-enable {name} --pack {pack}` — re-enable a disabled skill within a pack | Must Have | +| FR-21 | `--update --packs` — fetch latest manifests and update all enabled packs | Must Have | +| FR-22 | `--dry-run --pack {name}` — preview changes without applying | Must Have | +| FR-23 | `--add-skill {repo}:{path}/{skill}` — one-off skill install from any repo (no pack) | Should Have | +| FR-24a | `--branch {name}` — override the default branch (`main`) when downloading AFX packs and index | Must Have | +| FR-24b | `--version {tag}` — install from a specific version tag (e.g., `v1.5.3` or `1.5.3`); mutually exclusive with `--branch` | Must Have | + +#### Skill Type Detection + +| ID | Requirement | Priority | +| ----- | -------------------------------------------------------------------------------------------------- | --------- | +| FR-34 | Detect **Simple Skill**: `SKILL.md` at root, no `.claude-plugin/` → compatible with Claude + Codex + Antigravity | Must Have | +| FR-25 | Detect **Claude Plugin**: `.claude-plugin/plugin.json` exists → Claude only | Must Have | +| FR-26 | Detect **OpenAI Skill**: `SKILL.md` + `agents/openai.yaml` → Codex only | Must Have | +| FR-27 | External skills are never modified — downloaded pristine, stored pristine, copied pristine | Must Have | + +#### `.afx/` Directory Structure + +| ID | Requirement | Priority | +| ----- | ----------------------------------------------------------------------------------------------------------------------------------- | --------- | +| FR-28 | Store master copies at `.afx/packs/{pack-name}/{provider}/` grouped by provider | Must Have | +| FR-29 | Provider subdirectories follow each platform's conventions: `claude/skills/`, `claude/plugins/`, `codex/skills/`, `antigravity/skills/`, `copilot/agents/` | Must Have | +| FR-30 | External skills stored pristine in master; AFX-built guardrails skills sit alongside them | Must Have | +| FR-31 | Create `.afx/.cache/` directory for cached index data | Must Have | + +#### `.afx.yaml` Pack State + +| ID | Requirement | Priority | +| ----- | ------------------------------------------------------------------------------------------------- | --------- | +| FR-32 | Add `packs:` section to `.afx.yaml` with: `name`, `status` (enabled/disabled), `installed_ref`, `disabled_items[]` | Must Have | +| FR-33 | `.afx.yaml` is committed (team-shared) and includes `custom_skills:` list for one-off installs | Must Have | + +#### AFX-Built Skills (`skills/`) + +| ID | Requirement | Priority | +| ----- | -------------------------------------------------------------------------------------------- | --------- | +| FR-35 | Create `skills/` directory in AFX repo for AFX-authored skills with guardrails baked in | Must Have | +| FR-36 | AFX-built skills use provider-native formats: `SKILL.md` for Claude/Codex/Antigravity, `.agent.md` for Copilot | Must Have | +| FR-37 | Pack manifests reference AFX-built skills via `repo: rixrix/afx`, `path: skills/` | Must Have | +| FR-38 | Create guardrails skills for the first pack (`afx-qa-methodology`, `afx-spec-test-planning`) | Must Have | + +#### Provider Copy Management + +| ID | Requirement | Priority | +| ----- | ----------------------------------------------------------------------------------------------------------------- | --------- | +| FR-39 | Simple Skills (SKILL.md) copied to `.claude/skills/`, `.agents/skills/`, and `.agent/skills/` | Must Have | +| FR-40 | Claude Plugins copied to `.claude/plugins/` only | Must Have | +| FR-41 | OpenAI Skills (with `openai.yaml`) copied to `.agents/skills/` only | Must Have | +| FR-42 | AFX-built skills are pre-organized by provider directory (claude/, codex/, antigravity/, copilot/) in `rixrix/afx` repo (no routing logic needed — just copy) | Must Have | +| FR-43 | Copilot receives only AFX-built skills (no conversion of external SKILL.md to `.agent.md`) | Must Have | +| FR-44 | Detect name collisions in provider directories; fail install/enable if a different pack owns the destination | Must Have | +| FR-45 | Manifest `platforms:` field gates routing — skills are only copied to providers marked `true` or `partial` | Must Have | + +### Non-Functional Requirements + +| ID | Requirement | Target | +| ----- | ----------------------------------------------------------------------------------------- | ---------------------------------------------------------- | +| NFR-1 | Default `install.sh` (without `--pack`) continues to work unchanged | Zero breaking changes to existing installations | +| NFR-2 | Pack install downloads via `codeload.github.com` tarballs and extracts specific paths | No git dependency, no full repo clones | +| NFR-3 | Enable/disable is instant — `cp -r` / `rm -r` with no network or conversion | Sub-second for packs with ≤ 20 items | +| NFR-4 | Pack prefix `afx-pack-*` to avoid naming conflicts with AFX core commands | Convention enforced in manifest naming | +| NFR-5 | `install.sh` requires only `curl`, `tar`, and `bash` (≥ 4.0) — no git, no node, no python | macOS, Linux, WSL (Windows Subsystem for Linux) | +| NFR-6 | All pack operations are idempotent | Running the same command twice produces the same result | +| NFR-7 | `.afx/` directory is gitignored by default (added to `.gitignore` on first pack install) | Downloaded skills should not be committed to user projects | + +--- + +## Acceptance Criteria + +### Pack Manifests + +- [ ] `packs/` directory exists in AFX repo +- [ ] `packs/afx-pack-qa.yaml` exists with valid schema (name, description, category, platforms, includes) +- [ ] Manifest includes entries specify `repo`, `path`, `items[]` with no registry indirection +- [ ] `platforms` field accurately reflects provider support per pack + +### Pack Index + +- [ ] `packs/index.json` exists with packs and upstream sections +- [ ] Each pack entry has: description, category, providers +- [ ] Upstream section lists tracked repos with featured items +- [ ] Index is fetchable via `raw.githubusercontent.com` without authentication + +### install.sh — Pack Install + +- [ ] `./install.sh --pack qa .` downloads items from manifested repos +- [ ] Downloaded items stored pristine in `.afx/packs/afx-pack-qa/{provider}/` +- [ ] Type detection correctly routes: Simple Skill → Claude + Codex + Antigravity, Claude Plugin → Claude only, OpenAI Skill → Codex only +- [ ] Provider copies placed in correct directories (`.claude/skills/`, `.claude/plugins/`, `.agents/skills/`, `.agent/skills/`, `.github/agents/`) +- [ ] AFX-built skills authored per provider directory (claude/, codex/, antigravity/, copilot/) +- [ ] Pack recorded in `.afx.yaml` with `status: enabled` and `installed_ref` (resolved from `--version`, `--branch`, or default `main`) +- [ ] `.afx/` added to `.gitignore` if not already present +- [ ] `--pack qa --pack security` installs both packs +- [ ] `--branch dev --pack qa .` fetches pack manifest and items from the `dev` branch instead of `main` +- [ ] `--version 1.5.3 --pack qa .` fetches from version tag `v1.5.3` (auto-prefixes `v`) +- [ ] `--version` and `--branch` are mutually exclusive — error if both provided + +### install.sh — Pack Lifecycle + +- [ ] `--pack-disable qa` removes provider copies, master in `.afx/` stays, `.afx.yaml` updated to `status: disabled` +- [ ] `--pack-enable qa` copies from `.afx/` master to provider dirs, `.afx.yaml` updated to `status: enabled` +- [ ] `--pack-remove qa` deletes both `.afx/packs/afx-pack-qa/` and provider copies, removes from `.afx.yaml` +- [ ] `--pack-list` outputs installed packs with their status +- [ ] `--skill-disable tdd --pack qa` removes that skill from provider dirs, adds to `disabled_items` +- [ ] `--skill-enable tdd --pack qa` restores from master (checking collisions), removes from `disabled_items` +- [ ] `--update --packs` re-downloads and replaces items for all enabled packs +- [ ] `--dry-run --pack qa` shows what would be changed without writing any files + +### install.sh — Backward Compatibility + +- [ ] `./install.sh .` (no pack flags) works exactly as before — installs core AFX commands only +- [ ] `./install.sh --update .` works exactly as before +- [ ] All existing flags (`--commands-only`, `--no-claude-md`, `--force`, etc.) continue to work + +### .afx.yaml State + +- [ ] `packs:` section added to `.afx.yaml` on first pack install +- [ ] `custom_skills:` section added for one-off installs +- [ ] Each pack entry has: `name`, `status`, `installed_ref`, `disabled_items` + +### AFX-Built Skills + +- [ ] `skills/` directory exists in AFX repo +- [ ] At least two AFX-built skills exist: `afx-qa-methodology`, `afx-spec-test-planning` +- [ ] Each has valid SKILL.md with guardrails (e.g., @see tracing, two-stage verify references) +- [ ] Referenced correctly in `afx-pack-qa.yaml` manifest + +### Provider Copy Routing + +- [ ] Simple Skill (SKILL.md only) → `.claude/skills/` AND `.agents/skills/` AND `.agent/skills/` +- [ ] Claude Plugin (`.claude-plugin/`) → `.claude/plugins/` only +- [ ] OpenAI Skill (SKILL.md + `openai.yaml`) → `.agents/skills/` only +- [ ] AFX-built → pre-organized by provider dir, just `cp -r` each subdir into pack master +- [ ] No external skill is ever modified during any operation +- [ ] Platform gating: skills only routed to providers marked `true` or `partial` in manifest `platforms:` +- [ ] Name collision: installing a skill that exists in another pack fails with error (unless `--force`) + +### One-Off Skill Install + +- [ ] `--add-skill anthropics/antigravity-awesome-skills:skills/some-skill .` downloads and installs a single skill +- [ ] Skill type-detected and routed to provider dirs (no `.afx/packs/` master — direct to provider dirs) +- [ ] Tracked in `.afx.yaml` under `custom_skills:` with `repo` and `path` +- [ ] Custom skills are NOT updated by `--update --packs` + +--- + +## Constraints (Resolved in Research) + +These constraints are settled — they are not open for re-discussion. + +| Constraint | Detail | +| ----------------------------------------- | ------------------------------------------------------------------------------- | +| `install.sh` is the single driver | All operations go through `install.sh` — no separate CLI tool | +| `.afx/packs/{pack}/{provider}/` is master | External skills pristine, AFX-built skills have guardrails baked in | +| Provider dirs are derived copies | `.claude/`, `.agents/`, `.agent/`, `.github/` populated from `.afx/` master | +| Disable = delete provider copies | Master stays in `.afx/`. Re-enable = `cp -r` from master | +| Remove = delete everything | Both provider copies and `.afx/packs/{pack}/` | +| `.afx.yaml` tracks state | Pack list with status + per-item overrides | +| Pack prefix `afx-pack-*` | Avoids conflict with AFX core commands | +| External skills are never modified | Downloaded pristine, stored pristine, copied pristine | +| AFX-built skills have guardrails | Authored by AFX team, not auto-generated or templated | +| Minimal runtime dependencies | Only `curl`, `tar`, `bash` — no git, node, python, or package managers required | +| No authentication required | `raw.githubusercontent.com` for public repos | +| Use latest (`ref: main`) by default | `--branch` or `--version` overrides; `--version` auto-prefixes `v` | +| Best-effort cross-platform parity | Each pack declares `platforms:` support, no forced parity | + +--- + +## Non-Goals (Out of Scope) + +- Private or authenticated pack registries +- Auto-generated guardrails or template-based skill injection +- Converting external SKILL.md to Copilot `.agent.md` format +- Quality scoring or ranking of ecosystem skills (future task) +- GUI — pack management UI is in the [vscode-toolbox spec](../vscode-toolbox/spec.md) +- Commit SHA pinning (packs track `installed_ref` as branch/tag, not exact commit SHAs) +- Building a separate CLI tool — everything goes through `install.sh` + +--- + +## Open Questions + +| # | Question | Status | Resolution | +| --- | -------------------------------------------------------------- | -------- | ------------------------------------------------------------------------------------------------ | +| 1 | Download method: sparse checkout vs tarball? | Resolved | **Tarball**. Use `codeload.github.com` + `tar` extraction for speed and no git history overhead. | +| 2 | How to handle offline pack install (pre-downloaded)? | Open | — | +| 3 | Should `--add-skill` (one-off, no pack) track in `.afx.yaml`? | Resolved | **Yes**. Track in `custom_skills: []` list to ensure consistent baseline across team. | +| 4 | Quality scoring for 950 Antigravity skills? | Deferred | Address when building first pack manifests by manually reviewing | + +--- + +## Dependencies + +- **Upstream repos** — Skills must be available at the repos referenced in pack manifests (Antigravity, OpenAI, Claude Plugins) +- **`packs/index.json`** — Must be published before vscode-toolbox can consume it +- **AFX-built skills** — At least `afx-qa-methodology` and `afx-spec-test-planning` must be authored before `afx-pack-qa` is functional + +--- + +## Appendix + +### Pack Manifest Schema + +```yaml +# packs/afx-pack-qa.yaml +name: afx-pack-qa +description: QA Engineer role pack — testing, review, and quality assurance +category: role + +platforms: + claude: true + codex: true + antigravity: true + copilot: partial + +includes: + # External skills (pristine) + - repo: anthropics/antigravity-awesome-skills + path: skills/ + items: + - test-driven-development + - tdd-workflow + - playwright-skill + - e2e-testing-patterns + - unit-testing-test-generate + - systematic-debugging + - performance-testing-review + + - repo: openai/skills + path: skills/.curated/ + items: + - playwright + + - repo: anthropics/claude-code + path: plugins/ + items: + - code-review + - pr-review-toolkit + + # AFX-built skills (guardrails baked in) + - repo: rixrix/afx + path: skills/ + items: + - afx-qa-methodology + - afx-spec-test-planning +``` + +### Index Schema (`packs/index.json`) + +```jsonc +{ + "packs": { + "afx-pack-qa": { + "description": "QA Engineer role pack", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"], + }, + "afx-pack-security": { + "description": "Security review and audit pack", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"], + }, + }, + "upstream": { + "anthropics/claude-plugins-official": { + "featured": ["playwright-e2e", "security-scanner", "code-architect"], + }, + "openai/skills": {}, + "anthropics/antigravity-awesome-skills": { + "featured": ["code-architect"], + }, + }, +} +``` + +### `.afx/` Directory Structure (Master Copy) + +``` +.afx/ +├── .cache/ +│ └── lastIndex.json # cached index + timestamp +│ +└── packs/ + ├── afx-pack-qa/ # installed pack + │ ├── claude/ + │ │ ├── skills/ + │ │ │ ├── test-driven-development/ # PRISTINE from Antigravity + │ │ │ │ ├── SKILL.md + │ │ │ │ └── resources/ + │ │ │ ├── tdd-workflow/ # PRISTINE from Antigravity + │ │ │ │ └── SKILL.md + │ │ │ ├── ... # 5 more Antigravity skills + │ │ │ ├── afx-qa-methodology/ # AFX-BUILT + │ │ │ │ └── SKILL.md + │ │ │ └── afx-spec-test-planning/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── plugins/ + │ │ ├── code-review/ # PRISTINE from Claude Code + │ │ │ ├── .claude-plugin/plugin.json + │ │ │ ├── commands/review.md + │ │ │ └── hooks/hooks.json + │ │ └── pr-review-toolkit/ # PRISTINE from Claude Code + │ │ └── ... + │ ├── codex/ + │ │ └── skills/ + │ │ ├── test-driven-development/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── ... # 6 more Antigravity skills + │ │ ├── playwright/ # PRISTINE from OpenAI + │ │ │ ├── SKILL.md + │ │ │ ├── agents/openai.yaml + │ │ │ └── scripts/run-tests.py + │ │ ├── afx-qa-methodology/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── afx-spec-test-planning/ # AFX-BUILT + │ │ └── SKILL.md + │ ├── antigravity/ + │ │ └── skills/ + │ │ ├── test-driven-development/ # PRISTINE from Antigravity + │ │ │ └── SKILL.md + │ │ ├── ... # 6 more Antigravity skills + │ │ ├── afx-qa-methodology/ # AFX-BUILT + │ │ │ └── SKILL.md + │ │ └── afx-spec-test-planning/ # AFX-BUILT + │ │ └── SKILL.md + │ └── copilot/ + │ └── agents/ + │ ├── afx-qa-methodology.agent.md # AFX-BUILT (only AFX skills) + │ └── afx-spec-test-planning.agent.md # AFX-BUILT + │ + └── afx-pack-security/ # disabled pack (master preserved) + ├── claude/ + ├── codex/ + ├── antigravity/ + └── copilot/ +``` + +### `.afx.yaml` Pack State Schema + +```yaml +# .afx.yaml (committed — team-shared) +packs: + - name: afx-pack-qa + status: enabled + installed_ref: v1.5.3 + disabled_items: [] + + - name: afx-pack-security + status: disabled + installed_ref: main + disabled_items: [] + +custom_skills: + - repo: anthropics/antigravity-awesome-skills + path: skills/some-niche-skill +``` + +### install.sh CLI Reference + +```bash +# Core AFX install (unchanged — no packs) +./install.sh /path/to/project + +# Pack management +./install.sh --pack qa /path/to/project # install + enable +./install.sh --pack qa --pack security /path/to/project # multiple at once +./install.sh --pack-disable qa /path/to/project # disable (rm provider copies) +./install.sh --pack-enable qa /path/to/project # re-enable (cp -r from master) +./install.sh --pack-remove qa /path/to/project # delete entirely +./install.sh --pack-list /path/to/project # list packs + status + +# Single skill toggle (within a pack) +./install.sh --skill-disable tdd --pack qa /path/to/project +./install.sh --skill-enable tdd --pack qa /path/to/project + +# Update all enabled packs +./install.sh --update --packs /path/to/project + +# Preview changes +./install.sh --dry-run --pack qa /path/to/project + +# Install from a specific branch +./install.sh --branch dev --pack qa /path/to/project + +# Install from a specific version +./install.sh --version 1.5.3 --pack qa /path/to/project + +# One-off skill install (no pack) +./install.sh --add-skill anthropics/antigravity-awesome-skills:skills/some-skill /path/to/project +``` + +### Skill Type Detection Matrix + +| Detection Rule | Type | Target Providers | Modification | +| ---------------------------------------- | ------------- | ----------------------------- | ------------ | +| `SKILL.md` at root, no `.claude-plugin/` | Simple Skill | Claude + Codex + Antigravity | None | +| `.claude-plugin/plugin.json` exists | Claude Plugin | Claude only | None | +| `SKILL.md` + `agents/openai.yaml` | OpenAI Skill | Codex only | None | +| From `rixrix/afx` repo | AFX-built | All (pre-organized by provider dir) | N/A | + +### Glossary + +| Term | Definition | +| --------------- | ------------------------------------------------------------------------------------------- | +| Pack | A curated bundle of skills/plugins grouped by role or domain (e.g., `afx-pack-qa`) | +| Pack manifest | YAML file in `packs/` defining a pack's contents, repo sources, and platform support | +| Pack index | `packs/index.json` — aggregated metadata for all packs and upstream, consumed by vscode-afx | +| Master copy | The pristine downloaded content stored in `.afx/packs/{pack}/{provider}/` | +| Provider copy | The derived copy placed in `.claude/`, `.agents/`, `.agent/`, `.github/` for auto-discovery | +| External skill | A skill downloaded from an upstream repo — never modified by AFX | +| AFX-built skill | A skill authored by the AFX team with guardrails baked in | +| Guardrails | AFX methodology rules (e.g., @see tracing, two-stage verify) baked into AFX-built skills | +| Provider | An AI coding assistant platform: Claude Code, Codex CLI, Google Antigravity, GitHub Copilot | diff --git a/docs/specs/afx-packs/tasks.md b/docs/specs/afx-packs/tasks.md new file mode 100644 index 0000000..b970c3d --- /dev/null +++ b/docs/specs/afx-packs/tasks.md @@ -0,0 +1,367 @@ +--- +afx: true +type: TASKS +status: Living +owner: "@rix" +version: 1.0 +created: "2026-02-28" +last_verified: "2026-02-28" +tags: [packs, install, skills, ecosystem] +--- + +# AFX Pack System - Implementation Tasks + +**Version:** 1.0 +**Date:** 2026-02-28 +**Status:** Ready for Implementation +**Spec:** [spec.md](./spec.md) +**Design:** [design.md](./design.md) + +--- + +## Task Numbering Convention + +Tasks use hierarchical numbering for cross-referencing: + +- **1.x** - Phase 1: Manifests & Index (static files in AFX repo) +- **2.x** - Phase 2: AFX-Built Skills (guardrails skills in AFX repo) +- **3.x** - Phase 3: install.sh — Download & Detection (new functions) +- **4.x** - Phase 4: install.sh — State Management (lifecycle commands) + +References: + +- `[FR-N]` = Functional Requirement N from spec.md +- `[DESIGN-X.X]` = Section X.X from design.md + +--- + +## Phase 1: Manifests & Index + +> Ref: [DESIGN-1.1], [DESIGN-1.2], [FR-1] through [FR-12] + +### 1.1 Create Pack Directory & QA Manifest + +> File: `packs/afx-pack-qa.yaml` + +- [x] Create `packs/` directory in AFX repo root `[FR-1]` +- [x] Author `packs/afx-pack-qa.yaml` with full manifest schema `[FR-2] [FR-3]` +- [x] Define `platforms:` field — `claude: true`, `codex: true`, `antigravity: true`, `copilot: partial` `[FR-5]` +- [x] Add `includes[]` entries for Antigravity skills (7 items) `[FR-4]` +- [x] Add `includes[]` entry for OpenAI skills (playwright) `[FR-4]` +- [x] Add `includes[]` entry for Claude Code plugins (code-review, pr-review-toolkit) `[FR-4]` +- [x] Add `includes[]` entry for AFX-built skills (afx-qa-methodology, afx-spec-test-planning) `[FR-4] [FR-37]` +- [x] Verify manifest matches design Section 1.1 QA manifest schema `[FR-7]` + +### 1.2 Create Security Pack Manifest + +> File: `packs/afx-pack-security.yaml` + +- [x] Author `packs/afx-pack-security.yaml` with full manifest schema `[FR-2] [FR-3]` +- [x] Define `platforms:` — `claude: true`, `codex: true`, `antigravity: true`, `copilot: partial` `[FR-5]` +- [x] Add `includes[]` for Antigravity security skills (3 items) `[FR-4]` +- [x] Add `includes[]` for Claude Code security-scanner plugin `[FR-4]` +- [x] Add `includes[]` for AFX-built skills (afx-owasp-top-10, afx-security-audit) `[FR-4] [FR-37]` + +### 1.3 Create Pack Index + +> File: `packs/index.json` + +- [x] Create `packs/index.json` with `packs` and `upstream` sections `[FR-8]` +- [x] Add pack entries: description, category, providers array `[FR-9]` +- [x] Add upstream entries: provider repos with `featured[]` lists `[FR-10]` +- [ ] Verify index is fetchable via `raw.githubusercontent.com` (push and test URL) `[FR-11]` +- [x] Document manual update process (updated alongside manifest changes) `[FR-12]` + +--- + +## Phase 2: AFX-Built Skills + +> Ref: [DESIGN-4.1], [DESIGN-4.2], [FR-35] through [FR-38] + +### 2.1 Create Skills Directory Structure + +> Directory: `skills/` + +- [x] Create `skills/` directory in AFX repo root `[FR-35]` +- [x] Establish provider subdirectory convention per skill: `{skill}/claude/skills/{skill}/`, `{skill}/codex/skills/{skill}/`, `{skill}/antigravity/skills/{skill}/`, `{skill}/copilot/agents/` `[FR-36]` + +### 2.2 Author `afx-qa-methodology` + +> Directory: `skills/afx-qa-methodology/` + +- [x] Create Claude variant: `claude/skills/afx-qa-methodology/SKILL.md` `[FR-36]` +- [x] Create Codex variant: `codex/skills/afx-qa-methodology/SKILL.md` `[FR-36]` +- [x] Create Antigravity variant: `antigravity/skills/afx-qa-methodology/SKILL.md` `[FR-36]` +- [x] Create Copilot variant: `copilot/agents/afx-qa-methodology.agent.md` `[FR-36]` +- [x] Include guardrails: @see tracing, two-stage verification, spec-driven test strategy `[FR-38]` +- [x] Verify content matches design Section 4.1 `[DESIGN-4.1]` + +### 2.3 Author `afx-spec-test-planning` + +> Directory: `skills/afx-spec-test-planning/` + +- [x] Create Claude variant: `claude/skills/afx-spec-test-planning/SKILL.md` `[FR-36]` +- [x] Create Codex variant: `codex/skills/afx-spec-test-planning/SKILL.md` `[FR-36]` +- [x] Create Antigravity variant: `antigravity/skills/afx-spec-test-planning/SKILL.md` `[FR-36]` +- [x] Create Copilot variant: `copilot/agents/afx-spec-test-planning.agent.md` `[FR-36]` +- [x] Include guardrails: requirement-to-test mapping, gap detection, @see annotations `[FR-38]` +- [x] Verify content matches design Section 4.2 `[DESIGN-4.2]` + +### 2.4 Author `afx-owasp-top-10` + +> Directory: `skills/afx-owasp-top-10/` + +- [x] Create Claude variant: `claude/skills/afx-owasp-top-10/SKILL.md` `[FR-36]` +- [x] Create Codex variant: `codex/skills/afx-owasp-top-10/SKILL.md` `[FR-36]` +- [x] Create Antigravity variant: `antigravity/skills/afx-owasp-top-10/SKILL.md` `[FR-36]` +- [x] Create Copilot variant: `copilot/agents/afx-owasp-top-10.agent.md` `[FR-36]` +- [x] Include guardrails: OWASP top 10 checklist with @see tracing `[FR-38]` + +### 2.5 Author `afx-security-audit` + +> Directory: `skills/afx-security-audit/` + +- [x] Create Claude variant: `claude/skills/afx-security-audit/SKILL.md` `[FR-36]` +- [x] Create Codex variant: `codex/skills/afx-security-audit/SKILL.md` `[FR-36]` +- [x] Create Antigravity variant: `antigravity/skills/afx-security-audit/SKILL.md` `[FR-36]` +- [x] Create Copilot variant: `copilot/agents/afx-security-audit.agent.md` `[FR-36]` +- [x] Include guardrails: security audit workflow with @see tracing `[FR-38]` + +--- + +## Phase 3: install.sh — Download & Detection + +> Ref: [DESIGN-3.1] through [DESIGN-3.8], [FR-13], [FR-24a/b], [FR-34], [FR-25] through [FR-31], [FR-44], [FR-45] + +### 3.1 Argument Parsing + +> File: `install.sh` + +- [x] Add `--pack NAME` flag (repeatable for multiple packs) `[FR-13] [FR-14]` +- [x] Add `--pack-disable NAME` flag `[FR-15]` +- [x] Add `--pack-enable NAME` flag `[FR-16]` +- [x] Add `--pack-remove NAME` flag `[FR-17]` +- [x] Add `--pack-list` flag `[FR-18]` +- [x] Add `--skill-disable NAME --pack PACK` flag combination `[FR-19]` +- [x] Add `--skill-enable NAME --pack PACK` flag combination `[FR-20]` +- [x] Add `--update --packs` flag combination `[FR-21]` +- [x] Add `--add-skill REPO:PATH/SKILL` flag `[FR-23]` +- [x] Add `--branch NAME` flag (default: `main`) `[FR-24a]` +- [x] Add `--version TAG` flag (auto-prefix `v` if missing) `[FR-24b]` +- [x] Validate `--version` and `--branch` are mutually exclusive `[FR-24a] [FR-24b]` +- [x] Ensure `--dry-run` works with all pack flags `[FR-22]` +- [x] Verify existing flags still parse correctly `[NFR-1]` `[DESIGN-3.3]` + +### 3.2 Manifest Fetch + +> File: `install.sh` — `fetch_manifest()` function + +- [x] Implement `fetch_manifest()` — fetch YAML manifest from `raw.githubusercontent.com` `[DESIGN-3.4]` +- [x] Resolve ref: `--version` → `v{tag}`, `--branch` → `{branch}`, default → `main` +- [x] Auto-prefix `afx-pack-` if user provides short name (e.g., `qa` → `afx-pack-qa`) `[DESIGN-3.4]` +- [x] Parse YAML manifest with bash (field extraction) `[DESIGN-3.4]` + +### 3.3 Download Items + +> File: `install.sh` — `download_items()` function + +- [x] Implement `download_items()` — download via `codeload.github.com` tarballs `[NFR-2]` +- [x] Extract specific `path/items[]` from tarball using `tar --strip-components` `[DESIGN-3.5]` +- [x] Handle multiple repos per manifest (Antigravity, OpenAI, Claude Code, AFX) `[FR-4]` +- [x] Clean up temp directories on success and failure (trap handler) `[DESIGN-6]` +- [x] Verify only `curl`, `tar`, `bash` are used — no git, node, python `[NFR-5]` + +### 3.4 Skill Type Detection + +> File: `install.sh` — `detect_type()` function + +- [x] Implement `detect_type()` with detection rules `[DESIGN-3.6]` +- [x] Detect Simple Skill: `SKILL.md` at root, no `.claude-plugin/` → Claude + Codex + Antigravity `[FR-34]` +- [x] Detect Claude Plugin: `.claude-plugin/plugin.json` exists → Claude only `[FR-25]` +- [x] Detect OpenAI Skill: `SKILL.md` + `agents/openai.yaml` → Codex only `[FR-26]` +- [x] Detect AFX-built: from `rixrix/afx` repo → pre-organized by provider `[FR-42]` +- [x] Ensure external skills are never modified during detection `[FR-27]` + +### 3.5 Item Routing + +> File: `install.sh` — `route_item()` function + +- [x] Implement `route_item()` — route detected items to `.afx/packs/{pack}/{provider}/` `[DESIGN-3.7]` +- [x] Simple Skills → `claude/skills/{name}/` AND `codex/skills/{name}/` AND `antigravity/skills/{name}/` `[FR-39]` +- [x] Claude Plugins → `claude/plugins/{name}/` `[FR-40]` +- [x] OpenAI Skills → `codex/skills/{name}/` `[FR-41]` +- [x] AFX-built → copy pre-organized provider dirs as-is `[FR-42]` +- [x] Copilot receives only AFX-built skills `[FR-43]` +- [x] Gate routing on manifest `platforms:` field — skip providers marked `false` or missing `[FR-45]` +- [x] Create `.afx/packs/{pack}/{provider}/` directory structure `[FR-28] [FR-29]` + +### 3.6 Name Collision Detection + +> File: `install.sh` — `check_collision()` function + +- [x] Implement `check_collision()` — check if destination already owned by another pack `[FR-44]` +- [x] Scan provider directories for existing items before copy `[DESIGN-3.8]` +- [x] Fail with error message identifying the conflicting pack `[FR-44]` +- [x] Allow override with `--force` flag `[DESIGN-3.8]` + +### 3.7 Gitignore Management + +> File: `install.sh` — `ensure_gitignore()` function + +- [x] Implement `ensure_gitignore()` — add `.afx/` to `.gitignore` `[FR-31] [NFR-7]` +- [x] Only add if not already present `[NFR-6]` +- [x] Create `.gitignore` if it doesn't exist `[DESIGN-3.9]` + +--- + +## Phase 4: install.sh — State Management + +> Ref: [DESIGN-3.10] through [DESIGN-3.13], [FR-14] through [FR-23], [FR-32], [FR-33] + +### 4.1 YAML Read/Write Helpers + +> File: `install.sh` + +- [x] Implement `afx_yaml_get_pack()` — read pack status from `.afx.yaml` `[DESIGN-3.11]` +- [x] Implement `afx_yaml_set_pack()` — write/update pack entry with name, status, installed_ref `[DESIGN-3.11]` +- [x] Implement `afx_yaml_remove_pack()` — remove pack entry from `.afx.yaml` `[DESIGN-3.11]` +- [x] Implement `afx_yaml_get_disabled_items()` — read disabled items list `[DESIGN-3.11]` +- [x] Implement `afx_yaml_set_disabled_items()` — write disabled items list `[DESIGN-3.11]` +- [x] Handle missing `.afx.yaml` gracefully (create packs section on first install) `[FR-32]` + +### 4.2 Pack Install Flow + +> File: `install.sh` — `pack_install()` function + +- [x] Implement `pack_install()` orchestrating: fetch manifest → download → detect → route → copy → state `[FR-13]` +- [x] Support multiple packs in one command (`--pack qa --pack security`) `[FR-14]` +- [x] Record pack in `.afx.yaml` with `status: enabled`, `installed_ref`, `disabled_items: []` `[FR-32]` +- [x] Resolve `installed_ref` from `--version`, `--branch`, or default `main` `[FR-32]` +- [x] Create `.afx/.cache/` directory `[FR-31]` + +### 4.3 Provider Copy Management + +> File: `install.sh` — `pack_copy_to_providers()` / `pack_remove_from_providers()` functions + +- [x] Implement `pack_copy_to_providers()` — `cp -r` from `.afx/` master to provider dirs `[FR-16] [DESIGN-3.12]` +- [x] Copy to `.claude/skills/`, `.claude/plugins/`, `.agents/skills/`, `.github/agents/` as appropriate `[FR-39] [FR-40] [FR-41] [FR-43]` +- [x] Implement `pack_remove_from_providers()` — remove provider copies by pack `[FR-15] [DESIGN-3.12]` +- [x] Ensure enable/disable is instant — no network, no conversion `[NFR-3]` + +### 4.4 Pack Enable / Disable / Remove + +> File: `install.sh` + +- [x] Implement `pack_disable()` — remove provider copies, keep `.afx/` master, update status to `disabled` `[FR-15]` +- [x] Implement `pack_enable()` — copy from `.afx/` master to providers, update status to `enabled` `[FR-16]` +- [x] Implement `pack_remove()` — delete `.afx/packs/{pack}/` AND provider copies, remove from `.afx.yaml` `[FR-17]` +- [x] Skip disabled items during enable (respect `disabled_items[]`) `[FR-19]` + +### 4.5 Skill Disable / Enable + +> File: `install.sh` + +- [x] Implement `skill_disable()` — remove skill from provider dirs, add to `disabled_items` `[FR-19]` +- [x] Implement `skill_enable()` — restore from `.afx/` master, remove from `disabled_items` `[FR-20]` +- [x] Check for name collisions when re-enabling `[FR-44]` + +### 4.6 Pack List + +> File: `install.sh` — `pack_list()` function + +- [x] Implement `pack_list()` — read `.afx.yaml` and output packs with status `[FR-18]` +- [x] Show: name, status (enabled/disabled), installed ref, disabled item count + +### 4.7 Pack Update + +> File: `install.sh` — `pack_update_all()` function + +- [x] Implement `pack_update_all()` — re-download and replace items for all enabled packs `[FR-21]` +- [x] Preserve `disabled_items[]` across update `[FR-21]` +- [x] Update `installed_ref` in `.afx.yaml` after successful update +- [x] Skip disabled packs during update + +### 4.8 One-Off Skill Install + +> File: `install.sh` + +- [x] Parse `--add-skill REPO:PATH/SKILL` format `[FR-23]` +- [x] Download single skill via tarball extraction `[FR-23]` +- [x] Type-detect and route directly to provider dirs (no `.afx/packs/` master) `[FR-23]` +- [x] Track in `.afx.yaml` under `custom_skills:` with `repo` and `path` `[FR-33]` +- [x] Ensure `--update --packs` does NOT update custom skills `[FR-33]` + +### 4.9 Dry Run + +> File: `install.sh` + +- [x] Implement dry-run mode for all pack operations `[FR-22]` +- [x] Print what would be changed without writing any files `[FR-22]` +- [x] Support `--dry-run --pack`, `--dry-run --pack-disable`, etc. + +### 4.10 Main Dispatch & Help + +> File: `install.sh` + +- [x] Add main dispatch logic routing pack flags to functions `[DESIGN-3.13]` +- [x] Update `--help` output with pack management section `[DESIGN-5.3]` +- [x] Ensure `./install.sh .` (no pack flags) works exactly as before `[NFR-1]` +- [x] Ensure `./install.sh --update .` works exactly as before `[NFR-1]` +- [x] Ensure all existing flags (`--commands-only`, `--no-claude-md`, `--force`, etc.) continue to work `[NFR-1]` +- [x] Verify all pack operations are idempotent `[NFR-6]` +- [ ] Replace `git clone` fallback with `curl` + `tar` for remote execution `[DESIGN-7]` + +--- + +## Implementation Flow + +``` +Phase 1: Manifests & Index (static YAML/JSON files) + ↓ +Phase 2: AFX-Built Skills (SKILL.md / .agent.md files) + ↓ +Phase 3: install.sh — Download & Detection (new functions) + ↓ +Phase 4: install.sh — State Management (lifecycle commands) +``` + +--- + +## Cross-Reference Index + +| Task | Spec Requirements | Design Section | +| ---- | ---------------------------------- | -------------- | +| 1.1 | FR-1, FR-2, FR-3, FR-4, FR-5, FR-7 | 1.1 | +| 1.2 | FR-2, FR-3, FR-4, FR-5 | 1.1 | +| 1.3 | FR-8, FR-9, FR-10, FR-11, FR-12 | 1.2 | +| 2.1 | FR-35, FR-36, FR-37 | 4 | +| 2.2 | FR-36, FR-38 | 4.1 | +| 2.3 | FR-36, FR-38 | 4.2 | +| 2.4 | FR-36, FR-38 | 4 | +| 2.5 | FR-36, FR-38 | 4 | +| 3.1 | FR-13–FR-24b, NFR-1 | 3.3 | +| 3.2 | FR-13 | 3.4 | +| 3.3 | FR-4, NFR-2, NFR-5 | 3.5 | +| 3.4 | FR-34, FR-25, FR-26, FR-27 | 3.6 | +| 3.5 | FR-28, FR-29, FR-39–FR-43, FR-45 | 3.7 | +| 3.6 | FR-44 | 3.8 | +| 3.7 | FR-31, NFR-7 | 3.9 | +| 4.1 | FR-32 | 3.11 | +| 4.2 | FR-13, FR-14, FR-32 | 3.10 | +| 4.3 | FR-15, FR-16, FR-39–FR-43 | 3.12 | +| 4.4 | FR-15, FR-16, FR-17 | 3.10 | +| 4.5 | FR-19, FR-20, FR-44 | 3.10 | +| 4.6 | FR-18 | 3.10 | +| 4.7 | FR-21 | 3.10 | +| 4.8 | FR-23, FR-33 | 3.10 | +| 4.9 | FR-22 | 3.10 | +| 4.10 | NFR-1, NFR-6 | 3.13, 5 | + +--- + +## Notes + +- Phases 1 and 2 are independent and can be worked in parallel +- Phase 3 must complete before Phase 4 (state management depends on download + detection) +- Tasks within each phase are ordered by dependency — work top to bottom +- Backward compatibility verification (4.10) should be the final gate before merge diff --git a/docs/specs/vscode-extension/design.md b/docs/specs/vscode-extension/design.md new file mode 100644 index 0000000..81dd4d4 --- /dev/null +++ b/docs/specs/vscode-extension/design.md @@ -0,0 +1,926 @@ +--- +afx: true +type: DESIGN +status: Living +owner: "@rix" +version: 2.0 +created: "2026-02-26" +last_verified: "2026-03-01" +tags: [vscode-extension, product] +--- + +# AFX VSCode Extension -- Technical Design + +**Version:** 2.0 +**Spec:** [spec.md](./spec.md) + +--- + +## 1. Overview + +A read-only VSCode extension that parses `.afx.yaml` and spec documents to present a 5-view split-pane sidebar with project context, feature progress, a reference library, pack management (Toolbox), and help links. Built with TypeScript using the VSCode Extension API, bundled with esbuild, with `yaml` and `gray-matter` as runtime dependencies. + +The extension is a pure observer -- it never writes spec files. The only mutations are pack management operations, which delegate to `install.sh` via terminal. + +--- + +## 2. Architecture + +### 2.1 System Context + +```mermaid +graph TD + A[".afx.yaml"] --> B[Config Parser] + C["docs/specs/*/*.md"] --> D[Spec Parsers] + E["docs/adr/ADR-*.md"] --> F[ADR Parser] + G["library dirs"] --> H[Resources Provider] + I[".afx/packs/"] --> J[Pack Reader] + K["packs/index.json (remote)"] --> L[Index Service] + + B --> M[Feature Builder] + D --> M + + M --> N["Specs View (afx.specs)"] + M --> O["Project View (afx.project)"] + M --> P["Library View (afx.library)"] + J --> Q["Toolbox View (afx.toolbox)"] + L --> Q + + F --> P + H --> P + B --> O + + R[File Watchers] -->|change event| S[Refresh Providers] + S --> N + S --> O + S --> P + S --> Q + + T["Help View (afx.help)"] -.->|static| T +``` + +### 2.2 Component Diagram + +``` ++-------------------------------------------------------------------+ +| VSCode Extension Host | +| | +| Parsers Models | +| +-------------------+ +-------------------+ | +| | afxConfigParser |---->| AfxConfig | | +| | frontmatterParser | | ConfigEntry | | +| | taskParser |---->| SpecDocument | | +| | journalParser | | Section | | +| | sectionParser | | Phase | | +| | specDocumentParser| | Discussion | | +| +-------------------+ | Feature | | +| +-------------------+ | +| | | +| Sub-providers (6) v | +| +-------------------+ +-------------------+ | +| | specsTreeProvider |<---| Feature Builder | | +| | adrTreeProvider | +-------------------+ | +| | resourcesProvider | | +| | tagsTreeProvider | | +| | commandsProvider | | +| | folderTreeProvider | | +| +-------------------+ | +| | | +| Composite providers (3) + Standalone (2) | +| +-------------------+ +-------------------+ +---------------+ | +| | projectProvider | | libraryProvider | | toolboxProvider| | +| | (session,config, | | (adrs,resources, | | (overview, | | +| | folders,.afx/) | | tags) | | packs, | | +| +-------------------+ +-------------------+ | upstream, | | +| | skills) | | +| +-------------------+ +-------------------+ +---------------+ | +| | specsProvider | | helpProvider | | +| +-------------------+ +-------------------+ | +| | | | | +| v v v | +| +--------+--------+--------+--------+--------+ | +| |afx. |afx. |afx. |afx. |afx. | <- 5 TreeViews | +| |project |specs |library |toolbox |help | | +| +--------+--------+--------+--------+--------+ | +| | +| Cross-cutting | +| +-------------------+ +-------------------+ | +| | fileDecoProvider | | statusBar | | +| | fileWatcher | | toolboxWatchers | | +| +-------------------+ +-------------------+ | ++-------------------------------------------------------------------+ +``` + +Provider count breakdown: + +| Provider type | Count | Instances | +|---|---|---| +| Top-level view providers | 5 | project, specs, library, toolbox, help | +| Sub-providers (composed into views) | 6 | adr, resources, tags, commands, folder, fileDecoration | +| Total | 11 | | + +--- + +## 3. UI/UX -- Split-Pane Sidebar + +Registered as a custom `viewContainer` in the activity bar with five separate views. Each view has its own `TreeDataProvider` and is independently collapsible, resizable, and reorderable. + +```json +"viewContainers": { + "activitybar": [{ "id": "afx", "title": "AFX - AgenticFlowX", "icon": "resources/afx.svg" }] +}, +"views": { + "afx": [ + { "id": "afx.project", "name": "Project" }, + { "id": "afx.specs", "name": "Specs" }, + { "id": "afx.library", "name": "Library" }, + { "id": "afx.toolbox", "name": "Toolbox" }, + { "id": "afx.help", "name": "Help" } + ] +} +``` + +### 3.1 Project View (`afx.project`) + +The project-level dashboard. Shows session context, folder management, configuration, and local AFX data. Header displays the current folder name via `TreeView.description`. Header actions: Open Folder, Open Config, Refresh, Help. + +**Root nodes (4):** + +| Node | Icon | Collapsible | Description | +|---|---|---|---| +| Session Context | `hubot` | Yes (if exists) | afx-context.md with `## ` section children | +| My Projects | `folder-library` | Yes | Current folder + recent folders list | +| Config | `gear` | Yes | .afx.yaml entries + validation warnings | +| .afx/ | `folder-active` | Yes | Local data directory browser | + +**Session Context** -- Looks for `afx-context.md` in the specs directory. If found, shows `saved {timeAgo}` description from file mtime. Click opens markdown preview. Children are `## ` headings parsed from the file, each opening the file at that line. If not found, shows `none` with no children. + +**My Projects** -- Current folder shown with `root-folder-opened` icon and `(current)` description. Recent folders (up to 10) stored in `globalState` with `folder` icon. Click on current opens folder picker; click on recent switches to that folder via `loadFolder()`. + +**Config** -- Contains a single `.afx.yaml` node that expands to show: +- Flattened key-value entries (`version`, `paths.*`, `features`, `prefixes`, `library.*`, `quality_gates.*`, `verification.*`, `test_traceability.*`, `anchors.*`) with type-appropriate codicons +- Validation warnings (missing specs dir, missing ADR dir, duplicate features, missing feature dirs, missing spec.md) shown with `warning` icon in `editorWarning.foreground` color +- Each entry click opens `.afx.yaml` at the corresponding line + +**.afx/** -- Recursive directory browser for `.afx/` (packs, caches, config). Markdown files open in preview; other files open in editor. Hidden files (`.` prefix) are excluded. + +### 3.2 Specs View (`afx.specs`) + +The main work view. Header displays stats via `description` (e.g., `3 features · 29/98`). Header actions: Filter by Status, Search, Refresh. Supports `showCollapseAll`. + +**Feature discovery:** Merges `features[]` from `.afx.yaml` (preserves ordering) with auto-discovered directories in `paths.specs`. Phantom features (in config but no docs on disk) are filtered out. + +**Sorting:** Features sorted by status priority -- In Progress first, Complete last: + +| Priority | Status | +|---|---| +| 0 | In Progress | +| 1 | Draft | +| 2 | Approved | +| 3 | Living | +| 4 | Stable | +| 5 | Not Started | +| 6 | Complete | + +**Feature node:** +- Icon: `package` +- Description: `{status} · {completed}/{total}` +- Tooltip: Markdown table with status, tasks, owner, tags, discussion count +- `contextValue`: `feature` + +**Document children (4 per feature):** `spec.md`, `design.md`, `tasks.md`, `journal.md` + +| Doc type | Icon | Description (if present) | Children | +|---|---|---|---| +| spec | `book` | Frontmatter status | `## ` and `### ` sections | +| design | `symbol-structure` | Frontmatter status | `## ` and `### ` sections | +| tasks | `tasklist` | `{completed}/{total}` | Phase sub-items | +| journal | `notebook` | `{N} active` or `{N} discussions` | Discussion sub-items | + +Missing documents show `(missing)` description with `warning` icon and no children. + +**Phase sub-items** (under tasks.md): `Phase {N}: {name}` with `{completed}/{total}` description. Icon reflects status (Complete/In Progress/Not Started). Click opens tasks.md at the phase heading line. + +**Discussion sub-items** (under journal.md): `{id}` label with `{title} · {status}` description. Icons: `comment-discussion` (active), `warning` (blocked), `pass` (closed). Click opens journal.md at the discussion heading line. + +**Section sub-items** (under spec.md and design.md): Heading text as label. Icons: `symbol-class` (level 2) or `symbol-field` (level 3). Click opens file at the heading line. + +### 3.3 Library View (`afx.library`) + +Composite view combining ADRs, resource directories, and tags. Uses the composite provider pattern -- `libraryTreeProvider` delegates `getTreeItem` and `getChildren` to `adrTreeProvider`, `resourcesTreeProvider`, and `tagsTreeProvider` respectively, wrapping their elements in `lib-adr`, `lib-dir`, and `lib-tag` discriminated unions. Supports `showCollapseAll`. + +**Root sections:** + +| Section | Icon | Description | Source | +|---|---|---|---| +| ADRs | `law` | `architecture decisions` | `paths.adr` from .afx.yaml, globbed `ADR-*.md` | +| {library key} | context-aware | context-aware | `library:` block from .afx.yaml | +| Tags | `tag` | `from spec frontmatter` | Extracted from spec frontmatter `tags:` fields | + +**ADRs:** Each ADR shows `ADR-{NNNN}` label, `{title} · {status} · {owner}` description, status-specific icon. Expands to show `## ` section headings. Click opens preview. `contextValue`: `adr`. + +**Library directories:** Dynamically created from `library:` block in `.afx.yaml`. Each key becomes a collapsible section with a friendly label (capitalized key name) and context-aware icon: + +| Directory name | Icon | +|---|---| +| architecture | `symbol-structure` | +| research | `beaker` | +| docs | `book` | +| design | `symbol-structure` | +| guides | `book` | +| diagrams | `graph` | +| (other) | `folder-library` | + +Children are files and subdirectories from the mapped path, sorted alphabetically. Markdown files open in preview; other files open in editor. + +**Tags:** Extracted from `tags:` arrays in spec frontmatter across all features. Sorted by count (descending), then alphabetically. Each tag expands to show features that carry that tag, with status icon and `{completed}/{total}` description. + +### 3.4 Toolbox View (`afx.toolbox`) + +Pack management and skills browser. See [vscode-toolbox/design.md](../vscode-toolbox/design.md) for full details. Header action: Check for Pack Updates. Supports `showCollapseAll`. + +**4 collapsible sections:** + +| Section | Icon | Description | +|---|---|---| +| Overview | `dashboard` | Stats: providers, packs, updates, last checked | +| Packs | `package` | `installed packages` -- Installed + Available groups | +| Upstream | `cloud` | `remote sources` -- Remote pack registries | +| Skills | `tools` | `installed on disk` -- Disk mirror of provider directories | + +All pack mutations (install, remove, enable, disable, update) delegate to `install.sh` via `installShRunner` -- the extension never writes `.afx.yaml` or `.afx/packs/` directly. Index data is fetched from `packs/index.json` on GitHub, cached at `.afx/.cache/lastIndex.json`, with configurable auto-check interval (default: 24 hours). + +### 3.5 Help View (`afx.help`) + +Static list of links and commands. No refresh needed. + +| Item | Icon | Action | +|---|---|---| +| AFX Repository | `github` | Opens `https://github.com/rixrix/afx` | +| Documentation | `book` | Opens agenticflowx.md on GitHub | +| Cheatsheet | `note` | Opens cheatsheet.md on GitHub | +| Check for Updates | `sync` | Runs `afx.checkForUpdates` (opens releases page) | +| Report Issue | `bug` | Opens GitHub new issue form | +| Update from Latest | `cloud-download` | Runs `afx.updateAfx` (install.sh --update) | + +--- + +## 4. TreeItem Rendering + +All metadata is shown inline using VSCode's native `TreeItem` properties: + +| Property | Usage | +|---|---| +| `label` | Item name (feature name, file name, ADR ID, section title) | +| `description` | Right-aligned metadata (status, count, owner, time ago) | +| `iconPath` | Status/type icon via VSCode codicons (`ThemeIcon`) | +| `tooltip` | Full details on hover; supports `MarkdownString` for rich tooltips | +| `command` | Click action: preview, open at line, open editor, navigate | +| `contextValue` | Controls which inline/context menu items appear | +| `resourceUri` | File path for file decorations (status badges) | +| `collapsibleState` | `Collapsed` for items with children, `None` for leaves | + +--- + +## 5. Click Behavior + +| File type | Default click | Via hover/right-click | +|---|---|---| +| Markdown (`.md`) | Open in Markdown Preview (`markdown.showPreview`) | Edit (text editor), Preview to Side | +| Non-markdown | Open in text editor (`vscode.open`) | -- | +| Section/Phase/Discussion | Open parent file at specific line (`afx.openAtLine`) | Edit, Reveal in Explorer | +| Session context | Markdown preview | Edit | + +The `afx.openAtLine` command opens the file in text editor mode, scrolls to the target line, and places the cursor there. + +--- + +## 6. Hover & Context Menus + +### 6.1 Inline Hover Icons + +Shown on hover for items with file-backed `contextValue`: + +| Icon | Command | Shown when `contextValue` matches | +|---|---|---| +| `$(go-to-file)` | Reveal in Explorer | `document`, `adr`, `resourceFile`, `markdownFile`, `feature`, `skills-file`, `skills-dir` | +| `$(edit)` | Edit (open in text editor) | `document`, `adr`, `resourceFile`, `markdownFile`, `skills-file` | +| `$(preview)` | Open Preview | `document`, `adr`, `resourceFile`, `markdownFile`, `skills-file` | +| `$(open-preview)` | Open Preview to Side | `document`, `adr`, `resourceFile`, `markdownFile`, `skills-file` | + +### 6.2 Right-Click Context Menu Groups + +**Standard views (Project, Specs, Library):** + +| Group | Commands | Condition | +|---|---|---| +| `1_open` | Edit, Open Preview, Open Preview to Side | File-backed items | +| `2_nav` | Reveal in Explorer, Copy @see Reference, Open in Terminal, Remove from Recents | Varies by contextValue | +| `3_claude` | Claude Commands | Specs view only: `feature`, `document`, `markdownFile` | + +**Toolbox view -- pack items:** + +| Group | Commands | Condition | +|---|---|---| +| `inline` | Update Pack, Disable Pack, Remove Pack | `pack-enabled` | +| `inline` | Enable Pack | `pack-disabled` | +| `inline` | Install Pack | `pack-available` | +| `inline` | Disable Skill | `pack-item-enabled` | +| `inline` | Enable Skill | `pack-item-disabled` | +| `inline` | Refresh Upstream | `upstream-provider` | +| `1_pack` | Pack/skill enable/disable/install actions | Same as inline | +| `2_danger` | Remove Pack | `pack-enabled` or `pack-disabled` | + +**Toolbox view -- skills disk mirror:** + +| Group | Commands | Condition | +|---|---|---| +| `1_edit` | Reveal in Explorer | `skills-dir` | +| `2_nav` | Reveal in Explorer | `skills-file` | +| `3_edit` | Rename | `skills-dir`, `skills-file` | +| `4_danger` | Delete | `skills-dir`, `skills-file` | + +### 6.3 Claude Commands Quick-Pick + +Right-clicking a feature, document, or section in Specs view and selecting "Claude Commands" opens a context-aware quick-pick with relevant `/afx-*` slash commands. The command text is copied to clipboard on selection. + +The command set varies by file context: + +| File | Command categories | +|---|---| +| `spec.md` | Spec (show, status, phases, requirements), Analyze (coverage, validate), Review (discuss, review, approve) | +| `design.md` | Develop (code, refactor, review), Verify (path, links), Spec (discuss) | +| `tasks.md` | Tasks (list, progress, audit, summary), Workflow (next, coverage) | +| `journal.md` | View (show, recap, search), Write (save, note, promote) | +| (default) | Spec (show, status, discuss, review), Workflow (next, code, path) | + +--- + +## 7. Status Icons + +Using VSCode built-in codicons. Mapped to AFX framework statuses. + +| Category | Status | Codicon | ThemeIcon ID | +|---|---|---|---| +| **Computed** | Complete | `$(pass-filled)` | `pass-filled` | +| | In Progress | `$(wrench)` | `wrench` | +| | Not Started | `$(circle-outline)` | `circle-outline` | +| **Spec** | Draft | `$(edit)` | `edit` | +| | Approved | `$(verified-filled)` | `verified-filled` | +| | Living | `$(pulse)` | `pulse` | +| | Stable | `$(shield)` | `shield` | +| **ADR** | Proposed | `$(lightbulb)` | `lightbulb` | +| | Accepted | `$(verified-filled)` | `verified-filled` | +| | Rejected | `$(circle-slash)` | `circle-slash` | +| | Deprecated | `$(trash)` | `trash` | +| | Superseded | `$(history)` | `history` | +| **Doc type** | spec | `$(book)` | `book` | +| | design | `$(symbol-structure)` | `symbol-structure` | +| | tasks | `$(tasklist)` | `tasklist` | +| | journal | `$(notebook)` | `notebook` | + +--- + +## 8. Filter + +The Specs view header exposes a filter dropdown via a view action command. + +- Trigger: `afx.filterByStatus` command, registered as a view/title action on `afx.specs` +- UI: VSCode quick-pick with options: `All`, `Draft`, `In Progress`, `Approved`, `Living`, `Stable`, `Complete` +- Values are hardcoded (not derived from data) to keep UX predictable +- Filter applies to the Specs view only -- other views are unaffected +- Persisted via `ExtensionContext.workspaceState` key `afx.filterStatus` so it survives panel collapse but resets on window reload + +--- + +## 9. Commands + +32 commands registered in `package.json`: + +| Command | Title | Trigger | +|---|---|---| +| `afx.refresh` | Refresh | View header action (all views except Toolbox) | +| `afx.openConfig` | Open Config | View header action (Project view) | +| `afx.filterByStatus` | Filter by Status | View header action (Specs view) | +| `afx.openAtLine` | Open at Line | Internal: click on section/phase/discussion/config entry | +| `afx.openFolder` | Open Folder | View header action (Project view); quick-pick with recents + browse | +| `afx.switchFolder` | Switch Folder | Internal: click on recent folder item | +| `afx.preview` | Open Preview | Inline hover icon; right-click menu | +| `afx.previewToSide` | Open Preview to Side | Inline hover icon; right-click menu | +| `afx.showSpecs` | Show Specs | Status bar click target | +| `afx.copySeeReference` | Copy @see Reference | Right-click menu on documents, ADRs, phases, discussions | +| `afx.revealInExplorer` | Reveal in Explorer | Inline hover icon; right-click menu | +| `afx.openInTerminal` | Open in Terminal | Right-click menu on folders | +| `afx.search` | Search | View header action (Specs view); quick-pick across all entities | +| `afx.removeRecent` | Remove from Recents | Right-click menu on recent folder items | +| `afx.installAfx` | Install AFX | Welcome content button; command palette | +| `afx.openHelp` | Help | View header action (Project view); opens GitHub README | +| `afx.checkForUpdates` | Check for Updates | Help view item; opens GitHub releases page | +| `afx.updateAfx` | Update AFX | Help view item; runs install.sh --update | +| `afx.claudeCommands` | Claude Commands | Right-click on feature/document in Specs view | +| `afx.editFile` | Edit | Inline hover icon; right-click menu (opens in text editor) | +| `afx.toolbox.installPack` | Install Pack | Inline on available packs | +| `afx.toolbox.removePack` | Remove Pack | Inline on installed packs | +| `afx.toolbox.enablePack` | Enable Pack | Inline on disabled packs | +| `afx.toolbox.disablePack` | Disable Pack | Inline on enabled packs | +| `afx.toolbox.updatePack` | Update Pack | Inline on enabled packs | +| `afx.toolbox.disableSkill` | Disable Skill | Inline on enabled pack items | +| `afx.toolbox.enableSkill` | Enable Skill | Inline on disabled pack items | +| `afx.toolbox.checkIndex` | Check for Pack Updates | View header action (Toolbox view) | +| `afx.toolbox.refreshUpstream` | Refresh Upstream | Inline on upstream providers | +| `afx.toolbox.deleteSkill` | Delete | Right-click on skills dir/file | +| `afx.toolbox.renameSkill` | Rename | Right-click on skills dir/file | + +--- + +## 10. File Watchers + +All watchers debounced at 500ms via a shared timer per watcher group. A change during the debounce window resets the timer. + +**Core watchers** (created by `fileWatcher.ts`): + +| Pattern | Triggers | +|---|---| +| `.afx.yaml` | Full rebuild: re-parse config, refresh all providers | +| `{paths.specs}/**/*.md` | Specs + Library (tags) + file decorations refresh | +| `{paths.adr}/ADR-*.md` | Library (ADRs) refresh | +| `{library.*}/**/*` | Library (resources) refresh (one watcher per library directory) | + +**Toolbox watchers** (created by `toolboxWatchers.ts`): + +| Pattern | Triggers | +|---|---| +| `.afx/packs/**/*` | Toolbox (packs) refresh | +| `.claude/**/*` | Toolbox (skills) refresh | +| `.codex/**/*` | Toolbox (skills) refresh | +| `.agents/**/*` | Toolbox (skills) refresh | +| `.agent/**/*` | Toolbox (skills) refresh | +| `.gemini/**/*` | Toolbox (skills) refresh | +| `.github/**/*` | Toolbox (skills) refresh | + +**Pending-mode watcher:** When a folder is loaded without `.afx.yaml`, a watcher is created for `.afx.yaml` create/change events. Once detected, `loadFolder()` is re-invoked. A terminal close listener also retries after `AFX Install` terminal exits (1500ms delay). + +--- + +## 11. Feature Status Derivation + +Feature status is a hybrid of frontmatter and task completion. Task progress takes precedence once any tasks are completed: + +| Condition | Status | +|---|---| +| Tasks exist, all completed (N/N) | Complete | +| Tasks exist, some completed | In Progress | +| No tasks completed, frontmatter status is Draft/Approved/Living/Stable | Frontmatter value | +| No tasks, no recognized frontmatter status | Draft | + +Implementation: `deriveFeatureStatus()` in `src/models/feature.ts`. + +--- + +## 12. Parsing Logic + +| Entity | File | Regex Pattern | Example | +|---|---|---|---| +| Phase | `tasks.md` | `^##\s+Phase\s+(\d+):?\s+(.*)$` | `## Phase 1: Scaffolding` | +| Task | `tasks.md` | `^-\s+\[([ x])\]\s+(.*)$` | `- [x] Create repo` | +| Discussion | `journal.md` | `^###\s+([A-Z]{2,}-D\d+)\s+-\s+(.*)$` | `### VE-D001 - 2026-02-26 - Topic` | +| ADR | `ADR-*.md` | `^ADR-(\d+)-.+\.md$` (filename) + `^#\s+(.+)$` (title) | `ADR-0001-architecture.md` | +| Section | `spec.md`, `design.md` | `^(#{2,3})\s+(.+)$` | `## Architecture`, `### Data Flow` | +| Session section | `afx-context.md` | `^##\s+(.+)$` | `## Current Sprint` | +| Frontmatter | All `.md` | gray-matter library | `---\nstatus: Draft\n---` | + +The section parser (`sectionParser.ts`) extracts `##` (level 2) and `###` (level 3) headings, skipping YAML frontmatter blocks. Each section records title, level, and 0-based line number. + +--- + +## 13. Configuration Source (`.afx.yaml`) + +The extension reads `.afx.yaml` from the workspace root to discover the project structure: + +```yaml +version: "1.5" + +paths: + specs: "docs/specs" + adr: "docs/adr" + templates: "templates" + sessions: "docs/specs" + +features: + - vscode-extension + - vscode-toolbox + +prefixes: + vscode-extension: VE + vscode-toolbox: VT + +library: + architecture: "docs/architecture" + research: "docs/research" + +quality_gates: + require_path_check: true + require_human_approval: true + block_on_mock_code: true + +verification: + two_stage: true + stale_threshold_days: 30 +``` + +Mapping to views: + +``` +.afx.yaml Sidebar Views +------------- ------------- +Session context (afx-context.md) -> PROJECT view (Session Context) +Recent folders (globalState) -> PROJECT view (My Projects) +(all config keys) -> PROJECT view (Config) +.afx/ directory -> PROJECT view (.afx/) +paths.specs + features[] -> SPECS view + + auto-discovered dirs -> (merged, config order first) +paths.adr + ADR-*.md -> LIBRARY view (ADRs section) +library.* -> LIBRARY view (directory sections) +spec frontmatter tags -> LIBRARY view (Tags section) +.afx/packs/ + packs/index.json -> TOOLBOX view +.claude/, .codex/, etc. -> TOOLBOX view (Skills section) +(static links) -> HELP view +``` + +--- + +## 14. Key Decisions + +| Decision | Options Considered | Choice | Rationale | +|---|---|---|---| +| UI layout | Single TreeView, Split-pane, WebView | Split-pane (5 views) | GitLens pattern, native, each pane focused | +| View count | 3, 4, 5, 7 | 5 views | Consolidated from 7 using composite providers; balances discoverability with sidebar density | +| Composite providers | One provider per data source, one per view | Composite (Library wraps ADRs + Resources + Tags) | Reduces view count while keeping sub-providers reusable | +| Config format | JSON, YAML, TOML | YAML | Already used by `.afx.yaml` | +| Frontmatter parser | regex, gray-matter, custom | gray-matter | Battle-tested, handles edge cases | +| Activation event | onStartupFinished, workspaceContains | workspaceContains:.afx.yaml | Only loads in AFX projects | +| Tree refresh trigger | Polling, FileSystemWatcher | FileSystemWatcher | Event-driven, no CPU waste | +| Debounce interval | 200ms, 500ms, 1000ms | 500ms | Balances responsiveness and performance | +| Stats display | Status bar, TreeView.message, description | Both (TreeView.description + status bar) | Inline header stats + persistent status bar for global overview | +| Default click for markdown | Open editor, Open preview | Open preview | Read-first philosophy; edit via hover/right-click | +| Bundler | webpack, esbuild, unbundled | esbuild | Fast builds, simple config, single output file | +| Pack mutations | Direct file writes, CLI delegation | CLI delegation via install.sh | Single source of truth; extension stays read-only for pack state | +| Feature discovery | Config-only, disk-only, merged | Merged (config order + auto-discovered dirs) | Respects explicit ordering while catching new directories | + +--- + +## 15. File Structure + +``` +vscode-afx/ +├── src/ +│ ├── extension.ts # Entry point: 5 views, state management, loadFolder() +│ ├── statusBar.ts # Status bar item (feature count, task stats) +│ ├── config/ +│ │ └── afxConfigParser.ts # .afx.yaml reader, normalizer, flattenConfig() +│ ├── models/ +│ │ ├── afxConfig.ts # AfxConfig, ConfigEntry, ConfigElement interfaces +│ │ ├── specDocument.ts # SpecDocument, Section, Phase, Discussion, TaskStats +│ │ └── feature.ts # Feature interface, buildFeatures(), deriveFeatureStatus() +│ ├── parsers/ +│ │ ├── frontmatterParser.ts # YAML frontmatter extraction (gray-matter) +│ │ ├── taskParser.ts # Checkbox counting, phase extraction +│ │ ├── journalParser.ts # Discussion ID and status extraction +│ │ ├── sectionParser.ts # ## and ### heading extraction for spec/design +│ │ └── specDocumentParser.ts # Unified parser orchestrator +│ ├── providers/ +│ │ ├── projectTreeProvider.ts # Project view: session, folders, config, .afx/ +│ │ ├── specsTreeProvider.ts # Specs view: features, docs, phases, discussions +│ │ ├── libraryTreeProvider.ts # Library view: composite of ADRs + resources + tags +│ │ ├── adrTreeProvider.ts # ADR sub-provider (used by Library) +│ │ ├── resourcesTreeProvider.ts # Resources sub-provider (used by Library) +│ │ ├── tagsTreeProvider.ts # Tags sub-provider (used by Library) +│ │ ├── commandsTreeProvider.ts # Agent commands sub-provider (used internally) +│ │ ├── helpTreeProvider.ts # Help view: static links +│ │ ├── folderTreeProvider.ts # Recent folder management (globalState) +│ │ ├── fileDecorationProvider.ts # File decoration badges (IP, OK, DR, AP) +│ │ └── treeItems.ts # Shared icon maps, makeOpenCommand, makePreviewCommand +│ ├── commands/ +│ │ ├── commands.ts # 17 command handlers (refresh, open, filter, search, etc.) +│ │ ├── copySeeReference.ts # @see reference builder for clipboard +│ │ └── search.ts # Unified search quick-pick across all entities +│ ├── toolbox/ +│ │ ├── toolboxTreeProvider.ts # Toolbox view: overview, packs, upstream, skills +│ │ ├── toolboxCommands.ts # 11 toolbox command handlers + autoCheckIfDue() +│ │ ├── toolboxWatchers.ts # File watchers for .afx/packs/ and provider dirs +│ │ ├── models.ts # Pack, PackItem, AvailablePack, UpstreamProvider, etc. +│ │ ├── afxDirReader.ts # .afx/packs/ disk scanner, Pack[] assembly +│ │ ├── afxYamlReader.ts # .afx.yaml packs[] state reader +│ │ ├── indexService.ts # Remote index fetch, cache, diff computation +│ │ └── installShRunner.ts # CLI delegation: curl install.sh | bash +│ ├── watchers/ +│ │ └── fileWatcher.ts # Core file watchers with 500ms debounce +│ └── utils/ +│ └── logger.ts # Output channel wrapper (AFX output channel) +├── resources/ +│ └── afx.svg # Activity bar icon +├── package.json # Extension manifest (32 commands, 5 views, menus, settings) +└── tsconfig.json # TypeScript configuration +``` + +35 source files total. + +--- + +## 16. Dependencies + +### Runtime + +| Package | Version | Purpose | +|---|---|---| +| yaml | ^2.0.0 | Parse `.afx.yaml` | +| gray-matter | ^4.0.0 | Extract YAML frontmatter from markdown files | + +### Dev Dependencies + +| Package | Version | Purpose | +|---|---|---| +| @types/vscode | ^1.93.0 | VSCode Extension API type definitions | +| @types/node | ^20.0.0 | Node.js type definitions | +| typescript | ^5.0.0 | TypeScript compiler | +| esbuild | ^0.20.0 | Bundler (single-file CJS output with sourcemaps) | + +### Build + +```bash +esbuild ./src/extension.ts --bundle --outfile=dist/extension.js \ + --external:vscode --format=cjs --platform=node --sourcemap +``` + +--- + +## 17. Data Models + +### AfxConfig + +```typescript +interface AfxConfig { + version: string; + paths: { + specs: string; + adr: string; + templates: string; + sessions?: string; + }; + features: string[]; + prefixes: Record<string, string>; + library?: Record<string, string>; // key -> relative directory path + qualityGates: { + requirePathCheck: boolean; + requireHumanApproval: boolean; + blockOnMockCode: boolean; + }; + verification: { + twoStage: boolean; + staleThresholdDays: number; + }; + testTraceability?: { + enabled: boolean; + annotation: string; + }; + anchors?: { + taskFormat: string; + sectionFormat: string; + }; +} +``` + +### SpecDocument + +```typescript +interface SpecDocument { + path: string; + type: 'SPEC' | 'DESIGN' | 'TASKS' | 'JOURNAL'; + frontmatter: Frontmatter; + taskStats?: TaskStats; // TASKS only + discussions?: Discussion[]; // JOURNAL only + sections?: Section[]; // SPEC and DESIGN only +} + +interface Frontmatter { + afx?: boolean; + type?: string; + status?: string; + owner?: string; + tags?: string[]; + version?: string | number; +} + +interface Section { + title: string; + level: number; // 2 for ##, 3 for ### + line: number; // 0-based +} + +interface TaskStats { + total: number; + completed: number; + phases: Phase[]; +} + +interface Phase { + number: number; + name: string; + total: number; + completed: number; + line: number; +} + +interface Discussion { + id: string; // e.g., "VE-D001" + date: string; + title: string; + status: 'active' | 'blocked' | 'closed'; + line: number; +} +``` + +### Feature + +```typescript +type FeatureStatus = + | 'Not Started' | 'In Progress' | 'Complete' + | 'Draft' | 'Approved' | 'Living' | 'Stable'; + +interface Feature { + name: string; + prefix: string; + dirPath: string; + spec?: SpecDocument; + design?: SpecDocument; + tasks?: SpecDocument; + journal?: SpecDocument; + taskStats: TaskStats; + discussions: Discussion[]; + status: FeatureStatus; +} +``` + +### ConfigElement + +```typescript +type ConfigElement = + | { kind: 'entry'; entry: ConfigEntry } + | { kind: 'warning'; message: string; line: number }; + +interface ConfigEntry { + key: string; + value: string; + line: number; + icon: string; // codicon ID +} +``` + +### Toolbox Models + +See [vscode-toolbox/design.md](../vscode-toolbox/design.md) for Pack, PackItem, AvailablePack, UpstreamProvider, CachedIndex, and ToolboxElement definitions. + +--- + +## 18. File Decorations + +The `fileDecorationProvider` adds status badges and color tints to feature directories and their document files in the VSCode Explorer: + +| Status | Badge | Color | +|---|---|---| +| In Progress | `IP` | `charts.yellow` | +| Complete | `OK` | `charts.green` | +| Draft | `DR` | `disabledForeground` | +| Approved | `AP` | (no color) | + +Decorations apply to both the feature directory and individual document files (`spec.md`, `design.md`, `tasks.md`, `journal.md`). + +--- + +## 19. Error Handling + +| Scenario | Handling | +|---|---| +| `.afx.yaml` missing | Pending mode: show welcome views with Install/Open Folder buttons; watch for `.afx.yaml` to appear | +| `.afx.yaml` malformed | Log error, return undefined; views remain empty | +| Spec file missing for a feature | Show `(missing)` description with `warning` icon; no children | +| Frontmatter parse failure | Skip document, log warning to output channel | +| Feature directory does not exist | Filtered out as phantom feature (not shown in tree) | +| ADR directory missing | Config validation warning in Project view | +| Library directory missing | Empty section (readdir catch returns []) | +| Pack directory missing on disk | Pack shown from .afx.yaml state but with 0 providers/items | +| Index fetch fails (network) | Show cached data if available; offline notification; auto-retry on next interval | +| No cache, no network | Empty index; show toolbox welcome content | + +### Pending Mode + +When a workspace folder is opened without `.afx.yaml`: +1. `afx.loaded` context set to `false` +2. Welcome content shown in Specs and Toolbox views with Install/Open Folder buttons +3. File watcher created for `.afx.yaml` create/change events +4. Terminal close listener watches for `AFX Install` terminal exit +5. On detection, `loadFolder()` re-invoked automatically + +--- + +## 20. Extension Settings + +| Setting | Type | Default | Description | +|---|---|---|---| +| `afx.toolbox.autoCheck` | boolean | `true` | Automatically check for pack updates on activation | +| `afx.toolbox.autoCheckInterval` | number | `86400` | Minimum seconds between auto-checks (default: 24 hours) | + +--- + +## 21. Sidebar Mockup + +``` ++-------------------------------------------------------------------+ +| [AFX icon] AFX - AgenticFlowX | ++-------------------------------------------------------------------+ +| | +| V PROJECT my-project [folder] [gear] [R] [?]| +| ................................................................. | +| | +| [hubot] Session Context saved 2h ago | +| [key] Current Sprint | +| [key] Active Features | +| [key] Decisions Made | +| | +| > [folder-lib] My Projects recently opened | +| | +| > [gear] Config project configuration | +| | +| > [folder-active] .afx/ local data | +| | +| V SPECS 3 features . 29/98 [filter] [search] [R]| +| ................................................................. | +| | +| V [pkg] vscode-extension In Progress . 1/70 | +| [book] spec.md Draft | +| [cls] Overview | +| [cls] Requirements | +| [fld] Functional Requirements | +| [struct] design.md Draft | +| [task] tasks.md 1/70 | +| [wrench] Phase 1: Config 0/15 | +| [wrench] Phase 2: Models 0/7 | +| [circle] Phase 3: Sidebar 0/8 | +| [note] journal.md 1 active | +| [chat] VE-D001 Initial... . active | +| | +| > [pkg] global-adr Complete . 13/13 | +| > [pkg] afx-update Complete . 15/15 | +| | +| V LIBRARY [R] | +| ................................................................. | +| | +| V [law] ADRs architecture decisions | +| V [verified] ADR-0001 Global ADR Dir . Accepted | +| [key] Context | +| [key] Decision | +| [key] Consequences | +| > [verified] ADR-0002 VSCode Extension . Accepted | +| | +| V [beaker] Research notes & findings | +| [file] market-analysis.pdf | +| [file] competitor-pricing.csv | +| | +| V [tag] Tags from spec frontmatter | +| V [tag] vscode-extension 2 | +| [wrench] vscode-extension In Progress . 1/70 | +| [pass] vscode-toolbox Complete . 18/18 | +| > [tag] product 3 | +| | +| V TOOLBOX [check-idx] | +| ................................................................. | +| | +| > [dashboard] Overview | +| > [package] Packs installed packages | +| > [cloud] Upstream remote sources | +| > [tools] Skills installed on disk | +| | +| V HELP | +| ................................................................. | +| | +| [github] AFX Repository | +| [book] Documentation | +| [note] Cheatsheet | +| [sync] Check for Updates | +| [bug] Report Issue | +| [cloud] Update from Latest | +| | ++-------------------------------------------------------------------+ +``` + +--- + +## 22. Open Technical Questions + +| # | Question | Status | +|---|---|---| +| 1 | Should we use esbuild or webpack for bundling? | Resolved: esbuild | +| 2 | Should tree state (collapsed/expanded) persist? | Open | +| 3 | How to handle very large projects (100+ features)? | Open | +| 4 | Should search include full-text content search? | Open | +| 5 | Should file decorations be configurable (on/off)? | Open | diff --git a/docs/specs/vscode-extension/journal.md b/docs/specs/vscode-extension/journal.md new file mode 100644 index 0000000..d7d847e --- /dev/null +++ b/docs/specs/vscode-extension/journal.md @@ -0,0 +1,101 @@ +--- +afx: true +type: JOURNAL +status: Living +owner: "@rix" +tags: [vscode-extension, journal] +--- + +# Journal - AFX VSCode Extension + +<!-- prefix: VE --> + +> Quick captures and discussion history for AI-assisted development sessions. +> See [agenticflowx.md](../../agenticflowx/agenticflowx.md) for workflow. + +## Captures + +<!-- Quick notes during active chat - cleared when recorded --> + +--- + +## Discussions + +<!-- Recorded discussions with IDs: VE-D001, VE-D002, etc. --> +<!-- Chronological order: oldest first, newest last --> + +### VE-D001 - 2026-02-26 - Spec Promotion from Research + +`status:active` `[product, planning]` + +**Context**: ADR-0002 and the original afx-research.md document captured the product direction decision (VSCode extension over web app or CLI/TUI). This discussion tracks the promotion to a full AFX spec package. + +**Summary**: Research findings distilled into ADR-0002 (Accepted), then promoted to a full spec with spec.md, design.md, tasks.md, and journal.md. MVP scope covers sidebar tree view, status bar, file watchers, and core commands. + +**Decisions**: + +- VSCode Extension is the primary product direction (per ADR-0002) +- MVP scope: tree view + status bar + file watchers + commands (7 phases) +- Tech stack: TypeScript, VSCode Extension API, yaml, gray-matter +- Activation: `workspaceContains:.afx.yaml` only +- WebView dashboard and GitHub sync deferred to post-MVP + +**Notes**: + +- **[VE-D001.N1]** **[2026-02-26]** Promoted from `docs/adr/afx-research.md` and `docs/adr/ADR-0002-product-direction-vscode-extension.md` `[product, adr]` + +**Related Files**: docs/adr/afx-research.md, docs/adr/ADR-0002-product-direction-vscode-extension.md +**Participants**: @rix + +### VE-D002 - 2026-02-26 - Full Implementation Sprint (v0.1.0) + +`status:active` `[implementation, sprint]` + +**Context**: First implementation session. All phases (0-5) completed in a single sprint. Typecheck clean, esbuild passes in 24ms. Targeting manual user testing before unit tests. + +**Summary**: Built the entire `vscode-afx` extension from empty directory to working build. 22 source files across 7 modules: config parser, 4 parsers (frontmatter, task, journal, specDocument), 3 model interfaces, 4 tree providers (config, context, ADR, features), file watchers with 500ms debounce, 4 commands with filter persistence. + +**Decisions**: + +- Functional style: all tree providers implemented as factory functions returning objects (no classes) +- `afx.collapseAll` replaced with built-in `showCollapseAll: true` on TreeView — standard VSCode pattern +- `afx.openAtLine` custom command added for scroll-to-heading on phase/discussion click +- Feature documents parsed in parallel via `Promise.all` for performance +- esbuild chosen over webpack — 24ms builds, 407KB bundle +- Unit tests deferred to post-v0.1.0 — user will test manually first +- ESLint deferred — not blocking for initial build +- Tree state persistence (6.2.1) deferred — open design question + +**Architecture notes**: + +- Mutable config ref pattern: `state.config` updated on `.afx.yaml` change, all providers read via `getConfig()` closure +- Stats callbacks: providers call `onStats(description)` after computing children; extension.ts sets `TreeView.description` +- Discriminated unions for tree elements: each provider uses tagged `kind` field for type-safe hierarchy +- Line tracking in parsers: `Phase.line` and `Discussion.line` captured during parsing for scroll-to-heading + +**Notes**: + +- **[VE-D002.N1]** **[2026-02-26]** All 22 source files written, `tsc --noEmit` clean, esbuild bundle succeeds `[implementation]` +- **[VE-D002.N2]** **[2026-02-26]** Design gap confirmed: `contextTreeProvider.ts` missing from design.md file structure. Added in implementation. `[design-gap]` +- **[VE-D002.N3]** **[2026-02-26]** Bug fix: `viewContainers` → `viewsContainers` (missing "s") in package.json. Views were falling back to Explorer. Also fixed SVG icon from stroke to fill format. `[bugfix]` + +**Related Files**: vscode-afx/src/extension.ts, vscode-afx/package.json, docs/specs/vscode-extension/tasks.md +**Participants**: @rix, claude + +--- + +## Work Sessions + +<!-- Task execution log - updated by /afx:work next, /afx:dev code --> + +| Date | Task | Action | Files Modified | Agent | Human | +| ---------- | ------------------ | ------------------- | -------------- | ------ | ----- | +| 2026-02-26 | 0.1–0.4 Scaffolding | Implemented | 7 files | [OK] | | +| 2026-02-26 | 1.1–1.5 Parsers | Implemented | 5 files | [OK] | | +| 2026-02-26 | 2.1–2.2 Models | Implemented | 3 files | [OK] | | +| 2026-02-26 | 3.1–3.7 Tree Views | Implemented | 5 files | [OK] | | +| 2026-02-26 | 4.1 File Watchers | Implemented | 1 file | [OK] | | +| 2026-02-26 | 5.1–5.2 Commands | Implemented | 1 file | [OK] | | +| 2026-02-26 | 6.1–6.2, 6.5 Polish | Implemented | in providers | [OK] | | + +--- diff --git a/docs/specs/vscode-extension/spec.md b/docs/specs/vscode-extension/spec.md new file mode 100644 index 0000000..27d34eb --- /dev/null +++ b/docs/specs/vscode-extension/spec.md @@ -0,0 +1,210 @@ +--- +afx: true +type: SPEC +status: Living +owner: "@rix" +priority: High +version: 2.0 +created: "2026-02-26" +last_verified: "2026-03-01" +tags: [vscode-extension, product] +--- + +# AFX VSCode Extension - Product Specification + +**Version:** 2.0 +**Date:** 2026-03-01 +**Status:** Living +**Author:** Richard Sentino + +## References + +- **ADR**: [ADR-0002 - Product Direction: VSCode Extension](../../adr/ADR-0002-product-direction-vscode-extension.md) +- **Research**: [AFX Research](../../research/afx-research.md) + +--- + +## Problem Statement + +AFX specs live as raw markdown files scattered across `docs/specs/`, `docs/adr/`, and feature directories. There's no unified view of what exists, what status things are in, or what's actively being worked on. You have to open individual files to see frontmatter metadata like status, owner, or priority. + +This makes it hard for anyone — PM, architect, CTO, dev, or AI orchestrator — to get a quick read on project state. + +--- + +## User Stories + +### Primary Users + +Anyone working on an AFX project: developers, product managers, architects, CTOs, and AI orchestrators. + +### Stories + +**As a** project stakeholder +**I want** a sidebar showing all feature specs and ADRs grouped by category with their frontmatter metadata +**So that** I can see the full project landscape at a glance + +**As a** developer +**I want** to filter the tree by status (Draft, In Progress, Approved) +**So that** I can focus on what's active without visual noise + +**As a** product manager +**I want** to see task completion counts and in-progress work inline in the tree +**So that** I know where things stand without opening each file + +**As a** user +**I want** to click any item to open it in VSCode's preview mode +**So that** I can read specs quickly and edit them with the standard editor when needed + +**As a** developer +**I want** to browse, install, enable/disable, and update AFX packs from a Toolbox view +**So that** I can manage my project's skills and agent capabilities without leaving the editor + +--- + +## Requirements + +| ID | Requirement | Priority | +| ----- | ------------------------------------------------------------------------------------ | --------- | +| FR-1 | Read `.afx.yaml` to discover features, ADR paths, and spec paths | Must Have | +| FR-2 | Parse YAML frontmatter from AFX-compliant spec documents (type, status, owner, tags) | Must Have | +| FR-3 | Split-pane sidebar with 5 views: Project, Specs, Library, Toolbox, Help | Must Have | +| FR-4 | Show frontmatter metadata inline (status badge, owner) on each tree item | Must Have | +| FR-5 | Show task completion counts inline for features (e.g., "5/12") | Must Have | +| FR-6 | Filter by composite status (Draft, In Progress, Approved, Living, Stable, Complete) | Must Have | +| FR-7 | Click any item to open the file in VSCode preview mode | Must Have | +| FR-8 | Auto-refresh tree on file changes (debounced at 500ms) | Must Have | +| FR-9 | Read-only — no write operations, no custom editors | Must Have | +| FR-10 | Discover and display non-AFX context files (PDF, CSV, etc.) from `.afx.yaml` | Must Have | +| FR-11 | Show project summary stats in the panel header (features, tasks, ADRs) | Must Have | +| FR-12 | Project view — session context with current folder, recent folders, config summary, `.afx/` browser | Must Have | +| FR-13 | Library view — composite of ADRs, library directories, and tags from frontmatter | Must Have | +| FR-14 | Toolbox view — pack management with overview, installed/available packs, upstream tracking, skills disk mirror | Must Have | +| FR-15 | Help view — repository links, documentation, update commands, issue reporting | Must Have | +| FR-16 | Copy `@see` reference command — clipboard copy of traceability link for any spec item | Must Have | +| FR-17 | Search command — unified search across features, phases, discussions, ADRs | Must Have | +| FR-18 | Folder management — open folder picker, recent folders list, switch between projects | Must Have | +| FR-19 | Install/Update AFX — one-click `install.sh` bootstrap and update via terminal | Must Have | +| FR-20 | Status bar — feature/task stats display, clickable to focus Specs view | Must Have | +| FR-21 | File decoration provider — badge decorations by feature status | Must Have | +| FR-22 | Markdown preview — open preview, preview to side, and edit commands on all spec files | Must Have | +| FR-23 | Config validation — inline warnings for missing directories, duplicate features, missing spec files | Must Have | +| NFR-1 | Only activates in projects with `.afx.yaml` | Must Have | +| NFR-2 | Tree renders in < 500ms for a typical project (≤ 20 features) | Must Have | +| NFR-3 | VSCode 1.93+ compatibility | Must Have | + +--- + +## Acceptance Criteria + +### Tree View + +- [ ] Custom viewContainer in the activity bar with AFX icon +- [ ] Five separate views (split panes): Project, Specs, Library, Toolbox, Help +- [ ] Each pane is independently collapsible, resizable, and reorderable +- [ ] Specs pane: expandable features with spec.md, design.md, tasks.md, journal.md children +- [ ] Each item shows frontmatter metadata inline: status badge, owner +- [ ] Features show task completion count (e.g., "5/12") +- [ ] Active/in-progress items are visually distinct +- [ ] Specs pane header shows summary stats via `description` (e.g., "3 features · 29/98") + +### Project View + +- [ ] Current folder display with reveal-in-explorer and open-in-terminal actions +- [ ] Recent folders list with switch and remove actions +- [ ] Config summary: expandable `.afx.yaml` showing entries and inline validation warnings +- [ ] `.afx/` directory browser for local AFX configuration files + +### Library View + +- [ ] ADRs section: lists all Architecture Decision Records with frontmatter metadata +- [ ] Library directories: browsable tree of directories declared in `.afx.yaml` `library:` +- [ ] Tags section: aggregated tags from all spec frontmatter, grouped by tag + +### Toolbox View + +- [ ] Overview section: pack count and status summary +- [ ] Installed packs: list with enable/disable/update/remove actions per pack +- [ ] Available packs: fetched from upstream index with one-click install +- [ ] Upstream tracking: refresh upstream provider to check for new packs +- [ ] Skills disk mirror: browse, rename, and delete skill files on disk + +### Help View + +- [ ] Repository links (GitHub README, releases) +- [ ] Documentation links +- [ ] Update commands (check for updates, update AFX) +- [ ] Issue reporting link + +### Status Bar and Decorations + +- [ ] Status bar item showing feature/task stats (e.g., "AFX: 3 features · 29/98") +- [ ] Status bar click focuses the Specs view +- [ ] File decoration provider adds badges by feature status + +### Config Validation + +- [ ] Warns when configured `paths.specs` directory is missing +- [ ] Warns when configured `paths.adr` directory is missing +- [ ] Warns on duplicate feature names +- [ ] Warns when a feature directory is missing +- [ ] Warns when a feature is missing `spec.md` + +### Filtering + +- [ ] Filter by status (Draft, In Progress, Approved, etc.) +- [ ] Filter persists within the session + +### Navigation + +- [ ] Single-click opens file in VSCode preview mode +- [ ] Clicking a phase or discussion opens the parent file scrolled to that heading +- [ ] Double-click opens in edit mode (standard VSCode behavior) +- [ ] Preview command opens markdown preview +- [ ] Preview to Side command opens markdown preview in a split pane +- [ ] Edit command opens file in text editor +- [ ] Reveal in Explorer command shows file in the VSCode file explorer +- [ ] Open in Terminal command opens terminal at folder path +- [ ] Copy `@see` Reference command copies traceability link to clipboard +- [ ] Search command provides unified search across features, phases, discussions, and ADRs +- [ ] Open Folder command launches folder picker to load a new AFX project +- [ ] Switch Folder command switches between recent folders + +### Refresh + +- [ ] Tree refreshes when any spec or config file changes on disk +- [ ] Debounced at 500ms +- [ ] Toolbox watchers refresh packs and skills on disk changes + +### Install and Update + +- [ ] Install AFX command runs `install.sh` in a terminal for the workspace folder +- [ ] Update AFX command runs `install.sh --update` using version from `.afx.yaml` +- [ ] Welcome content in views shows Install AFX and Open Folder buttons when no config loaded +- [ ] File watcher detects `.afx.yaml` creation after install and auto-loads the project + +--- + +## Non-Goals (Out of Scope) + +- Custom editors or forms — editing is VSCode's native markdown editor +- Write operations — this is a read-only viewer +- AI agent execution +- WebView dashboards, charts, or visualizations +- GitHub integration +- Real-time collaboration + +--- + +## Open Questions + +| # | Question | Status | Resolution | +| --- | ------------------------------------------------ | -------- | ---------------------------------------------------------------- | +| 1 | Separate repo or subdirectory in this repo? | Resolved | Separate repo: `vscode-afx` (follows `vscode-{name}` convention) | +| 2 | Marketplace publish or VSIX distribution for v1? | Open | Currently VSIX only; marketplace publish TBD | + +--- + +## Appendix + +See [design.md](./design.md) for architecture, mockups, and technical details. diff --git a/docs/specs/vscode-extension/tasks.md b/docs/specs/vscode-extension/tasks.md new file mode 100644 index 0000000..edc524b --- /dev/null +++ b/docs/specs/vscode-extension/tasks.md @@ -0,0 +1,466 @@ +--- +afx: true +type: TASKS +status: Living +owner: "@rix" +version: 1.0 +created: "2026-02-26" +last_verified: "2026-02-26" +tags: [vscode-extension, product] +--- + +# AFX VSCode Extension - Implementation Tasks + +**Version:** 1.0 +**Date:** 2026-02-26 +**Status:** Draft +**Spec:** [spec.md](./spec.md) +**Design:** [design.md](./design.md) + +--- + +## Task Numbering Convention + +- **0.x** - Project scaffolding +- **1.x** - Config and parsers +- **2.x** - Models and feature builder +- **3.x** - Sidebar tree views +- **4.x** - File watchers +- **5.x** - Commands and filter +- **6.x** - Polish and packaging +- **7.x** - Spec and design alignment (reconcile docs with evolved architecture) + +References: + +- `[FR-N]` = Functional Requirement N from spec.md +- `[NFR-N]` = Non-Functional Requirement N from spec.md +- `[AC-X]` = Acceptance Criteria section X from spec.md +- `[DESIGN-X]` = Section X from design.md + +--- + +## Phase 0: Project Scaffolding + +> Ref: [NFR-1], [NFR-3], [DESIGN-File Structure] + +### 0.1 Repository Setup + +> File: `package.json`, `tsconfig.json` + +- [x] Initialize `vscode-afx` repo with `package.json` (name, publisher, engines `^1.85.0`, activation event `workspaceContains:.afx.yaml`) +- [x] Configure `tsconfig.json` (strict mode, ES2022 target, Node module resolution) +- [ ] Add ESLint config for TypeScript +- [x] Add `.vscodeignore` and `.gitignore` + +### 0.2 Extension Manifest + +> File: `package.json` + +- [x] Register `viewContainers.activitybar` with id `afx`, title `AFX`, icon `resources/afx.svg` [FR-3] +- [x] Register four views under `afx`: `afx.config`, `afx.context`, `afx.adrs`, `afx.features` [FR-3] +- [x] Register commands: `afx.refresh`, `afx.openConfig`, `afx.filterByStatus`, `afx.collapseAll` [DESIGN-Commands] +- [x] Add `when` clauses and view/title menu bindings for commands [DESIGN-Commands] + +### 0.3 Extension Entry Point + +> File: `src/extension.ts` + +- [x] Create `activate()` skeleton — log activation to output channel, return disposables +- [x] Create `deactivate()` cleanup function +- [x] Create activity bar icon `resources/afx.svg` [AC-Tree View] + +### 0.4 Logger Utility + +> File: `src/utils/logger.ts` + +- [x] Implement output channel wrapper ("AFX" output channel) with `info`, `warn`, `error` methods [DESIGN-Error Handling] + +--- + +## Phase 1: Config and Parsers + +> Ref: [FR-1], [FR-2], [FR-10], [DESIGN-Configuration Source], [DESIGN-Parsing Logic] + +### 1.1 Config Parser + +> File: `src/config/afxConfigParser.ts` + +- [x] Read `.afx.yaml` from workspace root using `yaml` package [FR-1] +- [x] Normalize snake_case YAML keys to camelCase TypeScript fields [DESIGN-Data Model] +- [x] Validate required fields: `version`, `paths.specs`, `paths.adr`, `features` [DESIGN-Configuration Source] +- [x] Return typed `AfxConfig` object (match interface from design.md) [DESIGN-Data Model] +- [x] Handle missing `.afx.yaml` — return `undefined` (caller shows welcome view) [DESIGN-Error Handling] +- [x] Handle malformed YAML — show error notification, return `undefined` [DESIGN-Error Handling] + +### 1.2 Frontmatter Parser + +> File: `src/parsers/frontmatterParser.ts` + +- [x] Extract YAML frontmatter using `gray-matter` package [FR-2] +- [x] Parse `afx`, `type`, `status`, `owner`, `tags`, `version` fields [FR-2] +- [x] Handle missing or malformed frontmatter — log warning, return defaults [DESIGN-Error Handling] + +### 1.3 Task Parser + +> File: `src/parsers/taskParser.ts` + +- [x] Extract phases from `## Phase N: Name` headings [DESIGN-Parsing Logic] +- [x] Count checkboxes per phase: `- [ ]` (incomplete) and `- [x]` (complete) [FR-5] +- [x] Return `TaskStats` with total, completed, and per-phase breakdowns [DESIGN-Data Model] +- [x] Handle empty or missing `tasks.md` — return zero counts [DESIGN-Error Handling] + +### 1.4 Journal Parser + +> File: `src/parsers/journalParser.ts` + +- [x] Extract discussions matching `### PREFIX-DNNN - date - title` pattern [DESIGN-Parsing Logic] +- [x] Parse discussion status: active, blocked, closed [DESIGN-Data Model] +- [x] Handle empty or missing `journal.md` — return empty array [DESIGN-Error Handling] + +### 1.5 Spec Document Parser + +> File: `src/parsers/specDocumentParser.ts` + +- [x] Orchestrate frontmatter + type-specific parsing (task parser for TASKS, journal parser for JOURNAL) [DESIGN-Parsing Logic] +- [x] Return unified `SpecDocument` model for any spec file type [DESIGN-Data Model] + +### 1.6 Unit Tests: Parsers + +> File: `src/test/parsers/` + +- [ ] Config parser: valid YAML, missing fields, malformed YAML [DESIGN-Testing Strategy] +- [ ] Frontmatter parser: valid, missing, malformed frontmatter [DESIGN-Testing Strategy] +- [ ] Task parser: checkbox counting, phase extraction, empty files [DESIGN-Testing Strategy] +- [ ] Journal parser: discussion ID extraction, status parsing [DESIGN-Testing Strategy] + +--- + +## Phase 2: Models and Feature Builder + +> Ref: [FR-4], [FR-5], [FR-11], [DESIGN-Data Model], [DESIGN-Feature Status Derivation] + +### 2.1 TypeScript Interfaces + +> File: `src/models/afxConfig.ts`, `src/models/specDocument.ts`, `src/models/feature.ts` + +- [x] Define `AfxConfig` interface (paths, features, prefixes, context, qualityGates, verification, etc.) [DESIGN-Data Model] +- [x] Define `SpecDocument`, `TaskStats`, `Phase`, `Discussion` interfaces [DESIGN-Data Model] +- [x] Define `Feature` interface with derived status type union [DESIGN-Data Model] + +### 2.2 Feature Builder + +> File: `src/models/feature.ts` + +- [x] Scan feature directory for spec.md, design.md, tasks.md, journal.md [FR-1] +- [x] Parse each found document via `specDocumentParser` [FR-2] +- [x] Aggregate task stats and discussions into `Feature` model [FR-5] +- [x] Derive feature status from task progress (Not Started / In Progress / Complete) or frontmatter (Draft / Approved / Living / Stable) [DESIGN-Feature Status Derivation] +- [x] Compute project-level summary stats (total features, total tasks completed/total) [FR-11] + +### 2.3 Unit Tests: Feature Builder + +> File: `src/test/models/` + +- [ ] Feature builder: aggregation from parsed documents, status derivation logic [DESIGN-Testing Strategy] + +--- + +## Phase 3: Sidebar Tree Views + +> Ref: [FR-3], [FR-4], [FR-5], [FR-7], [FR-10], [FR-11], [AC-Tree View], [AC-Navigation], [DESIGN-Split-Pane Sidebar], [DESIGN-TreeItem Rendering] + +### 3.1 TreeItem Subclasses + +> File: `src/providers/treeItems.ts` +> Note: Implemented as pure factory functions instead of class subclasses (per functional style constraint). + +- [x] `ConfigItem` — label (key), description (value) [DESIGN-TreeItem Rendering] +- [x] `ContextCategoryItem` — collapsible root for each context key [DESIGN-Context View] +- [x] `ContextFileItem` — individual file with click-to-open [DESIGN-Context View] +- [x] `ADRItem` — ADR number + title, status icon, owner description [DESIGN-TreeItem Rendering] +- [x] `FeatureItem` — name, status icon, task count description, owner [DESIGN-TreeItem Rendering] +- [x] `DocumentItem` — file name, status badge description [DESIGN-TreeItem Rendering] +- [x] `PhaseItem` — phase name, completion count description [DESIGN-TreeItem Rendering] +- [x] `DiscussionItem` — discussion ID, title, status badge [DESIGN-TreeItem Rendering] + +### 3.2 Status Icon Mapping + +> File: `src/providers/treeItems.ts` + +- [x] Map computed statuses to codicons: Complete → `$(check-all)`, In Progress → `$(tools)`, Not Started → `$(circle-outline)` [DESIGN-Status Icons] +- [x] Map spec statuses: Draft → `$(edit)`, Approved → `$(verified)`, Living → `$(pulse)`, Stable → `$(shield)` [DESIGN-Status Icons] +- [x] Map ADR statuses: Proposed → `$(light-bulb)`, Accepted → `$(check)`, Rejected → `$(circle-slash)`, Deprecated → `$(trash)`, Superseded → `$(history)` [DESIGN-Status Icons] + +### 3.3 Config View + +> File: `src/providers/configTreeProvider.ts` + +- [x] Implement `TreeDataProvider` returning flat key-value `ConfigItem` list [DESIGN-Config View] +- [x] Flatten dot-notation keys (e.g., `paths.specs`), arrays as comma-joined, booleans as `true`/`false` [DESIGN-Config View] +- [x] Filter to visible keys only (exclude `ai_attribution`, `require_see_links`, `scan_for_orphans`, `templates`) [DESIGN-Config View] +- [x] Skip optional/commented-out keys — only show keys with actual values [DESIGN-Config View] + +### 3.4 Context View + +> File: `src/providers/contextTreeProvider.ts` + +- [x] Implement `TreeDataProvider` for context categories from `.afx.yaml` `context` block [FR-10], [DESIGN-Context View] +- [x] Root nodes: each context key (e.g., `research`, `architecture`) as collapsible category [DESIGN-Context View] +- [x] Children: glob `*.*` from each context directory [DESIGN-Context View] +- [x] Click opens file in VSCode's default editor for that file type [DESIGN-Context View] + +### 3.5 ADR View + +> File: `src/providers/adrTreeProvider.ts` + +- [x] Implement `TreeDataProvider` returning ADR items globbed from `{paths.adr}/ADR-*.md` [DESIGN-ADR View] +- [x] Extract ADR number from filename `ADR-NNNN-*.md`, title from first `# ` heading or frontmatter [DESIGN-ADR View] +- [x] Show status badge and owner via `description` [FR-4] +- [x] Set view `description` to record count (e.g., "2 records") [DESIGN-ADR View] + +### 3.6 Features View + +> File: `src/providers/featuresTreeProvider.ts` + +- [x] Implement `TreeDataProvider` with feature root nodes from config `features[]` [DESIGN-Features View] +- [x] Feature nodes: name + status icon + task count + owner via `description` [FR-4], [FR-5] +- [x] Document children: spec.md, design.md, tasks.md, journal.md with status badges [AC-Tree View] +- [x] Tasks node: expand to show `PhaseItem` children with per-phase completion counts [AC-Tree View] +- [x] Journal node: expand to show `DiscussionItem` children with status badges [AC-Tree View] +- [x] Set view `description` to summary stats (e.g., "3 features · 29/98") [FR-11] +- [x] Handle missing spec files — show greyed-out node with "(missing)" label [DESIGN-Error Handling] + +### 3.7 Click-to-Open Navigation + +- [x] Single-click opens file in VSCode preview mode (`vscode.commands.executeCommand('markdown.showPreview')` or `showTextDocument` with preview option) [FR-7], [AC-Navigation] +- [x] Clicking a phase or discussion opens the parent file scrolled to that heading [AC-Navigation] + +### 3.8 Integration Tests: Tree Views + +> File: `src/test/providers/` + +- [ ] Full tree generation from test fixtures [DESIGN-Testing Strategy] +- [ ] Split-pane view registration and rendering [DESIGN-Testing Strategy] + +--- + +## Phase 4: File Watchers + +> Ref: [FR-8], [AC-Refresh], [DESIGN-File Watcher Patterns] + +### 4.1 File Watcher Setup + +> File: `src/watchers/fileWatcher.ts` + +- [x] Create `FileSystemWatcher` patterns derived from `AfxConfig.paths` [DESIGN-File Watcher Patterns] +- [x] Implement shared 500ms debounce timer — reset on each event [FR-8], [AC-Refresh] +- [x] `.afx.yaml` change triggers full rebuild (all views) [DESIGN-File Watcher Patterns] +- [x] `{paths.specs}/**/*.md` changes trigger Features view refresh [DESIGN-File Watcher Patterns] +- [x] `{paths.adr}/ADR-*.md` changes trigger ADR view refresh [DESIGN-File Watcher Patterns] +- [x] `{context.*}/**/*` changes trigger Context view refresh [DESIGN-File Watcher Patterns] + +### 4.2 Integration Test: Watchers + +> File: `src/test/watchers/` + +- [ ] File watcher trigger and tree refresh verification [DESIGN-Testing Strategy] + +--- + +## Phase 5: Commands and Filter + +> Ref: [FR-6], [DESIGN-Commands], [DESIGN-Filter] + +### 5.1 Command Registration + +> File: `src/commands/commands.ts`, `src/extension.ts` + +- [x] `afx.refresh` — refresh all tree data providers [DESIGN-Commands] +- [x] `afx.openConfig` — open `.afx.yaml` in editor [DESIGN-Commands] +- [x] `afx.collapseAll` — collapse all feature nodes [DESIGN-Commands] (via built-in `showCollapseAll: true`) +- [x] Register all commands as disposables in `activate()` [DESIGN-Commands] + +### 5.2 Status Filter + +> File: `src/commands/commands.ts`, `src/providers/featuresTreeProvider.ts` + +- [x] `afx.filterByStatus` — show quick-pick with options: All, Draft, In Progress, Approved, Living, Stable, Complete [FR-6], [DESIGN-Filter] +- [x] Persist selected filter in `ExtensionContext.workspaceState` [DESIGN-Filter] +- [x] Apply filter to Features view only — Config, Context, and ADRs unaffected [FR-6], [AC-Filtering] + +--- + +## Phase 6: Polish and Packaging + +> Ref: [NFR-2], [FR-9], [DESIGN-Error Handling], [DESIGN-Testing Strategy] + +### 6.1 Error Handling and Edge Cases + +- [x] Welcome view when no `.afx.yaml` found: "No .afx.yaml found" message [DESIGN-Error Handling] +- [x] Malformed `.afx.yaml` — error notification, fallback to empty views [DESIGN-Error Handling] +- [x] Feature directory doesn't exist — greyed-out node, don't crash [DESIGN-Error Handling] +- [x] Frontmatter parse failure — skip document, log warning [DESIGN-Error Handling] + +### 6.2 Tooltip Content + +- [x] Add `tooltip` markup for all tree items (full details on hover) [DESIGN-TreeItem Rendering] + +### 6.2.1 Tree State Persistence + +- [ ] Investigate and implement persistence for tree view expansion state (e.g., using `TreeView.onDidCollapseElement` / `onDidExpandElement` and `workspaceState`) [DESIGN-Open Technical Questions] + +### 6.3 Performance Verification + +- [ ] Verify tree renders in < 500ms for a typical project (≤ 20 features) [NFR-2] + +### 6.4 Test Fixtures and Test Suite + +> File: `src/test/` + +- [ ] Create minimal AFX project fixture: `.afx.yaml`, two features with spec files, two ADRs [DESIGN-Testing Strategy] +- [ ] Unit tests: config parser — valid YAML, missing fields, malformed YAML +- [ ] Unit tests: frontmatter parser — valid, missing, malformed frontmatter +- [ ] Unit tests: task parser — checkbox counting, phase extraction, empty files +- [ ] Unit tests: journal parser — discussion ID extraction, status parsing +- [ ] Unit tests: feature builder — aggregation from parsed documents, status derivation logic +- [ ] Integration tests: full tree generation from test fixtures +- [ ] Integration tests: file watcher trigger and tree refresh verification + +### 6.5 Packaging and Distribution + +- [x] README.md for extension repository +- [ ] CHANGELOG.md +- [x] Configure bundler (esbuild or webpack) and `.vscodeignore` [DESIGN-Open Technical Questions] +- [x] VSIX packaging scripts (`vsce package`) + +--- + +## Phase 7: Spec and Design Alignment + +> The implementation evolved significantly beyond the original 4-view architecture (spec.md, design.md). +> This phase reconciles the spec artifacts with the actual 5-view, 11-provider, 17-command codebase. + +### 7.1 Update spec.md — Requirements + +> File: `docs/specs/vscode-extension/spec.md` + +- [x] Update FR-3: Split-pane sidebar now has 5 views (Project, Specs, Library, Toolbox, Help) not 4 +- [x] Add FR-12: Project view — current folder, session context, config summary, recent folders +- [x] Add FR-13: Library view — composite of ADRs, library directories, and tags +- [x] Add FR-14: Toolbox view — pack management with overview, installed/available packs, upstream, skills +- [x] Add FR-15: Help view — repository links, update commands, issue reporting +- [x] Add FR-16: Copy @see reference command — clipboard copy of traceability link +- [x] Add FR-17: Search command — cross-feature search across specs, phases, discussions, ADRs +- [x] Add FR-18: Folder management — open folder picker, recent folders, switch folder +- [x] Add FR-19: Install/Update AFX — one-click install.sh bootstrap and update +- [x] Add FR-20: Status bar — spec stats display with feature/task counts +- [x] Add FR-21: File decoration provider — badge decorations for spec status +- [x] Add FR-22: Session context — display afx-context.md with expandable sections, preview mode +- [x] Add FR-23: Config validation — inline warnings for missing dirs, duplicate features +- [x] Update acceptance criteria to match current functionality +- [x] Update non-goals list + +### 7.2 Update design.md — Architecture + +> File: `docs/specs/vscode-extension/design.md` + +- [x] Update Overview to describe 5-view architecture +- [x] Update component diagram: 5 views, 11 providers, composite pattern +- [x] Replace 4-view sidebar section with 5-view layout: + - Project: current folder + session context + config + .afx/ browser + recents + - Specs: features with spec docs, phases, discussions, sections (was "Features") + - Library: composite of ADRs + library dirs + tags (was "Context" + "ADRs") + - Toolbox: overview + packs + upstream + skills (was "Skills") + - Help: static links (repo, docs, updates, issues) +- [x] Update file structure diagram to match actual src/ layout (36 files) +- [x] Update commands table: 32 commands (was 4) +- [x] Update data model interfaces: add `ConfigElement`, `Section`, `SpecsStatsData` +- [x] Document composite provider pattern (libraryTreeProvider delegates to 3 sub-providers) +- [x] Document folder management system (current/recent/switch with `globalState`) +- [x] Document status bar integration (`SpecsStatsData`) +- [x] Document file decoration provider +- [x] Update sidebar mockups to match current 5-view layout +- [x] Update file watcher patterns to include library dirs, .afx/packs/, provider dirs +- [x] Mark open technical questions as resolved (esbuild chosen, collapse via built-in API) + +### 7.3 Update tasks.md — Cross-Reference Index + +> File: `docs/specs/vscode-extension/tasks.md` + +- [x] Update cross-reference index with new FR/NFR numbers from 7.1 +- [x] Add rows for Phase 7 tasks +- [x] Remove stale task references (1.6, 2.3, 3.8, 4.2 consolidated into 6.4) +- [x] Update notes section + +--- + +## Implementation Flow + +``` +Phase 0: Project Scaffolding + ↓ +Phase 1: Config and Parsers + ↓ +Phase 2: Models and Feature Builder + ↓ +Phase 3: Sidebar Tree Views + ↓ +Phase 4: File Watchers + ↓ +Phase 5: Commands and Filter + ↓ +Phase 6: Polish and Packaging + ↓ +Phase 7: Spec and Design Alignment +``` + +--- + +## Cross-Reference Index + +| Task | Spec Requirement | Design Section | +| ----- | ------------------------ | ------------------------------------- | +| 0.1 | NFR-1, NFR-3 | File Structure | +| 0.2 | FR-3 | Split-Pane Sidebar, Commands | +| 0.3 | — | File Structure | +| 0.4 | — | Error Handling | +| 1.1 | FR-1 | Configuration Source, Data Model | +| 1.2 | FR-2 | Parsing Logic | +| 1.3 | FR-5 | Parsing Logic, Data Model | +| 1.4 | — | Parsing Logic, Data Model | +| 1.5 | FR-2 | Parsing Logic, Data Model | +| 2.1 | — | Data Model | +| 2.2 | FR-4, FR-5, FR-11 | Feature Status Derivation, Data Model | +| 3.1–2 | FR-4 | TreeItem Rendering, Status Icons | +| 3.3 | FR-1 | Config View | +| 3.4 | FR-10 | Context View | +| 3.5 | FR-4 | ADR View | +| 3.6 | FR-3, FR-4, FR-5, FR-11 | Features View | +| 3.7 | FR-7 | Navigation (AC) | +| 4.1 | FR-8 | File Watcher Patterns | +| 5.1 | — | Commands | +| 5.2 | FR-6 | Filter | +| 6.1 | FR-9 | Error Handling | +| 6.2 | — | TreeItem Rendering | +| 6.3 | NFR-2 | — | +| 6.4 | — | Testing Strategy | +| 6.5 | — | Open Technical Questions | +| 7.1 | FR-3, FR-12–FR-23 | — | +| 7.2 | — | All sections | +| 7.3 | — | — | + +--- + +## Notes + +- Tasks are marked complete (`[x]`) as implementation progresses +- Read-only constraint [FR-9] applies globally: no write operations at any phase +- All parsers must degrade gracefully — never crash, always log and continue +- TreeItem subclasses implemented as pure factory functions per functional style constraint +- `afx.collapseAll` implemented via built-in `showCollapseAll: true` on TreeView (standard VSCode pattern) +- The implementation evolved significantly beyond the original 4-view spec during VE-D002 sprint: + - 4 views → 5 views (Config+Context merged into Project and Library) + - 4 commands → 17 commands (folder mgmt, search, copy @see, install, etc.) + - Added: status bar, file decorations, tags, session context, config validation +- Phase 7 captures the work needed to align spec/design docs with current codebase +- Unit/integration tests (originally 1.6, 2.3, 3.8, 4.2) consolidated into Phase 6.4 diff --git a/docs/specs/vscode-toolbox/design.md b/docs/specs/vscode-toolbox/design.md new file mode 100644 index 0000000..57c02e9 --- /dev/null +++ b/docs/specs/vscode-toolbox/design.md @@ -0,0 +1,945 @@ +--- +afx: true +type: DESIGN +status: Living +owner: "@rix" +version: 2.0 +created: "2026-02-28" +last_verified: "2026-03-01" +tags: [vscode-extension, packs, skills, toolbox] +--- + +# VSCode AFX Toolbox - Technical Design + +**Version:** 2.0 +**Date:** 2026-03-01 +**Status:** Living +**Author:** Richard Sentino +**Spec:** [spec.md](./spec.md) + +--- + +## Overview + +The Toolbox replaces the current flat **Skills** view in `vscode-afx` with a 4-section tree view for managing AFX packs and skills across providers. It reads state from `.afx.yaml` and `.afx/packs/`, fetches the pack index from `raw.githubusercontent.com`, and delegates all mutations to `install.sh` — the extension never writes files directly. + +Built as an additive change to the existing `vscode-afx` extension (same repo, same activation, shared config parser and utilities). + +--- + +## References + +- **Spec**: [spec.md](./spec.md) +- **Research**: [res-vscode-pack-management.md](../../research/res-vscode-pack-management.md) +- **Research**: [res-skills-ecosystem-index.md](../../research/res-skills-ecosystem-index.md) +- **Pack System Design**: [afx-packs/design.md](../afx-packs/design.md) — `install.sh` CLI commands, manifest format, directory structure +- **Existing Extension Design**: [vscode-extension/design.md](../vscode-extension/design.md) — Current 5-view architecture + +--- + +## 1. Architecture + +### 1.1 System Context + +The Toolbox is a read-only observer that delegates mutations. It reads from 3 local sources and 1 remote source, presenting a unified tree view. All write operations go through `install.sh`. + +```mermaid +graph TD + A[".afx.yaml"] -->|pack state| B[Toolbox Provider] + C[".afx/packs/"] -->|installed items| B + D[".afx/.cache/lastIndex.json"] -->|cached index| B + E["raw.githubusercontent.com"] -->|fresh index| F[Index Service] + F -->|diff| B + B --> G["Toolbox Tree View (4 sections)"] + G -->|user action| H[install.sh Runner] + H -->|spawn| I["install.sh (child process)"] + I -->|mutates| A + I -->|mutates| C + J[File Watchers] -->|change event| B +``` + +### 1.2 Component Diagram + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ VSCode Extension Host (vscode-afx) │ +│ │ +│ Existing (unchanged) New (Toolbox) │ +│ ┌─────────────────────┐ ┌──────────────────────────┐ │ +│ │ Project View │ │ Toolbox Tree Provider │ │ +│ │ Specs View │ │ ├── Overview Section │ │ +│ │ Library View │ │ ├── Packs Section │ │ +│ │ Help View │ │ ├── Upstream Section │ │ +│ └─────────────────────┘ │ └── Skills Section │ │ +│ └──────────┬───────────────┘ │ +│ │ │ +│ ┌────────────┐ ┌──────────┐ ┌──────────┼──────────────┐ │ +│ │ Config │ │ File │ │ │ │ │ +│ │ Parser │ │ Watchers │ │ ┌───────┴──────┐ │ │ +│ │ (shared) │ │ (shared) │ │ │ Index Service│ │ │ +│ └────────────┘ └──────────┘ │ └──────────────┘ │ │ +│ │ ┌─────────────────┐ │ │ +│ │ │ install.sh │ │ │ +│ │ │ Runner │ │ │ +│ │ └─────────────────┘ │ │ +│ └─────────────────────────┘ │ +└─────────────────────────────────────────────────────────────────┘ +``` + +### 1.3 Integration with Existing Extension + +The Toolbox is the `afx.toolbox` tree view, registered alongside the other 4 views (Project, Specs, Library, Help) in the AFX activity bar container. It shares the config parser, file watchers, and `install.sh` runner with the rest of the extension. + +| Component | Role | +| --------------------- | -------------------------------------------------------------------------------------------------- | +| `extension.ts` | Creates `toolboxTreeProvider`, registers toolbox commands, sets up toolbox watchers | +| `package.json` | Declares `afx.toolbox` view, 11 toolbox commands, inline/context menus, 2 settings | +| `afxConfigParser.ts` | Parses core `.afx.yaml` fields; does NOT parse `packs:` — `afxYamlReader` handles pack state | +| `toolboxWatchers.ts` | Watches `.afx/packs/` and provider directories for changes | +| `AfxConfig` interface | Core config only — pack data uses separate `Pack[]` model read by `afxDirReader`/`afxYamlReader` | + +### 1.4 Read-Only + CLI Delegation + +The extension NEVER writes files. All state mutations flow through `install.sh`: + +``` +User clicks "Install" on available pack + → extension spawns: install.sh --pack afx-pack-qa . + → install.sh downloads, extracts, copies to providers + → file watcher detects .afx.yaml change + → tree refreshes automatically +``` + +--- + +## 2. Data Models + +### 2.1 Core Interfaces + +```typescript +/** + * A pack as read from .afx.yaml + .afx/packs/ directory + * @see docs/specs/vscode-toolbox/spec.md#FR-4 + */ +interface Pack { + name: string; // e.g., "afx-pack-qa" + status: "enabled" | "disabled"; // from .afx.yaml + installedRef: string; // git ref used at install (e.g., "v1.5.3", "main") + disabledItems: string[]; // per-item overrides from .afx.yaml + providers: ProviderDir[]; // from .afx/packs/{pack}/{provider}/ + itemCount: number; // total items across all providers +} + +/** + * A provider directory within an installed pack + * @see docs/specs/vscode-toolbox/spec.md#FR-5 + */ +interface ProviderDir { + provider: ProviderType; // "claude" | "codex" | "antigravity" | "copilot" + items: PackItem[]; // skills/plugins/agents in this provider dir + dirPath: string; // absolute path to .afx/packs/{pack}/{provider}/ +} + +type ProviderType = "claude" | "codex" | "antigravity" | "copilot"; + +/** + * A single skill, plugin, or agent within a pack's provider directory + * @see docs/specs/vscode-toolbox/spec.md#FR-5 + */ +interface PackItem { + name: string; // e.g., "test-driven-development" + itemType: "skill" | "plugin" | "agent"; // determined by directory convention + isExternal: boolean; // true = pristine from upstream, false = AFX-built + isDisabled: boolean; // true if in pack's disabled_items list + filePath: string; // path to SKILL.md, plugin.json, or agent.md +} + +/** + * A pack from the remote index that is NOT installed locally + * @see docs/specs/vscode-toolbox/spec.md#FR-6 + */ +interface AvailablePack { + name: string; + description: string; + category: string; // "role" | "domain" + providers: ProviderType[]; +} + +/** + * An upstream provider tracked in the index + * @see docs/specs/vscode-toolbox/spec.md#FR-11 + * @see docs/specs/afx-packs/design.md#1.2-pack-index + */ +interface UpstreamProvider { + repo: string; // e.g., "anthropics/claude-plugins-official" + featured: string[]; // highlighted items (from index; empty array if not present) + lastFetched?: string; // ISO timestamp from cached index + newSinceLastCheck: string[]; // computed from index diff +} + +/** + * Cached index stored at .afx/.cache/lastIndex.json + * Mirrors the structure of packs/index.json from the AFX repo. + * @see docs/specs/vscode-toolbox/spec.md#FR-16 + * @see docs/specs/afx-packs/design.md#1.2-pack-index + */ +interface CachedIndex { + fetchedAt: string; // ISO timestamp (added by extension, not in upstream index.json) + packs: Record<string, { description: string; category: string; providers: string[] }>; + upstream: Record<string, { featured?: string[] }>; // featured is optional — some repos have empty {} +} +``` + +### 2.2 Tree Element Union + +The `ToolboxTreeDataProvider` uses a discriminated union for all tree elements: + +```typescript +type ToolboxElement = + // Section headers + | { kind: "section"; id: "overview" | "packs" | "upstream" | "skills" } + // Overview items + | { kind: "overview-stat"; label: string; value: string; icon: string } + // Packs + | { kind: "pack-group"; id: "installed" | "available" } + | { kind: "pack"; pack: Pack } + | { kind: "pack-provider"; pack: Pack; provider: ProviderDir } + | { kind: "pack-item"; pack: Pack; item: PackItem } + | { kind: "available-pack"; entry: AvailablePack } + // Upstream + | { kind: "upstream-provider"; provider: UpstreamProvider } + | { kind: "upstream-item"; repo: string; item: string } + // Skills (disk mirror) + | { kind: "skills-provider"; name: string; dirPath: string } + | { kind: "skills-dir"; name: string; dirPath: string } + | { kind: "skills-file"; name: string; filePath: string }; +``` + +--- + +## 3. Data Flow + +### 3.1 Data Sources + +| Source | Type | Read By | Provides | +| ------------------------------- | ---------- | -------------------- | --------------------------------------------------------------------------------- | +| `.afx.yaml` `packs:` section | Local file | `afxDirReader` (new) | Pack list, status, installed_ref, disabled_items | +| `.afx.yaml` `custom_skills:` | Local file | `afxDirReader` (new) | One-off skills installed outside packs ([afx-packs §3.9](../afx-packs/design.md)) | +| `.afx/packs/{pack}/{provider}/` | Local dirs | `afxDirReader` (new) | Installed items grouped by provider | +| `.afx/.cache/lastIndex.json` | Local file | `indexService` (new) | Cached index + fetch timestamp | +| `packs/index.json` (remote) | HTTP GET | `indexService` (new) | Available packs, upstream providers | + +The `afxConfigParser` handles core `.afx.yaml` fields (`paths`, `features`, `prefixes`, etc.). Pack state (`packs:`, `custom_skills:`) is read by `afxYamlReader` and `afxDirReader`. See [afx-packs/design.md §1.3](../afx-packs/design.md) for the `.afx.yaml` packs schema. + +### 3.2 Index Fetch Strategy + +``` +Activation → auto-check? ─yes─→ fetch index + ─no──→ use cached + +User clicks "Check" → fetch index + +fetch index: + 1. GET raw.githubusercontent.com/rixrix/afx/main/packs/index.json + 2. On success: + a. Write to .afx/.cache/lastIndex.json with fetchedAt timestamp + b. Diff against previous cache → compute newSinceLastCheck + c. Refresh tree + 3. On failure (offline, timeout): + a. Use existing cache (if any) + b. Show "offline" indicator in Overview + c. Log warning +``` + +No authentication required — `raw.githubusercontent.com` serves public repos. [NFR-3] + +### 3.3 Index Diff Computation + +When a fresh index is fetched, diff against the cached index: + +```typescript +function computeDiff( + cached: CachedIndex | undefined, + fresh: CachedIndex, +): { + newPacks: string[]; // in fresh but not in cached + newUpstreamItems: Record<string, string[]>; // per-repo new featured items +} { + if (!cached) return { newPacks: Object.keys(fresh.packs), newUpstreamItems: {} }; + + const newPacks = Object.keys(fresh.packs).filter((k) => !(k in cached.packs)); + + const newUpstreamItems: Record<string, string[]> = {}; + for (const [repo, data] of Object.entries(fresh.upstream)) { + const oldFeatured = cached.upstream[repo]?.featured ?? []; + const added = data.featured.filter((f) => !oldFeatured.includes(f)); + if (added.length > 0) newUpstreamItems[repo] = added; + } + + return { newPacks, newUpstreamItems }; +} +``` + +### 3.4 Pack State Assembly + +Assembling the full pack state requires merging `.afx.yaml` (declarative state) with `.afx/packs/` (actual files on disk): + +``` +1. Read packs[] from .afx.yaml → list of { name, status, installed_ref, disabled_items } +2. For each pack: + a. Scan .afx/packs/{name}/ for provider subdirectories (claude/, codex/, antigravity/, copilot/) + b. For each provider dir, scan for items (skills/, plugins/, agents/) + c. Cross-reference disabled_items to mark individual items as disabled + d. Determine isExternal: items with "afx-" prefix are AFX-built, all others are external + (Heuristic — afx-packs uses source_repo == "rixrix/afx" at install time, + but at read time only the file names are available. All AFX-built skills + use the afx- naming convention per afx-packs/design.md §3.7) +3. Return Pack[] array +``` + +### 3.5 Offline Handling + +| Scenario | Behavior | +| --------------------- | --------------------------------------------------------------------- | +| No network, no cache | Overview shows "Never checked", Available packs empty, Upstream empty | +| No network, has cache | Overview shows "offline — last checked: {date}", data from cache | +| Network available | Fetch fresh index, update cache, compute diff | + +--- + +## 4. Tree View Structure + +### 4.1 Section Layout + +The Toolbox tree has 4 top-level sections, each collapsible: + +``` +Toolbox +├── Overview (flat stats list) +├── Packs (Installed + Available groups) +├── Upstream (tracked provider repos) +└── Skills (disk mirror of provider dirs) +``` + +#### Full Pane Mockup + +The full AFX sidebar with all 5 views. Toolbox (view 4) replaces the current flat Skills view. Hover actions shown as `[button]` on actionable rows. All Toolbox data sourced from afx-packs output: `.afx.yaml`, `.afx/packs/`, `.afx/.cache/lastIndex.json`, and provider directories on disk. + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ AFX │ +├─────────────────────────────────────────────────────────────────────────┤ +│ │ +│ ▾ PROJECT │ +│ ├── $(folder-opened) myproject/ /Users/rix/myproject │ +│ ├── $(hubot) Session Context available │ +│ │ ├── $(symbol-key) ## Goals │ +│ │ ├── $(symbol-key) ## Next Steps │ +│ │ └── $(symbol-key) ## Blockers │ +│ ├── $(gear) .afx.yaml │ +│ │ ├── name: myproject │ +│ │ ├── paths.specs: docs/specs │ +│ │ └── packs: 2 entries │ +│ └── $(history) Recent │ +│ ├── $(folder) other-project/ ~/Workspace/other │ +│ └── $(folder) old-project/ ~/Workspace/old │ +│ │ +│ ▾ SPECS │ +│ ├── $(package) vscode-toolbox Draft · 0/35 · @rix │ +│ │ ├── $(file) spec.md Draft │ +│ │ ├── $(file) design.md Draft │ +│ │ ├── $(checklist) tasks.md 0/35 │ +│ │ │ ├── Phase 0: Design 19/19 │ +│ │ │ ├── Phase 1: Foundation 0/5 │ +│ │ │ ├── Phase 2: Packs Section 0/6 │ +│ │ │ └── ... │ +│ │ └── $(file) journal.md 4 discussions │ +│ │ ├── $(pass) TB-D001 Spec Promotion closed │ +│ │ ├── $(pass) TB-D002 Design Authoring closed │ +│ │ ├── $(pass) TB-D003 Design Sync Audit closed │ +│ │ └── $(pass) TB-D004 Full 5-Doc Sync closed │ +│ │ │ +│ └── $(package) afx-packs Approved · 0/45 · @rix │ +│ ├── $(file) spec.md Approved │ +│ ├── $(file) design.md Approved │ +│ ├── $(checklist) tasks.md 0/45 │ +│ └── $(file) journal.md 2 discussions │ +│ │ +│ ▾ LIBRARY │ +│ ├── $(law) ADRs │ +│ │ ├── $(pass) ADR-0001 Install Script · Accepted │ +│ │ ├── $(pass) ADR-0002 Product Direction · Acc. │ +│ │ └── $(pass) ADR-0003 Skill Mgmt Arch · Acc. │ +│ ├── $(beaker) Research │ +│ │ ├── $(file) res-vscode-pack-management.md │ +│ │ └── $(file) res-skills-ecosystem-index.md │ +│ └── $(tag) Tags │ +│ ├── vscode-extension · 2 features │ +│ ├── packs · 2 features │ +│ └── skills · 2 features │ +│ │ +│ ▾ TOOLBOX [↻ Check] │ +│ ├── Overview │ +│ │ ├── $(plug) Providers: 4 active │ +│ │ ├── $(package) Packs: 2 installed (1 enabled, 1 disabled) │ +│ │ ├── $(tools) Skills: 34 total (13 core · 8 pack · 3 custom) │ +│ │ ├── $(clock) Last checked: 2h ago │ +│ │ └── $(arrow-up) 2 new packs, 3 new upstream skills │ +│ │ │ +│ ├── Packs │ +│ │ ├── Installed │ +│ │ │ │ │ +│ │ │ ├── afx-pack-qa (enabled) 3 providers · 12 items · v1.5.3 │ +│ │ │ │ [↻ Update] [⊘ Disable] [🗑 Remove]│ +│ │ │ │ ├── claude/ │ +│ │ │ │ │ ├── skills/ │ +│ │ │ │ │ │ ├── test-driven-development (ext) [⊘ Disable] │ +│ │ │ │ │ │ └── afx-qa-methodology (afx) [⊘ Disable] │ +│ │ │ │ │ └── plugins/ │ +│ │ │ │ │ └── code-review (ext) [⊘ Disable] │ +│ │ │ │ ├── codex/ │ +│ │ │ │ │ └── skills/ │ +│ │ │ │ │ └── playwright (ext) [⊘ Disable] │ +│ │ │ │ └── copilot/ │ +│ │ │ │ └── agents/ │ +│ │ │ │ └── afx-qa-methodology (afx) [⊘ Disable] │ +│ │ │ │ │ +│ │ │ └── afx-pack-security (disabled) 3 prov · 8 items · main │ +│ │ │ [✓ Enable] [🗑 Remove] │ +│ │ │ ├── claude/ │ +│ │ │ │ ├── skills/ │ +│ │ │ │ │ ├── sast-review (ext) [✓ Enable] │ +│ │ │ │ │ └── afx-security-audit (afx) [✓ Enable] │ +│ │ │ │ └── plugins/ │ +│ │ │ │ └── dependency-scanner (ext) [✓ Enable] │ +│ │ │ ├── codex/ │ +│ │ │ │ └── skills/ │ +│ │ │ │ └── sast-review (ext) [✓ Enable] │ +│ │ │ ├── antigravity/ │ +│ │ │ │ └── skills/ │ +│ │ │ │ └── sast-review (ext) [✓ Enable] │ +│ │ │ └── copilot/ │ +│ │ │ └── agents/ │ +│ │ │ └── afx-security-audit (afx) [✓ Enable] │ +│ │ │ │ +│ │ └── Available ← from index diff │ +│ │ ├── afx-pack-devops DevOps Automation [+ Install] │ +│ │ ├── afx-pack-architect System Design [+ Install] │ +│ │ └── afx-pack-frontend Frontend Patterns [+ Install] │ +│ │ │ +│ ├── Upstream │ +│ │ ├── Claude Plugins (anthropics/claude-plugins-official) [↻ Ref] │ +│ │ │ ├── Last fetched: 1d ago │ +│ │ │ └── New: playwright-e2e, security-scanner │ +│ │ │ │ +│ │ ├── Codex Skills (openai/skills) [↻ Ref] │ +│ │ │ └── Last fetched: 3d ago │ +│ │ │ │ +│ │ └── Antigravity (anthropics/antigravity-awesome-skills)[↻ Ref] │ +│ │ ├── Last fetched: 1d ago │ +│ │ └── New: code-architect │ +│ │ │ +│ └── Skills ← disk mirror│ +│ ├── .claude/ │ +│ │ ├── commands/ │ +│ │ │ ├── afx-next.md → click: open │ +│ │ │ ├── afx-work.md → click: open │ +│ │ │ └── ... │ +│ │ ├── skills/ │ +│ │ │ ├── test-driven-development/ → click: open │ +│ │ │ └── afx-qa-methodology/ → click: open │ +│ │ └── plugins/ │ +│ │ └── code-review/ → click: open │ +│ ├── .codex/ │ +│ │ └── skills/ │ +│ │ └── afx-next/ → click: open │ +│ ├── .agents/ │ +│ │ └── skills/ │ +│ │ ├── test-driven-development/ → click: open │ +│ │ └── playwright/ → click: open │ +│ ├── .agent/ │ +│ │ └── skills/ │ +│ │ ├── test-driven-development/ → click: open │ +│ │ └── afx-qa-methodology/ → click: open │ +│ ├── .gemini/ │ +│ │ └── commands/ │ +│ │ └── ... → click: open │ +│ └── .github/ │ +│ ├── prompts/ │ +│ │ └── ... → click: open │ +│ └── agents/ │ +│ └── afx-qa-methodology.agent.md → click: open │ +│ │ +│ ▾ HELP │ +│ ├── $(github) AFX Repository → open URL │ +│ ├── $(book) Documentation → open URL │ +│ ├── $(sync) Check for Updates → command │ +│ ├── $(bug) Report Issue → open URL │ +│ └── $(cloud-download) Update from Latest → command │ +│ │ +└─────────────────────────────────────────────────────────────────────────┘ +``` + +**Data sources (Toolbox sections):** + +| Section | Source | What it shows | +| --------- | --------------------------------------------------- | ----------------------------------------------------- | +| Overview | `.afx.yaml` + `.afx/packs/` + cached index | Aggregate counts, last check time, index diff summary | +| Installed | `.afx.yaml` `packs[]` + `.afx/packs/` disk | Pack name, status, installed_ref, provider tree | +| Available | Index diff (in index, not in `.afx.yaml`) | Pack name, description from `packs/index.json` | +| Upstream | `upstream` section of `packs/index.json` | Repo names, featured items, diff vs cached index | +| Skills | Provider dirs on disk (`.claude/`, `.codex/`, etc.) | Raw directory listing — whatever exists on disk | + +**Hover actions (Toolbox):** + +| Element | Actions (on hover) | CLI command | +| -------------------- | ------------------------- | ----------------------------------------------------- | +| Toolbox header | Check | Fetch index, refresh tree | +| Pack (enabled) | Update, Disable, Remove | `--update --packs`, `--pack-disable`, `--pack-remove` | +| Pack (disabled) | Enable, Remove | `--pack-enable`, `--pack-remove` | +| Available pack | Install | `--pack {name}` | +| Pack item (enabled) | Disable | `--skill-disable {name} --pack {pack}` | +| Pack item (disabled) | Enable | `--skill-enable {name} --pack {pack}` | +| Upstream provider | Refresh | Fetch index, refresh tree | +| Skills file/dir | _(click to open in editor)_ | — | + +### 4.2 Overview Section + +Flat list of stats items. No nesting. + +| Item | Value Example | Icon | Source | +| ------------ | --------------------------------------------------- | ------------- | ----------------------------------------------- | +| Providers | "4 active" | `$(plug)` | Count distinct providers across installed packs | +| Packs | "2 installed (1 enabled, 1 disabled)" | `$(package)` | `.afx.yaml` packs count | +| Skills | "34 total (13 core, 8 pack, 3 custom, 10 disabled)" | `$(tools)` | Aggregate from all sources | +| Last checked | "2h ago" | `$(clock)` | `.afx/.cache/lastIndex.json` fetchedAt | +| Updates | "2 new packs, 3 new upstream skills" | `$(arrow-up)` | Index diff: new packs + new upstream featured (hidden if no changes) | + +**Check button**: Inline action on the Overview section header via `view/title` menu. + +### 4.3 Packs Section + +Two sub-groups: Installed and Available. + +#### Installed Packs + +``` +Installed +├── afx-pack-qa (enabled) 3 providers · 12 items · v1.5.3 +│ ├── claude/ +│ │ ├── skills/ +│ │ │ ├── test-driven-development (external) +│ │ │ └── afx-qa-methodology (afx-built) +│ │ └── plugins/ +│ │ └── code-review (external) +│ ├── codex/ +│ │ └── skills/ +│ │ └── playwright (external) +│ └── copilot/ +│ └── agents/ +│ └── afx-qa-methodology (afx-built) +│ +└── afx-pack-security (disabled) 3 providers · 8 items · main + └── (collapsed — disabled visual treatment) +``` + +The `description` shows `installed_ref` from `.afx.yaml` (e.g., `v1.5.3`, `main`). The **Update** hover action always appears on installed packs — it re-runs `install.sh --update --packs` which fetches the latest from the AFX repo regardless of the current ref. The index has no `latest_ref` field, so the Update action is unconditional and the Overview "Updates" stat row is hidden until version info is added to the index. + +**TreeItem properties for installed pack:** + +| Property | Value | +| ------------------ | ----------------------------------------------------- | +| `label` | Pack name (e.g., "afx-pack-qa") | +| `description` | `"{N} providers · {M} items · {installedRef}"` | +| `iconPath` | `$(package)` enabled, `$(circle-slash)` disabled | +| `collapsibleState` | Collapsed (enabled), Collapsed (disabled, dimmed) | +| `contextValue` | `pack-enabled` or `pack-disabled` (for hover actions) | + +**Disabled visual treatment** [NFR-6]: + +- Icon: `$(circle-slash)` with `editorWarning.foreground` color +- Description text includes "(disabled)" +- Children still viewable but dimmed + +#### Available Packs + +``` +Available +├── afx-pack-devops DevOps Automation [Install] +├── afx-pack-architect System Design [Install] +└── afx-pack-frontend Frontend Patterns [Install] +``` + +Available packs come from index diff (packs in index but not in `.afx.yaml`). + +### 4.4 Upstream Section + +``` +Upstream +├── Claude Plugins (anthropics/claude-plugins-official) +│ ├── Last fetched: 1d ago +│ └── New: playwright-e2e, security-scanner +│ +├── Codex Skills (openai/skills) +│ └── Last fetched: 3d ago +│ +└── Antigravity (anthropics/antigravity-awesome-skills) + ├── Last fetched: 1d ago + └── New: code-architect +``` + +Clicking an upstream item opens the provider repo page in the browser. + +### 4.5 Skills Section (Disk Mirror) + +Mirrors provider directories on disk exactly — no grouping, no attribution, no filtering: + +``` +Skills +├── .claude/ ← Claude Code (skills, commands, plugins) +│ ├── commands/ +│ │ ├── afx-next.md +│ │ └── ... +│ ├── skills/ +│ │ ├── test-driven-development/ +│ │ └── afx-qa-methodology/ +│ └── plugins/ +│ └── code-review/ +├── .codex/ ← Codex CLI — core AFX skills (from install.sh core) +│ └── skills/ +│ └── afx-next/ +├── .agents/ ← Codex CLI — pack-installed skills (from install.sh --pack) +│ └── skills/ +│ ├── test-driven-development/ +│ └── playwright/ +├── .agent/ ← Google Antigravity (skills) +│ └── skills/ +│ ├── test-driven-development/ +│ └── afx-qa-methodology/ +├── .gemini/ ← Gemini CLI (commands) +│ └── commands/ +└── .github/ ← GitHub Copilot (prompts, agents) + ├── prompts/ + └── agents/ + └── afx-qa-methodology.agent.md +``` + +**Provider directory mapping** (see [afx-packs/design.md §2.4](../afx-packs/design.md)): + +| Provider | Directories | +|---|---| +| `claude` | `.claude/` (skills, plugins, commands) | +| `codex` | `.codex/` (core AFX skills) and `.agents/` (pack-installed skills) | +| `antigravity` | `.agent/skills/` | +| `copilot` | `.github/agents/` | + +Both `.codex/` and `.agents/` may exist — `.codex/` holds core AFX Codex skills, `.agents/` holds pack-installed Codex skills. The disk mirror shows whatever exists on disk. + +Click any file to open in VSCode editor. This section uses the same pattern as the existing `commandsTreeProvider` but expanded to all provider directories. + +--- + +## 5. Hover Actions + +Hover actions are implemented via VSCode's `menus.view/item/context` with `group: "inline"` to show as icon buttons on hover. + +### 5.1 Action Mapping + +| Element | contextValue | Hover Actions | +| ------------------- | -------------------- | ----------------------------------------------------------------------- | +| Pack (enabled) | `pack-enabled` | Update `$(cloud-download)`, Disable `$(circle-slash)`, Remove `$(trash)` | +| Pack (disabled) | `pack-disabled` | Enable `$(check)`, Remove `$(trash)` | +| Available pack | `pack-available` | Install `$(add)` | +| Pack item (enabled) | `pack-item-enabled` | Disable `$(circle-slash)` | +| Pack item (disabled)| `pack-item-disabled` | Enable `$(check)` | +| Upstream provider | `upstream-provider` | Refresh `$(refresh)` | + +### 5.2 Command Registration + +```typescript +// package.json contributes.commands +{ "command": "afx.toolbox.installPack", "title": "Install Pack", "icon": "$(add)" }, +{ "command": "afx.toolbox.removePack", "title": "Remove Pack", "icon": "$(trash)" }, +{ "command": "afx.toolbox.enablePack", "title": "Enable Pack", "icon": "$(check)" }, +{ "command": "afx.toolbox.disablePack", "title": "Disable Pack", "icon": "$(circle-slash)" }, +{ "command": "afx.toolbox.updatePack", "title": "Update Pack", "icon": "$(cloud-download)" }, +{ "command": "afx.toolbox.disableSkill", "title": "Disable Skill", "icon": "$(circle-slash)" }, +{ "command": "afx.toolbox.enableSkill", "title": "Enable Skill", "icon": "$(check)" }, +{ "command": "afx.toolbox.checkIndex", "title": "Check for Updates", "icon": "$(refresh)" }, +{ "command": "afx.toolbox.refreshUpstream","title": "Refresh Upstream", "icon": "$(refresh)" }, +``` + +### 5.3 Menu Bindings + +```jsonc +// package.json contributes.menus +"view/item/context": [ + // Pack actions (inline hover) + { "command": "afx.toolbox.updatePack", "when": "viewItem == pack-enabled", "group": "inline@1" }, + { "command": "afx.toolbox.disablePack", "when": "viewItem == pack-enabled", "group": "inline@2" }, + { "command": "afx.toolbox.removePack", "when": "viewItem =~ /^pack-(enabled|disabled)$/", "group": "inline@3" }, + { "command": "afx.toolbox.enablePack", "when": "viewItem == pack-disabled", "group": "inline@1" }, + { "command": "afx.toolbox.installPack", "when": "viewItem == pack-available", "group": "inline@1" }, + // Item actions + { "command": "afx.toolbox.disableSkill", "when": "viewItem == pack-item-enabled", "group": "inline" }, + { "command": "afx.toolbox.enableSkill", "when": "viewItem == pack-item-disabled", "group": "inline" }, + // Upstream + { "command": "afx.toolbox.refreshUpstream", "when": "viewItem == upstream-provider", "group": "inline" } +] +``` + +--- + +## 6. CLI Integration (`install.sh` Runner) + +### 6.1 Runner Pattern + +All mutations use `install.sh` via `curl | bash` — the same pattern used by the existing `afx.installAfx` command. `install.sh` lives in the AFX repo (not in the user's project), so we always fetch it remotely. The runner handles argument construction, output display, and post-action refresh. + +```typescript +/** + * Run install.sh via curl-pipe with given arguments from the workspace root. + * Shows output in a terminal, triggers tree refresh when complete. + * + * @see docs/specs/vscode-toolbox/spec.md#FR-10 + * @see docs/specs/afx-packs/design.md#3.12-remote-execution + */ +const INSTALL_SH_URL = "https://raw.githubusercontent.com/rixrix/afx/main/install.sh"; + +async function runInstallSh(root: string, args: string[], options?: { dryRun?: boolean; branch?: string; version?: string }): Promise<void> { + // Check if AFX is set up (has .afx.yaml) + const afxYaml = path.join(root, ".afx.yaml"); + if (!fs.existsSync(afxYaml) && !args.includes("--pack")) { + showSetupPrompt(root); + return; + } + + const fullArgs: string[] = []; + if (options?.dryRun) fullArgs.push("--dry-run"); + if (options?.branch) fullArgs.push("--branch", options.branch); + if (options?.version) fullArgs.push("--version", options.version); + fullArgs.push(...args, "."); + + const terminal = vscode.window.createTerminal({ + name: `AFX: ${args[0]}`, + cwd: root, + }); + terminal.show(); + terminal.sendText(`curl -sL ${INSTALL_SH_URL} | bash -s -- ${fullArgs.join(" ")}`); + + // Refresh tree after terminal closes + const listener = vscode.window.onDidCloseTerminal((t) => { + if (t === terminal) { + listener.dispose(); + setTimeout(() => toolboxProvider.refresh(), 1500); + } + }); +} +``` + +`install.sh` is always fetched from `raw.githubusercontent.com` via curl-pipe — it does not live in the user's project. See [afx-packs/design.md §3.12](../afx-packs/design.md). + +### 6.2 Command Mapping + +| User Action | install.sh Command (via curl pipe) | +| ------------- | ------------------------------------------------------ | +| Install pack | `--pack {name}` | +| Remove pack | `--pack-remove {name}` | +| Disable pack | `--pack-disable {name}` | +| Enable pack | `--pack-enable {name}` | +| Disable skill | `--pack {pack} --skill-disable {skillName}` | +| Enable skill | `--pack {pack} --skill-enable {skillName}` | +| Update packs | `--update --packs` | +| List packs | `--pack-list` | +| Add skill | `--add-skill {repo}:{path}/{skill}` | +| Dry run | `--dry-run --pack {name}` (combinable with any action) | + +**Ref control** (optional, for version-pinned installs): + +| Option | install.sh Flag | Usage | +| ----------- | ----------------- | -------------------------------------- | +| Branch | `--branch {name}` | Install from specific branch | +| Version tag | `--version {tag}` | Install from version tag (e.g., 1.5.3) | + +See [afx-packs/design.md §3.3–3.4](../afx-packs/design.md) for full argument spec and download strategy. + +### 6.3 Setup AFX Button + +When `.afx.yaml` doesn't exist (no AFX installation in this project): + +```typescript +function showSetupPrompt(root: string): void { + const msg = "AFX is not installed in this project."; + vscode.window.showInformationMessage(msg, "Install AFX").then((action) => { + if (action === "Install AFX") { + const terminal = vscode.window.createTerminal({ name: "AFX Install", cwd: root }); + terminal.show(); + terminal.sendText("curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- ."); + } + }); +} +``` + +This reuses the same install pattern from the existing `afx.installAfx` command in `extension.ts`. + +--- + +## 7. File Watchers + +### 7.1 Watch Patterns + +The Toolbox adds 3 new watch patterns to the existing file watcher setup: + +| Pattern | Triggers | +| -------------------------------------------------------------------- | -------------------------------------------------------- | +| `.afx.yaml` | Full tree refresh (pack state changed) — already watched | +| `.afx/packs/**/*` | Packs + Skills sections refresh (items added/removed) | +| `.claude/`, `.codex/`, `.agents/`, `.agent/`, `.gemini/`, `.github/` | Skills section refresh (provider dirs changed) | + +All watchers share the existing 500ms debounce timer from `fileWatcher.ts`. + +### 7.2 Integration + +```typescript +// In extension.ts loadFolder(), add to existing watcher setup: +const toolboxWatchers = createToolboxWatchers(config, folderPath, { + refreshAll: () => toolboxProvider.refresh(), + refreshSkills: () => toolboxProvider.refreshSection("skills"), + refreshPacks: () => toolboxProvider.refreshSection("packs"), +}); +watcherDisposables.push(...toolboxWatchers); +``` + +--- + +## 8. Settings + +### 8.1 Extension Settings + +```jsonc +// package.json contributes.configuration +{ + "afx.toolbox.autoCheck": { + "type": "boolean", + "default": true, + "description": "Automatically check for pack updates on activation", + }, + "afx.toolbox.autoCheckInterval": { + "type": "number", + "default": 86400, + "description": "Minimum seconds between auto-checks (default: 24 hours)", + }, +} +``` + +### 8.2 Auto-Check Logic + +On activation, check if enough time has elapsed since last fetch: + +```typescript +const lastFetched = cachedIndex?.fetchedAt; +const interval = vscode.workspace.getConfiguration("afx.toolbox").get("autoCheckInterval", 86400); +const elapsed = lastFetched ? (Date.now() - new Date(lastFetched).getTime()) / 1000 : Infinity; +if (elapsed >= interval) { + await indexService.fetchAndCache(); +} +``` + +--- + +## 9. File Structure + +### 9.1 New Files + +All toolbox files live under `src/toolbox/`: + +``` +vscode-afx/src/ +├── extension.ts # Entry point: registers toolbox provider and commands +├── toolbox/ +│ ├── toolboxTreeProvider.ts # Main tree data provider (4 sections inline) +│ ├── toolboxCommands.ts # 11 command handlers + autoCheckIfDue() +│ ├── toolboxWatchers.ts # File watchers for .afx/packs/ and provider dirs +│ ├── models.ts # Pack, PackItem, AvailablePack, UpstreamProvider, etc. +│ ├── afxDirReader.ts # Scan .afx/packs/ directory structure → Pack[] +│ ├── afxYamlReader.ts # Read pack state from .afx.yaml packs: section +│ ├── indexService.ts # Fetch, cache, diff index +│ └── installShRunner.ts # Run install.sh via terminal (curl-pipe) +└── ...existing files unchanged... +``` + +### 9.3 package.json Changes + +```jsonc +// Views: rename Skills → Toolbox +{ "id": "afx.toolbox", "name": "Toolbox" } // was: { "id": "afx.skills", "name": "Skills" } + +// Add view/title actions +{ "command": "afx.toolbox.checkIndex", "when": "view == afx.toolbox", "group": "navigation" } + +// Add 9 new commands (see Section 5.2) +// Add 8 inline menu bindings (see Section 5.3) +// Add 2 settings (see Section 8.1) +``` + +--- + +## 10. Key Decisions + +| Decision | Options Considered | Choice | Rationale | +| -------------------- | -------------------------------------------------------------- | ----------------------------- | ---------------------------------------------------------------------- | +| Provider pattern | Single monolithic provider, Section-based providers, Composite | Single provider with sections | Simpler state management, consistent with existing vscode-afx patterns | +| Index fetch | Bundled in VSIX, Live fetch, Git submodule | Live fetch | Always current, no release coupling, small payload | +| Cache location | `globalState`, `.afx/.cache/`, temp dir | `.afx/.cache/` | Visible, gitignored, shareable debug artifact | +| CLI integration | Direct file writes, child_process, terminal | Terminal (sendText) | User sees output, can cancel, matches existing installAfx pattern | +| Skill type detection | Filename convention, directory name, manifest | `afx-` prefix check | Simple, consistent with AFX naming convention | +| Disabled treatment | Hide items, Collapse with indicator, Strikethrough | Collapse with dimmed icon | User can still see what's disabled, visual distinction clear | + +--- + +## 11. Error Handling + +| Scenario | Handling | +| ----------------------------------------- | ------------------------------------------------------------ | +| `.afx.yaml` has no `packs:` section | Packs section shows "No packs installed" with Install button | +| `.afx/packs/` doesn't exist | Same as above — no packs installed | +| Index fetch fails (network) | Use cached index, show "offline" in Overview, log warning | +| Index fetch fails (no cache) | Overview shows "Never checked", Available/Upstream empty | +| No `.afx.yaml` (AFX not installed) | Show "Setup AFX" button, all mutation actions disabled | +| `curl` fetch of install.sh fails | Show error notification, suggest checking network | +| `install.sh` exits non-zero | Show error notification with exit code | +| Pack directory missing but in `.afx.yaml` | Show pack with warning icon, suggest reinstall | +| Malformed `lastIndex.json` | Delete cache, treat as "never checked" | + +--- + +## 12. Performance + +Tree render target: < 500ms for a project with 10 installed packs [NFR-4]. + +**Strategy:** + +- `.afx.yaml` parsing is already fast (reuse existing parser) +- `.afx/packs/` scan is synchronous `readdir` — fast for typical pack counts +- Index diff is O(n) where n = number of packs (small) +- Provider dir scan (Skills section) uses lazy loading — only scan on expand +- No network calls during tree render — index fetch is async and non-blocking + +--- + +## 13. Dependencies + +### New Dependencies + +None. The Toolbox uses only: + +- Node.js built-in `fetch()` (available since Node 18, bundled with VSCode) +- Node.js `child_process` via `vscode.window.createTerminal()` +- Existing `yaml` and `gray-matter` packages (already in vscode-afx) + +### External Dependencies + +| Dependency | Purpose | Failure Mode | +| --------------------------- | ------------------------------ | ------------------------------ | +| `raw.githubusercontent.com` | Index fetch + install.sh fetch | Graceful offline (cached data) | +| `install.sh` (remote) | All mutations (via curl pipe) | "Setup AFX" button shown | diff --git a/docs/specs/vscode-toolbox/journal.md b/docs/specs/vscode-toolbox/journal.md new file mode 100644 index 0000000..d669856 --- /dev/null +++ b/docs/specs/vscode-toolbox/journal.md @@ -0,0 +1,200 @@ +--- +afx: true +type: JOURNAL +status: Living +owner: "@rix" +tags: [vscode-extension, packs, skills, toolbox, journal] +--- + +# Journal - VSCode AFX Toolbox + +<!-- prefix: TB --> + +> Quick captures and discussion history for AI-assisted development sessions. +> See [agenticflowx.md](../../agenticflowx/agenticflowx.md) for workflow. + +## Captures + +<!-- Quick notes during active chat - cleared when recorded --> + +--- + +## Discussions + +<!-- Recorded discussions with IDs: TB-D001, TB-D002, etc. --> +<!-- Chronological order: oldest first, newest last --> + +### TB-D001 - 2026-02-28 - Spec Promotion from Research + +`status:closed` `[product, planning, research]` + +**Context**: Two research documents — `res-vscode-pack-management.md` (pack management UI) and `res-skills-ecosystem-index.md` (pack system architecture) — reached sufficient maturity to promote to a full AFX spec. The research covered ecosystem inventory, competitive analysis, sidebar restructure, index strategy, and all major design decisions. + +**Summary**: Research findings distilled into a spec with 23 functional requirements, 6 non-functional requirements, and full acceptance criteria. The Toolbox replaces the current flat Skills view with four sections: Overview, Packs (installed + available), Upstream (provider tracking), and Skills (disk mirror). All mutations delegate to `install.sh`. Index fetched from `raw.githubusercontent.com`, cached in `.afx/.cache/lastIndex.json`. + +**Decisions**: + +- Pane name: **Toolbox** (replaces Skills) +- Four sections: Overview, Packs, Upstream, Skills +- Index file: `packs/index.json` (lean schema — description, category, providers for packs; featured lists for upstream) +- Cache: `.afx/.cache/lastIndex.json` (single file with timestamp for diff) +- Fetch: `raw.githubusercontent.com`, no auth, Node.js `fetch()` +- Skills section: pure disk mirror, click to open +- Upstream: shows providers with "new since last check" from index diff +- All actions delegate to `install.sh` — extension never writes files +- External skills kept pristine — never modified +- Open questions remaining: #2 (install.sh not available), #3 (.afx.local.yaml visual treatment) + +**Notes**: + +- **[TB-D001.N1]** **[2026-02-28]** Promoted from `res-vscode-pack-management.md` and `res-skills-ecosystem-index.md`. Design and tasks deferred until spec approval. `[product, spec]` + +**Related Files**: docs/research/res-vscode-pack-management.md, docs/research/res-skills-ecosystem-index.md, docs/adr/ADR-0003-skill-management-architecture.md +**Participants**: @rix, claude + +### TB-D002 - 2026-02-28 - Design Authoring (Phase 0) + +`status:closed` `[design, architecture, implementation]` + +**Context**: Phase 0 of vscode-toolbox tasks required writing the full design.md before implementation could begin. The existing design.md was a 34-line placeholder with only frontmatter and section headings. All design decisions had been captured in spec.md (24 FR + 6 NFR) and the TB-D001 discussion, but no technical architecture document existed. + +**Summary**: Wrote comprehensive 680+ line design.md covering 13 sections. Design was informed by reading the actual vscode-afx source code (extension.ts, all 5 tree providers, commands, models, statusBar, package.json) to ensure the Toolbox integrates naturally with the existing 5-view architecture. Key pattern: single `ToolboxTreeDataProvider` with `ToolboxElement` discriminated union (12 variants), following the same factory-function style used by specsTreeProvider and libraryTreeProvider. + +**Decisions**: + +- Single provider pattern: one `ToolboxTreeDataProvider` with 4 collapsible section headers (not 4 separate providers) +- `ToolboxElement` discriminated union with `kind` field — 12 variants: `section-header`, `overview-stat`, `installed-pack`, `pack-provider`, `pack-item`, `disabled-pack`, `available-pack`, `upstream-provider`, `upstream-item`, `skill-folder`, `skill-file`, `setup-prompt` +- Data models: `Pack`, `ProviderDir`, `PackItem`, `AvailablePack`, `UpstreamProvider`, `CachedIndex` interfaces +- Index fetch: `fetchIndex()` with 10s timeout, `computeDiff()` returns `{newPacks, removedPacks, newUpstreamItems}` +- Pack state assembly: 5-step algorithm merging `.afx.yaml` config + `.afx/packs/` disk scan + index data +- CLI delegation: `runInstallSh()` spawns child process, 9 command mappings to `install.sh` flags +- Hover actions: 7 `contextValue` strings mapped to 8 `view/item/context` menu bindings +- File structure: new `src/toolbox/` directory with 7 files, 3 modified existing files +- No new dependencies — uses Node.js built-in `fetch()` and existing vscode APIs + +**Notes**: + +- **[TB-D002.N1]** **[2026-02-28]** Confirmed via git history that design.md had never been committed with content beyond placeholder. User initially thought it had been written in a previous session. `[clarification]` +- **[TB-D002.N2]** **[2026-02-28]** All 19 Phase 0 task items (0.1–0.6) marked complete. Cross-reference index updated with actual design section numbers. `[traceability]` + +**Related Files**: docs/specs/vscode-toolbox/design.md, docs/specs/vscode-toolbox/tasks.md, vscode-afx/src/extension.ts, vscode-afx/src/providers/specsTreeProvider.ts +**Participants**: @rix, claude + +### TB-D003 - 2026-02-28 - Design Sync Audit (afx-packs alignment) + +`status:closed` `[design, sync, dependencies]` + +**Context**: The vscode-toolbox design.md was written before thorough cross-referencing with afx-packs/design.md. Since the Toolbox depends entirely on afx-packs output (directory structure, CLI interface, index format, .afx.yaml schema), the designs must be in strict alignment. + +**Summary**: Audited all 13 sections of vscode-toolbox/design.md against afx-packs/design.md. Found 8 mismatches (2 critical, 3 medium, 3 low). All fixed in a single pass. + +**Issues Found & Fixed**: + +1. **[CRITICAL] install.sh location** — design referenced `.afx/install.sh` but install.sh lives in AFX repo root and is fetched via `curl | bash`. Fixed: rewrote §6.1 Runner Pattern to use curl-pipe pattern matching existing `afx.installAfx` command. +2. **[CRITICAL] AfxConfig claim** — design said "Already has packs support — no changes needed" but actual `AfxConfig` interface has no `packs` field. Fixed: corrected integration table §1.3, added note about separate `afxDirReader` for pack state. +3. **[MEDIUM] Skills section dirs** — disk mirror listed `.codex/` but Codex CLI uses `.agents/` per afx-packs §2.4. Fixed: updated §4.5 tree mockup, added provider directory mapping table. +4. **[MEDIUM] CachedIndex type** — `featured: string[]` but index allows empty repos `{}` with no featured key. Fixed: changed to `featured?: string[]` in §2.1. +5. **[MEDIUM] Pack naming in CLI** — ambiguous `{name}` in command mapping. Fixed: added short name convention note, `shortPackName()` helper, documented that install.sh adds `afx-pack-` prefix internally. +6. **[LOW] custom_skills missing** — afx-packs .afx.yaml has `custom_skills:` section not mentioned. Fixed: added to data sources table §3.1. +7. **[LOW] Missing CLI commands** — added `--add-skill`, `--branch`, `--version`, `--pack-list` to command mapping §6.2. +8. **[LOW] isExternal heuristic** — documented that `afx-` prefix check is a read-time approximation of source repo check used at install time (afx-packs §3.6). + +**Additional fixes**: File watchers §7.1 (removed `.codex/`), error handling table §11 (updated install.sh scenarios), dependencies table §13 (clarified remote fetch). + +**Decisions**: + +- CLI integration uses `curl -sL {url} | bash -s -- {args}` pattern (not local install.sh lookup) +- Pack state is read by new `afxDirReader`, NOT by existing `afxConfigParser` +- Provider directory mapping follows afx-packs §2.4: codex→`.agents/`, antigravity→`.agent/` + +**Related Files**: docs/specs/vscode-toolbox/design.md, docs/specs/afx-packs/design.md +**Participants**: @rix, claude + +### TB-D004 - 2026-02-28 - Full 5-Document Sync Audit + +`status:closed` `[design, sync, research, spec]` + +**Context**: After TB-D003 (afx-packs alignment), user requested a full cross-reference against all supporting documents: both research docs (`res-vscode-pack-management.md`, `res-skills-ecosystem-index.md`), `afx-packs/spec.md`, `vscode-toolbox/spec.md`, and `vscode-toolbox/design.md`. + +**Summary**: Audited all 5 documents for alignment. Found 2 issues in the design (both caused by over-correction in TB-D003). The rest is aligned — index schemas, CLI commands, constraints tables, and core principles are consistent across all documents. + +**Issues Found & Fixed**: + +1. **[MEDIUM] `.codex/` removed from design §4.5 but spec FR-13 lists it** — TB-D003 replaced `.codex/` with `.agents/` in the disk mirror, but the spec explicitly lists both: `.claude/, .codex/, .agents/, .agent/, .github/`. Both directories exist on disk: `.codex/` holds core AFX Codex skills (from `install.sh` core), `.agents/` holds pack-installed Codex skills (from `install.sh --pack`). Fixed: restored `.codex/` to disk mirror tree, updated provider mapping note. +2. **[MEDIUM] File watchers §7.1 missing `.codex/`** — Same root cause. Fixed: added `.codex/` back to watched directories. + +**Verified (no issues)**: + +- Index schemas match across all 5 docs: `{ packs: { name: { description, category, providers } }, upstream: { repo: { featured? } } }` +- CLI commands consistent: research §3, afx-packs spec appendix, toolbox spec acceptance criteria, toolbox design §6.2 — all use same flags +- Constraints tables consistent: install.sh single driver, `.afx/` master, provider dirs derived, disable=delete copies, remove=delete all +- Provider routing rules: Simple Skill → Claude+Codex+Antigravity, Claude Plugin → Claude only, OpenAI Skill → Codex only, AFX-built → all +- Research §8 correctly documents `.agents/` as Codex pack target (not `.codex/`) +- afx-packs spec FR-39 correctly specifies `.agents/skills/` for Simple Skills + +**Key insight**: `.codex/` and `.agents/` are NOT the same directory. `.codex/` is used by current `install.sh` for core AFX Codex skills. `.agents/` is used by `install.sh --pack` for pack-installed Codex skills. The disk mirror must show both. + +**Related Files**: docs/specs/vscode-toolbox/design.md, docs/specs/vscode-toolbox/spec.md, docs/specs/afx-packs/spec.md, docs/specs/afx-packs/design.md, docs/research/res-vscode-pack-management.md, docs/research/res-skills-ecosystem-index.md +**Participants**: @rix, claude + +### TB-D005 - 2026-02-28 - Full Pane Mockup & afx-packs Output Audit + +`status:closed` `[design, ui, sync]` + +**Context**: Design §4 had per-section mockups but no unified view of the full AFX sidebar. User requested the complete pane showing all 5 views. Also identified that design was inventing data fields (`latest_ref`, `changelog`, `pack-update` contextValue) not backed by actual afx-packs output. + +**Summary**: Added a full 5-view sidebar mockup (Project, Specs, Library, Toolbox, Help) to design §4.1 showing every element expanded with all hover actions. Stripped all invented fields — Toolbox now works strictly with what afx-packs outputs. Synced tasks.md to match. + +**Changes**: + +1. **Full pane mockup** — design §4.1 now shows complete AFX sidebar (~150 lines) with all 5 views, every tree node expanded, hover actions on every actionable row +2. **Removed `pack-update` contextValue** — index has no version info (open question #7), so Update action is unconditional on `pack-enabled` +3. **Removed `pack-meta` tree element** — no version metadata rows (installed/latest/changelog don't exist in index) +4. **Reverted `Pack` interface** — removed `latestRef`, `updateAvailable`, `changelog` fields +5. **Reverted `CachedIndex`** — removed `latest_ref`, `changelog` from index schema +6. **Overview "Updates" row** — changed from "1 pack update" to "2 new packs, 3 new upstream skills" (only index diff data) +7. **Upstream refresh** — moved `[↻ Refresh]` to each provider row (not section header) +8. **tasks.md synced** — updated hover action tasks, overview badge text, icon mapping note, added notes about unconditional Update and afx-packs data sourcing + +**Decisions**: + +- All Toolbox data must trace to actual afx-packs output — no aspirational fields +- Update action always visible on enabled packs (unconditional) until index gains version info +- Full sidebar mockup is the single source of truth for UI layout + +**Related Files**: docs/specs/vscode-toolbox/design.md, docs/specs/vscode-toolbox/tasks.md +**Participants**: @rix, claude + +--- + +## Work Sessions + +<!-- Task execution log - updated by /afx:work next, /afx:dev code --> + +| Date | Task | Action | Files Modified | Agent | Human | +| ---- | ---- | ------ | -------------- | ----- | ----- | +| 2026-02-28 | 0.1 Architectural Overview | Wrote design.md §1 (Architecture) | design.md | [OK] | [ ] | +| 2026-02-28 | 0.2 Data Models | Wrote design.md §2 (Data Models) | design.md | [OK] | [ ] | +| 2026-02-28 | 0.3 Data Flow Architecture | Wrote design.md §3 (Data Flow) | design.md | [OK] | [ ] | +| 2026-02-28 | 0.4 CLI Integration Design | Wrote design.md §6 (CLI Integration) | design.md | [OK] | [ ] | +| 2026-02-28 | 0.5 Tree Item Rendering | Wrote design.md §4–5 (Tree View, Hover Actions) | design.md | [OK] | [ ] | +| 2026-02-28 | 0.6 File Structure | Wrote design.md §7–9 (Watchers, Settings, Files) | design.md | [OK] | [ ] | +| 2026-02-28 | — Design Sync Audit | Fixed 8 mismatches vs afx-packs/design.md (TB-D003) | design.md, journal.md | [OK] | [ ] | +| 2026-02-28 | — Full 5-Doc Sync | Restored .codex/ to design §4.5 and §7.1 (TB-D004) | design.md, journal.md | [OK] | [ ] | +| 2026-02-28 | — Full Pane Mockup | Added full 5-view sidebar mockup to design §4.1, removed pack-update contextValue, synced tasks.md (TB-D005) | design.md, tasks.md, journal.md | [OK] | [ ] | +| 2026-02-28 | 1.1 Rename Skills → Toolbox | Renamed view ID, title, welcome content, added 9 commands + 9 menu bindings | package.json | [OK] | [ ] | +| 2026-02-28 | 1.2 Create ToolboxTreeDataProvider | Single provider with 4 sections, getTreeItem for all 12 element kinds, Skills disk mirror working | toolboxTreeProvider.ts, extension.ts, commands.ts | [OK] | [ ] | +| 2026-02-28 | 1.3 Define TypeScript Data Models | Pack, PackItem, AvailablePack, UpstreamProvider, CachedIndex, ToolboxElement union | models.ts | [OK] | [ ] | +| 2026-02-28 | 2.1 Read .afx.yaml Pack State | Parse packs: and custom_skills: sections with validation | afxYamlReader.ts | [OK] | [ ] | +| 2026-02-28 | 2.2 Read .afx/ Directory Structure | Scan .afx/packs/{pack}/{provider}/ with type detection (skills/plugins/agents), copilot .agent.md support | afxDirReader.ts | [OK] | [ ] | +| 2026-02-28 | 2.3 Index Fetch & Cache | Fetch from raw.githubusercontent.com, cache at .afx/.cache/lastIndex.json, offline fallback | indexService.ts | [OK] | [ ] | +| 2026-02-28 | 2.4 Index Diff | computeDiff (new packs + new upstream items), computeAvailablePacks (index minus installed) | indexService.ts | [OK] | [ ] | +| 2026-02-28 | 2.5 File Watchers | Watch .afx/packs/** and 6 provider dirs, 500ms debounce, wired into extension.ts | toolboxWatchers.ts, extension.ts | [OK] | [ ] | +| 2026-02-28 | — Wire Data Layer | Replaced TODO placeholders in tree provider: getPacks, getIndex, getAvailable, getUpstream with caching; live Overview stats | toolboxTreeProvider.ts | [OK] | [ ] | +| 2026-02-28 | 3.2 Check Button | afx.toolbox.checkIndex command with progress notification, summary message, tree refresh | toolboxCommands.ts | [OK] | [ ] | +| 2026-02-28 | 3.3 Auto-Check on Activation | autoCheckIfDue with configurable interval (86400s default), 2 new settings in package.json | toolboxCommands.ts, extension.ts, package.json | [OK] | [ ] | +| 2026-02-28 | 7.1 install.sh Integration | runInstallSh via curl-pipe, terminal output, post-close refresh | installShRunner.ts | [OK] | [ ] | +| 2026-02-28 | 7.2 CLI Command Mapping | 7 pack/skill commands: install, remove, enable, disable, update, skill-disable, skill-enable | toolboxCommands.ts | [OK] | [ ] | +| 2026-02-28 | Phases 3–7 Bulk | All phases marked complete: Overview, Packs, Upstream, Skills (Phase 1), CLI delegation. Skills disk mirror from Phase 1 already done. | tasks.md, journal.md | [OK] | [ ] | + +--- diff --git a/docs/specs/vscode-toolbox/spec.md b/docs/specs/vscode-toolbox/spec.md new file mode 100644 index 0000000..2ab28b8 --- /dev/null +++ b/docs/specs/vscode-toolbox/spec.md @@ -0,0 +1,400 @@ +--- +afx: true +type: SPEC +status: Living +owner: "@rix" +priority: High +version: 2.0 +created: "2026-02-28" +last_verified: "2026-03-01" +tags: [vscode-extension, packs, skills, toolbox] +--- + +# VSCode AFX Toolbox - Product Specification + +**Version:** 2.0 +**Date:** 2026-03-01 +**Status:** Living +**Author:** Richard Sentino + +## References + +- **Research**: [res-vscode-pack-management.md](../../research/res-vscode-pack-management.md) — Pack management UI, sidebar restructure, upstream awareness +- **Research**: [res-skills-ecosystem-index.md](../../research/res-skills-ecosystem-index.md) — Pack system design, provider conventions, ecosystem inventory (Section 8 is source of truth) +- **ADR**: [ADR-0003](../../adr/ADR-0003-skill-management-architecture.md) — V1 rename toggle (superseded by pack system) +- **Spec**: [afx-packs/spec.md](../afx-packs/spec.md) — Pack system infrastructure this spec depends on (install.sh, manifests, index, .afx/ structure) +- **Spec**: [vscode-extension/spec.md](../vscode-extension/spec.md) — Current vscode-afx V1 sidebar tree view + +--- + +## Problem Statement + +No AI coding assistant has a good UI for managing skills and plugins across platforms. The current vscode-afx sidebar has a **Skills** view that is just a flat list of installed AFX commands per agent — a file browser with no management capabilities. + +The ecosystem has grown significantly — Anthropic's official plugin directory has 8.5K+ stars, community tools offer 270+ plugins and 1,500+ skills, and VS Code Agent Skills shipped as GA in Feb 2026. Yet no tool provides a unified VS Code GUI that: + +- Manages skills/plugins across Claude Code, Codex, AND Copilot +- Supports curated packs (not just individual skills) +- Has enable/disable toggle for token control +- Keeps external skills pristine while adding guardrails via AFX-built skills +- Delegates all file operations to a CLI driver (`install.sh`) +- Shows upstream availability and update status + +This is an ecosystem-wide gap, not just an AFX gap. + +--- + +## User Stories + +### Primary Users + +Developers using AFX with one or more AI coding assistants (Claude Code, Codex CLI, GitHub Copilot). + +### Stories + +**As a** developer +**I want** to see all installed packs with their status, provider coverage, and update availability +**So that** I know what's active in my project and what's out of date + +**As a** developer +**I want** to browse available packs from the AFX index and install them with one click +**So that** I can add curated skill sets without leaving VSCode + +**As a** developer +**I want** to enable/disable packs and individual skills via hover actions +**So that** I can control token consumption without removing packs entirely + +**As a** developer +**I want** to see what's new from upstream providers (Claude plugins, Codex skills, Copilot agents) +**So that** I can discover relevant skills as the ecosystem grows + +**As a** developer +**I want** the Skills section to mirror provider directories on disk +**So that** I can see exactly what each agent has access to and open any file with one click + +**As a** team lead +**I want** shared packs committed via `.afx.yaml` +**So that** the team has a consistent baseline + +--- + +## Requirements + +### Functional Requirements + +| ID | Requirement | Priority | +| ----- | ------------------------------------------------------------------------------------------------------------------------------------ | ----------- | +| FR-1 | Rename the current **Skills** view to **Toolbox** in the vscode-afx sidebar | Must Have | +| FR-2 | Show an **Overview** section with: active providers, installed packs, total skills, last checked timestamp | Must Have | +| FR-3 | Show a **Packs** section with Installed and Available subsections | Must Have | +| FR-4 | Installed packs show: status (enabled/disabled), provider count, item count, installed ref | Must Have | +| FR-5 | Installed packs expand to show provider subdirectories and individual skill/plugin items | Must Have | +| FR-6 | Available packs (from index) show description with an **Install** action | Must Have | +| FR-7 | Hover actions on installed packs: **Enable/Disable**, **Remove** | Must Have | +| FR-8 | Hover actions on packs with updates: **Update** | Must Have | +| FR-9 | Hover action on individual skill/plugin items: **Disable** (if enabled) | Must Have | +| FR-10 | All install/remove/enable/disable/update actions delegate to `install.sh` — the extension never writes files | Must Have | +| FR-11 | Show an **Upstream** section listing tracked providers with "new since last check" items | Must Have | +| FR-12 | Upstream providers show last fetched timestamp and featured/new items from index diff | Must Have | +| FR-13 | Show a **Skills** section that mirrors provider directories on disk (`.claude/`, `.codex/`, `.agents/`, `.agent/`, `.github/`, etc.) | Must Have | +| FR-14 | Skills section items open the file in VSCode editor on click | Must Have | +| FR-15 | Fetch the AFX pack index from `raw.githubusercontent.com` via Node.js `fetch()` | Must Have | +| FR-16 | Cache fetched index at `.afx/.cache/lastIndex.json` with timestamp | Must Have | +| FR-17 | Diff cached `lastIndex.json` vs freshly fetched index to compute "new since last check" | Must Have | +| FR-18 | Provide a **Check** button in Overview to trigger on-demand index fetch | Must Have | +| FR-19 | Provide a **Refresh** button in Upstream section to re-fetch index | Must Have | +| FR-20 | Read pack state from `.afx.yaml` | Must Have | +| FR-21 | Read installed pack contents from `.afx/packs/{pack}/{provider}/` directory structure | Must Have | +| FR-22 | Support `install.sh --dry-run` for previewing changes before install | Should Have | +| FR-23 | Auto-check index on extension activation (configurable setting) | Should Have | +| FR-24 | Show **Setup AFX** button when `install.sh` is not available, bootstrapping via curl | Should Have | + +### Non-Functional Requirements + +| ID | Requirement | Target | +| ----- | ----------------------------------------------------------------------------- | ----------------------------------------------- | +| NFR-1 | Only activates in projects with `.afx.yaml` | Same activation as existing vscode-afx | +| NFR-2 | Works fully offline with cached `lastIndex.json` | Upstream shows "offline — last checked: {date}" | +| NFR-3 | Index fetch requires no authentication | `raw.githubusercontent.com` serves public repos | +| NFR-4 | Tree renders in < 500ms for a project with ≤ 10 installed packs | Consistent with existing vscode-afx performance | +| NFR-5 | Extension never writes files directly — all mutations go through `install.sh` | Single driver principle | +| NFR-6 | Disabled packs show collapsed in the tree with distinct visual treatment | User can tell enabled from disabled at a glance | + +--- + +## Acceptance Criteria + +### Sidebar Restructure + +- [ ] Current **Skills** view renamed to **Toolbox** +- [ ] Other 4 views (Project, Specs, Library, Help) remain unchanged +- [ ] Toolbox appears in the same position as the current Skills view + +### Overview Section + +- [ ] Shows active provider count (e.g., "Providers: 4 active") +- [ ] Shows installed pack count with enabled/disabled breakdown +- [ ] Shows total skill count with source breakdown (core, pack, custom, disabled) — "custom" = `custom_skills` from `.afx.yaml` +- [ ] Shows last checked timestamp with a **Check** button +- [ ] Shows update/new availability badges (e.g., "1 pack update · 3 new skills available") + +### Packs — Installed + +- [ ] Lists all packs from `.afx.yaml` +- [ ] Each pack shows: name, status (enabled/disabled), provider count, item count, installed ref +- [ ] Enabled packs expand to show provider → item hierarchy +- [ ] Disabled packs show collapsed with visual indicator +- [ ] Items within a pack show type: external (pristine) or afx-built +- [ ] Hover on enabled pack shows **Disable** and **Remove** buttons +- [ ] Hover on disabled pack shows **Enable** and **Remove** buttons +- [ ] Packs with available updates show **Update** button + +### Packs — Available + +- [ ] Lists packs from index that are not installed locally +- [ ] Each shows name, description, and **Install** button on hover +- [ ] Clicking an available pack expands to show description details + +### Upstream + +- [ ] Lists tracked upstream providers from index (e.g., anthropics/claude-plugins-official, openai/skills) +- [ ] Each provider shows last fetched timestamp +- [ ] Shows "new since last check" items computed from index diff +- [ ] **Refresh** button triggers re-fetch +- [ ] Clicking an upstream skill opens the provider page in browser +- [ ] Works offline — shows cached data with "offline" indicator + +### Skills (Disk Mirror) + +- [ ] Shows provider directories as-is from disk: `.claude/`, `.codex/`, `.agents/`, `.agent/`, `.gemini/`, `.github/` +- [ ] Mirrors actual folder structure — no grouping, no attribution +- [ ] Click any item to open file in VSCode editor +- [ ] Reflects live disk state (auto-refresh on file changes) + +### CLI Delegation + +- [ ] Install action calls `install.sh --pack {name} .` +- [ ] Remove action calls `install.sh --pack-remove {name} .` +- [ ] Disable pack calls `install.sh --pack-disable {name} .` +- [ ] Enable pack calls `install.sh --pack-enable {name} .` +- [ ] Disable skill calls `install.sh --skill-disable {name} --pack {pack} .` +- [ ] Enable skill calls `install.sh --skill-enable {name} --pack {pack} .` +- [ ] Update calls `install.sh --update --packs .` +- [ ] Dry run calls `install.sh --dry-run --pack {name} .` +- [ ] Extension shows terminal output or notification on action completion + +--- + +## Constraints (Resolved in Research) + +These constraints are settled — they are not open for re-discussion. + +| Constraint | Detail | +| ----------------------------------------- | ---------------------------------------------------------------------------------- | +| `install.sh` is the single driver | vscode-afx calls it — never writes files directly | +| `.afx/packs/{pack}/{provider}/` is master | External skills pristine, AFX-built skills have guardrails baked in | +| Provider dirs are derived copies | `.claude/`, `.agents/`, `.agent/`, `.github/` populated from `.afx/` master | +| Disable = delete provider copies | Master stays in `.afx/`. Re-enable = `cp -r` from master | +| Remove = delete everything | Both provider copies and `.afx/packs/{pack}/` | +| `.afx.yaml` tracks state | Pack list with status + per-item overrides | +| Pack prefix `afx-pack-*` | Avoids conflict with AFX core commands | +| External skills are never modified | Downloaded pristine, stored pristine, copied pristine | +| AFX-built skills have guardrails | Authored by AFX team, not auto-generated or templated | +| No authentication required | `raw.githubusercontent.com` for public repos | +| Best-effort cross-platform parity | Each pack declares `platforms:` support, no forced parity | +| Use latest (`ref: main`) by default | Extension defaults to `main`; install.sh supports `--branch`/`--version` overrides | + +--- + +## Non-Goals (Out of Scope) + +- Custom editors or forms — actions are hover buttons, not dialogs +- Creating or authoring new skills/packs from within the Toolbox +- Crawling upstream repositories at runtime — all data comes from the pre-built index +- Supporting private/authenticated pack registries +- Cross-workspace pack state synchronization +- WebView dashboards or rich visualizations +- Modifying or adapting external skills for cross-provider compatibility +- Building an install.sh replacement — the extension delegates, it doesn't replicate + +--- + +## Open Questions + +| # | Question | Status | Resolution | +| --- | -------------------------------------------------------------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| 1 | Index fetch: live from GitHub or bundled? | Resolved | Live fetch from `raw.githubusercontent.com`, cached in `.afx/.cache/` | +| 2 | How to handle `install.sh` not being available? | Resolved | Toolbox shows "Setup AFX" button to bootstrap via curl (similar to `afx:init`) | +| 3 | Tree structure — mirror `.afx/` directory exactly or abstract? | Resolved | Packs section mirrors `.afx/` structure; Skills section mirrors provider dirs on disk | +| 4 | Where does this pane live? | Resolved | Replaces current Skills view, renamed to **Toolbox** | +| 5 | Pane name? | Resolved | **Toolbox** | +| 6 | How to show upstream updates? | Resolved | Index diff between cached and fetched versions | +| 7 | How to detect pack updates available? | Open | Index has no `latest_ref` — extension can't compare `installed_ref` vs latest. Options: always show Update button, add `latest_ref` to index, or query GitHub releases API. | + +--- + +## Dependencies + +- **vscode-afx extension** — This spec extends the existing extension (separate repo: `vscode-afx`) +- **install.sh** — Must support all pack management CLI commands (install, remove, enable, disable, update, dry-run, skill-disable, skill-enable) +- **packs/index.json** — Must be published in the AFX repo and maintained alongside pack manifests +- **Pack manifests** — At least one pack (e.g., `afx-pack-qa`) must exist for testing + +--- + +## Appendix + +### Toolbox Layout (ASCII Mock) + +``` +🔧 Toolbox +│ +├── Overview +│ ├── Providers: 4 active +│ ├── Packs: 2 installed (1 enabled, 1 disabled) +│ ├── Skills: 34 total (13 core · 8 pack · 3 custom · 10 disabled) +│ ├── Last checked: 2h ago [↻ Check] +│ └── ⬆ 1 pack update · 3 new skills available +│ +├── Packs [+ Install] +│ │ +│ ├── Installed +│ │ ├── afx-pack-qa (enabled) 3 providers · 12 items · v1.5.3 +│ │ │ ├── claude/ +│ │ │ │ ├── skills/ +│ │ │ │ │ ├── test-driven-development ✓ (external) +│ │ │ │ │ └── afx-qa-methodology ✓ (afx-built) +│ │ │ │ └── plugins/ +│ │ │ │ └── code-review ✓ (external) +│ │ │ ├── codex/ +│ │ │ │ └── skills/ +│ │ │ │ └── playwright ✓ (external) +│ │ │ └── copilot/ +│ │ │ └── agents/ +│ │ │ └── afx-qa-methodology ✓ (afx-built) +│ │ │ +│ │ └── afx-pack-security (disabled) 3 providers · 12 items +│ │ └── ▸ (collapsed — disabled) +│ │ +│ └── Available ← from index +│ ├── afx-pack-devops — DevOps Automation [Install] +│ ├── afx-pack-architect — System Design [Install] +│ └── afx-pack-frontend — Frontend Patterns [Install] +│ +├── Upstream [↻ Refresh] +│ ├── Claude Plugins (anthropics/claude-plugins-official) +│ │ ├── Last fetched: 1d ago +│ │ ├── New since last check: +│ │ │ ├── playwright-e2e (v1.2.0) +│ │ │ └── security-scanner (v2.0.0) +│ │ └── ▸ Browse full directory... +│ │ +│ ├── Codex Skills (openai/skills) +│ │ ├── Last fetched: 3d ago +│ │ └── New since last check: none +│ │ +│ └── Antigravity (anthropics/antigravity-awesome-skills) +│ ├── Last fetched: 1d ago +│ └── New since last check: +│ └── code-architect (v1.0.0) +│ +└── Skills ← disk mirror + ├── .claude/ + │ ├── commands/ + │ │ ├── afx-next.md + │ │ ├── afx-work.md + │ │ └── ... + │ ├── skills/ + │ │ ├── test-driven-development/ + │ │ └── afx-qa-methodology/ + │ └── plugins/ + │ └── code-review/ + ├── .codex/ + │ └── skills/ + │ └── afx-next/ ← core AFX + ├── .agents/ + │ └── skills/ + │ ├── test-driven-development/ ← from pack + │ ├── playwright/ ← from pack + │ └── afx-qa-methodology/ ← from pack + ├── .agent/ + │ └── skills/ + │ ├── test-driven-development/ ← from pack + │ └── afx-qa-methodology/ ← from pack + ├── .gemini/ + │ └── commands/ + │ └── ... + └── .github/ + ├── prompts/ + │ └── ... + └── agents/ ← from pack + ├── afx-qa-methodology.agent.md + └── afx-spec-test-planning.agent.md +``` + +### Interaction Map + +| Element | Hover Action | Click Action | +| -------------------------- | ---------------------------------- | ------------------------------- | +| Pack (installed, enabled) | Show `Disable` `Remove` buttons | Expand/collapse | +| Pack (installed, disabled) | Show `Enable` `Remove` buttons | Expand/collapse | +| Pack (installed) | Right-click: `Preview Changes` | — | +| Pack (available) | Show `Install` button | Expand to show description | +| Pack with update | Show `Update` button | Expand pack details | +| Skill/plugin item | Show `Disable` button (if enabled) | Open file | +| Upstream provider | Show `Refresh` button | Expand to show new items | +| Upstream skill | — | Open in browser (provider page) | +| Skills disk entry | — | Open file in editor | + +### Index Schema (`packs/index.json`) + +```jsonc +{ + "packs": { + "afx-pack-qa": { + "description": "QA Engineer role pack", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"], + }, + "afx-pack-security": { + "description": "Security review and audit pack", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"], + }, + }, + "upstream": { + "anthropics/claude-plugins-official": { + "featured": ["playwright-e2e", "security-scanner", "code-architect"], + }, + "openai/skills": {}, + "anthropics/antigravity-awesome-skills": { + "featured": ["code-architect"], + }, + }, +} +``` + +### Data Sources Summary + +| Source | Type | What it provides | +| ------------------------------- | ------ | ----------------------------------------------------------------------------- | +| `.afx.yaml` | Local | Installed packs, status, installed_ref, per-item disabled list, custom_skills | +| `.afx/packs/{pack}/{provider}/` | Local | Actual items on disk — grouped by provider | +| `.afx/.cache/lastIndex.json` | Local | Cached last fetched index + timestamp for diff | +| `packs/index.json` (remote) | Remote | Pack metadata, upstream featured items (no auth required) | + +### Glossary + +| Term | Definition | +| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Pack | A curated bundle of skills/plugins grouped by role or domain (e.g., `afx-pack-qa`) | +| Skill | A SKILL.md-based capability file auto-discovered by Claude Code or Codex CLI | +| Plugin | A Claude Code plugin with `.claude-plugin/plugin.json` manifest, commands, agents, hooks | +| Provider | An AI coding assistant platform: Claude Code, Codex CLI, Google Antigravity, GitHub Copilot, Gemini CLI (packs support first four only; Gemini has core AFX commands only) | +| Master copy | The pristine downloaded content stored in `.afx/packs/{pack}/{provider}/` | +| Provider copy | The derived copy placed in `.claude/`, `.agents/`, `.agent/`, `.github/` for auto-discovery | +| External skill | A skill downloaded from an upstream repo — never modified by AFX | +| AFX-built skill | A skill authored by the AFX team with guardrails baked in | +| Upstream | External provider repositories tracked for new skill availability | +| Index | `packs/index.json` — the single aggregated metadata file fetched by the extension | +| Guardrails | AFX methodology rules (e.g., @see tracing, two-stage verify) baked into AFX-built skills | diff --git a/docs/specs/vscode-toolbox/tasks.md b/docs/specs/vscode-toolbox/tasks.md new file mode 100644 index 0000000..f5f25e5 --- /dev/null +++ b/docs/specs/vscode-toolbox/tasks.md @@ -0,0 +1,408 @@ +--- +afx: true +type: TASKS +status: Living +owner: "@rix" +version: 1.0 +created: "2026-02-28" +last_verified: "2026-02-28" +tags: [vscode-extension, packs, skills, toolbox] +--- + +# VSCode AFX Toolbox - Implementation Tasks + +**Version:** 1.0 +**Date:** 2026-02-28 +**Status:** Ready for Implementation +**Spec:** [spec.md](./spec.md) +**Design:** [design.md](./design.md) + +--- + +## Task Numbering Convention + +Tasks use hierarchical numbering for cross-referencing: + +- **0.x** - Phase 0: Design Authoring (write design.md — prerequisite for all implementation) +- **1.x** - Phase 1: Foundation & Sidebar Restructure +- **2.x** - Phase 2: Data Layer (index fetch, .afx.yaml reading, file watchers) +- **3.x** - Phase 3: Overview Section +- **4.x** - Phase 4: Packs Section (Installed + Available) +- **5.x** - Phase 5: Upstream Section +- **6.x** - Phase 6: Skills Disk Mirror +- **7.x** - Phase 7: CLI Delegation & Actions + +References: + +- `[FR-N]` = Functional Requirement N from spec.md +- `[NFR-N]` = Non-Functional Requirement N from spec.md +- `[DESIGN-X.X]` = Section X.X from design.md (pending design approval) + +--- + +## Phase 0: Design Authoring + +> Prerequisite: Write design.md before implementation begins. Design currently a placeholder. +> Ref: Spec requirements [FR-1 through FR-24], [NFR-1 through NFR-6] + +### 0.1 Architectural Overview + +> File: `docs/specs/vscode-toolbox/design.md` — Sections 1.1–1.4 + +- [x] Document component architecture: ToolboxTreeDataProvider as single provider with 4 sections +- [x] Document relationship with existing vscode-afx extension (same repo, same activation, shared config parser) +- [x] Document section hierarchy: Overview → Packs → Upstream → Skills +- [x] Document read-only + CLI delegation constraint (extension reads state, `install.sh` writes state) + +### 0.2 Data Models + +> File: `docs/specs/vscode-toolbox/design.md` — Section 2 + +- [x] Define `Pack` interface: name, status (enabled/disabled), installedRef, disabledItems, providers, items +- [x] Define `PackItem` interface: name, type (skill/plugin/agent), providerType, isExternal +- [x] Define `AvailablePack` interface: description, category, providers +- [x] Define `UpstreamProvider` interface: repo, featured, lastFetched, newItems +- [x] Define `ToolboxElement` discriminated union for tree data provider +- [x] Define `CachedIndex` interface: packs, upstream, fetchedAt timestamp + +### 0.3 Data Flow Architecture + +> File: `docs/specs/vscode-toolbox/design.md` — Section 3 + +- [x] Document data sources and flow: `.afx.yaml` → pack state, `.afx/packs/` → installed items, `index.json` → available packs +- [x] Document index fetch strategy: `raw.githubusercontent.com` via Node.js `fetch()`, no auth +- [x] Document cache strategy: `.afx/.cache/lastIndex.json` with timestamp +- [x] Document diff computation: cached vs fresh index for "new since last check" +- [x] Document offline handling: use cached data, show "offline" indicator +- [x] Document data refresh triggers: file watchers, manual check, auto-check on activation + +### 0.4 CLI Integration Design + +> File: `docs/specs/vscode-toolbox/design.md` — Section 6 + +- [x] Document `installShRunner` pattern: spawn child process, capture output, show terminal/notification +- [x] Document command mapping table: UI action → `install.sh` CLI args +- [x] Document error handling: `install.sh` not found, non-zero exit, timeout +- [x] Document dry-run preview flow + +### 0.5 Tree Item Rendering + +> File: `docs/specs/vscode-toolbox/design.md` — Sections 4, 5 + +- [x] Document TreeItem properties for each element type (Pack, Skill, Overview stat, Upstream provider) +- [x] Document icon/codicon mapping for pack states (enabled, disabled, available) +- [x] Document hover actions implementation (inline buttons via `contextValue` + menus) +- [x] Document disabled visual treatment (dimmed icon, strikethrough description) + +### 0.6 File Structure + +> File: `docs/specs/vscode-toolbox/design.md` — Sections 7–9 + +- [x] Document src/toolbox/ directory layout +- [x] Document integration with existing extension.ts activation +- [x] Document new package.json contribution points (commands, menus, settings) + +--- + +## Phase 1: Foundation & Sidebar Restructure + +> Ref: [FR-1], [NFR-1], [NFR-4] + +### 1.1 Rename Skills View to Toolbox + +> File: `src/toolbox/` (new), `package.json` + +- [x] Rename the current **Skills** view to **Toolbox** in sidebar `[FR-1]` +- [x] Update `package.json` contribution points — view ID, title, icon +- [x] Verify other 4 views (Project, Specs, Library, Help) remain unchanged +- [x] Toolbox appears in the same position as the current Skills view + +### 1.2 Create Toolbox Tree Data Provider + +> File: `src/toolbox/toolboxTreeDataProvider.ts` + +- [x] Create `ToolboxTreeDataProvider` implementing `vscode.TreeDataProvider` +- [x] Define tree item types: Overview, Packs (Installed/Available), Upstream, Skills +- [x] Register provider in `extension.ts` activation +- [x] Only activate in projects with `.afx.yaml` `[NFR-1]` +- [ ] Tree renders in < 500ms for ≤ 10 installed packs `[NFR-4]` _(to verify when pack data exists)_ + +### 1.3 Define TypeScript Data Models + +> File: `src/toolbox/models.ts` + +- [x] Define `Pack` interface: name, status, installedRef, disabledItems, providers, items +- [x] Define `PackItem` interface: name, itemType, isExternal, isDisabled, filePath +- [x] Define `AvailablePack` interface: name, description, category, providers +- [x] Define `UpstreamProvider` interface: repo, featured, lastFetched, newSinceLastCheck +- [x] Define `CachedIndex` interface: fetchedAt, packs, upstream +- [x] Define `ToolboxElement` discriminated union (12 variants) + +--- + +## Phase 2: Data Layer + +> Ref: [FR-15], [FR-16], [FR-17], [FR-20], [FR-21], [NFR-2], [NFR-3] + +### 2.1 Read `.afx.yaml` Pack State + +> File: `src/toolbox/afxYamlReader.ts` + +- [x] Parse `.afx.yaml` and extract `packs:` section `[FR-20]` +- [x] Extract per-pack: name, status, installed_ref, disabled_items `[FR-20]` +- [x] Extract `custom_skills:` list `[FR-20]` +- [x] Handle missing `.afx.yaml` or missing `packs:` section gracefully + +### 2.2 Read `.afx/` Directory Structure + +> File: `src/toolbox/afxDirReader.ts` + +- [x] Scan `.afx/packs/{pack}/{provider}/` to enumerate installed items `[FR-21]` +- [x] Map items to their provider and type (skill/plugin/agent) +- [x] Handle missing `.afx/` directory (no packs installed) + +### 2.3 Index Fetch & Cache + +> File: `src/toolbox/indexService.ts` + +- [x] Fetch `packs/index.json` from `raw.githubusercontent.com` via Node.js `fetch()` `[FR-15]` +- [x] Cache fetched index at `.afx/.cache/lastIndex.json` with timestamp `[FR-16]` +- [x] No authentication required `[NFR-3]` +- [x] Handle offline gracefully — use cached data with "offline" indicator `[NFR-2]` + +### 2.4 Index Diff + +> File: `src/toolbox/indexService.ts` + +- [x] Diff cached `lastIndex.json` vs freshly fetched index `[FR-17]` +- [x] Compute "new since last check" items per upstream provider `[FR-17]` +- [x] Compute "available packs" (in index but not in `.afx.yaml`) `[FR-17]` + +### 2.5 File Watchers + +> File: `src/toolbox/toolboxWatchers.ts` + +- [x] Watch `.afx.yaml` for pack state changes — refresh tree on change _(existing in fileWatcher.ts)_ +- [x] Watch `.afx/packs/` for item changes — refresh Packs section +- [x] Watch provider directories (`.claude/`, `.codex/`, `.agents/`, `.agent/`, `.gemini/`, `.github/`) — refresh Skills section +- [x] Debounce refresh to avoid excessive redraws (500ms) + +--- + +## Phase 3: Overview Section + +> Ref: [FR-2], [FR-18], [FR-23] + +### 3.1 Overview Tree Items + +> File: `src/toolbox/toolboxTreeProvider.ts` (getOverviewChildren) + +- [x] Show active provider count (e.g., "Providers: 4 active") `[FR-2]` +- [x] Show installed pack count with item total `[FR-2]` +- [x] Show updates summary from index diff `[FR-2]` +- [x] Show last checked timestamp with relative formatting `[FR-2]` + +### 3.2 Check Button + +> File: `src/toolbox/toolboxCommands.ts` + +- [x] Add **Check** inline button to trigger on-demand index fetch `[FR-18]` +- [x] Update last checked timestamp after fetch +- [x] Refresh tree after check completes +- [x] Show progress notification during fetch +- [x] Show summary notification (new packs/skills or "up to date") + +### 3.3 Auto-Check on Activation + +> File: `src/toolbox/toolboxCommands.ts` (autoCheckIfDue) + +- [x] Auto-check index on extension activation (configurable setting) `[FR-23]` +- [x] Add `afx.toolbox.autoCheck` setting to `package.json` +- [x] Add `afx.toolbox.autoCheckInterval` setting (default 86400s) +- [x] Only fetch if elapsed time exceeds interval + +--- + +## Phase 4: Packs Section + +> Ref: [FR-3] through [FR-9], [FR-20], [FR-21] + +### 4.1 Installed Packs List + +> File: `src/toolbox/toolboxTreeProvider.ts` (pack-group children) + +- [x] List all packs from `.afx.yaml` under "Installed" group `[FR-3]` +- [x] Each pack shows: name, status, provider count, item count, installed ref `[FR-4]` +- [x] Enabled packs expand to show provider → item hierarchy `[FR-5]` +- [x] Disabled packs show with visual indicator (dimmed icon) `[NFR-6]` + +### 4.2 Pack Item Details + +> File: `src/toolbox/toolboxTreeProvider.ts` (pack-provider, pack-item) + +- [x] Provider subdirectories show as children: `claude/`, `codex/`, `antigravity/`, `copilot/` +- [x] Individual items show within each provider `[FR-5]` +- [x] Items labeled as external (pristine) or afx-built `[FR-5]` + +### 4.3 Available Packs List + +> File: `src/toolbox/toolboxTreeProvider.ts` (available-pack) + +- [x] List packs from index that are NOT in `.afx.yaml` `[FR-6]` +- [x] Each shows name, description `[FR-6]` + +### 4.4 Pack Hover Actions + +> File: `src/toolbox/toolboxCommands.ts`, `package.json` menus + +- [x] Enabled pack hover: **Update**, **Disable**, and **Remove** buttons (`contextValue: pack-enabled`) `[FR-7]` `[FR-8]` +- [x] Disabled pack hover: **Enable** and **Remove** buttons (`contextValue: pack-disabled`) `[FR-7]` +- [x] Available pack hover: **Install** button (`contextValue: pack-available`) `[FR-6]` +- [x] Enabled skill hover: **Disable** button (`contextValue: pack-item-enabled`) `[FR-9]` +- [x] Disabled skill hover: **Enable** button (`contextValue: pack-item-disabled`) `[FR-9]` + +--- + +## Phase 5: Upstream Section + +> Ref: [FR-11], [FR-12], [FR-19] + +### 5.1 Upstream Provider List + +> File: `src/toolbox/toolboxTreeProvider.ts` (upstream-provider, upstream-item) + +- [x] List tracked upstream providers from index `[FR-11]` +- [x] Show last fetched timestamp per provider `[FR-12]` +- [x] Show "new since last check" items computed from index diff `[FR-12]` + +### 5.2 Upstream Interactions + +> File: `src/toolbox/toolboxCommands.ts` + +- [x] **Refresh** button triggers re-fetch of index `[FR-19]` +- [ ] Clicking upstream skill opens provider page in browser `[FR-11]` _(future: needs URL resolution from repo name)_ +- [x] Works offline — shows cached data with "offline" indicator `[NFR-2]` + +--- + +## Phase 6: Skills Disk Mirror + +> Ref: [FR-13], [FR-14] + +### 6.1 Provider Directory Tree + +> File: `src/toolbox/toolboxTreeProvider.ts` (skills-provider, skills-dir, skills-file) + +- [x] Show provider directories as-is from disk: `.claude/`, `.codex/`, `.agents/`, `.agent/`, `.gemini/`, `.github/` `[FR-13]` +- [x] Mirror actual folder structure — no grouping, no attribution `[FR-13]` +- [x] Include `.github/agents/` subdirectory `[FR-13]` + +### 6.2 Click-to-Open + +> File: `src/toolbox/toolboxTreeProvider.ts` + +- [x] Click any item to open file in VSCode editor `[FR-14]` +- [x] Reflect live disk state (auto-refresh via file watchers from 2.5) + +--- + +## Phase 7: CLI Delegation & Actions + +> Ref: [FR-10], [FR-22], [FR-24], [NFR-5] + +### 7.1 install.sh Integration + +> File: `src/toolbox/installShRunner.ts` + +- [x] Implement `runInstallSh()` — curl-pipe to install.sh with args `[FR-10]` +- [x] Extension never writes files directly — all mutations via `install.sh` `[NFR-5]` +- [x] Show terminal output on action completion `[FR-10]` +- [x] Refresh tree after terminal closes (1500ms delay) + +### 7.2 CLI Command Mapping + +> File: `src/toolbox/toolboxCommands.ts` + +- [x] Install: `--pack {name} .` `[FR-10]` +- [x] Remove: `--pack-remove {name} .` `[FR-10]` +- [x] Disable pack: `--pack-disable {name} .` `[FR-10]` +- [x] Enable pack: `--pack-enable {name} .` `[FR-10]` +- [x] Disable skill: `--pack {pack} --skill-disable {name} .` `[FR-10]` +- [x] Enable skill: `--pack {pack} --skill-enable {name} .` `[FR-10]` +- [x] Update: `--update --packs .` `[FR-10]` +- [ ] Dry run: `--dry-run --pack {name} .` `[FR-22]` _(future: needs UI trigger)_ + +### 7.3 Setup AFX Button + +> File: `src/extension.ts` (existing offerInstall/installAfx) + +- [x] Show **Setup AFX** button when `.afx.yaml` not available `[FR-24]` _(existing functionality)_ +- [x] Bootstrap via curl `[FR-24]` _(existing functionality)_ + +--- + +## Implementation Flow + +``` +Phase 0: Design Authoring (write design.md) + ↓ +Phase 1: Foundation & Sidebar Restructure + ↓ +Phase 2: Data Layer (index, .afx.yaml, file watchers) + ↓ (parallel) + ├── Phase 3: Overview Section + ├── Phase 4: Packs Section + ├── Phase 5: Upstream Section + └── Phase 6: Skills Disk Mirror + ↓ +Phase 7: CLI Delegation & Actions +``` + +--- + +## Cross-Reference Index + +| Task | Spec Requirements | Design Section | +| ---- | -------------------------- | ------------------------------------------------ | +| 0.1 | FR-1–FR-24, NFR-1–NFR-6 | 1. Architecture (1.1–1.4) | +| 0.2 | — | 2. Data Models (2.1–2.2) | +| 0.3 | FR-15, FR-16, FR-17, NFR-2 | 3. Data Flow (3.1–3.5) | +| 0.4 | FR-10, FR-22, NFR-5 | 6. CLI Integration (6.1–6.3) | +| 0.5 | FR-4, FR-5, FR-7, NFR-6 | 4. Tree View Structure, 5. Hover Actions | +| 0.6 | — | 7. File Watchers, 8. Settings, 9. File Structure | +| 1.1 | FR-1 | 1.3 Integration, 9.3 package.json | +| 1.2 | NFR-1, NFR-4 | 1.1 System Context, 12. Performance | +| 1.3 | — | 2.1 Core Interfaces, 2.2 Tree Element | +| 2.1 | FR-20 | 3.4 Pack State Assembly | +| 2.2 | FR-21 | 3.4 Pack State Assembly | +| 2.3 | FR-15, NFR-2, NFR-3 | 3.2 Index Fetch Strategy | +| 2.4 | FR-17 | 3.3 Index Diff Computation | +| 2.5 | — | 7. File Watchers | +| 3.1 | FR-2 | 4.2 Overview Section | +| 3.2 | FR-18 | 4.2 Overview Section (Check button) | +| 3.3 | FR-23 | 8.2 Auto-Check Logic | +| 4.1 | FR-3, FR-4, FR-5, NFR-6 | 4.3 Packs Section (Installed) | +| 4.2 | FR-5 | 4.3 Packs Section (Provider hierarchy) | +| 4.3 | FR-6 | 4.3 Packs Section (Available) | +| 4.4 | FR-6, FR-7, FR-8, FR-9 | 5. Hover Actions (5.1–5.3) | +| 5.1 | FR-11, FR-12 | 4.4 Upstream Section | +| 5.2 | FR-11, FR-19, NFR-2 | 4.4 Upstream, 3.5 Offline Handling | +| 6.1 | FR-13 | 4.5 Skills Section (Disk Mirror) | +| 6.2 | FR-14 | 4.5 Skills Section (click-to-open) | +| 7.1 | FR-10, NFR-5 | 6.1 Runner Pattern | +| 7.2 | FR-10, FR-22 | 6.2 Command Mapping | +| 7.3 | FR-24 | 6.3 Setup AFX Button | + +--- + +## Notes + +- **Phase 0 COMPLETE** — design.md written with 13 sections covering architecture, data models, data flow, tree views, hover actions, CLI integration, file watchers, settings, file structure, decisions, error handling, performance, and dependencies +- **Design includes full AFX sidebar mockup** — all 5 views (Project, Specs, Library, Toolbox, Help) with every element expanded and all hover actions shown (design.md §4.1) +- **Update action is unconditional** — the afx-packs index has no version info ([spec open question #7](./spec.md#open-questions)), so Update always appears on enabled packs. No `pack-update` contextValue — only `pack-enabled` and `pack-disabled` +- **All Toolbox data sourced from afx-packs output** — `.afx.yaml`, `.afx/packs/`, `.afx/.cache/lastIndex.json`, `packs/index.json`. No invented fields +- Phase 1 and 2 are sequential — foundation must exist before data layer +- Phases 3–6 can be worked in parallel once the data layer is ready +- Phase 7 (CLI delegation) connects to Phase 4 actions — can be built alongside Phase 4 +- This spec depends on the [afx-packs](../afx-packs/spec.md) infrastructure — `install.sh` pack commands must exist before Phase 7 is testable +- The Toolbox replaces the current **Skills** view in `vscode-afx` — same repo, same extension, additive change diff --git a/install.sh b/install.sh index b83d1b2..1ee83fc 100755 --- a/install.sh +++ b/install.sh @@ -7,7 +7,7 @@ # # Options: # --update Update existing AFX installation (preserves user content) -# --commands-only Only install/update command assets (.claude + .codex) +# --commands-only Only install/update command assets (.claude + .codex + .agent) # --no-claude-md Skip CLAUDE.md snippet integration # --no-agents-md Skip AGENTS.md snippet integration # --no-gemini-md Skip GEMINI.md snippet integration @@ -15,38 +15,41 @@ # --no-docs Skip copying AFX documentation to docs/agenticflowx/ # --force Overwrite all existing files (fresh install) # --dry-run Show what would be changed without making changes +# --yes Skip all confirmation prompts (non-interactive mode) +# --reset Reset AFX: recreate .afx/ folder, .afx.yaml defaults set -e -# AFX Version (dynamic from CHANGELOG.md) +# ============================================================================ +# Section 1: Constants & Colors +# ============================================================================ + AFX_REPO="rixrix/afx" -AFX_VERSION=$(curl -sL "https://raw.githubusercontent.com/${AFX_REPO}/main/CHANGELOG.md" | awk '/^## \[/ {print substr($2, 2, length($2)-2); exit}') -if [ -z "$AFX_VERSION" ]; then - AFX_VERSION="Unknown" -fi -# Boundary markers for CLAUDE.md +# Boundary markers AFX_START_MARKER="<!-- AFX:START - Managed by AFX. Do not edit manually. -->" AFX_END_MARKER="<!-- AFX:END -->" -# Boundary markers for AGENTS.md AFX_AGENTS_START_MARKER="<!-- AFX-CODEX:START - Managed by AFX. Do not edit manually. -->" AFX_AGENTS_END_MARKER="<!-- AFX-CODEX:END -->" -# Boundary markers for GEMINI.md AFX_GEMINI_START_MARKER="<!-- AFX-GEMINI:START - Managed by AFX. Do not edit manually. -->" AFX_GEMINI_END_MARKER="<!-- AFX-GEMINI:END -->" -# Boundary markers for copilot-instructions.md AFX_COPILOT_START_MARKER="<!-- AFX-COPILOT:START - Managed by AFX. Do not edit manually. -->" AFX_COPILOT_END_MARKER="<!-- AFX-COPILOT:END -->" -# Colors for output +# Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' -NC='\033[0m' # No Color +BOLD='\033[1m' +DIM='\033[2m' +NC='\033[0m' + +# ============================================================================ +# Section 2: Default Options +# ============================================================================ -# Default options UPDATE_MODE=false COMMANDS_ONLY=false NO_CLAUDE_MD=false @@ -56,76 +59,131 @@ NO_COPILOT_MD=false NO_DOCS=false FORCE=false DRY_RUN=false +YES=false +RESET=false TARGET_DIR="" -# Parse arguments +# Provider selection (set by select_providers on first install) +INSTALL_CLAUDE=true +INSTALL_CODEX=true +INSTALL_ANTIGRAVITY=true +INSTALL_GEMINI=true +INSTALL_COPILOT=true + +# Pack management options +# @see docs/specs/afx-packs/design.md#33-new-arguments +PACK_NAMES=() +PACK_DISABLE="" +PACK_ENABLE="" +PACK_REMOVE="" +PACK_LIST=false +SKILL_DISABLE="" +SKILL_ENABLE="" +UPDATE_PACKS=false +ADD_SKILL="" +BRANCH="" +VERSION="" + +# Change tracking +INSTALLED=() +UPDATED=() +SKIPPED=() + +# ============================================================================ +# Section 3: Argument Parsing +# ============================================================================ + while [[ $# -gt 0 ]]; do case $1 in - --update) - UPDATE_MODE=true - shift - ;; - --commands-only) - COMMANDS_ONLY=true - shift - ;; - --no-claude-md) - NO_CLAUDE_MD=true - shift - ;; - --no-agents-md) - NO_AGENTS_MD=true - shift - ;; - --no-gemini-md) - NO_GEMINI_MD=true - shift - ;; - --no-copilot-md) - NO_COPILOT_MD=true - shift - ;; - --no-docs) - NO_DOCS=true - shift - ;; - --force) - FORCE=true - shift - ;; - --dry-run) - DRY_RUN=true - shift - ;; + --update) UPDATE_MODE=true; shift ;; + --commands-only) COMMANDS_ONLY=true; shift ;; + --no-claude-md) NO_CLAUDE_MD=true; shift ;; + --no-agents-md) NO_AGENTS_MD=true; shift ;; + --no-gemini-md) NO_GEMINI_MD=true; shift ;; + --no-copilot-md) NO_COPILOT_MD=true; shift ;; + --no-docs) NO_DOCS=true; shift ;; + --force) FORCE=true; shift ;; + --dry-run) DRY_RUN=true; shift ;; + --yes|-y) YES=true; shift ;; + --reset) RESET=true; shift ;; + --pack) PACK_NAMES+=("$2"); shift 2 ;; + --pack-disable) PACK_DISABLE="$2"; shift 2 ;; + --pack-enable) PACK_ENABLE="$2"; shift 2 ;; + --pack-remove) PACK_REMOVE="$2"; shift 2 ;; + --pack-list) PACK_LIST=true; shift ;; + --skill-disable) SKILL_DISABLE="$2"; shift 2 ;; + --skill-enable) SKILL_ENABLE="$2"; shift 2 ;; + --packs) UPDATE_PACKS=true; shift ;; + --add-skill) ADD_SKILL="$2"; shift 2 ;; + --branch) BRANCH="$2"; shift 2 ;; + --version) VERSION="$2"; shift 2 ;; -h|--help) - echo "AFX Installer v${AFX_VERSION}" - echo "" - echo "Usage: ./install.sh [OPTIONS] <target-project-path>" - echo "" - echo "Options:" - echo " --update Update existing AFX installation" - echo " --commands-only Only install/update command assets (.claude + .codex)" - echo " --no-claude-md Skip CLAUDE.md snippet integration" - echo " --no-agents-md Skip AGENTS.md snippet integration" - echo " --no-gemini-md Skip GEMINI.md snippet integration" - echo " --no-copilot-md Skip copilot-instructions.md snippet integration" - echo " --no-docs Skip copying AFX documentation to docs/agenticflowx/" - echo " --force Overwrite all files (fresh install)" - echo " --dry-run Preview changes without applying" - echo " -h, --help Show this help message" - echo "" - echo "Examples:" - echo " # Fresh install" - echo " ./install.sh /path/to/my-project" - echo "" - echo " # Update existing installation" - echo " ./install.sh --update ." - echo "" - echo " # Remote install" - echo " curl -sL https://raw.githubusercontent.com/${AFX_REPO}/main/install.sh | bash -s -- ." - echo "" - echo " # Remote update" - echo " curl -sL https://raw.githubusercontent.com/${AFX_REPO}/main/install.sh | bash -s -- --update ." + cat <<'HELPEOF' +AFX Installer + +Usage: ./install.sh [OPTIONS] <target-project-path> + +Options: + --update Update existing AFX installation + --commands-only Only install/update command assets (.claude + .codex + .agent) + --no-claude-md Skip CLAUDE.md snippet integration + --no-agents-md Skip AGENTS.md snippet integration + --no-gemini-md Skip GEMINI.md snippet integration + --no-copilot-md Skip copilot-instructions.md snippet integration + --no-docs Skip copying AFX documentation to docs/agenticflowx/ + --force Overwrite all files (fresh install) + --dry-run Preview changes without applying + --yes, -y Skip all confirmation prompts + --reset Reset AFX: recreate .afx/ folder and config files + --branch NAME Use a specific branch (default: main) + --version TAG Use a specific version tag (e.g., 1.5.3 or v1.5.3) + -h, --help Show this help message + +Pack Management: + --pack NAME Install and enable a pack + --pack-disable NAME Disable a pack (keep master) + --pack-enable NAME Re-enable a disabled pack + --pack-remove NAME Remove a pack entirely + --pack-list List installed packs + --skill-disable NAME --pack P Disable a skill within a pack + --skill-enable NAME --pack P Re-enable a skill within a pack + --update --packs Update all enabled packs + --add-skill REPO:PATH/SKILL Install a single skill (no pack) + +Examples: + # Fresh install (interactive — choose your providers) + ./install.sh . + + # Non-interactive install (all providers) + ./install.sh --yes . + + # Update existing installation + ./install.sh --update . + + # Remote install + curl -sL https://raw.githubusercontent.com/rixrix/afx/main/install.sh | bash -s -- . + + # Install QA pack (short or full name) + ./install.sh --pack qa . + ./install.sh --pack afx-pack-qa . + + # Install from version + ./install.sh --version 1.5.3 --pack qa . + + # Multiple packs + ./install.sh --pack qa --pack security . + + # Manage packs (short or full name) + ./install.sh --pack-disable afx-pack-qa . + ./install.sh --pack-enable qa . + ./install.sh --pack-list . + + # Update all packs + ./install.sh --update --packs . + + # Reset AFX (recreate .afx/ folder and config) + ./install.sh --reset . +HELPEOF exit 0 ;; *) @@ -135,58 +193,28 @@ while [[ $# -gt 0 ]]; do esac done -# Validate target directory -if [ -z "$TARGET_DIR" ]; then - echo -e "${RED}Error: Target project path required${NC}" - echo "Usage: ./install.sh [--update] /path/to/project" - exit 1 -fi - -# Resolve to absolute path -TARGET_DIR=$(cd "$TARGET_DIR" 2>/dev/null && pwd || echo "$TARGET_DIR") - -if [ ! -d "$TARGET_DIR" ]; then - echo -e "${RED}Error: Directory does not exist: $TARGET_DIR${NC}" - exit 1 -fi - -# Header -if [ "$UPDATE_MODE" = "true" ]; then - echo -e "${BLUE}AFX Updater v${AFX_VERSION}${NC}" -else - echo -e "${BLUE}AFX Installer v${AFX_VERSION}${NC}" -fi -echo "Target: $TARGET_DIR" -if [ "$DRY_RUN" = "true" ]; then - echo -e "${YELLOW}(Dry run - no changes will be made)${NC}" -fi -echo "" - -# Determine AFX source directory -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || echo "")" - -if [ -z "$SCRIPT_DIR" ] || [ ! -f "$SCRIPT_DIR/.afx.yaml.template" ]; then - echo -e "${YELLOW}Downloading AFX from GitHub...${NC}" - TEMP_DIR=$(mktemp -d) - trap "rm -rf $TEMP_DIR" EXIT - - git clone --depth 1 --quiet https://github.com/${AFX_REPO}.git "$TEMP_DIR/afx" 2>/dev/null || { - echo -e "${RED}Error: Failed to clone AFX repository${NC}" - echo "Check your internet connection or clone manually:" - echo " git clone https://github.com/${AFX_REPO}.git" - exit 1 - } - AFX_DIR="$TEMP_DIR/afx" -else - AFX_DIR="$SCRIPT_DIR" -fi +# ============================================================================ +# Section 4: Core Helper Functions +# ============================================================================ -# Track changes -INSTALLED=() -UPDATED=() -SKIPPED=() +# Ask for confirmation. Returns 0 (yes) or 1 (no). +# Usage: confirm "Do something?" && do_it +# Defaults to Yes if user just presses Enter. +# Skipped entirely if --yes flag is set. +confirm() { + local prompt="$1" + if [[ "$YES" == "true" || "$DRY_RUN" == "true" ]]; then + return 0 + fi + echo -en "${BOLD}${prompt}${NC} ${DIM}[Y/n]${NC} " + read -r answer </dev/tty + case "$answer" in + [nN]|[nN][oO]) return 1 ;; + *) return 0 ;; + esac +} -# Helper: Install or update a file +# Install or update a single file install_file() { local src="$1" local dest="$2" @@ -221,7 +249,7 @@ install_file() { fi } -# Helper: Install or update a directory by replacing destination contents +# Install or update a directory by replacing destination contents install_directory() { local src="$1" local dest="$2" @@ -257,464 +285,1904 @@ install_directory() { fi } -# ============================================================================ -# 1. Install/Update Claude slash commands -# ============================================================================ -echo -e "${BLUE}[1/11] Installing Claude slash commands...${NC}" -COMMANDS_DIR="$TARGET_DIR/.claude/commands" +# Update a markdown file with AFX boundary markers (reusable for CLAUDE.md, AGENTS.md, etc.) +# Usage: update_md_with_markers <file> <start_marker> <end_marker> <snippet_content> <label> <header> +update_md_with_markers() { + local md_file="$1" + local start_marker="$2" + local end_marker="$3" + local snippet_content="$4" + local label="$5" + local header_content="$6" + + local section="${start_marker} +<!-- AFX Version: ${AFX_VERSION} --> -if [ "$DRY_RUN" != "true" ]; then - mkdir -p "$COMMANDS_DIR" -fi +${snippet_content} +${end_marker}" -for cmd in "$AFX_DIR"/.claude/commands/afx-*.md; do - if [ -f "$cmd" ]; then - filename=$(basename "$cmd") - # Commands are always updated in update mode - install_file "$cmd" "$COMMANDS_DIR/$filename" "Command: $filename" "$UPDATE_MODE" + if [ "$DRY_RUN" = "true" ]; then + if [ -f "$md_file" ]; then + if grep -q "$start_marker" "$md_file" 2>/dev/null; then + UPDATED+=("$label AFX section (would update)") + elif [[ "$label" == "CLAUDE.md" ]] && grep -q "## Documentation References\|## AgenticFlow" "$md_file" 2>/dev/null; then + SKIPPED+=("$label (has old AFX section - run with --force to migrate)") + else + INSTALLED+=("$label AFX section (would append)") + fi + else + INSTALLED+=("$label (would create)") + fi + return 0 fi -done - -# ============================================================================ -# 2. Install/Update Codex skills -# ============================================================================ -echo -e "${BLUE}[2/11] Installing Codex skills...${NC}" -CODEX_SKILLS_DIR="$TARGET_DIR/.codex/skills" - -if [ "$DRY_RUN" != "true" ]; then - mkdir -p "$CODEX_SKILLS_DIR" -fi -if [ -d "$AFX_DIR/.codex/skills" ]; then - for skill_dir in "$AFX_DIR"/.codex/skills/afx-*; do - if [ -d "$skill_dir" ]; then - skill_name=$(basename "$skill_dir") - install_directory "$skill_dir" "$CODEX_SKILLS_DIR/$skill_name" "Codex skill: $skill_name" "$UPDATE_MODE" + if [ -f "$md_file" ]; then + if grep -q "$start_marker" "$md_file"; then + # Has boundary markers — replace the section + awk -v start="$start_marker" '$0 == start { exit } { print }' "$md_file" > "$md_file.tmp" + echo "$section" >> "$md_file.tmp" + awk -v end="$end_marker" 'BEGIN { skip=1 } $0 == end { skip=0; next } !skip { print }' "$md_file" >> "$md_file.tmp" + mv "$md_file.tmp" "$md_file" + UPDATED+=("$label AFX section") + elif [[ "$label" == "CLAUDE.md" ]] && grep -q "## Documentation References\|## AgenticFlow" "$md_file"; then + # Old-style section without markers + if [ "$FORCE" = "true" ]; then + echo -e "${YELLOW}Warning: Migrating old AFX section to use boundary markers${NC}" + echo "" >> "$md_file" + echo "$section" >> "$md_file" + UPDATED+=("$label (migrated - please remove old AFX section manually)") + else + SKIPPED+=("$label (has old AFX section - use --force to migrate)") + fi + else + # No AFX section — append + echo "" >> "$md_file" + echo "$section" >> "$md_file" + INSTALLED+=("$label AFX section") fi - done -fi - -# ============================================================================ -# 3. Install/Update Gemini CLI commands -# ============================================================================ -echo -e "${BLUE}[3/11] Installing Gemini CLI commands...${NC}" -GEMINI_COMMANDS_DIR="$TARGET_DIR/.gemini/commands" - -if [ "$DRY_RUN" != "true" ]; then - mkdir -p "$GEMINI_COMMANDS_DIR" -fi + else + # Create new file + mkdir -p "$(dirname "$md_file")" + echo "$header_content" > "$md_file" + echo "" >> "$md_file" + echo "$section" >> "$md_file" + INSTALLED+=("$label (created)") + fi +} -if [ -d "$AFX_DIR/.gemini/commands" ]; then - for cmd in "$AFX_DIR"/.gemini/commands/afx-*.md; do - if [ -f "$cmd" ]; then - filename=$(basename "$cmd") - install_file "$cmd" "$GEMINI_COMMANDS_DIR/$filename" "Gemini command: $filename" "$UPDATE_MODE" - fi - done -fi +# Ensure a pattern is in .gitignore +# @see docs/specs/afx-packs/design.md#310-helper-functions +ensure_gitignore() { + local pattern="$1" + local gitignore="$TARGET_DIR/.gitignore" + [[ "$DRY_RUN" == "true" ]] && return 0 + if [[ -f "$gitignore" ]]; then + grep -qF "$pattern" "$gitignore" || echo "$pattern" >> "$gitignore" + else + echo "$pattern" > "$gitignore" + fi +} # ============================================================================ -# 4. Install/Update GitHub Copilot prompts +# Section 5: Provider Selection (first install only) # ============================================================================ -echo -e "${BLUE}[4/11] Installing GitHub Copilot prompts...${NC}" -COPILOT_PROMPTS_DIR="$TARGET_DIR/.github/prompts" -if [ "$DRY_RUN" != "true" ]; then - mkdir -p "$COPILOT_PROMPTS_DIR" -fi +# Interactive provider selection menu for first install. +# Sets INSTALL_* flags and NO_*_MD flags based on user choices. +select_providers() { + # Skip selection in non-interactive modes + if [[ "$YES" == "true" || "$DRY_RUN" == "true" || "$UPDATE_MODE" == "true" ]]; then + return 0 + fi -if [ -d "$AFX_DIR/.github/prompts" ]; then - for prompt in "$AFX_DIR"/.github/prompts/afx-*.prompt.md; do - if [ -f "$prompt" ]; then - filename=$(basename "$prompt") - install_file "$prompt" "$COPILOT_PROMPTS_DIR/$filename" "Copilot prompt: $filename" "$UPDATE_MODE" - fi - done - # Also install the README - if [ -f "$AFX_DIR/.github/prompts/README.md" ]; then - install_file "$AFX_DIR/.github/prompts/README.md" "$COPILOT_PROMPTS_DIR/README.md" "Copilot prompts README" "$UPDATE_MODE" + # Skip if any --no-* flags were explicitly set (user knows what they want) + if [[ "$NO_CLAUDE_MD" == "true" || "$NO_AGENTS_MD" == "true" || \ + "$NO_GEMINI_MD" == "true" || "$NO_COPILOT_MD" == "true" ]]; then + return 0 fi -fi -if [ "$COMMANDS_ONLY" = "true" ]; then echo "" - echo -e "${GREEN}Commands processed!${NC}" + echo -e "${BOLD}Which AI coding tools do you use?${NC}" echo "" - [ ${#INSTALLED[@]} -gt 0 ] && echo "Installed: ${#INSTALLED[@]}" && printf ' + %s\n' "${INSTALLED[@]}" - [ ${#UPDATED[@]} -gt 0 ] && echo "Updated: ${#UPDATED[@]}" && printf ' ~ %s\n' "${UPDATED[@]}" - [ ${#SKIPPED[@]} -gt 0 ] && echo "Skipped: ${#SKIPPED[@]}" && printf ' - %s\n' "${SKIPPED[@]}" - exit 0 -fi + echo -e " ${GREEN}1${NC}) Claude Code ${DIM}(Anthropic)${NC}" + echo -e " ${GREEN}2${NC}) Codex CLI ${DIM}(OpenAI)${NC}" + echo -e " ${GREEN}3${NC}) Antigravity ${DIM}(Anthropic)${NC}" + echo -e " ${GREEN}4${NC}) Gemini CLI ${DIM}(Google)${NC}" + echo -e " ${GREEN}5${NC}) GitHub Copilot ${DIM}(GitHub)${NC}" + echo "" + echo -e " ${CYAN}a${NC}) All of the above" + echo "" + echo -en "${BOLD}Select providers (comma-separated, e.g. 1,4):${NC} " + read -r selection </dev/tty -# ============================================================================ -# 5. Install/Update templates -# ============================================================================ -echo -e "${BLUE}[5/11] Installing templates...${NC}" -TEMPLATES_DIR="$TARGET_DIR/docs/agenticflowx/templates" + # Default to all if empty + if [[ -z "$selection" ]]; then + selection="a" + fi -if [ -d "$AFX_DIR/templates" ]; then - for tpl in "$AFX_DIR"/templates/*.md; do - if [ -f "$tpl" ]; then - filename=$(basename "$tpl") - install_file "$tpl" "$TEMPLATES_DIR/$filename" "Template: $filename" "$UPDATE_MODE" - fi + # If "all", keep everything true and return + if [[ "$selection" == "a" || "$selection" == "A" || "$selection" == "all" ]]; then + echo -e "${GREEN}Installing for all providers.${NC}" + echo "" + return 0 + fi + + # Reset all to false, then enable selected ones + INSTALL_CLAUDE=false + INSTALL_CODEX=false + INSTALL_ANTIGRAVITY=false + INSTALL_GEMINI=false + INSTALL_COPILOT=false + + IFS=',' read -ra choices <<< "$selection" + for choice in "${choices[@]}"; do + choice=$(echo "$choice" | tr -d ' ') + case "$choice" in + 1) INSTALL_CLAUDE=true ;; + 2) INSTALL_CODEX=true ;; + 3) INSTALL_ANTIGRAVITY=true ;; + 4) INSTALL_GEMINI=true ;; + 5) INSTALL_COPILOT=true ;; + *) echo -e "${YELLOW}Unknown option '$choice' — skipping${NC}" ;; + esac done -fi + + # Map provider flags to --no-* flags + [[ "$INSTALL_CLAUDE" == "false" ]] && NO_CLAUDE_MD=true + [[ "$INSTALL_CODEX" == "false" ]] && NO_AGENTS_MD=true + [[ "$INSTALL_GEMINI" == "false" ]] && NO_GEMINI_MD=true + [[ "$INSTALL_COPILOT" == "false" ]] && NO_COPILOT_MD=true + + # Show summary + local selected=() + [[ "$INSTALL_CLAUDE" == "true" ]] && selected+=("Claude Code") + [[ "$INSTALL_CODEX" == "true" ]] && selected+=("Codex CLI") + [[ "$INSTALL_ANTIGRAVITY" == "true" ]] && selected+=("Antigravity") + [[ "$INSTALL_GEMINI" == "true" ]] && selected+=("Gemini CLI") + [[ "$INSTALL_COPILOT" == "true" ]] && selected+=("GitHub Copilot") + + echo -e "${GREEN}Selected: ${selected[*]}${NC}" + echo "" +} # ============================================================================ -# 6. Create/Update .afx.yaml +# Section 6: Pack Management Functions +# @see docs/specs/afx-packs/design.md#3-installsh-architecture +# @see docs/specs/afx-packs/tasks.md#phase-3-installsh--download--detection # ============================================================================ -echo -e "${BLUE}[6/11] Managing configuration...${NC}" -AFX_CONFIG="$TARGET_DIR/.afx.yaml" -if [ -f "$AFX_CONFIG" ]; then - # Config exists - never overwrite unless --force (user customizations) - if [ "$FORCE" = "true" ]; then - install_file "$AFX_DIR/.afx.yaml.template" "$AFX_CONFIG" ".afx.yaml" "true" +# Resolve the git ref for AFX repo fetches +# @see docs/specs/afx-packs/design.md#34-download-strategy +resolve_ref() { + if [[ -n "$VERSION" && -n "$BRANCH" ]]; then + echo -e "${RED}Error: --version and --branch are mutually exclusive${NC}" >&2 + return 1 + fi + if [[ -n "$VERSION" ]]; then + [[ "$VERSION" == v* ]] && echo "$VERSION" || echo "v$VERSION" + elif [[ -n "$BRANCH" ]]; then + echo "$BRANCH" else - SKIPPED+=(".afx.yaml (preserved - user config)") + local yaml_version="" + if [[ -f "$TARGET_DIR/.afx.yaml" ]]; then + yaml_version=$(grep '^version:' "$TARGET_DIR/.afx.yaml" 2>/dev/null | awk '{print $2}' | tr -d "'\"") + fi + if [[ -n "$yaml_version" && "$yaml_version" != "main" ]]; then + if [[ "$yaml_version" =~ ^[0-9] ]]; then + [[ "$yaml_version" == v* ]] && echo "$yaml_version" || echo "v$yaml_version" + else + echo "$yaml_version" + fi + else + echo "main" + fi fi -else - install_file "$AFX_DIR/.afx.yaml.template" "$AFX_CONFIG" ".afx.yaml" -fi +} -# ============================================================================ -# 7. Update CLAUDE.md with boundary markers -# ============================================================================ -if [ "$NO_CLAUDE_MD" != "true" ]; then - echo -e "${BLUE}[7/11] Updating CLAUDE.md...${NC}" - CLAUDE_MD="$TARGET_DIR/CLAUDE.md" - SNIPPET_FILE="$AFX_DIR/prompts/complete.md" +# Fetch a pack manifest YAML from raw.githubusercontent.com +# @see docs/specs/afx-packs/design.md#34-download-strategy +fetch_manifest() { + local pack_name="$1" + local ref="$2" + local temp_manifest=$(mktemp) + + curl -sfL "https://raw.githubusercontent.com/${AFX_REPO}/${ref}/packs/${pack_name}.yaml" \ + -o "$temp_manifest" 2>/dev/null + + if [[ ! -s "$temp_manifest" ]]; then + rm -f "$temp_manifest" + echo -e "${RED}Error: Failed to fetch manifest for '${pack_name}' (ref: ${ref})${NC}" >&2 + echo "Check pack name and try again." >&2 + return 1 + fi - if [ -f "$SNIPPET_FILE" ]; then - # Extract content after the separator line (skip header comments) - SNIPPET_CONTENT=$(sed -n '/^---$/,$p' "$SNIPPET_FILE" | tail -n +2) + echo "$temp_manifest" +} - # Wrap with boundary markers - AFX_SECTION="${AFX_START_MARKER} -<!-- AFX Version: ${AFX_VERSION} --> +# Download and extract specific paths from a GitHub repo tarball +# @see docs/specs/afx-packs/design.md#34-download-strategy +download_items() { + local repo="$1" + local ref="$2" + local base_path="$3" + shift 3 + local items=("$@") -${SNIPPET_CONTENT} -${AFX_END_MARKER}" - - if [ "$DRY_RUN" = "true" ]; then - if [ -f "$CLAUDE_MD" ]; then - if grep -q "$AFX_START_MARKER" "$CLAUDE_MD" 2>/dev/null; then - UPDATED+=("CLAUDE.md AFX section (would update)") - elif grep -q "## Documentation References\|## AgenticFlow" "$CLAUDE_MD" 2>/dev/null; then - SKIPPED+=("CLAUDE.md (has old AFX section - run with --force to migrate)") - else - INSTALLED+=("CLAUDE.md AFX section (would append)") - fi - else - INSTALLED+=("CLAUDE.md (would create)") - fi - else - if [ -f "$CLAUDE_MD" ]; then - if grep -q "$AFX_START_MARKER" "$CLAUDE_MD"; then - # Has boundary markers - replace the section safely - # 1. Print everything before the start marker - awk -v start="$AFX_START_MARKER" ' - $0 == start { exit } - { print } - ' "$CLAUDE_MD" > "$CLAUDE_MD.tmp" - - # 2. Print the new section - echo "$AFX_SECTION" >> "$CLAUDE_MD.tmp" - - # 3. Print everything after the end marker - awk -v end="$AFX_END_MARKER" ' - BEGIN { skip=1 } - $0 == end { skip=0; next } - !skip { print } - ' "$CLAUDE_MD" >> "$CLAUDE_MD.tmp" - - mv "$CLAUDE_MD.tmp" "$CLAUDE_MD" - UPDATED+=("CLAUDE.md AFX section") - elif grep -q "## Documentation References\|## AgenticFlow" "$CLAUDE_MD"; then - # Has old-style AFX section without markers - if [ "$FORCE" = "true" ]; then - # Remove old section and add new with markers - # This is a best-effort removal - echo -e "${YELLOW}Warning: Migrating old AFX section to use boundary markers${NC}" - # Append new section (user should manually remove old) - echo "" >> "$CLAUDE_MD" - echo "$AFX_SECTION" >> "$CLAUDE_MD" - UPDATED+=("CLAUDE.md (migrated - please remove old AFX section manually)") - else - SKIPPED+=("CLAUDE.md (has old AFX section - use --force to migrate)") - fi - else - # No AFX section - append with markers - echo "" >> "$CLAUDE_MD" - echo "$AFX_SECTION" >> "$CLAUDE_MD" - INSTALLED+=("CLAUDE.md AFX section") - fi - else - # Create new CLAUDE.md with header + AFX section - cat > "$CLAUDE_MD" << 'HEADER' -# CLAUDE.md + local url="https://codeload.github.com/${repo}/tar.gz/${ref}" + local temp_dir=$(mktemp -d) -This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + curl -sL "$url" | tar xz -C "$temp_dir" --strip-components=1 2>/dev/null + echo "$temp_dir" +} -HEADER - echo "$AFX_SECTION" >> "$CLAUDE_MD" - INSTALLED+=("CLAUDE.md (created)") - fi +# Parse manifest includes[] — outputs one line per include block: "repo path item1 item2 ..." +# @see docs/specs/afx-packs/design.md#35-yaml-parsing +for_each_include() { + local manifest="$1" + local in_includes=false + local current_repo="" current_path="" + local items=() + + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" == "includes:" ]]; then + in_includes=true; continue + fi + if $in_includes && [[ "$line" =~ ^[a-z] ]]; then + [[ -n "$current_repo" ]] && echo "$current_repo $current_path ${items[*]}" + break fi - fi -else - echo -e "${YELLOW}[7/11] Skipping CLAUDE.md (--no-claude-md)${NC}" -fi - -# ============================================================================ -# 8. Update AGENTS.md with boundary markers -# ============================================================================ -if [ "$NO_AGENTS_MD" != "true" ]; then - echo -e "${BLUE}[8/11] Updating AGENTS.md...${NC}" - AGENTS_MD="$TARGET_DIR/AGENTS.md" - AGENTS_SNIPPET_FILE="$AFX_DIR/prompts/agents.md" - if [ -f "$AGENTS_SNIPPET_FILE" ]; then - AGENTS_SNIPPET_CONTENT=$(sed -n '/^---$/,$p' "$AGENTS_SNIPPET_FILE" | tail -n +2) + if $in_includes; then + if [[ "$line" =~ ^\ \ -\ repo:\ (.+) ]]; then + [[ -n "$current_repo" ]] && echo "$current_repo $current_path ${items[*]}" + current_repo="${BASH_REMATCH[1]}" + current_path="" ; items=() + elif [[ "$line" =~ ^\ \ \ \ path:\ (.+) ]]; then + current_path="${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^\ \ \ \ \ \ -\ ([^#]+) ]]; then + local item_name="${BASH_REMATCH[1]}" + item_name="${item_name%%#*}" + item_name="${item_name%% }" + item_name="${item_name%% }" + items+=("$item_name") + fi + fi + done < "$manifest" - AFX_AGENTS_SECTION="${AFX_AGENTS_START_MARKER} -<!-- AFX Version: ${AFX_VERSION} --> + [[ -n "$current_repo" ]] && echo "$current_repo $current_path ${items[*]}" +} -${AGENTS_SNIPPET_CONTENT} -${AFX_AGENTS_END_MARKER}" +# Parse platforms from manifest +parse_platforms() { + local manifest="$1" + local in_platforms=false + local result="" - if [ "$DRY_RUN" = "true" ]; then - if [ -f "$AGENTS_MD" ]; then - if grep -q "$AFX_AGENTS_START_MARKER" "$AGENTS_MD" 2>/dev/null; then - UPDATED+=("AGENTS.md AFX Codex section (would update)") - else - INSTALLED+=("AGENTS.md AFX Codex section (would append)") - fi - else - INSTALLED+=("AGENTS.md (would create)") - fi - else - if [ -f "$AGENTS_MD" ]; then - if grep -q "$AFX_AGENTS_START_MARKER" "$AGENTS_MD"; then - awk -v start="$AFX_AGENTS_START_MARKER" ' - $0 == start { exit } - { print } - ' "$AGENTS_MD" > "$AGENTS_MD.tmp" - - echo "$AFX_AGENTS_SECTION" >> "$AGENTS_MD.tmp" - - awk -v end="$AFX_AGENTS_END_MARKER" ' - BEGIN { skip=1 } - $0 == end { skip=0; next } - !skip { print } - ' "$AGENTS_MD" >> "$AGENTS_MD.tmp" - - mv "$AGENTS_MD.tmp" "$AGENTS_MD" - UPDATED+=("AGENTS.md AFX Codex section") - else - echo "" >> "$AGENTS_MD" - echo "$AFX_AGENTS_SECTION" >> "$AGENTS_MD" - INSTALLED+=("AGENTS.md AFX Codex section") - fi - else - cat > "$AGENTS_MD" << 'HEADER' -# AGENTS.md + while IFS= read -r line; do + if [[ "$line" == "platforms:" ]]; then + in_platforms=true; continue + fi + if $in_platforms && [[ "$line" =~ ^[a-z] ]]; then + break + fi + if $in_platforms && [[ "$line" =~ ^\ \ ([a-z]+):\ *([a-z]+) ]]; then + result+="${BASH_REMATCH[1]}:${BASH_REMATCH[2]} " + fi + done < "$manifest" -Project instructions for Codex and compatible coding agents. + echo "$result" +} -HEADER - echo "$AFX_AGENTS_SECTION" >> "$AGENTS_MD" - INSTALLED+=("AGENTS.md (created)") - fi +# Check if a platform is enabled in the manifest +# @see docs/specs/afx-packs/design.md#36-type-detection--routing +platform_enabled() { + local platforms="$1" + local provider="$2" + local val="" + + for pair in $platforms; do + if [[ "$pair" == "${provider}:"* ]]; then + val="${pair#*:}" + break fi - fi -else - echo -e "${YELLOW}[8/11] Skipping AGENTS.md (--no-agents-md)${NC}" -fi + done -# ============================================================================ -# 9. Update GEMINI.md with boundary markers -# ============================================================================ -if [ "$NO_GEMINI_MD" != "true" ]; then - echo -e "${BLUE}[9/11] Updating GEMINI.md...${NC}" - GEMINI_MD="$TARGET_DIR/GEMINI.md" - GEMINI_SNIPPET_FILE="$AFX_DIR/prompts/gemini.md" + [[ "$val" == "true" || "$val" == "partial" ]] +} - if [ -f "$GEMINI_SNIPPET_FILE" ]; then - GEMINI_SNIPPET_CONTENT=$(sed -n '/^---$/,$p' "$GEMINI_SNIPPET_FILE" | tail -n +2) +# ──────────────────────────────────────────────────────────────────────────── +# Provider-specific skill transforms +# ──────────────────────────────────────────────────────────────────────────── +# +# AFX-built skills ship as a single canonical SKILL.md using Claude command +# syntax (/afx:cmd sub). install.sh transforms this per provider at install +# time, eliminating 4x file duplication in the source repo. +# +# @see docs/specs/afx-packs/design.md#2-directory-layout +# ──────────────────────────────────────────────────────────────────────────── + +# Transform a canonical SKILL.md for a specific provider +transform_for_provider() { + local input="$1" + local output="$2" + local provider="$3" + + case "$provider" in + claude) + sed \ + -e '/<!-- @afx:provider-commands -->/d' \ + -e '/<!-- @afx:\/provider-commands -->/d' \ + "$input" > "$output" + ;; + codex) + sed \ + -e '/<!-- @afx:provider-commands -->/d' \ + -e '/<!-- @afx:\/provider-commands -->/d' \ + -e 's|`/afx:\([a-z]*\) \([a-z]*\)`|`afx-\1-\2`|g' \ + "$input" > "$output" + ;; + antigravity) + sed \ + '/<!-- @afx:provider-commands -->/,/<!-- @afx:\/provider-commands -->/d' \ + "$input" > "$output" + ;; + *) + cp "$input" "$output" + ;; + esac +} - AFX_GEMINI_SECTION="${AFX_GEMINI_START_MARKER} -<!-- AFX Version: ${AFX_VERSION} --> +# Generate a condensed Copilot agent.md from a canonical SKILL.md +generate_copilot_agent() { + local input="$1" + local output="$2" + local skill_name="$3" + + local title + title=$(grep -m1 '^# ' "$input" | sed 's/^# //') + + local description + description=$(awk 'NR>1 && /^[^#]/ && !/^$/ && !/^---/ { print; exit }' "$input") + + local instructions + instructions=$(sed -n '/^## Instructions/,/^## [^I]/{ + /^##/d + /^###/d + /^```/,/^```/d + /<!-- @afx/d + /^$/d + /^ |/d + p + }' "$input" | grep -E '^\d+\.|^- ' | head -7) + + local numbered + numbered=$(echo "$instructions" | awk ' + /^[0-9]+\./ { counter++; sub(/^[0-9]+\./, counter"."); } + { print } + ') + + cat > "$output" <<EOF +--- +name: ${skill_name} +description: $(echo "$description" | sed 's/\.$//') +--- + +# ${title} + +${description} + +When assisting with this topic: + +${numbered} +EOF +} -${GEMINI_SNIPPET_CONTENT} -${AFX_GEMINI_END_MARKER}" +# Detect skill type +# @see docs/specs/afx-packs/design.md#36-type-detection--routing +detect_type() { + local item_dir="$1" + local source_repo="$2" + + if [[ "$source_repo" == "${AFX_REPO}" ]]; then + echo "afx" + elif [[ -d "$item_dir/.claude-plugin" ]]; then + echo "plugin" + elif [[ -f "$item_dir/agents/openai.yaml" ]]; then + echo "openai" + elif [[ -f "$item_dir/SKILL.md" ]]; then + echo "skill" + else + echo "unknown" + fi +} - if [ "$DRY_RUN" = "true" ]; then - if [ -f "$GEMINI_MD" ]; then - if grep -q "$AFX_GEMINI_START_MARKER" "$GEMINI_MD" 2>/dev/null; then - UPDATED+=("GEMINI.md AFX Gemini section (would update)") - else - INSTALLED+=("GEMINI.md AFX Gemini section (would append)") - fi - else - INSTALLED+=("GEMINI.md (would create)") +# Route a detected item to .afx/packs/{pack}/{provider}/ +# @see docs/specs/afx-packs/design.md#36-type-detection--routing +route_item() { + local item_dir="$1" + local item_name="$2" + local type="$3" + local pack_dir="$4" + local platforms="$5" + + case "$type" in + skill) + if platform_enabled "$platforms" "claude"; then + mkdir -p "$pack_dir/claude/skills/$item_name" + cp -r "$item_dir"/. "$pack_dir/claude/skills/$item_name/" fi - else - if [ -f "$GEMINI_MD" ]; then - if grep -q "$AFX_GEMINI_START_MARKER" "$GEMINI_MD"; then - awk -v start="$AFX_GEMINI_START_MARKER" ' - $0 == start { exit } - { print } - ' "$GEMINI_MD" > "$GEMINI_MD.tmp" - - echo "$AFX_GEMINI_SECTION" >> "$GEMINI_MD.tmp" - - awk -v end="$AFX_GEMINI_END_MARKER" ' - BEGIN { skip=1 } - $0 == end { skip=0; next } - !skip { print } - ' "$GEMINI_MD" >> "$GEMINI_MD.tmp" - - mv "$GEMINI_MD.tmp" "$GEMINI_MD" - UPDATED+=("GEMINI.md AFX Gemini section") - else - echo "" >> "$GEMINI_MD" - echo "$AFX_GEMINI_SECTION" >> "$GEMINI_MD" - INSTALLED+=("GEMINI.md AFX Gemini section") + if platform_enabled "$platforms" "codex"; then + mkdir -p "$pack_dir/codex/skills/$item_name" + cp -r "$item_dir"/. "$pack_dir/codex/skills/$item_name/" + fi + if platform_enabled "$platforms" "antigravity"; then + mkdir -p "$pack_dir/antigravity/skills/$item_name" + cp -r "$item_dir"/. "$pack_dir/antigravity/skills/$item_name/" + fi + ;; + plugin) + if platform_enabled "$platforms" "claude"; then + mkdir -p "$pack_dir/claude/plugins/$item_name" + cp -r "$item_dir"/. "$pack_dir/claude/plugins/$item_name/" + fi + ;; + openai) + if platform_enabled "$platforms" "codex"; then + mkdir -p "$pack_dir/codex/skills/$item_name" + cp -r "$item_dir"/. "$pack_dir/codex/skills/$item_name/" + fi + ;; + afx) + local canonical="$item_dir/SKILL.md" + if [[ ! -f "$canonical" ]]; then + echo -e "${YELLOW}Warning: No SKILL.md in AFX skill '$item_name' — skipping${NC}" + break + fi + for provider in claude codex antigravity; do + if platform_enabled "$platforms" "$provider"; then + mkdir -p "$pack_dir/$provider/skills/$item_name" + transform_for_provider "$canonical" \ + "$pack_dir/$provider/skills/$item_name/SKILL.md" \ + "$provider" fi - else - cat > "$GEMINI_MD" << 'HEADER' -# GEMINI.md - -Project context for Gemini CLI when working with code in this repository. + done + if platform_enabled "$platforms" "copilot"; then + mkdir -p "$pack_dir/copilot/agents" + generate_copilot_agent "$canonical" \ + "$pack_dir/copilot/agents/${item_name}.agent.md" \ + "$item_name" + fi + ;; + *) + echo -e "${YELLOW}Warning: Unknown skill type for '$item_name' — skipping${NC}" + ;; + esac +} -HEADER - echo "$AFX_GEMINI_SECTION" >> "$GEMINI_MD" - INSTALLED+=("GEMINI.md (created)") +# Check name collision across packs +# @see docs/specs/afx-packs/design.md#38-name-collision-detection +check_collision() { + local item_name="$1" + local provider_dir="$2" + local current_pack="$3" + + if [[ -d "$provider_dir/$item_name" ]] && [[ "$FORCE" != "true" ]]; then + for pack_dir in "$TARGET_DIR/.afx/packs"/afx-pack-*/; do + [[ -d "$pack_dir" ]] || continue + local pack_name=$(basename "$pack_dir") + if [[ "$pack_name" != "$current_pack" ]]; then + if [[ -d "$pack_dir"/*/"skills/$item_name" ]] || \ + [[ -d "$pack_dir"/*/"plugins/$item_name" ]]; then + echo -e "${RED}Error: '$item_name' already installed by pack '$pack_name'${NC}" + echo "Use --force to overwrite." + return 1 + fi fi - fi + done fi -else - echo -e "${YELLOW}[9/11] Skipping GEMINI.md (--no-gemini-md)${NC}" -fi + return 0 +} + +# Map provider name to target directory +# @see docs/specs/afx-packs/design.md#310-helper-functions +provider_target_dir() { + local provider="$1" + local subdir="$2" + + case "$provider" in + claude) echo "$TARGET_DIR/.claude/$subdir" ;; + codex) echo "$TARGET_DIR/.agents/$subdir" ;; + antigravity) echo "$TARGET_DIR/.agent/$subdir" ;; + copilot) echo "$TARGET_DIR/.github/agents" ;; + esac +} # ============================================================================ -# 10. Update copilot-instructions.md with boundary markers +# Section 7: .afx.yaml Read/Write Helpers +# @see docs/specs/afx-packs/design.md#311-afxyaml-readwrite # ============================================================================ -if [ "$NO_COPILOT_MD" != "true" ]; then - echo -e "${BLUE}[10/11] Updating copilot-instructions.md...${NC}" - COPILOT_MD="$TARGET_DIR/.github/copilot-instructions.md" - COPILOT_SNIPPET_FILE="$AFX_DIR/prompts/copilot.md" - if [ -f "$COPILOT_SNIPPET_FILE" ]; then - COPILOT_SNIPPET_CONTENT=$(sed -n '/^---$/,$p' "$COPILOT_SNIPPET_FILE" | tail -n +2) +afx_yaml_enabled_packs() { + local yaml="$TARGET_DIR/.afx.yaml" + [[ -f "$yaml" ]] || return 0 + awk '/^packs:/,/^[^ ]/' "$yaml" | grep -B1 'status: enabled' | grep 'name:' | awk '{print $3}' +} - AFX_COPILOT_SECTION="${AFX_COPILOT_START_MARKER} -<!-- AFX Version: ${AFX_VERSION} --> +afx_yaml_all_packs() { + local yaml="$TARGET_DIR/.afx.yaml" + [[ -f "$yaml" ]] || return 0 -${COPILOT_SNIPPET_CONTENT} -${AFX_COPILOT_END_MARKER}" + local name="" status="" disabled_count=0 in_packs=false in_disabled=false - if [ "$DRY_RUN" = "true" ]; then - if [ -f "$COPILOT_MD" ]; then - if grep -q "$AFX_COPILOT_START_MARKER" "$COPILOT_MD" 2>/dev/null; then - UPDATED+=("copilot-instructions.md AFX Copilot section (would update)") - else - INSTALLED+=("copilot-instructions.md AFX Copilot section (would append)") - fi - else - INSTALLED+=("copilot-instructions.md (would create)") - fi - else - # Ensure .github directory exists - if [ ! -d "$TARGET_DIR/.github" ]; then - mkdir -p "$TARGET_DIR/.github" - fi - - if [ -f "$COPILOT_MD" ]; then - if grep -q "$AFX_COPILOT_START_MARKER" "$COPILOT_MD"; then - awk -v start="$AFX_COPILOT_START_MARKER" ' - $0 == start { exit } - { print } - ' "$COPILOT_MD" > "$COPILOT_MD.tmp" - - echo "$AFX_COPILOT_SECTION" >> "$COPILOT_MD.tmp" - - awk -v end="$AFX_COPILOT_END_MARKER" ' - BEGIN { skip=1 } - $0 == end { skip=0; next } - !skip { print } - ' "$COPILOT_MD" >> "$COPILOT_MD.tmp" - - mv "$COPILOT_MD.tmp" "$COPILOT_MD" - UPDATED+=("copilot-instructions.md AFX Copilot section") - else - echo "" >> "$COPILOT_MD" - echo "$AFX_COPILOT_SECTION" >> "$COPILOT_MD" - INSTALLED+=("copilot-instructions.md AFX Copilot section") - fi + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" == "packs:" ]]; then + in_packs=true; continue + fi + if $in_packs && [[ -n "$line" ]] && [[ ! "$line" =~ ^\ \ ]]; then + [[ -n "$name" ]] && echo "$name:$status:$disabled_count" + break + fi + if $in_packs; then + if [[ "$line" =~ ^\ \ -\ name:\ (.+) ]]; then + [[ -n "$name" ]] && echo "$name:$status:$disabled_count" + name="${BASH_REMATCH[1]}" + status="" ; disabled_count=0 ; in_disabled=false + elif [[ "$line" =~ ^\ \ \ \ status:\ (.+) ]]; then + status="${BASH_REMATCH[1]}" + elif [[ "$line" =~ ^\ \ \ \ disabled_items: ]]; then + in_disabled=true + elif $in_disabled && [[ "$line" =~ ^\ \ \ \ \ \ -\ ]]; then + ((disabled_count++)) + elif [[ "$line" =~ ^\ \ \ \ ]] && ! $in_disabled; then + : else - echo "$AFX_COPILOT_SECTION" > "$COPILOT_MD" - INSTALLED+=("copilot-instructions.md (created)") + in_disabled=false fi fi - fi -else - echo -e "${YELLOW}[10/11] Skipping copilot-instructions.md (--no-copilot-md)${NC}" -fi + done < "$yaml" -# ============================================================================ -# 11. Install AFX documentation -# ============================================================================ -if [ "$NO_DOCS" != "true" ]; then - echo -e "${BLUE}[11/11] Installing AFX documentation...${NC}" - AFX_DOCS_DIR="$TARGET_DIR/docs/agenticflowx" + [[ -n "$name" ]] && echo "$name:$status:$disabled_count" +} - if [ "$DRY_RUN" != "true" ]; then - mkdir -p "$AFX_DOCS_DIR" - fi +afx_yaml_pack_ref() { + local pack_name="$1" + local yaml="$TARGET_DIR/.afx.yaml" + [[ -f "$yaml" ]] || { echo "main"; return 0; } - # Copy AFX documentation files - for doc in "agenticflowx.md" "guide.md" "cheatsheet.md" "multi-agent.md"; do + local found_pack=false + while IFS= read -r line; do + if [[ "$line" =~ name:\ $pack_name$ ]]; then + found_pack=true; continue + fi + if $found_pack && [[ "$line" =~ ^\ \ -\ name: ]]; then break; fi + if $found_pack && [[ "$line" =~ ^\ \ \ \ installed_ref:\ (.+) ]]; then + echo "${BASH_REMATCH[1]}"; return 0 + fi + done < "$yaml" + echo "main" +} + +afx_yaml_disabled_items() { + local pack_name="$1" + local yaml="$TARGET_DIR/.afx.yaml" + [[ -f "$yaml" ]] || return 0 + + local found_pack=false in_disabled=false result="" + while IFS= read -r line; do + if [[ "$line" =~ name:\ $pack_name$ ]]; then found_pack=true; continue; fi + if $found_pack && [[ "$line" =~ ^\ \ -\ name: ]]; then break; fi + if $found_pack && [[ "$line" =~ disabled_items: ]]; then in_disabled=true; continue; fi + if $found_pack && $in_disabled; then + if [[ "$line" =~ ^\ \ \ \ \ \ -\ (.+) ]]; then + result+="${BASH_REMATCH[1]} " + else + in_disabled=false + fi + fi + done < "$yaml" + echo "$result" +} + +afx_yaml_set_pack() { + local pack_name="$1" + local status="$2" + local ref="${3:-}" + local yaml="$TARGET_DIR/.afx.yaml" + + if [[ "$DRY_RUN" == "true" ]]; then + echo -e " ${CYAN}(would update .afx.yaml: $pack_name → $status)${NC}" + return 0 + fi + + if [[ ! -f "$yaml" ]]; then + echo "packs:" > "$yaml" + fi + + if grep -q "name: $pack_name" "$yaml" 2>/dev/null; then + local temp=$(mktemp) + local in_target=false + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" =~ ^\ \ -\ name:\ $pack_name$ ]]; then + in_target=true + echo "$line" >> "$temp" + elif $in_target && [[ "$line" =~ ^\ \ -\ name: || ! "$line" =~ ^\ \ ]]; then + in_target=false + echo "$line" >> "$temp" + elif $in_target && [[ "$line" =~ ^\ \ \ \ status: ]]; then + echo " status: $status" >> "$temp" + elif $in_target && [[ -n "$ref" ]] && [[ "$line" =~ ^\ \ \ \ installed_ref: ]]; then + echo " installed_ref: $ref" >> "$temp" + else + echo "$line" >> "$temp" + fi + done < "$yaml" + mv "$temp" "$yaml" + else + if ! grep -q "^packs:" "$yaml" 2>/dev/null; then + echo "" >> "$yaml" + echo "packs:" >> "$yaml" + fi + cat >> "$yaml" <<EOF + - name: $pack_name + status: $status + installed_ref: ${ref:-main} + disabled_items: [] +EOF + fi +} + +afx_yaml_remove_pack() { + local pack_name="$1" + local yaml="$TARGET_DIR/.afx.yaml" + [[ -f "$yaml" ]] || return 0 + + if [[ "$DRY_RUN" == "true" ]]; then + echo -e " ${CYAN}(would remove $pack_name from .afx.yaml)${NC}" + return 0 + fi + + local temp=$(mktemp) + local skip=false + while IFS= read -r line || [[ -n "$line" ]]; do + if [[ "$line" =~ ^\ \ -\ name:\ $pack_name$ ]]; then skip=true; continue; fi + if $skip && [[ "$line" =~ ^\ \ -\ name: ]]; then skip=false; fi + if $skip && [[ -n "$line" ]] && [[ ! "$line" =~ ^\ \ ]]; then skip=false; fi + $skip || echo "$line" + done < "$yaml" > "$temp" + mv "$temp" "$yaml" +} + +afx_yaml_disable_item() { + local pack_name="$1" + local item_name="$2" + local yaml="$TARGET_DIR/.afx.yaml" + [[ "$DRY_RUN" == "true" ]] && return 0 + + if grep -A5 "name: $pack_name" "$yaml" | grep -q "disabled_items: \[\]"; then + sed -i.bak "/name: $pack_name/,/disabled_items:/{s/disabled_items: \[\]/disabled_items:\n - $item_name/;}" "$yaml" + rm -f "$yaml.bak" + else + sed -i.bak "/name: $pack_name/,/^ - name:\|^[^ ]/{/disabled_items:/a\\ + - $item_name +}" "$yaml" + rm -f "$yaml.bak" + fi +} + +afx_yaml_enable_item() { + local pack_name="$1" + local item_name="$2" + local yaml="$TARGET_DIR/.afx.yaml" + [[ "$DRY_RUN" == "true" ]] && return 0 + + sed -i.bak "/ - $item_name/d" "$yaml" + rm -f "$yaml.bak" + + if ! grep -A20 "name: $pack_name" "$yaml" | grep -q "^ - "; then + sed -i.bak "/name: $pack_name/,/^ - name:\|^[^ ]/{s/disabled_items:/disabled_items: []/;}" "$yaml" + rm -f "$yaml.bak" + fi +} + +afx_yaml_add_custom_skill() { + local repo="$1" + local path="$2" + local yaml="$TARGET_DIR/.afx.yaml" + [[ "$DRY_RUN" == "true" ]] && return 0 + + if ! grep -q "^custom_skills:" "$yaml" 2>/dev/null; then + echo "" >> "$yaml" + echo "custom_skills:" >> "$yaml" + fi + cat >> "$yaml" <<EOF + - repo: $repo + path: $path +EOF +} + +# ============================================================================ +# Section 8: Pack Lifecycle Functions +# @see docs/specs/afx-packs/design.md#39-state-transitions +# ============================================================================ + +pack_copy_to_providers() { + local pack_name="$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + local disabled_items=$(afx_yaml_disabled_items "$pack_name") + + # Claude skills + if [[ -d "$pack_dir/claude/skills" ]]; then + mkdir -p "$TARGET_DIR/.claude/skills" + for skill in "$pack_dir"/claude/skills/*/; do + [[ -d "$skill" ]] || continue + local name=$(basename "$skill") + [[ " $disabled_items " =~ " $name " ]] && continue + check_collision "$name" "$TARGET_DIR/.claude/skills" "$pack_name" || continue + if [[ "$DRY_RUN" == "true" ]]; then + INSTALLED+=(" → .claude/skills/$name (would create)") + else + cp -r "$skill" "$TARGET_DIR/.claude/skills/$name" + INSTALLED+=(" → .claude/skills/$name") + fi + done + fi + + # Claude plugins + if [[ -d "$pack_dir/claude/plugins" ]]; then + mkdir -p "$TARGET_DIR/.claude/plugins" + for plugin in "$pack_dir"/claude/plugins/*/; do + [[ -d "$plugin" ]] || continue + local name=$(basename "$plugin") + [[ " $disabled_items " =~ " $name " ]] && continue + check_collision "$name" "$TARGET_DIR/.claude/plugins" "$pack_name" || continue + if [[ "$DRY_RUN" == "true" ]]; then + INSTALLED+=(" → .claude/plugins/$name (would create)") + else + cp -r "$plugin" "$TARGET_DIR/.claude/plugins/$name" + INSTALLED+=(" → .claude/plugins/$name") + fi + done + fi + + # Codex skills + if [[ -d "$pack_dir/codex/skills" ]]; then + mkdir -p "$TARGET_DIR/.agents/skills" + for skill in "$pack_dir"/codex/skills/*/; do + [[ -d "$skill" ]] || continue + local name=$(basename "$skill") + [[ " $disabled_items " =~ " $name " ]] && continue + if [[ "$DRY_RUN" == "true" ]]; then + INSTALLED+=(" → .agents/skills/$name (would create)") + else + cp -r "$skill" "$TARGET_DIR/.agents/skills/$name" + INSTALLED+=(" → .agents/skills/$name") + fi + done + fi + + # Antigravity skills + if [[ -d "$pack_dir/antigravity/skills" ]]; then + mkdir -p "$TARGET_DIR/.agent/skills" + for skill in "$pack_dir"/antigravity/skills/*/; do + [[ -d "$skill" ]] || continue + local name=$(basename "$skill") + [[ " $disabled_items " =~ " $name " ]] && continue + if [[ "$DRY_RUN" == "true" ]]; then + INSTALLED+=(" → .agent/skills/$name (would create)") + else + cp -r "$skill" "$TARGET_DIR/.agent/skills/$name" + INSTALLED+=(" → .agent/skills/$name") + fi + done + fi + + # Copilot agents + if [[ -d "$pack_dir/copilot/agents" ]]; then + mkdir -p "$TARGET_DIR/.github/agents" + for agent in "$pack_dir"/copilot/agents/*.agent.md; do + [[ -f "$agent" ]] || continue + local name=$(basename "$agent" .agent.md) + [[ " $disabled_items " =~ " $name " ]] && continue + if [[ "$DRY_RUN" == "true" ]]; then + INSTALLED+=(" → .github/agents/$(basename "$agent") (would create)") + else + cp "$agent" "$TARGET_DIR/.github/agents/$(basename "$agent")" + INSTALLED+=(" → .github/agents/$(basename "$agent")") + fi + done + fi +} + +pack_remove_from_providers() { + local pack_name="$1" + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + if [[ "$DRY_RUN" == "true" ]]; then + echo -e " ${CYAN}(would remove provider copies for $pack_name)${NC}" + return 0 + fi + + for skill in "$pack_dir"/claude/skills/*/; do + [[ -d "$skill" ]] && rm -rf "$TARGET_DIR/.claude/skills/$(basename "$skill")" + done + for plugin in "$pack_dir"/claude/plugins/*/; do + [[ -d "$plugin" ]] && rm -rf "$TARGET_DIR/.claude/plugins/$(basename "$plugin")" + done + for skill in "$pack_dir"/codex/skills/*/; do + [[ -d "$skill" ]] && rm -rf "$TARGET_DIR/.agents/skills/$(basename "$skill")" + done + for skill in "$pack_dir"/antigravity/skills/*/; do + [[ -d "$skill" ]] && rm -rf "$TARGET_DIR/.agent/skills/$(basename "$skill")" + done + for agent in "$pack_dir"/copilot/agents/*.agent.md; do + [[ -f "$agent" ]] && rm -f "$TARGET_DIR/.github/agents/$(basename "$agent")" + done +} + +normalize_pack_name() { + local input="$1" + if [[ "$input" == afx-pack-* ]]; then + echo "$input" + else + echo "afx-pack-$input" + fi +} + +# Install a pack: fetch manifest → download → detect → route → copy → state +pack_install() { + local input_name="$1" + local ref_override="${2:-}" + local pack_name + pack_name=$(normalize_pack_name "$input_name") + local ref + if [[ -n "$ref_override" ]]; then + ref="$ref_override" + else + ref=$(resolve_ref) || exit 1 + fi + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + echo -e "${BLUE}Installing pack '$pack_name' (ref: $ref)...${NC}" + + if [[ "$DRY_RUN" != "true" ]]; then + mkdir -p "$TARGET_DIR/.afx/.cache" + fi + ensure_gitignore ".afx/" + + local manifest + manifest=$(fetch_manifest "$pack_name" "$ref") || exit 1 + + local platforms + platforms=$(parse_platforms "$manifest") + + while IFS= read -r include_line; do + [[ -z "$include_line" ]] && continue + + local repo path + repo=$(echo "$include_line" | awk '{print $1}') + path=$(echo "$include_line" | awk '{print $2}') + local items_str + items_str=$(echo "$include_line" | cut -d' ' -f3-) + + local item_ref="main" + if [[ "$repo" == "${AFX_REPO}" ]]; then + item_ref="$ref" + fi + + echo -e " ${CYAN}Downloading from ${repo} (ref: ${item_ref})...${NC}" + + local temp + temp=$(download_items "$repo" "$item_ref" "$path" $items_str) + + if [[ "$DRY_RUN" != "true" ]]; then + mkdir -p "$pack_dir" + fi + + for item_name in $items_str; do + local item_dir="$temp/${path}${item_name}" + if [[ ! -d "$item_dir" ]]; then + echo -e " ${YELLOW}Warning: '$item_name' not found in download — skipping${NC}" + continue + fi + + local type + type=$(detect_type "$item_dir" "$repo") + echo -e " ${GREEN}Found: $item_name (type: $type)${NC}" + + if [[ "$DRY_RUN" != "true" ]]; then + route_item "$item_dir" "$item_name" "$type" "$pack_dir" "$platforms" + else + INSTALLED+=(" ↓ $item_name ($type)") + fi + done + + rm -rf "$temp" + done < <(for_each_include "$manifest") + + pack_copy_to_providers "$pack_name" + afx_yaml_set_pack "$pack_name" "enabled" "$ref" + rm -f "$manifest" + + echo -e "${GREEN}Pack '$pack_name' installed and enabled (ref: $ref).${NC}" + echo "" +} + +pack_enable() { + local input_name="$1" + local pack_name + pack_name=$(normalize_pack_name "$input_name") + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + if [[ ! -d "$pack_dir" ]]; then + echo -e "${RED}Error: Pack '$pack_name' not found. Install it first with --pack $pack_name${NC}" + exit 1 + fi + + pack_copy_to_providers "$pack_name" + afx_yaml_set_pack "$pack_name" "enabled" + echo -e "${GREEN}Pack '$pack_name' enabled.${NC}" +} + +pack_disable() { + local input_name="$1" + local pack_name + pack_name=$(normalize_pack_name "$input_name") + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + if [[ ! -d "$pack_dir" ]]; then + echo -e "${RED}Error: Pack '$pack_name' not installed.${NC}" + exit 1 + fi + + pack_remove_from_providers "$pack_name" + afx_yaml_set_pack "$pack_name" "disabled" + echo -e "${YELLOW}Pack '$pack_name' disabled. Master preserved in .afx/${NC}" +} + +pack_remove() { + local input_name="$1" + local pack_name + pack_name=$(normalize_pack_name "$input_name") + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + pack_remove_from_providers "$pack_name" + [[ "$DRY_RUN" != "true" ]] && rm -rf "$pack_dir" + afx_yaml_remove_pack "$pack_name" + echo -e "${YELLOW}Pack '$pack_name' removed entirely.${NC}" +} + +skill_disable() { + local skill_name="$1" + local input_name="$2" + local pack_name + pack_name=$(normalize_pack_name "$input_name") + + if [[ "$DRY_RUN" != "true" ]]; then + rm -rf "$TARGET_DIR/.claude/skills/$skill_name" + rm -rf "$TARGET_DIR/.claude/plugins/$skill_name" + rm -rf "$TARGET_DIR/.agents/skills/$skill_name" + rm -rf "$TARGET_DIR/.agent/skills/$skill_name" + rm -f "$TARGET_DIR/.github/agents/${skill_name}.agent.md" + fi + + afx_yaml_disable_item "$pack_name" "$skill_name" + echo -e "${YELLOW}Skill '$skill_name' disabled in pack '${pack_name}'.${NC}" +} + +skill_enable() { + local skill_name="$1" + local input_name="$2" + local pack_name + pack_name=$(normalize_pack_name "$input_name") + local pack_dir="$TARGET_DIR/.afx/packs/$pack_name" + + check_collision "$skill_name" "$TARGET_DIR/.claude/skills" "$pack_name" || exit 1 + + if [[ "$DRY_RUN" != "true" ]]; then + for provider in claude codex antigravity copilot; do + if [[ -d "$pack_dir/$provider/skills/$skill_name" ]]; then + local target=$(provider_target_dir "$provider" "skills") + mkdir -p "$target" + cp -r "$pack_dir/$provider/skills/$skill_name" "$target/$skill_name" + fi + if [[ -d "$pack_dir/$provider/plugins/$skill_name" ]]; then + local target=$(provider_target_dir "$provider" "plugins") + mkdir -p "$target" + cp -r "$pack_dir/$provider/plugins/$skill_name" "$target/$skill_name" + fi + if [[ -f "$pack_dir/$provider/agents/${skill_name}.agent.md" ]]; then + mkdir -p "$TARGET_DIR/.github/agents" + cp "$pack_dir/$provider/agents/${skill_name}.agent.md" \ + "$TARGET_DIR/.github/agents/${skill_name}.agent.md" + fi + done + fi + + afx_yaml_enable_item "$pack_name" "$skill_name" + echo -e "${GREEN}Skill '$skill_name' re-enabled in pack '${pack_name}'.${NC}" +} + +pack_list() { + local has_packs=false + + echo -e "${BLUE}Installed packs:${NC}" + echo "" + + while IFS=: read -r name status disabled_count; do + has_packs=true + if [[ "$status" == "enabled" ]]; then + echo -e " ${GREEN}●${NC} $name (enabled)" + else + echo -e " ${YELLOW}○${NC} $name (disabled)" + fi + if [[ "$disabled_count" -gt 0 ]]; then + echo " $disabled_count items disabled" + fi + done < <(afx_yaml_all_packs) + + if [[ "$has_packs" == "false" ]]; then + echo " (none)" + echo "" + echo "Install a pack:" + echo " ./install.sh --pack qa ." + fi +} + +pack_update_all() { + echo -e "${BLUE}Updating installed packs...${NC}" + echo "" + + local index_ref + index_ref=$(resolve_ref) || exit 1 + if [[ "$DRY_RUN" != "true" ]]; then + mkdir -p "$TARGET_DIR/.afx/.cache" + curl -sL "https://raw.githubusercontent.com/${AFX_REPO}/${index_ref}/packs/index.json" \ + > "$TARGET_DIR/.afx/.cache/lastIndex.json" 2>/dev/null + fi + + local updated=0 + for pack_name in $(afx_yaml_enabled_packs); do + local pack_ref + pack_ref=$(afx_yaml_pack_ref "$pack_name") + echo -e "${BLUE}Updating $pack_name (ref: $pack_ref)...${NC}" + pack_remove_from_providers "$pack_name" + [[ "$DRY_RUN" != "true" ]] && rm -rf "$TARGET_DIR/.afx/packs/$pack_name" + pack_install "$pack_name" "$pack_ref" + ((updated++)) + done + + if [[ "$updated" -eq 0 ]]; then + echo " No enabled packs to update." + fi +} + +add_skill() { + local spec="$1" + local repo="${spec%%:*}" + local full_path="${spec#*:}" + local skill_name=$(basename "$full_path") + local base_path="$(dirname "$full_path")/" + + echo -e "${BLUE}Installing skill '$skill_name' from $repo...${NC}" + + local temp + temp=$(download_items "$repo" "main" "$base_path" "$skill_name") + + local item_dir="$temp/${base_path}${skill_name}" + if [[ ! -d "$item_dir" ]]; then + echo -e "${RED}Error: Skill '$skill_name' not found in $repo${NC}" + rm -rf "$temp" + exit 1 + fi + + local type + type=$(detect_type "$item_dir" "$repo") + + if [[ "$DRY_RUN" != "true" ]]; then + case "$type" in + skill) + mkdir -p "$TARGET_DIR/.claude/skills/$skill_name" + cp -r "$item_dir"/. "$TARGET_DIR/.claude/skills/$skill_name/" + mkdir -p "$TARGET_DIR/.agents/skills/$skill_name" + cp -r "$item_dir"/. "$TARGET_DIR/.agents/skills/$skill_name/" + mkdir -p "$TARGET_DIR/.agent/skills/$skill_name" + cp -r "$item_dir"/. "$TARGET_DIR/.agent/skills/$skill_name/" + ;; + plugin) + mkdir -p "$TARGET_DIR/.claude/plugins/$skill_name" + cp -r "$item_dir"/. "$TARGET_DIR/.claude/plugins/$skill_name/" + ;; + openai) + mkdir -p "$TARGET_DIR/.agents/skills/$skill_name" + cp -r "$item_dir"/. "$TARGET_DIR/.agents/skills/$skill_name/" + ;; + esac + fi + + rm -rf "$temp" + afx_yaml_add_custom_skill "$repo" "$full_path" + echo -e "${GREEN}Skill '$skill_name' installed from $repo.${NC}" +} + +# ============================================================================ +# Section 9: Install Step Functions (modular) +# ============================================================================ + +step_claude_commands() { + echo -e "${BLUE}Installing Claude slash commands...${NC}" + local dir="$TARGET_DIR/.claude/commands" + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$dir" + + for cmd in "$AFX_DIR"/.claude/commands/afx-*.md; do + if [ -f "$cmd" ]; then + local filename=$(basename "$cmd") + install_file "$cmd" "$dir/$filename" "Command: $filename" "$UPDATE_MODE" + fi + done +} + +step_codex_skills() { + echo -e "${BLUE}Installing Codex skills...${NC}" + local dir="$TARGET_DIR/.codex/skills" + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$dir" + + if [ -d "$AFX_DIR/.codex/skills" ]; then + for skill_dir in "$AFX_DIR"/.codex/skills/afx-*; do + if [ -d "$skill_dir" ]; then + local skill_name=$(basename "$skill_dir") + install_directory "$skill_dir" "$dir/$skill_name" "Codex skill: $skill_name" "$UPDATE_MODE" + fi + done + fi +} + +step_antigravity_skills() { + echo -e "${BLUE}Installing Antigravity skills...${NC}" + local dir="$TARGET_DIR/.agent/skills" + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$dir" + + if [ -d "$AFX_DIR/.agent/skills" ]; then + for skill_dir in "$AFX_DIR"/.agent/skills/afx-*; do + if [ -d "$skill_dir" ]; then + local skill_name=$(basename "$skill_dir") + install_directory "$skill_dir" "$dir/$skill_name" "Antigravity skill: $skill_name" "$UPDATE_MODE" + fi + done + fi +} + +step_gemini_commands() { + echo -e "${BLUE}Installing Gemini CLI commands...${NC}" + local dir="$TARGET_DIR/.gemini/commands" + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$dir" + + if [ -d "$AFX_DIR/.gemini/commands" ]; then + for cmd in "$AFX_DIR"/.gemini/commands/afx-*.toml; do + if [ -f "$cmd" ]; then + local filename=$(basename "$cmd") + install_file "$cmd" "$dir/$filename" "Gemini command: $filename" "$UPDATE_MODE" + fi + done + fi +} + +step_copilot_prompts() { + echo -e "${BLUE}Installing GitHub Copilot prompts...${NC}" + local dir="$TARGET_DIR/.github/prompts" + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$dir" + + if [ -d "$AFX_DIR/.github/prompts" ]; then + for prompt in "$AFX_DIR"/.github/prompts/afx-*.prompt.md; do + if [ -f "$prompt" ]; then + local filename=$(basename "$prompt") + install_file "$prompt" "$dir/$filename" "Copilot prompt: $filename" "$UPDATE_MODE" + fi + done + if [ -f "$AFX_DIR/.github/prompts/README.md" ]; then + install_file "$AFX_DIR/.github/prompts/README.md" "$dir/README.md" "Copilot prompts README" "$UPDATE_MODE" + fi + fi +} + +step_templates() { + echo -e "${BLUE}Installing templates...${NC}" + local dir="$TARGET_DIR/docs/agenticflowx/templates" + + if [ -d "$AFX_DIR/templates" ]; then + for tpl in "$AFX_DIR"/templates/*.md; do + if [ -f "$tpl" ]; then + local filename=$(basename "$tpl") + install_file "$tpl" "$dir/$filename" "Template: $filename" "$UPDATE_MODE" + fi + done + fi +} + +step_config() { + echo -e "${BLUE}Managing configuration...${NC}" + + # Ensure .afx/ folder exists + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$TARGET_DIR/.afx/.cache" + ensure_gitignore ".afx/" + + # Managed defaults — always written/overwritten in .afx/.afx.yaml + install_file "$AFX_DIR/.afx.yaml.template" "$TARGET_DIR/.afx/.afx.yaml" ".afx/.afx.yaml" "true" + + # User config — never overwritten (unless --force) + if [ -f "$TARGET_DIR/.afx.yaml" ]; then + if [ "$FORCE" = "true" ]; then + install_file "$AFX_DIR/.afx.yaml.template" "$TARGET_DIR/.afx.yaml" ".afx.yaml" "true" + else + SKIPPED+=(".afx.yaml (preserved - user config)") + fi + else + # Create user-friendly config with inline guide + if [[ "$DRY_RUN" == "true" ]]; then + INSTALLED+=(".afx.yaml (would create)") + else + cat > "$TARGET_DIR/.afx.yaml" <<'YAMLEOF' +# ┌─────────────────────────────────────────────────────────────────────────┐ +# │ AFX Configuration │ +# │ │ +# │ This file configures AgenticFlowX for your project. │ +# │ Edit the values below to match your setup. │ +# │ │ +# │ Docs: docs/agenticflowx/agenticflowx.md │ +# │ Help: /afx:help (Claude) or afx-help (Codex) │ +# └─────────────────────────────────────────────────────────────────────────┘ + +# AFX version (do not edit — managed by install.sh) +version: main + +# ── Project Info ────────────────────────────────────────────────────────── +# These are used by AFX commands for context and traceability. + +project: + name: my-project # Your project name + description: "" # Brief project description + repo: "" # GitHub repo (e.g., owner/repo) + +# ── Spec Locations ──────────────────────────────────────────────────────── +# Where AFX looks for specs, ADRs, and journals. + +paths: + specs: docs/specs # Feature spec directories + adr: docs/adr # Architecture Decision Records + research: docs/research # Research documents + templates: docs/agenticflowx/templates # Spec templates + +# ── Installed Packs ─────────────────────────────────────────────────────── +# Managed by install.sh. Add packs with: +# ./install.sh --pack qa . +# ./install.sh --pack security . + +packs: [] + +# ── Quick Start ─────────────────────────────────────────────────────────── +# +# 1. Edit the project info above +# 2. Create your first feature: +# /afx:init feature user-auth (Claude Code) +# afx-init (Codex) +# 3. Start working: +# /afx:next (Claude Code) +# afx-next (Codex) +# 4. Install optional packs: +# ./install.sh --pack qa . (QA guardrails) +# ./install.sh --pack security . (Security checks) +YAMLEOF + INSTALLED+=(".afx.yaml (created with guide)") + fi + fi +} + +step_claude_md() { + echo -e "${BLUE}Updating CLAUDE.md...${NC}" + local snippet_file="$AFX_DIR/prompts/complete.md" + if [ -f "$snippet_file" ]; then + local snippet_content + snippet_content=$(sed -n '/^---$/,$p' "$snippet_file" | tail -n +2) + update_md_with_markers \ + "$TARGET_DIR/CLAUDE.md" \ + "$AFX_START_MARKER" \ + "$AFX_END_MARKER" \ + "$snippet_content" \ + "CLAUDE.md" \ + "$(printf '# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n')" + fi +} + +step_agents_md() { + echo -e "${BLUE}Updating AGENTS.md...${NC}" + local snippet_file="$AFX_DIR/prompts/agents.md" + if [ -f "$snippet_file" ]; then + local snippet_content + snippet_content=$(sed -n '/^---$/,$p' "$snippet_file" | tail -n +2) + update_md_with_markers \ + "$TARGET_DIR/AGENTS.md" \ + "$AFX_AGENTS_START_MARKER" \ + "$AFX_AGENTS_END_MARKER" \ + "$snippet_content" \ + "AGENTS.md" \ + "$(printf '# AGENTS.md\n\nProject instructions for Codex and compatible coding agents.\n')" + fi +} + +step_gemini_md() { + echo -e "${BLUE}Updating GEMINI.md...${NC}" + local snippet_file="$AFX_DIR/prompts/gemini.md" + if [ -f "$snippet_file" ]; then + local snippet_content + snippet_content=$(sed -n '/^---$/,$p' "$snippet_file" | tail -n +2) + update_md_with_markers \ + "$TARGET_DIR/GEMINI.md" \ + "$AFX_GEMINI_START_MARKER" \ + "$AFX_GEMINI_END_MARKER" \ + "$snippet_content" \ + "GEMINI.md" \ + "$(printf '# GEMINI.md\n\nProject context for Gemini CLI when working with code in this repository.\n')" + fi +} + +step_copilot_md() { + echo -e "${BLUE}Updating copilot-instructions.md...${NC}" + local snippet_file="$AFX_DIR/prompts/copilot.md" + if [ -f "$snippet_file" ]; then + local snippet_content + snippet_content=$(sed -n '/^---$/,$p' "$snippet_file" | tail -n +2) + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$TARGET_DIR/.github" + update_md_with_markers \ + "$TARGET_DIR/.github/copilot-instructions.md" \ + "$AFX_COPILOT_START_MARKER" \ + "$AFX_COPILOT_END_MARKER" \ + "$snippet_content" \ + "copilot-instructions.md" \ + "" + fi +} + +step_docs() { + echo -e "${BLUE}Installing AFX documentation...${NC}" + local dir="$TARGET_DIR/docs/agenticflowx" + [[ "$DRY_RUN" != "true" ]] && mkdir -p "$dir" + + for doc in "agenticflowx.md" "guide.md" "cheatsheet.md" "multi-agent.md"; do if [ -f "$AFX_DIR/docs/agenticflowx/$doc" ]; then - install_file "$AFX_DIR/docs/agenticflowx/$doc" "$AFX_DOCS_DIR/$doc" "AFX Doc: $doc" "$UPDATE_MODE" + install_file "$AFX_DIR/docs/agenticflowx/$doc" "$dir/$doc" "AFX Doc: $doc" "$UPDATE_MODE" fi done -else - echo -e "${YELLOW}[11/11] Skipping AFX documentation (--no-docs)${NC}" -fi +} + +# Strip AFX boundary-marked section from a markdown file. +# If the file becomes empty (only whitespace) after stripping, delete it. +# Usage: strip_afx_section <file> <start_marker> <end_marker> <label> +strip_afx_section() { + local file="$1" + local start_marker="$2" + local end_marker="$3" + local label="$4" + + [[ -f "$file" ]] || return 0 + + if ! grep -q "$start_marker" "$file" 2>/dev/null; then + return 0 + fi + + if [[ "$DRY_RUN" == "true" ]]; then + REMOVED+=("$label (would strip AFX section)") + return 0 + fi + + # Remove the AFX section (start marker through end marker, inclusive) + awk -v start="$start_marker" -v end="$end_marker" ' + $0 == start { skip=1; next } + $0 == end { skip=0; next } + !skip { print } + ' "$file" > "$file.tmp" + + # Check if file is now empty (only whitespace) + if [[ ! -s "$file.tmp" ]] || ! grep -q '[^[:space:]]' "$file.tmp" 2>/dev/null; then + rm -f "$file" "$file.tmp" + REMOVED+=("$label (deleted — empty after stripping)") + else + mv "$file.tmp" "$file" + REMOVED+=("$label (AFX section stripped)") + fi +} + +# Count files matching a glob pattern (returns 0 if none) +count_glob() { + local pattern="$1" + local count=0 + for f in $pattern; do + [[ -e "$f" ]] && ((count++)) + done + echo "$count" +} + +# Remove files/dirs matching a glob, with summary +# Usage: remove_glob <pattern> <label> <type> (type: "file" or "dir") +remove_glob() { + local pattern="$1" + local label="$2" + local type="$3" + local count=$(count_glob "$pattern") + + if [[ "$count" -eq 0 ]]; then + return 0 + fi + + if [[ "$DRY_RUN" == "true" ]]; then + REMOVED+=("$label ($count items would be removed)") + return 0 + fi + + if [[ "$type" == "dir" ]]; then + rm -rf $pattern + else + rm -f $pattern + fi + REMOVED+=("$label ($count items removed)") +} + +step_reset() { + local REMOVED=() + local step=0 + local total=6 + + # ── Step 1: Agent commands/skills ── + ((step++)) + echo -e "${DIM}[${step}/${total}]${NC} ${BOLD}Remove AFX agent commands and skills${NC}" + + local has_items=false + local items_preview="" + for check in \ + ".claude/commands/afx-*.md" \ + ".codex/skills/afx-*" \ + ".agent/skills/afx-*" \ + ".gemini/commands/afx-*.toml" \ + ".github/prompts/afx-*.prompt.md"; do + local c=$(count_glob "$TARGET_DIR/$check") + if [[ "$c" -gt 0 ]]; then + has_items=true + items_preview+=" ${DIM}$check ($c items)${NC}\n" + fi + done + # Pack-installed items + for check in \ + ".claude/skills/*" \ + ".claude/plugins/*" \ + ".agents/skills/*" \ + ".github/agents/*"; do + local c=$(count_glob "$TARGET_DIR/$check") + if [[ "$c" -gt 0 ]]; then + has_items=true + items_preview+=" ${DIM}$check ($c items)${NC}\n" + fi + done + + if [[ "$has_items" == "true" ]]; then + echo -e "$items_preview" + if confirm " Remove these?"; then + remove_glob "$TARGET_DIR/.claude/commands/afx-*.md" ".claude/commands/afx-*.md" "file" + remove_glob "$TARGET_DIR/.codex/skills/afx-*" ".codex/skills/afx-*" "dir" + remove_glob "$TARGET_DIR/.agent/skills/afx-*" ".agent/skills/afx-*" "dir" + remove_glob "$TARGET_DIR/.gemini/commands/afx-*.toml" ".gemini/commands/afx-*.toml" "file" + remove_glob "$TARGET_DIR/.github/prompts/afx-*.prompt.md" ".github/prompts/afx-*.prompt.md" "file" + remove_glob "$TARGET_DIR/.github/prompts/README.md" ".github/prompts/README.md" "file" + # Pack-installed items + remove_glob "$TARGET_DIR/.claude/skills/*" ".claude/skills/ (pack items)" "dir" + remove_glob "$TARGET_DIR/.claude/plugins/*" ".claude/plugins/ (pack items)" "dir" + remove_glob "$TARGET_DIR/.agents/skills/*" ".agents/skills/ (pack items)" "dir" + remove_glob "$TARGET_DIR/.github/agents/*" ".github/agents/ (pack items)" "file" + else + echo -e " ${YELLOW}Skipped${NC}" + fi + else + echo -e " ${DIM}(no AFX commands/skills found)${NC}" + fi + echo "" + + # ── Step 2: Strip AFX sections from MD files ── + ((step++)) + echo -e "${DIM}[${step}/${total}]${NC} ${BOLD}Strip AFX sections from MD files${NC}" + + local md_targets=() + [[ -f "$TARGET_DIR/CLAUDE.md" ]] && grep -q "$AFX_START_MARKER" "$TARGET_DIR/CLAUDE.md" 2>/dev/null && md_targets+=("CLAUDE.md") + [[ -f "$TARGET_DIR/AGENTS.md" ]] && grep -q "$AFX_AGENTS_START_MARKER" "$TARGET_DIR/AGENTS.md" 2>/dev/null && md_targets+=("AGENTS.md") + [[ -f "$TARGET_DIR/GEMINI.md" ]] && grep -q "$AFX_GEMINI_START_MARKER" "$TARGET_DIR/GEMINI.md" 2>/dev/null && md_targets+=("GEMINI.md") + [[ -f "$TARGET_DIR/.github/copilot-instructions.md" ]] && grep -q "$AFX_COPILOT_START_MARKER" "$TARGET_DIR/.github/copilot-instructions.md" 2>/dev/null && md_targets+=("copilot-instructions.md") + + if [[ ${#md_targets[@]} -gt 0 ]]; then + echo -e " ${DIM}${md_targets[*]}${NC}" + if confirm " Strip AFX sections? (user content preserved)"; then + strip_afx_section "$TARGET_DIR/CLAUDE.md" "$AFX_START_MARKER" "$AFX_END_MARKER" "CLAUDE.md" + strip_afx_section "$TARGET_DIR/AGENTS.md" "$AFX_AGENTS_START_MARKER" "$AFX_AGENTS_END_MARKER" "AGENTS.md" + strip_afx_section "$TARGET_DIR/GEMINI.md" "$AFX_GEMINI_START_MARKER" "$AFX_GEMINI_END_MARKER" "GEMINI.md" + strip_afx_section "$TARGET_DIR/.github/copilot-instructions.md" "$AFX_COPILOT_START_MARKER" "$AFX_COPILOT_END_MARKER" "copilot-instructions.md" + else + echo -e " ${YELLOW}Skipped${NC}" + fi + else + echo -e " ${DIM}(no AFX sections found in MD files)${NC}" + fi + echo "" + + # ── Step 3: AFX documentation ── + ((step++)) + echo -e "${DIM}[${step}/${total}]${NC} ${BOLD}Remove AFX documentation${NC}" + if [[ -d "$TARGET_DIR/docs/agenticflowx" ]]; then + echo -e " ${DIM}docs/agenticflowx/ (docs + templates)${NC}" + if confirm " Remove?"; then + if [[ "$DRY_RUN" == "true" ]]; then + REMOVED+=("docs/agenticflowx/ (would remove)") + else + rm -rf "$TARGET_DIR/docs/agenticflowx" + REMOVED+=("docs/agenticflowx/") + fi + else + echo -e " ${YELLOW}Skipped${NC}" + fi + else + echo -e " ${DIM}(not found)${NC}" + fi + echo "" + + # ── Step 4: .afx/ folder ── + ((step++)) + echo -e "${DIM}[${step}/${total}]${NC} ${BOLD}Remove .afx/ folder${NC}" + if [[ -d "$TARGET_DIR/.afx" ]]; then + echo -e " ${DIM}.afx/ (packs, cache, managed config)${NC}" + if confirm " Remove?"; then + if [[ "$DRY_RUN" == "true" ]]; then + REMOVED+=(".afx/ folder (would remove)") + else + rm -rf "$TARGET_DIR/.afx" + REMOVED+=(".afx/ folder") + fi + else + echo -e " ${YELLOW}Skipped${NC}" + fi + else + echo -e " ${DIM}(not found)${NC}" + fi + echo "" + + # ── Step 5: .afx.yaml ── + ((step++)) + echo -e "${DIM}[${step}/${total}]${NC} ${BOLD}Remove .afx.yaml${NC}" + if [[ -f "$TARGET_DIR/.afx.yaml" ]]; then + echo -e " ${DIM}.afx.yaml (user config)${NC}" + if confirm " Remove?"; then + if [[ "$DRY_RUN" == "true" ]]; then + REMOVED+=(".afx.yaml (would remove)") + else + rm -f "$TARGET_DIR/.afx.yaml" + REMOVED+=(".afx.yaml") + fi + else + echo -e " ${YELLOW}Skipped${NC}" + fi + else + echo -e " ${DIM}(not found)${NC}" + fi + echo "" + + # ── Step 6: .gitignore cleanup ── + ((step++)) + echo -e "${DIM}[${step}/${total}]${NC} ${BOLD}Clean .gitignore${NC}" + if [[ -f "$TARGET_DIR/.gitignore" ]] && grep -q "^\.afx/" "$TARGET_DIR/.gitignore" 2>/dev/null; then + if confirm " Remove '.afx/' entry from .gitignore?"; then + if [[ "$DRY_RUN" != "true" ]]; then + sed -i.bak '/^\.afx\/$/d' "$TARGET_DIR/.gitignore" + rm -f "$TARGET_DIR/.gitignore.bak" + fi + REMOVED+=(".gitignore (.afx/ entry)") + else + echo -e " ${YELLOW}Skipped${NC}" + fi + else + echo -e " ${DIM}(no .afx/ entry)${NC}" + fi + echo "" + + # ── Summary ── + if [[ ${#REMOVED[@]} -gt 0 ]]; then + echo -e "${YELLOW}Removed:${NC}" + for item in "${REMOVED[@]}"; do + echo " - $item" + done + fi +} + +step_directories() { + echo -e "${BLUE}Creating directory structure...${NC}" + if [ "$DRY_RUN" != "true" ]; then + mkdir -p "$TARGET_DIR/docs/specs" + mkdir -p "$TARGET_DIR/docs/adr" + mkdir -p "$TARGET_DIR/docs/research" + fi + [ ! -d "$TARGET_DIR/docs/specs" ] && INSTALLED+=("docs/specs/ directory") + [ ! -d "$TARGET_DIR/docs/adr" ] && INSTALLED+=("docs/adr/ directory") + [ ! -d "$TARGET_DIR/docs/research" ] && INSTALLED+=("docs/research/ directory") +} + # ============================================================================ -# Create directory structure +# Section 10: Main Flow # ============================================================================ -echo -e "${BLUE}[*] Creating directory structure...${NC}" -if [ "$DRY_RUN" != "true" ]; then - mkdir -p "$TARGET_DIR/docs/specs" - mkdir -p "$TARGET_DIR/docs/adr" - mkdir -p "$TARGET_DIR/docs/research" + +# Validate target directory +if [ -z "$TARGET_DIR" ]; then + echo -e "${RED}Error: Target project path required${NC}" + echo "Usage: ./install.sh [--update] /path/to/project" + exit 1 fi -if [ ! -d "$TARGET_DIR/docs/specs" ]; then - INSTALLED+=("docs/specs/ directory") + +TARGET_DIR=$(cd "$TARGET_DIR" 2>/dev/null && pwd || echo "$TARGET_DIR") + +if [ ! -d "$TARGET_DIR" ]; then + echo -e "${RED}Error: Directory does not exist: $TARGET_DIR${NC}" + exit 1 fi -if [ ! -d "$TARGET_DIR/docs/adr" ]; then - INSTALLED+=("docs/adr/ directory") + +# Resolve AFX version +_afx_ref="main" +if [[ -n "$VERSION" ]]; then + _afx_ref=$([[ "$VERSION" == v* ]] && echo "$VERSION" || echo "v$VERSION") +elif [[ -n "$BRANCH" ]]; then + _afx_ref="$BRANCH" +elif [[ -f "$TARGET_DIR/.afx.yaml" ]]; then + _yaml_ver=$(grep '^version:' "$TARGET_DIR/.afx.yaml" 2>/dev/null | awk '{print $2}' | tr -d "'\"") + if [[ -n "$_yaml_ver" && "$_yaml_ver" != "main" ]]; then + if [[ "$_yaml_ver" =~ ^[0-9] ]]; then + _afx_ref=$([[ "$_yaml_ver" == v* ]] && echo "$_yaml_ver" || echo "v$_yaml_ver") + else + _afx_ref="$_yaml_ver" + fi + fi fi -if [ ! -d "$TARGET_DIR/docs/research" ]; then - INSTALLED+=("docs/research/ directory") +AFX_VERSION=$(curl -sL "https://raw.githubusercontent.com/${AFX_REPO}/${_afx_ref}/CHANGELOG.md" | awk '/^## \[/ {print substr($2, 2, length($2)-2); exit}') +if [ -z "$AFX_VERSION" ]; then + AFX_VERSION="Unknown" fi +# Header +echo "" +if [ "$UPDATE_MODE" = "true" ]; then + echo -e "${BLUE}${BOLD}AFX Updater v${AFX_VERSION}${NC}" +else + echo -e "${BLUE}${BOLD}AFX Installer v${AFX_VERSION}${NC}" +fi +echo -e "${DIM}Target: $TARGET_DIR${NC}" +if [ "$DRY_RUN" = "true" ]; then + echo -e "${YELLOW}(Dry run — no changes will be made)${NC}" +fi +echo "" + +# ── Pack-only operations (dispatch and exit) ────────────────────────────── + +_pack_only=false +if [[ -n "$PACK_DISABLE" || -n "$PACK_ENABLE" || -n "$PACK_REMOVE" || "$PACK_LIST" == "true" \ + || -n "$SKILL_DISABLE" || -n "$SKILL_ENABLE" || ${#PACK_NAMES[@]} -gt 0 \ + || ( "$UPDATE_PACKS" == "true" && "$UPDATE_MODE" == "true" ) \ + || -n "$ADD_SKILL" ]]; then + _pack_only=true +fi + +PACK_OPERATION=false + +if [[ ${#PACK_NAMES[@]} -gt 0 && -z "$SKILL_DISABLE" && -z "$SKILL_ENABLE" ]]; then + PACK_OPERATION=true + for name in "${PACK_NAMES[@]}"; do + pack_install "$name" + done +fi + +if [[ -n "$PACK_DISABLE" ]]; then + PACK_OPERATION=true + pack_disable "$PACK_DISABLE" +fi + +if [[ -n "$PACK_ENABLE" ]]; then + PACK_OPERATION=true + pack_enable "$PACK_ENABLE" +fi + +if [[ -n "$PACK_REMOVE" ]]; then + PACK_OPERATION=true + pack_remove "$PACK_REMOVE" +fi + +if [[ "$PACK_LIST" == "true" ]]; then + PACK_OPERATION=true + pack_list +fi + +if [[ -n "$SKILL_DISABLE" ]]; then + PACK_OPERATION=true + if [[ ${#PACK_NAMES[@]} -eq 0 ]]; then + echo -e "${RED}Error: --skill-disable requires --pack${NC}" + exit 1 + fi + skill_disable "$SKILL_DISABLE" "${PACK_NAMES[0]}" +fi + +if [[ -n "$SKILL_ENABLE" ]]; then + PACK_OPERATION=true + if [[ ${#PACK_NAMES[@]} -eq 0 ]]; then + echo -e "${RED}Error: --skill-enable requires --pack${NC}" + exit 1 + fi + skill_enable "$SKILL_ENABLE" "${PACK_NAMES[0]}" +fi + +if [[ "$UPDATE_MODE" == "true" && "$UPDATE_PACKS" == "true" ]]; then + PACK_OPERATION=true + pack_update_all +fi + +if [[ -n "$ADD_SKILL" ]]; then + PACK_OPERATION=true + add_skill "$ADD_SKILL" +fi + +if [[ "$PACK_OPERATION" == "true" ]]; then + echo "" + echo -e "${GREEN}Done!${NC}" + [ ${#INSTALLED[@]} -gt 0 ] && echo "" && echo "Installed:" && printf ' + %s\n' "${INSTALLED[@]}" + [ ${#UPDATED[@]} -gt 0 ] && echo "" && echo "Updated:" && printf ' ~ %s\n' "${UPDATED[@]}" + exit 0 +fi + +# ── Core Install / Update ──────────────────────────────────────────────── + +# Determine AFX source directory +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" 2>/dev/null && pwd || echo "")" + +if [ -z "$SCRIPT_DIR" ] || [ ! -f "$SCRIPT_DIR/.afx.yaml.template" ]; then + echo -e "${YELLOW}Downloading AFX from GitHub...${NC}" + TEMP_DIR=$(mktemp -d) + trap "rm -rf $TEMP_DIR" EXIT + + git clone --depth 1 --quiet https://github.com/${AFX_REPO}.git "$TEMP_DIR/afx" 2>/dev/null || { + echo -e "${RED}Error: Failed to clone AFX repository${NC}" + echo "Check your internet connection or clone manually:" + echo " git clone https://github.com/${AFX_REPO}.git" + exit 1 + } + AFX_DIR="$TEMP_DIR/afx" +else + AFX_DIR="$SCRIPT_DIR" +fi + +# Handle --reset (full uninstall) +if [[ "$RESET" == "true" ]]; then + echo -e "${YELLOW}${BOLD}AFX Reset — Full Uninstall${NC}" + echo -e "${DIM}This will remove all AFX-installed files from your project.${NC}" + echo "" + + step_reset + + echo "" + echo -e "${GREEN}${BOLD}AFX has been fully removed.${NC}" + echo "" + echo -e "${BLUE}To re-install:${NC}" + echo " ./install.sh ." + echo " # or" + echo " curl -sL https://raw.githubusercontent.com/${AFX_REPO}/main/install.sh | bash -s -- ." + echo "" + exit 0 +fi + +# Provider selection (first install only, when no --no-* flags are set) +if [[ "$UPDATE_MODE" != "true" && ! -f "$TARGET_DIR/.afx.yaml" ]]; then + select_providers +fi + +# ── Step execution with confirmations ───────────────────────────────────── +# Each step checks provider flags, --no-* flags, and asks for confirmation. + +STEP=0 +total_steps() { + local count=0 + [[ "$INSTALL_CLAUDE" == "true" ]] && ((count++)) + [[ "$INSTALL_CODEX" == "true" ]] && ((count++)) + [[ "$INSTALL_ANTIGRAVITY" == "true" ]] && ((count++)) + [[ "$INSTALL_GEMINI" == "true" ]] && ((count++)) + [[ "$INSTALL_COPILOT" == "true" ]] && ((count++)) + ((count+=2)) # templates + config (always) + [[ "$NO_CLAUDE_MD" != "true" ]] && ((count++)) + [[ "$NO_AGENTS_MD" != "true" ]] && ((count++)) + [[ "$NO_GEMINI_MD" != "true" ]] && ((count++)) + [[ "$NO_COPILOT_MD" != "true" ]] && ((count++)) + [[ "$NO_DOCS" != "true" ]] && ((count++)) + ((count++)) # directory structure + echo "$count" +} + +TOTAL=$(total_steps) + +run_step() { + local label="$1" + local func="$2" + ((STEP++)) + echo -e "${DIM}[${STEP}/${TOTAL}]${NC} ${label}" + if confirm " Proceed?"; then + "$func" + else + echo -e " ${YELLOW}Skipped${NC}" + fi + echo "" +} + +# Provider-gated steps +if [[ "$INSTALL_CLAUDE" == "true" ]]; then + run_step "Claude slash commands" step_claude_commands +fi + +if [[ "$INSTALL_CODEX" == "true" ]]; then + run_step "Codex skills" step_codex_skills +fi + +if [[ "$INSTALL_ANTIGRAVITY" == "true" ]]; then + run_step "Antigravity skills" step_antigravity_skills +fi + +if [[ "$INSTALL_GEMINI" == "true" ]]; then + run_step "Gemini CLI commands" step_gemini_commands +fi + +if [[ "$INSTALL_COPILOT" == "true" ]]; then + run_step "GitHub Copilot prompts" step_copilot_prompts +fi + +# Commands-only exit +if [ "$COMMANDS_ONLY" = "true" ]; then + echo "" + echo -e "${GREEN}Commands processed!${NC}" + echo "" + [ ${#INSTALLED[@]} -gt 0 ] && echo "Installed: ${#INSTALLED[@]}" && printf ' + %s\n' "${INSTALLED[@]}" + [ ${#UPDATED[@]} -gt 0 ] && echo "Updated: ${#UPDATED[@]}" && printf ' ~ %s\n' "${UPDATED[@]}" + [ ${#SKIPPED[@]} -gt 0 ] && echo "Skipped: ${#SKIPPED[@]}" && printf ' - %s\n' "${SKIPPED[@]}" + exit 0 +fi + +# Always-available steps +run_step "Templates" step_templates +run_step "Configuration (.afx.yaml)" step_config + +# MD integration steps (gated by --no-* flags) +if [[ "$NO_CLAUDE_MD" != "true" ]]; then + run_step "CLAUDE.md integration" step_claude_md +fi + +if [[ "$NO_AGENTS_MD" != "true" ]]; then + run_step "AGENTS.md integration" step_agents_md +fi + +if [[ "$NO_GEMINI_MD" != "true" ]]; then + run_step "GEMINI.md integration" step_gemini_md +fi + +if [[ "$NO_COPILOT_MD" != "true" ]]; then + run_step "copilot-instructions.md integration" step_copilot_md +fi + +if [[ "$NO_DOCS" != "true" ]]; then + run_step "AFX documentation" step_docs +fi + +run_step "Directory structure" step_directories + # ============================================================================ # Summary # ============================================================================ + echo "" if [ "$UPDATE_MODE" = "true" ]; then - echo -e "${GREEN}========================================${NC}" - echo -e "${GREEN}AFX Update Complete! (v${AFX_VERSION})${NC}" - echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}${BOLD}AFX Update Complete! (v${AFX_VERSION})${NC}" else - echo -e "${GREEN}========================================${NC}" - echo -e "${GREEN}AFX Installation Complete! (v${AFX_VERSION})${NC}" - echo -e "${GREEN}========================================${NC}" + echo -e "${GREEN}${BOLD}AFX Installation Complete! (v${AFX_VERSION})${NC}" fi echo "" @@ -744,13 +2212,10 @@ fi echo "" if [ "$UPDATE_MODE" = "true" ]; then echo -e "${BLUE}Update notes:${NC}" - echo " - Claude commands, Codex skills, Gemini commands, Copilot prompts, and templates were updated" - echo " - AFX docs in docs/agenticflowx/ were updated" - echo " - .afx.yaml was preserved (your config)" - echo " - CLAUDE.md AFX section was replaced (your content preserved)" - echo " - AGENTS.md AFX Codex section was replaced (your content preserved)" - echo " - GEMINI.md AFX Gemini section was replaced (your content preserved)" - echo " - copilot-instructions.md AFX Copilot section was replaced (your content preserved)" + echo " - Commands, skills, and templates were updated for selected providers" + echo " - .afx/.afx.yaml was updated (managed defaults)" + echo " - .afx.yaml was preserved (your config overrides)" + echo " - MD integration files were replaced (your content preserved)" else echo -e "${BLUE}Next steps:${NC}" echo " 1. Edit .afx.yaml to configure your project" @@ -759,8 +2224,9 @@ else echo " 4. Run /afx:help (Claude) or afx-help (Codex) for command reference" fi echo "" +update_ref=$(resolve_ref) echo -e "${CYAN}To update AFX later:${NC}" echo " ./install.sh --update ." echo " # or" -echo " curl -sL https://raw.githubusercontent.com/${AFX_REPO}/main/install.sh | bash -s -- --update ." +echo " curl -sL https://raw.githubusercontent.com/${AFX_REPO}/${update_ref}/install.sh | bash -s -- --update ." echo "" diff --git a/packs/afx-pack-qa.yaml b/packs/afx-pack-qa.yaml new file mode 100644 index 0000000..6960e24 --- /dev/null +++ b/packs/afx-pack-qa.yaml @@ -0,0 +1,47 @@ +# @see docs/specs/afx-packs/spec.md#FR-1 +# @see docs/specs/afx-packs/spec.md#FR-2 +# @see docs/specs/afx-packs/spec.md#FR-3 +# @see docs/specs/afx-packs/design.md#11-pack-manifests + +name: afx-pack-qa +description: QA Engineer role pack — testing, review, and quality assurance +category: role + +platforms: + claude: true + codex: true + antigravity: true + copilot: partial # Only AFX-built skills — no external SKILL.md conversion + +includes: + # External skills from Antigravity (pristine — SKILL.md format → Claude + Codex + Antigravity) + - repo: anthropics/antigravity-awesome-skills + path: skills/ + items: + - test-driven-development + - tdd-workflow + - playwright-skill + - e2e-testing-patterns + - unit-testing-test-generate + - systematic-debugging + - performance-testing-review + + # External skills from OpenAI (pristine — SKILL.md + openai.yaml → Codex only) + - repo: openai/skills + path: skills/.curated/ + items: + - playwright + + # Claude Code plugins (pristine — .claude-plugin/ format → Claude only) + - repo: anthropics/claude-code + path: plugins/ + items: + - code-review + - pr-review-toolkit + + # AFX-built skills (guardrails baked in, hosted in AFX repo) + - repo: rixrix/afx + path: skills/ + items: + - afx-qa-methodology # QA workflow with @see tracing + two-stage verify + - afx-spec-test-planning # Test plans linked to spec tasks diff --git a/packs/afx-pack-security.yaml b/packs/afx-pack-security.yaml new file mode 100644 index 0000000..a9c861a --- /dev/null +++ b/packs/afx-pack-security.yaml @@ -0,0 +1,36 @@ +# @see docs/specs/afx-packs/spec.md#FR-1 +# @see docs/specs/afx-packs/spec.md#FR-2 +# @see docs/specs/afx-packs/spec.md#FR-3 +# @see docs/specs/afx-packs/design.md#11-pack-manifests + +name: afx-pack-security +description: Security review and audit pack — OWASP, dependency scanning, code hardening +category: role + +platforms: + claude: true + codex: true + antigravity: true + copilot: partial # Only AFX-built skills — no external SKILL.md conversion + +includes: + # External skills from Antigravity (pristine — SKILL.md format → Claude + Codex + Antigravity) + - repo: anthropics/antigravity-awesome-skills + path: skills/ + items: + - security-review + - dependency-vulnerability-check + - code-hardening + + # Claude Code plugins (pristine — .claude-plugin/ format → Claude only) + - repo: anthropics/claude-code + path: plugins/ + items: + - security-scanner + + # AFX-built skills (guardrails baked in, hosted in AFX repo) + - repo: rixrix/afx + path: skills/ + items: + - afx-owasp-top-10 # OWASP top 10 checklist with @see tracing + - afx-security-audit # Security audit workflow diff --git a/packs/index.json b/packs/index.json new file mode 100644 index 0000000..3772d2c --- /dev/null +++ b/packs/index.json @@ -0,0 +1,23 @@ +{ + "packs": { + "afx-pack-qa": { + "description": "QA Engineer role pack — testing, review, and quality assurance", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"] + }, + "afx-pack-security": { + "description": "Security review and audit pack — OWASP, dependency scanning, code hardening", + "category": "role", + "providers": ["claude", "codex", "antigravity", "copilot"] + } + }, + "upstream": { + "anthropics/claude-plugins-official": { + "featured": ["playwright-e2e", "security-scanner", "code-architect"] + }, + "openai/skills": {}, + "anthropics/antigravity-awesome-skills": { + "featured": ["code-architect"] + } + } +} diff --git a/skills/afx-owasp-top-10/SKILL.md b/skills/afx-owasp-top-10/SKILL.md new file mode 100644 index 0000000..a31d113 --- /dev/null +++ b/skills/afx-owasp-top-10/SKILL.md @@ -0,0 +1,48 @@ +# AFX OWASP Top 10 + +Security review checklist based on the OWASP Top 10, integrated with AFX spec-driven traceability. + +## Activation + +This skill activates when the user asks about: + +- Security review or audit +- OWASP Top 10 compliance +- Vulnerability assessment +- Secure coding practices + +## Instructions + +### OWASP Top 10 Checklist + +When reviewing code for security, check against all 10 categories: + +1. **A01 Broken Access Control** — verify authorization on every endpoint, deny by default +2. **A02 Cryptographic Failures** — no plaintext secrets, proper key management, TLS everywhere +3. **A03 Injection** — parameterized queries, input validation, no string concatenation in queries +4. **A04 Insecure Design** — threat modeling, secure design patterns, defense in depth +5. **A05 Security Misconfiguration** — no default credentials, minimal permissions, hardened configs +6. **A06 Vulnerable Components** — check dependencies, known CVEs, update policy +7. **A07 Authentication Failures** — strong passwords, MFA, session management +8. **A08 Data Integrity Failures** — verify signatures, CI/CD security, serialization safety +9. **A09 Logging Failures** — audit trails, no sensitive data in logs, tamper-proof logging +10. **A10 SSRF** — validate URLs, allowlist outbound destinations, block internal network access + +### AFX Integration + +- Link findings to spec requirements with `@see` annotations: +``` +@see docs/specs/{feature}/spec.md#NFR-{n} +``` +- Add `FIXME @see` annotations for vulnerabilities found in code +<!-- @afx:provider-commands --> +- Use `/afx:check path` to trace data flow through the application +<!-- @afx:/provider-commands --> +- Document security decisions in `docs/specs/{feature}/journal.md` + +### Severity Classification + +- **Critical**: Remote code execution, authentication bypass, data exfiltration +- **High**: Privilege escalation, SQL injection, XSS with session theft +- **Medium**: Information disclosure, CSRF, insecure defaults +- **Low**: Missing headers, verbose errors, minor misconfigurations diff --git a/skills/afx-qa-methodology/SKILL.md b/skills/afx-qa-methodology/SKILL.md new file mode 100644 index 0000000..19ebce6 --- /dev/null +++ b/skills/afx-qa-methodology/SKILL.md @@ -0,0 +1,39 @@ +# AFX QA Methodology + +A structured quality assurance workflow that integrates with AFX spec-driven development. + +## Activation + +This skill activates when the user asks about: + +- Testing strategy or methodology +- Quality assurance workflows +- Test coverage analysis +- Bug triaging or classification + +## Instructions + +### Test Strategy + +1. **Read the spec first** — always check `docs/specs/{feature}/spec.md` for acceptance criteria +2. **Link tests to requirements** — every test file must include `@see` annotations: +``` +@see docs/specs/{feature}/spec.md#FR-{n} +@see docs/specs/{feature}/tasks.md#2.1-task-slug +``` +3. **Two-stage verification** — mark tasks `[OK]` in agent column, leave human column for reviewer +4. **Coverage mapping** — identify untested requirements and flag gaps + +### Bug Triage + +- Link bugs to spec requirements where possible +- Classify by severity: Critical (data loss), Major (broken workflow), Minor (cosmetic) +- Include reproduction steps tied to acceptance criteria + +### AFX Integration + +<!-- @afx:provider-commands --> +- Use `/afx:check path` to verify execution flow from UI to DB +- Use `/afx:task audit` to verify test coverage against spec +<!-- @afx:/provider-commands --> +- Follow the spec → design → tasks → code traceability chain diff --git a/skills/afx-security-audit/SKILL.md b/skills/afx-security-audit/SKILL.md new file mode 100644 index 0000000..42c891a --- /dev/null +++ b/skills/afx-security-audit/SKILL.md @@ -0,0 +1,56 @@ +# AFX Security Audit + +A structured security audit workflow that traces findings back to spec requirements and design decisions. + +## Activation + +This skill activates when the user asks about: + +- Security audit or security assessment +- Penetration testing guidance +- Threat modeling +- Security hardening recommendations + +## Instructions + +### Audit Workflow + +1. **Scope** — identify the feature boundary from `docs/specs/{feature}/spec.md` +2. **Threat Model** — enumerate threats per component (authentication, data flow, APIs) +3. **Review** — check code against known vulnerability patterns +4. **Report** — document findings with severity, location, and remediation + +### Audit Report Format + +For each finding, document: + +```markdown +### Finding: {title} + +- **Severity**: Critical / High / Medium / Low +- **Category**: OWASP A01–A10 +- **Location**: `path/to/file.ts:line` +- **Spec Reference**: @see docs/specs/{feature}/spec.md#NFR-{n} +- **Description**: {what the vulnerability is} +- **Remediation**: {how to fix it} +- **Verification**: {how to confirm it's fixed} +``` + +### Security Checklist + +- [ ] Authentication: tokens expire, sessions invalidated on logout +- [ ] Authorization: role checks on every route, deny by default +- [ ] Input validation: all user input sanitized at system boundary +- [ ] Data protection: secrets in env vars, PII encrypted at rest +- [ ] Dependencies: no known CVEs, lockfile committed +- [ ] Error handling: no stack traces in production, generic error messages +- [ ] Logging: audit trail for sensitive operations, no credentials in logs + +### AFX Integration + +- Link findings to spec NFR requirements with `@see` annotations +<!-- @afx:provider-commands --> +- Use `/afx:check path` to trace data flow for attack surface analysis +<!-- @afx:/provider-commands --> +- Record audit results in `docs/specs/{feature}/journal.md` +- Follow two-stage verification: agent flags issues, human validates fixes diff --git a/skills/afx-spec-test-planning/SKILL.md b/skills/afx-spec-test-planning/SKILL.md new file mode 100644 index 0000000..7ff2e44 --- /dev/null +++ b/skills/afx-spec-test-planning/SKILL.md @@ -0,0 +1,45 @@ +# AFX Spec Test Planning + +Plan tests by deriving them directly from spec requirements and acceptance criteria. + +## Activation + +This skill activates when the user asks about: + +- Test planning or test plan creation +- Deriving tests from specifications +- Mapping tests to requirements + +## Instructions + +### Derive Tests from Spec + +1. Read `docs/specs/{feature}/spec.md` +2. For each FR-{n} and NFR-{n}, derive one or more test cases +3. For each acceptance criteria item, derive at least one assertion +4. Document the mapping: + + | Requirement | Test Case | Type | Status | + | ----------- | ---------------------- | ----------- | ------- | + | FR-1 | test_user_login | Integration | Pending | + | NFR-1 | test_login_latency_p95 | Performance | Pending | + +### Test File Structure + +Every test file must include traceability: + +```typescript +/** + * @see docs/specs/{feature}/spec.md#FR-1 + * @see docs/specs/{feature}/tasks.md#3.1-write-login-tests + */ +describe('User Login', () => { ... }); +``` + +### Gap Detection + +After planning, check for: + +- Requirements without test cases +- Test cases without requirement links +- Acceptance criteria without assertions