From 77998b6003aa113efe9fbc5a64c2b373694856f4 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais Date: Mon, 4 May 2026 14:31:00 +0200 Subject: [PATCH 01/20] chore: fix CI workflow, restructure SKILL.md headers, sync README/router with disk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 0 (bug fixes): - Fix license-header-check workflow placeholder SHA (@874b1c... -> @main). GitHub Actions could not resolve the placeholder; the workflow had never produced a single check run. Match the org-canonical pattern (~22/30 LF repos pin to @main, including the lfx-backstage scaffold template). - Add `with: copyright_line + include_files: "*.md"` to extend scanning to markdown (not in default filetypes). - Restructure all 15 SKILL.md license headers as YAML comments inside the frontmatter (line 2-3, after opening `---`). Required because the upstream workflow runs `head -4 | grep` and the previous HTML comments landed at line ~12, after frontmatter. YAML comments are silently ignored by the frontmatter parser; verified all 15 files still parse correctly with `name`, `description`, `allowed-tools` keys intact. - Add license header to README.md (top of file). - Fix wrong clone URL in 3 places: linuxfoundation/skills.git was working via GitHub redirect but is fragile; updated to linuxfoundation/lfx-skills.git in README.md (lines 8, 41) and docs/platform-install.md (line 15). Phase 1 (README/router coverage): - Update README's three skill lists (Verify autocomplete, Skill Overview table, Project Structure tree) to include all 15 skills. Previously missing: /lfx-pr-resolve, /lfx-git-setup, /lfx-snowflake-access, plus partial coverage of /lfx-cdp-snowflake-connectors. Also added the two undocumented coordinator references (fga-protected-types.md, indexed-data-types.md) and pr-resolve's evals/ directory to the tree. - Update /lfx routing table (lfx/SKILL.md) to cover the 5 unrouted skills: /lfx-pr-catchup, /lfx-git-setup, /lfx-intercom, /lfx-snowflake-access, /lfx-cdp-snowflake-connectors. Added matching Routing sections with Skill() invocation examples. /lfx-backend-builder and /lfx-ui-builder remain intentionally unrouted (invoked via /lfx-coordinator, not directly). - Add "Defer to specialized skills" section to /lfx-coordinator so it hands off Intercom and CDP-Snowflake-connector requests instead of attempting full-stack coordination on them. - Cross-link /lfx-pr-catchup → /lfx-pr-resolve in the drill-down step: if a drilled PR has unresolved comments, suggest invoking pr-resolve. - Document MCP prerequisites at the top of lfx-cdp-snowflake-connectors (lists the 5 mcp__claude_ai_LFX_BI_Layer__* tools the skill calls). Verification: - All 42 tracked .md files now have copyright in `head -4` (simulated the upstream workflow locally — it would PASS). - All 15 SKILL.md frontmatter still parses correctly via Ruby's YAML. - README's 3 lists exactly match `ls -d lfx*/`. - /lfx routing covers 12 user-facing skills (excludes builders by design). Skipped from the planned scope: - Standardizing skill descriptions with trigger phrases (deferred per reviewer direction — keep current descriptions). - .gitignore cleanup items (deferred). - install.sh "Available skills" output bug (will be made obsolete by the Phase 3 CLI rewrite). Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Josep Garcia-Reyero Sais --- .github/workflows/license-header-check.yml | 5 ++- README.md | 34 ++++++++++++--- docs/platform-install.md | 4 +- lfx-backend-builder/SKILL.md | 2 + lfx-cdp-snowflake-connectors/SKILL.md | 14 ++++++ lfx-coordinator/SKILL.md | 13 ++++++ lfx-git-setup/SKILL.md | 2 + lfx-intercom/SKILL.md | 2 + lfx-pr-catchup/SKILL.md | 13 +++++- lfx-pr-resolve/SKILL.md | 2 + lfx-preflight/SKILL.md | 2 + lfx-product-architect/SKILL.md | 2 + lfx-research/SKILL.md | 2 + lfx-setup/SKILL.md | 2 + lfx-snowflake-access/SKILL.md | 2 + lfx-test-journey/SKILL.md | 2 + lfx-ui-builder/SKILL.md | 2 + lfx/SKILL.md | 50 ++++++++++++++++++++++ 18 files changed, 146 insertions(+), 9 deletions(-) diff --git a/.github/workflows/license-header-check.yml b/.github/workflows/license-header-check.yml index bbfe404..fc7798e 100644 --- a/.github/workflows/license-header-check.yml +++ b/.github/workflows/license-header-check.yml @@ -12,4 +12,7 @@ permissions: jobs: license-header-check: name: License Header Check - uses: linuxfoundation/lfx-public-workflows/.github/workflows/license-header-check.yml@874b1c3f4e5d6789abcdeffedcba1234567890ab + uses: linuxfoundation/lfx-public-workflows/.github/workflows/license-header-check.yml@main + with: + copyright_line: "Copyright The Linux Foundation and each contributor to LFX." + include_files: "*.md" diff --git a/README.md b/README.md index 6e021b2..0368b0c 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + + + # LFX Skills A collection of AI coding skills that encode the full development workflow for the LFX Self-Service platform. These skills turn your AI coding assistant into a context-aware development partner that understands LFX conventions, architecture, and code patterns — eliminating the need to repeatedly explain project structure, naming rules, or coding standards. @@ -5,8 +8,8 @@ A collection of AI coding skills that encode the full development workflow for t ## Quick Install ```bash -git clone https://github.com/linuxfoundation/skills.git -cd skills +git clone https://github.com/linuxfoundation/lfx-skills.git +cd lfx-skills ./install.sh ``` @@ -38,7 +41,7 @@ If you prefer to install manually instead of using `./install.sh`: ### Step 1: Clone this repo ```bash -git clone https://github.com/linuxfoundation/skills.git +git clone https://github.com/linuxfoundation/lfx-skills.git ``` ### Step 2: Install the skills @@ -60,7 +63,7 @@ This makes all `/lfx*` skills available globally. Restart your AI coding assistant (or open a new session) in any LFX repo and type `/lfx` — you should see all skills in the autocomplete list: ``` -/lfx ← start here (plain-language entry point) +/lfx ← start here (plain-language entry point) /lfx-coordinator /lfx-research /lfx-backend-builder @@ -68,9 +71,13 @@ Restart your AI coding assistant (or open a new session) in any LFX repo and typ /lfx-product-architect /lfx-preflight /lfx-pr-catchup +/lfx-pr-resolve /lfx-setup /lfx-test-journey +/lfx-git-setup /lfx-intercom +/lfx-snowflake-access +/lfx-cdp-snowflake-connectors ``` ### Alternative: Per-repo installation @@ -132,10 +139,13 @@ The skills form a layered system where each skill has a clear responsibility and | `/lfx-product-architect` | Answers "where should this go?", traces data flows, makes placement decisions, explains design patterns | Read-only | Bash, Read, Glob, Grep, AskUserQuestion | | `/lfx-preflight` | Pre-PR validation — Phase 1 auto-fixes (format, license, lint, build), Phase 2 code review (15 report-only checks for Angular). Pass `--skip-review` to skip Phase 2 | Validate + review | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | | `/lfx-pr-catchup` | Morning PR dashboard — unresolved comments, status changes, stale PRs, approved-but-not-merged across all your open PRs | Read-only | Bash, Read, Glob, Grep, AskUserQuestion | +| `/lfx-pr-resolve` | Address PR review feedback end-to-end — fetches threads, makes changes, commits, responds per-thread, resolves, posts summary | Audit + fix | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion, **Skill** | | `/lfx-setup` | Environment setup — prerequisites, clone, install, env vars, dev server. Adapts to Angular or Go repos | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion | | `/lfx-test-journey` | Combine branches from multiple repos into worktrees for journey testing | Interactive | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | +| `/lfx-git-setup` | Interactive setup for DCO sign-off and GPG-signed commits. Use when commits aren't showing as Verified or onboarding to LFX | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | | `/lfx-intercom` | Add or fix Intercom integration against the LFX canonical pattern — audits JWT setup, shutdown, Auth0 claim, app IDs, CSP | Audit + fix | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | -| `/lfx-cdp-snowflake-connectors` | Streamlines adding a new Snowflake connector to CDP — requires knowledge of the source specs | Interactive and guided | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | +| `/lfx-snowflake-access` | Guide users requesting Snowflake access — generates Terraform HCL for `users.tf` or `service_accounts.tf` and walks through the PR | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | +| `/lfx-cdp-snowflake-connectors` | Streamlines adding a new Snowflake connector to CDP — requires knowledge of the source specs. Requires LFX BI Layer MCP server | Interactive and guided | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, **MCP (LFX BI Layer)** | > **Note:** Tool names in the table above follow Claude Code conventions. See [docs/tool-mapping.md](docs/tool-mapping.md) for equivalents on other platforms. @@ -442,6 +452,8 @@ An **interactive setup guide** that walks through environment configuration step ├── lfx-coordinator/ │ ├── SKILL.md # Orchestrator — plans, delegates, validates │ └── references/ +│ ├── fga-protected-types.md # FGA-protected resource types (read-restricted) +│ ├── indexed-data-types.md # Queryable resource types and indexing │ └── shared-types.md # Shared package conventions ├── lfx-research/ │ └── SKILL.md # Read-only exploration and API validation @@ -469,12 +481,24 @@ An **interactive setup guide** that walks through environment configuration step │ └── SKILL.md # Pre-PR validation and auto-fix ├── lfx-pr-catchup/ │ └── SKILL.md # Morning PR catch-up dashboard +├── lfx-pr-resolve/ +│ ├── SKILL.md # Address PR review feedback end-to-end +│ └── evals/ +│ └── evals.json # Skill behavior assertions ├── lfx-setup/ │ └── SKILL.md # Environment setup guide ├── lfx-test-journey/ │ └── SKILL.md # Multi-branch journey testing +├── lfx-git-setup/ +│ ├── SKILL.md # DCO sign-off and GPG-signed commit setup +│ └── references/ +│ ├── linux.md # Linux-specific GPG/DCO instructions +│ ├── mac.md # macOS-specific GPG/DCO instructions +│ └── windows.md # Windows-specific GPG/DCO instructions ├── lfx-intercom/ │ └── SKILL.md # Intercom integration — add or fix to LFX standard +├── lfx-snowflake-access/ +│ └── SKILL.md # Request Snowflake access via Terraform PR └── lfx-cdp-snowflake-connectors/ └── SKILL.md # Snowflake connector scaffolding for CDP ``` diff --git a/docs/platform-install.md b/docs/platform-install.md index b68a1fd..efcfa54 100644 --- a/docs/platform-install.md +++ b/docs/platform-install.md @@ -12,8 +12,8 @@ Claude Code is the reference implementation. Skills are auto-discovered from `~/ **Automatic installation:** ```bash -git clone https://github.com/linuxfoundation/skills.git -cd skills +git clone https://github.com/linuxfoundation/lfx-skills.git +cd lfx-skills ./install.sh ``` diff --git a/lfx-backend-builder/SKILL.md b/lfx-backend-builder/SKILL.md index 8a72dcc..f2b4120 100644 --- a/lfx-backend-builder/SKILL.md +++ b/lfx-backend-builder/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-backend-builder description: > Generate compliant backend code for LFX repos — Express.js proxy endpoints diff --git a/lfx-cdp-snowflake-connectors/SKILL.md b/lfx-cdp-snowflake-connectors/SKILL.md index d5058b8..54eff7f 100644 --- a/lfx-cdp-snowflake-connectors/SKILL.md +++ b/lfx-cdp-snowflake-connectors/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-cdp-snowflake-connectors description: Use when adding a new snowflake-connector data source to crowd.dev — a new platform or a new source within an existing platform that needs buildSourceQuery, transformer, activity types, migration, and all associated type registrations scaffolded. allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, mcp__claude_ai_LFX_BI_Layer__get_all_sources, mcp__claude_ai_LFX_BI_Layer__get_source_details, mcp__claude_ai_LFX_BI_Layer__list_metrics, mcp__claude_ai_LFX_BI_Layer__get_dimensions, mcp__claude_ai_LFX_BI_Layer__query_metrics @@ -6,6 +8,18 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, mcp__claude # Scaffold Snowflake Connector +## Prerequisites + +This skill requires the **LFX BI Layer MCP server** to be configured in your AI tool. The skill calls these MCP tools to discover Snowflake source schemas, dimensions, and metrics: + +- `mcp__claude_ai_LFX_BI_Layer__get_all_sources` +- `mcp__claude_ai_LFX_BI_Layer__get_source_details` +- `mcp__claude_ai_LFX_BI_Layer__list_metrics` +- `mcp__claude_ai_LFX_BI_Layer__get_dimensions` +- `mcp__claude_ai_LFX_BI_Layer__query_metrics` + +If these tools are not available in your environment, the skill will fail in Phase 2 (Schema Collection) when querying source schemas. Set up the LFX BI Layer MCP server before invoking this skill — see your team's MCP onboarding docs. + ## Overview This skill scaffolds all code required to add a new snowflake-connector data source to crowd.dev. It covers up to 11 touch points, enforces zero-assumption practices, and requires explicit user validation for every piece of business logic before writing any file to disk. diff --git a/lfx-coordinator/SKILL.md b/lfx-coordinator/SKILL.md index 55f861b..4354f0a 100644 --- a/lfx-coordinator/SKILL.md +++ b/lfx-coordinator/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-coordinator description: > Guided development workflow for building, fixing, updating, or refactoring @@ -16,6 +18,17 @@ allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion, Skill You coordinate development across LFX repos. You NEVER write code — you delegate ALL code changes to `/lfx-backend-builder` and `/lfx-ui-builder`. You do not have Write or Edit tools. +## Defer to specialized skills when applicable + +Before starting your normal coordination workflow, check whether the request belongs to a specialized skill that handles its own end-to-end flow. If so, tell the user and stop: + +- **Intercom integration work** (add Intercom to an Angular app, fix JWT setup, audit CSP, fix shutdown, fix Auth0 claim) → defer to `/lfx-intercom`. That skill audits and fixes against the LFX canonical Intercom pattern. +- **CDP / crowd.dev Snowflake connectors** (add a new snowflake-connector data source, scaffold buildSourceQuery + transformer + activity types + migration) → defer to `/lfx-cdp-snowflake-connectors`. That skill enforces zero-assumption scaffolding with explicit user validation per business-logic decision. + +If the request matches one of these, respond with: "This is best handled by `/` — it owns the full workflow for this. Want me to hand off?" Do not attempt to coordinate it yourself. + +For everything else (full-stack feature work, bug fixes, refactors across the regular LFX repos), continue with the workflow below. + ## Input Validation Before starting, verify you have enough context. Auto-detect as much as possible — minimize questions to the user. diff --git a/lfx-git-setup/SKILL.md b/lfx-git-setup/SKILL.md index 4d57c70..38a611b 100644 --- a/lfx-git-setup/SKILL.md +++ b/lfx-git-setup/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-git-setup description: > Interactive setup guide for LFX contributors to configure Git for DCO signoff diff --git a/lfx-intercom/SKILL.md b/lfx-intercom/SKILL.md index 05313ae..753b746 100644 --- a/lfx-intercom/SKILL.md +++ b/lfx-intercom/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-intercom description: > Add or fix Intercom integration in an LFX Angular app. Detects existing diff --git a/lfx-pr-catchup/SKILL.md b/lfx-pr-catchup/SKILL.md index ed54646..7cc60d0 100644 --- a/lfx-pr-catchup/SKILL.md +++ b/lfx-pr-catchup/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-pr-catchup description: > Morning PR catch-up dashboard — shows unresolved comments, status changes, @@ -242,7 +244,16 @@ gh pr view $NUMBER --repo $OWNER/$REPO --json comments,reviews,statusCheckRollup gh pr checks $NUMBER --repo $OWNER/$REPO ``` -Present the drill-down in a readable format, then offer to drill into another PR or end. +Present the drill-down in a readable format. + +If the drilled-down PR has unresolved review comments (HIGH signal), proactively suggest: + +``` +This PR has [N] unresolved comments. Want me to address them? +Run: /lfx-pr-resolve #[number] +``` + +Then offer to drill into another PR or end. ## Scope Boundaries diff --git a/lfx-pr-resolve/SKILL.md b/lfx-pr-resolve/SKILL.md index 998fd70..6c2dbb6 100644 --- a/lfx-pr-resolve/SKILL.md +++ b/lfx-pr-resolve/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-pr-resolve description: > Address PR review comments — fetches unresolved threads, makes code changes, diff --git a/lfx-preflight/SKILL.md b/lfx-preflight/SKILL.md index f5d1fbb..3eba828 100644 --- a/lfx-preflight/SKILL.md +++ b/lfx-preflight/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-preflight description: > Pre-PR validation for any LFX repo — Phase 1 auto-fixes (license, format, lint, build), diff --git a/lfx-product-architect/SKILL.md b/lfx-product-architect/SKILL.md index 988953a..613c4c6 100644 --- a/lfx-product-architect/SKILL.md +++ b/lfx-product-architect/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-product-architect description: > Understand LFX system architecture, decide where code should go, trace data flows, diff --git a/lfx-research/SKILL.md b/lfx-research/SKILL.md index c1e5a9f..7963d08 100644 --- a/lfx-research/SKILL.md +++ b/lfx-research/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-research description: > Read-only exploration skill for LFX repos — upstream API validation, codebase diff --git a/lfx-setup/SKILL.md b/lfx-setup/SKILL.md index 34d9838..7aa7fcd 100644 --- a/lfx-setup/SKILL.md +++ b/lfx-setup/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-setup description: > Environment setup for any LFX repo — prerequisites, clone, install, env vars, diff --git a/lfx-snowflake-access/SKILL.md b/lfx-snowflake-access/SKILL.md index 1962272..e27f686 100644 --- a/lfx-snowflake-access/SKILL.md +++ b/lfx-snowflake-access/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-snowflake-access description: > Guide users through requesting Snowflake access at the Linux Foundation. Handles two request diff --git a/lfx-test-journey/SKILL.md b/lfx-test-journey/SKILL.md index f34efa5..007c6d5 100644 --- a/lfx-test-journey/SKILL.md +++ b/lfx-test-journey/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-test-journey description: > Combine multiple feature branches across repos into worktrees for diff --git a/lfx-ui-builder/SKILL.md b/lfx-ui-builder/SKILL.md index eb6c26a..c0864ae 100644 --- a/lfx-ui-builder/SKILL.md +++ b/lfx-ui-builder/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx-ui-builder description: > Generate compliant Angular 20 frontend code — components, services, templates, diff --git a/lfx/SKILL.md b/lfx/SKILL.md index 37f3e9f..f4cac5c 100644 --- a/lfx/SKILL.md +++ b/lfx/SKILL.md @@ -1,4 +1,6 @@ --- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT name: lfx description: > Starting point for LFX development. Describe what you want in plain language @@ -75,8 +77,13 @@ Listen to what the user says and classify their intent. **Do not ask technical q | "What APIs ...", "Does ... exist?", "Find ...", "Research ..." | To explore and research | `/lfx-research` | | "Check my changes", "Ready for PR?", "Validate ...", "Preflight" | To validate before PR | `/lfx-preflight` | | "Address PR comments", "Fix review feedback", "Resolve PR threads", "Handle PR comments" | To address PR review feedback | `/lfx-pr-resolve` | +| "Show me my PRs", "Morning catch-up", "PR status overview", "Which PRs need attention", "What's stale" | To see open PR status across repos | `/lfx-pr-catchup` | | "Set up", "Install", "Environment", "Getting started" | Environment setup | `/lfx-setup` | | "Test a journey", "Combine branches", "Integration test", "Test across branches", "Multi-branch test" | To test across branches | `/lfx-test-journey` | +| "Set up Git signing", "Configure DCO", "GPG keys for commits", "Why aren't my commits Verified", "git commit -s" | Git signing / DCO setup | `/lfx-git-setup` | +| "Fix Intercom", "Audit Intercom", "Add Intercom integration", "JWT setup for Intercom", "Intercom CSP" | Intercom integration in an LFX Angular app | `/lfx-intercom` | +| "Add a Snowflake connector", "Scaffold a CDP source", "New crowd.dev data source", "snowflake-connector platform" | Scaffold a new CDP Snowflake connector | `/lfx-cdp-snowflake-connectors` | +| "I need Snowflake access", "Add me to Snowflake", "Need a service account", "Request Snowflake permissions" | Request Snowflake access via Terraform PR | `/lfx-snowflake-access` | | "Show me an example", "How do I use this?", "Help" | Guidance | Show quickstart examples | ## Step 3: Translate and Route @@ -157,6 +164,49 @@ Skill(skill: "lfx-test-journey", args: "status") Skill(skill: "lfx-test-journey", args: "refresh committee-onboarding") ``` +### Routing to `/lfx-pr-catchup` + +Pass an org filter or stale-days override if the user mentioned one, otherwise invoke with no args: + +``` +Skill(skill: "lfx-pr-catchup") +Skill(skill: "lfx-pr-catchup", args: "linuxfoundation") +Skill(skill: "lfx-pr-catchup", args: "stale=14") +``` + +### Routing to `/lfx-git-setup` + +No translation needed — invoke directly: + +``` +Skill(skill: "lfx-git-setup") +``` + +### Routing to `/lfx-intercom` + +No translation needed — the skill audits and fixes the Intercom integration in the current Angular repo: + +``` +Skill(skill: "lfx-intercom") +``` + +### Routing to `/lfx-cdp-snowflake-connectors` + +The skill scaffolds one connector per run. Pass the platform/source name if the user mentioned it: + +``` +Skill(skill: "lfx-cdp-snowflake-connectors") +Skill(skill: "lfx-cdp-snowflake-connectors", args: "platform: discourse, source: discourse_posts") +``` + +### Routing to `/lfx-snowflake-access` + +No translation needed — the skill collects user details and generates the Terraform PR for the lfx-snowflake-terraform repo: + +``` +Skill(skill: "lfx-snowflake-access") +``` + ### Showing Examples When the user asks for examples or help, read and present the quickstart guide: From 65fc24542927fb120956e25962035facb147b573 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais Date: Mon, 4 May 2026 14:51:15 +0200 Subject: [PATCH 02/20] chore: address Copilot review feedback MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two of the three Copilot comments on PR #52: 1. Remove duplicate license header (the YAML frontmatter `# Copyright` from this branch + the pre-existing `` HTML comment after the closing `---` were both kept). Removed the redundant HTML `` and `` lines across all 14 affected SKILL.md files. Kept the `` comment where present (different content, still useful as cross-platform guidance to readers). Collapsed the leftover blank lines so the post-frontmatter section stays clean. 2. Replace ambiguous `[number]` placeholder in lfx-pr-catchup's drill-down suggestion with `` and a concrete `#142` example to make it clear users shouldn't paste it verbatim. The third Copilot comment (SHA-pin the reusable workflow) is being addressed in a reply on the PR thread — sticking with @main per the org-canonical pattern (~22/30 LF repos including the lfx-backstage scaffold template pin to @main; supply chain risk is bounded since both repos are linuxfoundation-controlled). Verification: - All 15 SKILL.md still parse as YAML with required keys present. - All 15 still have copyright in `head -4` (license-header check passes). - No remaining `` HTML comments anywhere. - 13 files retain the tool-names comment (the 2 without it never had one). Co-Authored-By: Claude Opus 4.7 (1M context) Signed-off-by: Josep Garcia-Reyero Sais --- lfx-backend-builder/SKILL.md | 2 -- lfx-coordinator/SKILL.md | 2 -- lfx-git-setup/SKILL.md | 3 --- lfx-intercom/SKILL.md | 2 -- lfx-pr-catchup/SKILL.md | 8 ++++---- lfx-pr-resolve/SKILL.md | 2 -- lfx-preflight/SKILL.md | 2 -- lfx-product-architect/SKILL.md | 2 -- lfx-research/SKILL.md | 2 -- lfx-setup/SKILL.md | 2 -- lfx-snowflake-access/SKILL.md | 2 -- lfx-test-journey/SKILL.md | 2 -- lfx-ui-builder/SKILL.md | 4 ---- lfx/SKILL.md | 2 -- 14 files changed, 4 insertions(+), 33 deletions(-) diff --git a/lfx-backend-builder/SKILL.md b/lfx-backend-builder/SKILL.md index f2b4120..8a74e80 100644 --- a/lfx-backend-builder/SKILL.md +++ b/lfx-backend-builder/SKILL.md @@ -9,8 +9,6 @@ description: > allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- - - # LFX Backend Code Generation diff --git a/lfx-coordinator/SKILL.md b/lfx-coordinator/SKILL.md index 4354f0a..e17c941 100644 --- a/lfx-coordinator/SKILL.md +++ b/lfx-coordinator/SKILL.md @@ -10,8 +10,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion, Skill --- - - # LFX Development Coordinator diff --git a/lfx-git-setup/SKILL.md b/lfx-git-setup/SKILL.md index 38a611b..d9c54ea 100644 --- a/lfx-git-setup/SKILL.md +++ b/lfx-git-setup/SKILL.md @@ -15,9 +15,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion, WebFetch --- - - - # LFX git Setup: DCO Signoff & GPG Signed Commits This skill walks contributors through two essential git contribution diff --git a/lfx-intercom/SKILL.md b/lfx-intercom/SKILL.md index 753b746..691a55c 100644 --- a/lfx-intercom/SKILL.md +++ b/lfx-intercom/SKILL.md @@ -10,8 +10,6 @@ description: > allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- - - # LFX Intercom Integration Skill diff --git a/lfx-pr-catchup/SKILL.md b/lfx-pr-catchup/SKILL.md index 7cc60d0..3213f0f 100644 --- a/lfx-pr-catchup/SKILL.md +++ b/lfx-pr-catchup/SKILL.md @@ -8,8 +8,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- - - # PR Catch-Up Dashboard @@ -249,10 +247,12 @@ Present the drill-down in a readable format. If the drilled-down PR has unresolved review comments (HIGH signal), proactively suggest: ``` -This PR has [N] unresolved comments. Want me to address them? -Run: /lfx-pr-resolve #[number] +This PR has unresolved comments. Want me to address them? +Run: /lfx-pr-resolve (e.g., /lfx-pr-resolve #142) ``` +Substitute the actual PR number (the same one the user just drilled into) when surfacing the suggestion. + Then offer to drill into another PR or end. ## Scope Boundaries diff --git a/lfx-pr-resolve/SKILL.md b/lfx-pr-resolve/SKILL.md index 6c2dbb6..6a6fc3c 100644 --- a/lfx-pr-resolve/SKILL.md +++ b/lfx-pr-resolve/SKILL.md @@ -11,8 +11,6 @@ description: > allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, Skill --- - - # PR Review Comment Resolver diff --git a/lfx-preflight/SKILL.md b/lfx-preflight/SKILL.md index 3eba828..df9309a 100644 --- a/lfx-preflight/SKILL.md +++ b/lfx-preflight/SKILL.md @@ -9,8 +9,6 @@ description: > allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- - - # Pre-Submission Preflight Check diff --git a/lfx-product-architect/SKILL.md b/lfx-product-architect/SKILL.md index 613c4c6..fbcf223 100644 --- a/lfx-product-architect/SKILL.md +++ b/lfx-product-architect/SKILL.md @@ -10,8 +10,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- - - # LFX Architecture Guide diff --git a/lfx-research/SKILL.md b/lfx-research/SKILL.md index 7963d08..85b2f4e 100644 --- a/lfx-research/SKILL.md +++ b/lfx-research/SKILL.md @@ -9,8 +9,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion, WebFetch --- - - # LFX Research & Exploration diff --git a/lfx-setup/SKILL.md b/lfx-setup/SKILL.md index 7aa7fcd..d5bf381 100644 --- a/lfx-setup/SKILL.md +++ b/lfx-setup/SKILL.md @@ -9,8 +9,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- - - # LFX Environment Setup Guide diff --git a/lfx-snowflake-access/SKILL.md b/lfx-snowflake-access/SKILL.md index e27f686..1b8fd96 100644 --- a/lfx-snowflake-access/SKILL.md +++ b/lfx-snowflake-access/SKILL.md @@ -15,8 +15,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion, WebFetch --- - - # Snowflake Access Request Guide diff --git a/lfx-test-journey/SKILL.md b/lfx-test-journey/SKILL.md index 007c6d5..8d5951f 100644 --- a/lfx-test-journey/SKILL.md +++ b/lfx-test-journey/SKILL.md @@ -9,8 +9,6 @@ description: > allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- - - # Journey Testing — Multi-Branch Integration Worktrees diff --git a/lfx-ui-builder/SKILL.md b/lfx-ui-builder/SKILL.md index c0864ae..d3d7df8 100644 --- a/lfx-ui-builder/SKILL.md +++ b/lfx-ui-builder/SKILL.md @@ -9,8 +9,6 @@ description: > allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- - - # LFX Frontend Code Generation @@ -80,8 +78,6 @@ Every new `.ts`, `.html`, and `.scss` file MUST start with the appropriate licen **HTML (`.html`):** ```html - - ``` **SCSS (`.scss`):** diff --git a/lfx/SKILL.md b/lfx/SKILL.md index f4cac5c..af64dfe 100644 --- a/lfx/SKILL.md +++ b/lfx/SKILL.md @@ -8,8 +8,6 @@ description: > allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion, Skill --- - - # LFX — Your Starting Point From 125aff29c3c0c590f6b7a2a4a7d3a8c4c011e3a2 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais Date: Tue, 5 May 2026 13:14:34 +0200 Subject: [PATCH 03/20] feat: support configurable LFX dev root via $LFX_DEV_ROOT Skills hardcoded ~/lf/ as the LFX clone location, locking out users who keep their repos elsewhere. Replace with ${LFX_DEV_ROOT:-$HOME/lf} across the three skills that scan the dev root, document the env var in README Prerequisites, and update the test-journey usage example. Backwards-compatible: with the var unset, behavior is identical to the previous ~/lf/ default. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Josep Garcia-Reyero Sais --- README.md | 3 ++- lfx-coordinator/SKILL.md | 4 ++-- lfx-research/SKILL.md | 4 ++-- lfx-test-journey/SKILL.md | 12 ++++++------ 4 files changed, 12 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0368b0c..a8a4220 100644 --- a/README.md +++ b/README.md @@ -31,6 +31,7 @@ New to LFX development? Type `/lfx` and say **"show me an example"** for a walkt - An AI coding assistant that supports skill-based workflows (e.g., Claude Code, Gemini CLI). See [docs/platform-install.md](docs/platform-install.md) for setup instructions. - Access to LFX repositories (for the skills to operate on) +- **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. Set it to your preferred location (e.g., `~/code/lfx`, `~/work/lfx`) and the skills will discover your local repos there. Add `export LFX_DEV_ROOT=...` to your shell rc to make it persistent. ## Manual Installation @@ -338,7 +339,7 @@ Combines feature branches from one or more repos into isolated git worktrees for /lfx-test-journey ``` This starts the interactive create flow: -1. Select which repos are involved (auto-discovers repos in `~/lf/`) +1. Select which repos are involved (auto-discovers repos in `$LFX_DEV_ROOT`, defaults to `~/lf/`) 2. Pick branches to include per repo (shows your unmerged branches) 3. Name the journey 4. The skill creates worktrees, merges branches, and tells you exactly where to `cd` and how to run the app diff --git a/lfx-coordinator/SKILL.md b/lfx-coordinator/SKILL.md index e17c941..ab31635 100644 --- a/lfx-coordinator/SKILL.md +++ b/lfx-coordinator/SKILL.md @@ -82,7 +82,7 @@ Use your Read, Glob, Grep, and Bash tools to quickly check: # Find the upstream service from the proxy code grep -r "proxyRequest" apps/lfx-one/src/server/services/.service.ts | head -5 # Check for local Go repos - ls -d ~/lf/lfx-v2-*-service 2>/dev/null + ls -d "${LFX_DEV_ROOT:-$HOME/lf}"/lfx-v2-*-service 2>/dev/null ``` - **Check the upstream Go service for the needed field.** Once you've identified the repo, check its domain model, Goa design, and conversions: ```bash @@ -143,7 +143,7 @@ Risk flags: When research reveals that the upstream Go microservice is missing a field or endpoint that the feature requires, the delegation plan MUST include a `/lfx-backend-builder` call for the Go repo. This is where the data model lives — without it, the field won't persist. The Go service is the source of truth. -**You discover the upstream service during research (Step 3)** by reading the Express proxy code. Do NOT hardcode paths — use what you found. For example, if `committee.service.ts` calls `proxyRequest(req, 'LFX_V2_SERVICE', '/committees/...')`, the API path prefix `/committees/` tells you the upstream is `lfx-v2-committee-service` and you check `~/lf/lfx-v2-committee-service/` for local availability. +**You discover the upstream service during research (Step 3)** by reading the Express proxy code. Do NOT hardcode paths — use what you found. For example, if `committee.service.ts` calls `proxyRequest(req, 'LFX_V2_SERVICE', '/committees/...')`, the API path prefix `/committees/` tells you the upstream is `lfx-v2-committee-service` and you check `${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-committee-service/` for local availability. **Common Go service changes for adding a field:** - Domain model: `internal/domain/model/*.go` — add the field to the struct diff --git a/lfx-research/SKILL.md b/lfx-research/SKILL.md index 85b2f4e..fbe732b 100644 --- a/lfx-research/SKILL.md +++ b/lfx-research/SKILL.md @@ -85,7 +85,7 @@ gh api repos/linuxfoundation//contents/design/.go \ | Surveys | `lfx-v2-survey-service` | | Members | `lfx-v2-member-service` | -**If the upstream Go repo exists locally** (check `~/lf/lfx-v2-*-service`), read the files directly instead of using `gh api`. Local reads are faster and more reliable. +**If the upstream Go repo exists locally** (check `${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-*-service`), read the files directly instead of using `gh api`. Local reads are faster and more reliable. **Report:** - Endpoint exists? (path, method, status codes) @@ -247,4 +247,4 @@ This keeps the user informed that exploration is happening and what's being chec - **Be specific** — include file paths, method names, field names - **Flag blockers** — if an upstream API doesn't exist, say so clearly - **Include example content** — read and include the key sections of example files -- **Prefer local reads** — if a Go repo exists at `~/lf/`, read it directly instead of using `gh api` +- **Prefer local reads** — if a Go repo exists at `${LFX_DEV_ROOT:-$HOME/lf}`, read it directly instead of using `gh api` diff --git a/lfx-test-journey/SKILL.md b/lfx-test-journey/SKILL.md index 8d5951f..89b3fb2 100644 --- a/lfx-test-journey/SKILL.md +++ b/lfx-test-journey/SKILL.md @@ -64,10 +64,10 @@ If a subcommand requires a journey name and the user didn't provide one, run **L ### Step 1: Discover Repos -Scan `~/lf/` for git repositories: +Scan `${LFX_DEV_ROOT:-$HOME/lf}` for git repositories: ```bash -for dir in ~/lf/*/; do +for dir in "${LFX_DEV_ROOT:-$HOME/lf}"/*/; do if [ -d "$dir/.git" ]; then echo "$dir" fi @@ -77,12 +77,12 @@ done Present as a numbered list and **STOP — use `AskUserQuestion` and wait for the user to respond before continuing**: ``` -Scanning ~/lf/ for git repos... +Scanning ${LFX_DEV_ROOT:-$HOME/lf} for git repos... Which repos are part of this journey? (type numbers, e.g. "1, 3") - 1. ~/lf/lfx-v2-ui - 2. ~/lf/lfx-v2-committee-service - 3. ~/lf/lfx-v2-meeting-service + 1. ${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-ui + 2. ${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-committee-service + 3. ${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-meeting-service ``` **⛔ GATE: You MUST call `AskUserQuestion` here and wait for the user's response. Do NOT continue to Step 2 until the user has selected repos.** Parse their response (comma-separated numbers or repo names). From 6d16aea5bada031064cdbdb430d104d87503b1e6 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais Date: Tue, 5 May 2026 13:57:28 +0200 Subject: [PATCH 04/20] feat(cli): replace install.sh with multi-subcommand lfx-skills CLI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The original install.sh was a 65-line one-shot that only knew how to symlink skills into ~/.claude/skills/. It can't probe the user's setup, support agents.md tools alongside Claude Code, install per-repo, diagnose problems, or persist what it did. This commit replaces it with a structured CLI that does all of that, while keeping ./install.sh as a thin shim so the documented entry point still works. Architecture: bin/lfx-skills Subcommand dispatcher and all command handlers. install (interactive wizard + non-interactive flags), uninstall, update, check, doctor, list, info, config, repos, version, help. lib/ui.sh Color-aware prompts, multi-select, confirm. LFX_SKILLS_YES=1 short-circuits to defaults. lib/probe.sh Pure detection: installed CLIs, ~/.claude*/ and ~/.agents*/ config dirs, dev root candidates, lf* git repos under a dev root, shell rc files. No side effects. lib/platforms.sh Per-platform target paths for the 4 (platform, scope) shapes: claude/agents x global/repo. lib/config.sh jq-backed I/O over ~/.config/lfx-skills/ config.json (manifest of every symlink the CLI has installed) and env.sh (exports LFX_DEV_ROOT and prepends bin/ to PATH). lib/symlinks.sh The 3-way create logic from the original install.sh, lifted unchanged, plus the eligible-skills filter that excludes clone-only meta-skills (lfx-install, lfx-new-skill — added in Phase 6). lib/doctor.sh Eight diagnostic categories (symlinks, source clone, dev root, platforms, frontmatter, routing, MCP deps, license headers). Records flow as pipe-delimited lines through stdin/stdout; doctor_format_human renders a colourised report and doctor_format_json emits a JSON array. No temp files. Hard dependency on jq (universally available on dev machines, one-line install everywhere). Targets bash 3.2+ (stock macOS) — no associative arrays, no mapfile. install.sh is now a 7-line shim that execs bin/lfx-skills install "\$@", preserving the documented entry point with no behavioural regression for existing users. .gitignore picks up the legacy .install-manifest.json (which older clones may have lying around) and leaves a comment about the negation block Phase 6 will need under .claude/skills/. The doctor's --fix flag is intentionally narrow: it handles the mechanical repairs only (recreate a symlink from the manifest, write env.sh from current config). Content-level issues (a skill directory with no SKILL.md) are flagged as fixable=no for the CLI but stay in the JSON output for the upcoming Phase 4 /lfx-doctor skill, which can exercise judgment the bash CLI can't. Spot-tested end-to-end on an isolated tmp env: install of all 15 skills across both global and per-repo targets for both platforms produces 90 manifest entries, doctor reports 0 errors, --fix recreates a deliberately-broken symlink, uninstall cleans up. Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Josep Garcia-Reyero Sais --- .gitignore | 10 +- bin/lfx-skills | 796 +++++++++++++++++++++++++++++++++++++++++++++++ install.sh | 65 +--- lib/config.sh | 234 ++++++++++++++ lib/doctor.sh | 432 +++++++++++++++++++++++++ lib/platforms.sh | 45 +++ lib/probe.sh | 93 ++++++ lib/symlinks.sh | 173 ++++++++++ lib/ui.sh | 218 +++++++++++++ 9 files changed, 2004 insertions(+), 62 deletions(-) create mode 100755 bin/lfx-skills create mode 100644 lib/config.sh create mode 100644 lib/doctor.sh create mode 100644 lib/platforms.sh create mode 100644 lib/probe.sh create mode 100644 lib/symlinks.sh create mode 100644 lib/ui.sh diff --git a/.gitignore b/.gitignore index 55cfde5..ec1a76c 100644 --- a/.gitignore +++ b/.gitignore @@ -19,9 +19,17 @@ Thumbs.db # Claude AI assistant .claude -# Installed skill symlinks (created by per-repo installation) +# Installed skill symlinks (created by per-repo installation). +# Phase 6 will commit a small set of relative symlinks under .claude/skills/ +# (lfx-install, lfx-doctor, lfx-skills-helper, lfx-new-skill); a negation +# block will be added there to un-ignore those specific paths. .claude/skills/ +# Legacy install manifest. Older clones of lfx-skills wrote this; the new CLI +# stores its manifest at ~/.config/lfx-skills/config.json, so this file is no +# longer used. Ignored in case any clone still has one lying around. +.install-manifest.json + # Environment and configuration files .env .env.local diff --git a/bin/lfx-skills b/bin/lfx-skills new file mode 100755 index 0000000..b04db20 --- /dev/null +++ b/bin/lfx-skills @@ -0,0 +1,796 @@ +#!/usr/bin/env bash +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +# +# lfx-skills — install, manage, and diagnose the LFX Skills collection. +# See `lfx-skills help` for the full command reference. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +CLONE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" +export CLONE_ROOT + +# shellcheck source=../lib/ui.sh +. "$CLONE_ROOT/lib/ui.sh" +# shellcheck source=../lib/probe.sh +. "$CLONE_ROOT/lib/probe.sh" +# shellcheck source=../lib/platforms.sh +. "$CLONE_ROOT/lib/platforms.sh" +# shellcheck source=../lib/config.sh +. "$CLONE_ROOT/lib/config.sh" +# shellcheck source=../lib/symlinks.sh +. "$CLONE_ROOT/lib/symlinks.sh" +# shellcheck source=../lib/doctor.sh +. "$CLONE_ROOT/lib/doctor.sh" + +# Hard requirement: jq for JSON I/O. Doctor and most subcommands need it. +require_jq() { + if ! probe_have_jq; then + ui_error "jq is required but not found on PATH." + ui_info "Install:" + ui_info " macOS: brew install jq" + ui_info " Linux: apt-get install jq / dnf install jq / pacman -S jq" + exit 1 + fi +} + +# ─── version ───────────────────────────────────────────────────────────── +cmd_version() { + if [ -d "$CLONE_ROOT/.git" ]; then + (cd "$CLONE_ROOT" && git describe --tags --always --dirty 2>/dev/null) || echo "unknown" + elif [ -f "$CLONE_ROOT/VERSION" ]; then + cat "$CLONE_ROOT/VERSION" + else + echo "unknown" + fi +} + +# ─── help ──────────────────────────────────────────────────────────────── +cmd_help() { + local sub="${1:-}" + case "$sub" in + install) _help_install ;; + uninstall) _help_uninstall ;; + update) _help_update ;; + check) _help_check ;; + doctor) _help_doctor ;; + list) _help_list ;; + info) _help_info ;; + config) _help_config ;; + repos) _help_repos ;; + version) echo "Usage: lfx-skills version — print version of this clone." ;; + "") _help_root ;; + *) ui_error "Unknown command: $sub"; _help_root; exit 1 ;; + esac +} + +_help_root() { + cat < [options] + +COMMANDS + install Install LFX skills (interactive wizard or non-interactive flags) + uninstall Remove installed symlinks (recorded in config.json) + update Re-apply the manifest; with --pull, git-pull the clone first + check Lightweight verification: does config match filesystem? (exit 0/1) + doctor Full diagnostics across 8 categories; --fix to repair interactively + list List installed or available skills + info SKILL Show frontmatter + install locations for a skill + config Show or modify ~/.config/lfx-skills/config.json (get/set) + repos List lf* git repos under \$LFX_DEV_ROOT + version Print version of this lfx-skills clone + help [CMD] Detailed help for CMD, or this overview + +EXAMPLES + lfx-skills install # interactive wizard + lfx-skills install --yes --platform=claude --scope=global + lfx-skills doctor + lfx-skills doctor --fix + lfx-skills list --available + lfx-skills uninstall + +CONFIG + ~/.config/lfx-skills/config.json manifest (single source of truth) + ~/.config/lfx-skills/env.sh exports LFX_DEV_ROOT + adds bin/ to PATH + +Run \`lfx-skills help \` for command-specific options. +EOF +} + +_help_install() { + cat <<'EOF' +USAGE + lfx-skills install [flags] + +INTERACTIVE + Run with no flags to step through: platform → scope → dev root → config + dirs (if global) → repos (if per-repo) → preview → apply → verify. + +NON-INTERACTIVE FLAGS + --yes Skip all confirmations (use defaults / supplied flags) + --platform=claude|agents|both + --scope=global|repo|both + --lfx-dev-root=PATH + --claude-config=PATH[,PATH...] Multi-config: pass several Claude config dirs + --agents-config=PATH Single agents config dir (default: ~/.agents) + --repos=PATH[,PATH...] Required if --scope=repo|both + --dry-run Show what would happen, then exit + +EXAMPLE + lfx-skills install --yes \ + --platform=both --scope=both \ + --lfx-dev-root=$HOME/lf \ + --claude-config=$HOME/.claude,$HOME/.claude-work \ + --repos=$HOME/lf/lfx-v2-ui,$HOME/lf/lfx-v2-meeting-service +EOF +} + +_help_uninstall() { + cat <<'EOF' +USAGE + lfx-skills uninstall [flags] + + Removes every symlink listed in config.json. Asks before deleting env.sh. + +FLAGS + --yes Skip confirmations + --scope=global|repo Limit removal to a scope + --repos=PATH[,PATH...] With --scope=repo: only remove from these repos + --keep-config Keep config.json + env.sh on disk +EOF +} + +_help_update() { + cat <<'EOF' +USAGE + lfx-skills update [--pull] + + Re-apply the install manifest. With --pull, git-pull the canonical clone + first. Detects skills present in the clone but missing from the manifest + (newly added upstream OR locally created via /lfx-new-skill) and asks + whether to install them. Detects manifest entries whose source skill is + gone and offers cleanup. +EOF +} + +_help_check() { + cat <<'EOF' +USAGE + lfx-skills check + + Lightweight verification of the install manifest vs filesystem. + Exit code: 0 = consistent, 1 = drift detected. + Use this in CI; use `lfx-skills doctor` for full human-readable output. +EOF +} + +_help_doctor() { + cat <<'EOF' +USAGE + lfx-skills doctor [--json] [--fix] + +FLAGS + --json Emit results as a JSON array (for scripts / Claude skills) + --fix Interactive fix: walk through fixable issues and prompt per issue +EOF +} + +_help_list() { + cat <<'EOF' +USAGE + lfx-skills list [--scope=global|repo] [--repo=PATH] [--available] + +FLAGS + --available Show every installable skill in this clone (regardless of install) + --scope=... Restrict to global or per-repo scope + --repo=PATH With --scope=repo, restrict to a specific repo +EOF +} + +_help_info() { + cat <<'EOF' +USAGE + lfx-skills info SKILL + + Print the SKILL.md frontmatter for SKILL plus every recorded install location. +EOF +} + +_help_config() { + cat <<'EOF' +USAGE + lfx-skills config Show full config.json (pretty-printed) + lfx-skills config get KEY Print one value + lfx-skills config set KEY=VAL Update a value (re-writes env.sh if relevant) +EOF +} + +_help_repos() { + cat <<'EOF' +USAGE + lfx-skills repos + + List lf* git repos under $LFX_DEV_ROOT (or the recorded dev root). +EOF +} + +# ─── repos ─────────────────────────────────────────────────────────────── +cmd_repos() { + local devroot="${LFX_DEV_ROOT:-}" + if [ -z "$devroot" ]; then + require_jq + devroot="$(config_get lfx_dev_root)" + fi + if [ -z "$devroot" ] || [ ! -d "$devroot" ]; then + ui_error "No usable dev root. Set LFX_DEV_ROOT or run \`lfx-skills install\`." + exit 1 + fi + probe_repos_in "$devroot" +} + +# ─── config ────────────────────────────────────────────────────────────── +cmd_config() { + require_jq + local sub="${1:-show}" + case "$sub" in + show|"") + if config_exists; then + config_read | jq . + else + ui_warn "No config.json yet. Run \`lfx-skills install\` first." + exit 1 + fi + ;; + get) + shift + local key="${1:-}" + [ -z "$key" ] && ui_die "Usage: lfx-skills config get KEY" + config_get "$key" + ;; + set) + shift + local kv="${1:-}" + [ -z "$kv" ] && ui_die "Usage: lfx-skills config set KEY=VALUE" + local key="${kv%%=*}" + local val="${kv#*=}" + [ "$key" = "$kv" ] && ui_die "Missing = in argument: $kv" + config_set "$key" "$val" + if [ "$key" = "lfx_dev_root" ] || [ "$key" = "canonical_clone" ]; then + write_env_sh + ui_success "Updated config and env.sh" + else + ui_success "Updated config" + fi + ;; + *) + ui_error "Unknown config subcommand: $sub" + _help_config + exit 1 + ;; + esac +} + +# ─── list ──────────────────────────────────────────────────────────────── +cmd_list() { + require_jq + local available=0 scope="" repo="" + while [ $# -gt 0 ]; do + case "$1" in + --available) available=1 ;; + --scope=*) scope="${1#*=}" ;; + --repo=*) repo="${1#*=}" ;; + *) ui_error "Unknown flag: $1"; exit 1 ;; + esac + shift + done + if [ "$available" = "1" ]; then + symlinks_eligible_skills "$CLONE_ROOT" + return + fi + if ! config_exists; then + ui_warn "No config.json yet. Run \`lfx-skills install\` or pass --available." + exit 1 + fi + local jq_filter='true' + if [ -n "$scope" ]; then + jq_filter=".scope == \"$scope\"" + if [ "$scope" = "repo" ] && [ -n "$repo" ]; then + jq_filter="$jq_filter and .repo == \"$repo\"" + fi + fi + config_read | jq -r --argjson _ 0 "(.symlinks // [])[] | select($jq_filter) | \"\(.platform)/\(.scope)\t\(.skill)\t\(.link)\"" | sort -u +} + +# ─── info ──────────────────────────────────────────────────────────────── +cmd_info() { + require_jq + local skill="${1:-}" + [ -z "$skill" ] && ui_die "Usage: lfx-skills info SKILL" + local skill_md="$CLONE_ROOT/$skill/SKILL.md" + if [ ! -f "$skill_md" ]; then + ui_error "No SKILL.md for \`$skill\` in $CLONE_ROOT" + exit 1 + fi + ui_section "$skill" + printf 'Source: %s\n' "$CLONE_ROOT/$skill" + printf '\n' + ui_bold "Frontmatter:" + awk 'NR==1 && /^---$/ {start=1; next} start && /^---$/ {exit} start {print " " $0}' "$skill_md" + if config_exists; then + printf '\n' + ui_bold "Install locations:" + local n_locations + n_locations="$(config_read | jq -r --arg s "$skill" '(.symlinks // []) | map(select(.skill == $s)) | length')" + if [ "$n_locations" -eq 0 ]; then + printf ' (not installed anywhere)\n' + else + config_read | jq -r --arg s "$skill" '(.symlinks // [])[] | select(.skill == $s) | " \(.platform)/\(.scope) \(.link)"' + fi + fi +} + +# ─── check (lightweight verify) ────────────────────────────────────────── +cmd_check() { + require_jq + if ! config_exists; then + ui_error "No config.json. Run \`lfx-skills install\` first." + exit 1 + fi + local link source missing=0 + while IFS= read -r link; do + [ -z "$link" ] && continue + if [ ! -L "$link" ]; then + missing=$((missing + 1)) + continue + fi + source="$(readlink "$link" 2>/dev/null || true)" + [ -d "$source" ] || missing=$((missing + 1)) + done < <(config_list_symlinks) + if [ "$missing" -gt 0 ]; then + ui_error "$missing symlink(s) broken or missing. Run \`lfx-skills doctor\` for details." + exit 1 + fi + ui_success "Manifest matches filesystem." +} + +# ─── doctor ────────────────────────────────────────────────────────────── +cmd_doctor() { + require_jq + local emit_json=0 do_fix=0 + while [ $# -gt 0 ]; do + case "$1" in + --json) emit_json=1 ;; + --fix) do_fix=1 ;; + *) ui_error "Unknown flag: $1"; exit 1 ;; + esac + shift + done + + # Run every check once; records flow through a shell variable. + local results + results="$(doctor_run_all)" + + if [ "$emit_json" = "1" ]; then + printf '%s\n' "$results" | doctor_format_json + return + fi + + local exit_code=0 + printf '%s\n' "$results" | doctor_format_human || exit_code=$? + + if [ "$do_fix" = "1" ]; then + ui_blank + ui_section "Auto-fix" + local any_fixable=0 + local sev id cat title detail fixable payload + # Read records from FD 3 so ui_confirm's `read` can still consume FD 0 (the + # user's terminal). Without this, the outer loop and ui_confirm fight over + # stdin and ui_confirm ends up "reading" the next record line as the reply. + while IFS='|' read -r sev id cat title detail fixable payload <&3; do + [ "$fixable" = "yes" ] || continue + any_fixable=1 + ui_blank + printf ' [%s] %s\n' "$id" "$title" + [ -n "$detail" ] && printf ' %s%s%s\n' "$_UI_DIM" "$detail" "$_UI_RESET" + if ui_confirm " Fix this?" N; then + doctor_fix_one "$id" "$payload" || ui_warn "Fix did not complete." + fi + done 3</dev/null + done + else + local s + s="$(symlinks_uninstall_all "$scope" "")" + ui_dim " $s removed/refused/missing/total" + fi + + if [ "$keep_config" = "0" ] && [ -z "$scope" ]; then + if ui_confirm "Remove ~/.config/lfx-skills/ entirely?" N; then + config_clear + ui_success "Removed config dir." + ui_info "Remove the env.sh sourcing line from your shell rc when convenient." + else + ui_info "Kept config dir (env.sh preserved)." + fi + fi + ui_success "Uninstall complete." +} + +# ─── update ────────────────────────────────────────────────────────────── +cmd_update() { + require_jq + local do_pull=0 + while [ $# -gt 0 ]; do + case "$1" in + --pull) do_pull=1 ;; + --yes) LFX_SKILLS_YES=1; export LFX_SKILLS_YES ;; + -h|--help) _help_update; return 0 ;; + *) ui_error "Unknown flag: $1"; _help_update; exit 1 ;; + esac + shift + done + + if ! config_exists; then + ui_die "No config.json — run \`lfx-skills install\` first." + fi + + if [ "$do_pull" = "1" ]; then + ui_step "git pull in $CLONE_ROOT…" + (cd "$CLONE_ROOT" && git pull --ff-only) || ui_warn "git pull failed — proceeding with current state." + fi + + # Re-apply: walk every recorded (platform, scope, base) tuple and re-install. + ui_step "Re-applying manifest…" + local rec + config_read | jq -c ' + [(.symlinks // [])[] | {platform, scope, base: (if .scope == "global" then .config_dir else .repo end)}] + | unique_by(.platform + "/" + .scope + "/" + .base) + | .[] + ' | while IFS= read -r rec; do + local platform scope base + platform="$(echo "$rec" | jq -r .platform)" + scope="$(echo "$rec" | jq -r .scope)" + base="$(echo "$rec" | jq -r .base)" + ui_dim " → $platform/$scope at $base" + symlinks_install_all "$CLONE_ROOT" "$platform" "$scope" "$base" >/dev/null + done + + # Detect new skills (in clone, not in manifest) + local installed_set new_skills + installed_set="$(config_read | jq -r '(.symlinks // [])[] | .skill' | sort -u)" + new_skills="" + while IFS= read -r skill; do + if ! printf '%s\n' "$installed_set" | grep -qx "$skill"; then + new_skills="${new_skills}${skill}"$'\n' + fi + done < <(symlinks_eligible_skills "$CLONE_ROOT") + if [ -n "$new_skills" ]; then + ui_blank + ui_warn "New skills found in clone but not in manifest:" + printf '%s' "$new_skills" | sed 's/^/ /' + ui_info "Run \`lfx-skills install\` again to add them at all configured locations." + fi + + ui_success "Update complete." +} + +# ─── dispatch ──────────────────────────────────────────────────────────── +main() { + local cmd="${1:-help}" + shift || true + case "$cmd" in + install) cmd_install "$@" ;; + uninstall) cmd_uninstall "$@" ;; + update) cmd_update "$@" ;; + check) cmd_check "$@" ;; + doctor) cmd_doctor "$@" ;; + list) cmd_list "$@" ;; + info) cmd_info "$@" ;; + config) cmd_config "$@" ;; + repos) cmd_repos "$@" ;; + version) cmd_version "$@" ;; + help|-h|--help) cmd_help "$@" ;; + *) ui_error "Unknown command: $cmd"; _help_root; exit 1 ;; + esac +} + +main "$@" diff --git a/install.sh b/install.sh index 4f1721f..9ac0a52 100755 --- a/install.sh +++ b/install.sh @@ -1,64 +1,7 @@ #!/usr/bin/env bash # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT - -# LFX Skills Installer -# Symlinks all LFX skills into ~/.claude/skills/ so they're available globally in your AI coding assistant. - -set -euo pipefail - -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -SKILLS_DIR="$HOME/.claude/skills" - -echo "Installing LFX skills..." -echo "" - -# Create the skills directory if it doesn't exist -mkdir -p "$SKILLS_DIR" - -# Track results -installed=0 -updated=0 -failed=0 - -# Install each skill directory (lfx-* and lfx/) -for skill_path in "$SCRIPT_DIR"/lfx-*/ "$SCRIPT_DIR"/lfx/; do - [ -d "$skill_path" ] || continue - - skill_name="$(basename "${skill_path%/}")" - target="$SKILLS_DIR/$skill_name" - - if [ -L "$target" ]; then - # Symlink exists — update it - rm "$target" - ln -s "$skill_path" "$target" - echo " Updated $skill_name" - updated=$((updated + 1)) - elif [ -e "$target" ]; then - # Something else exists at the target path - echo " Skipped $skill_name (non-symlink already exists at $target)" - failed=$((failed + 1)) - else - ln -s "$skill_path" "$target" - echo " Installed $skill_name" - installed=$((installed + 1)) - fi -done - -echo "" -echo "Done! $((installed + updated)) skills ready ($installed new, $updated updated)." - -if [ $failed -gt 0 ]; then - echo "$failed skills skipped due to conflicts — check the paths above." -fi - -echo "" -echo "Next steps:" -echo " 1. Restart your AI coding assistant (or open a new session)" -echo " 2. Type /lfx to get started" -echo "" -echo "Available skills:" -for skill_path in "$SKILLS_DIR"/lfx*; do - [ -e "$skill_path" ] || continue - echo " /$(basename "$skill_path")" -done +# +# Thin shim: the real installer lives at bin/lfx-skills. +# Preserved as `./install.sh` because that's the documented entry point. +exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/bin/lfx-skills" install "$@" diff --git a/lib/config.sh b/lib/config.sh new file mode 100644 index 0000000..5b51473 --- /dev/null +++ b/lib/config.sh @@ -0,0 +1,234 @@ +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +# +# Config + env.sh I/O for bin/lfx-skills. Sourced; do not execute directly. +# Requires: jq (caller validates with probe_have_jq). + +CONFIG_DIR_DEFAULT="${XDG_CONFIG_HOME:-$HOME/.config}/lfx-skills" +CONFIG_JSON_DEFAULT="$CONFIG_DIR_DEFAULT/config.json" +ENV_SH_DEFAULT="$CONFIG_DIR_DEFAULT/env.sh" + +# Allow overrides via env (used by tests). +config_dir() { printf '%s\n' "${LFX_SKILLS_CONFIG_DIR:-$CONFIG_DIR_DEFAULT}"; } +config_json_path(){ printf '%s\n' "${LFX_SKILLS_CONFIG_JSON:-$(config_dir)/config.json}"; } +env_sh_path() { printf '%s\n' "${LFX_SKILLS_ENV_SH:-$(config_dir)/env.sh}"; } + +# config_exists → return 0 if config.json exists, 1 otherwise. +config_exists() { [ -f "$(config_json_path)" ]; } + +# config_init [canonical_clone] [lfx_dev_root] +# Ensure config dir + config.json exist with schema_version=1 baseline. +# Idempotent: leaves existing config alone, only fills missing top-level keys. +config_init() { + local clone="${1:-}" devroot="${2:-}" + mkdir -p "$(config_dir)" + local cfg + cfg="$(config_json_path)" + if [ ! -f "$cfg" ]; then + local now + now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + jq -n \ + --arg clone "$clone" \ + --arg devroot "$devroot" \ + --arg now "$now" \ + '{ + schema_version: "1", + lfx_dev_root: $devroot, + canonical_clone: $clone, + shell_rc_detected: [], + installed_at: $now, + last_updated_at: $now, + platforms: {}, + symlinks: [] + }' > "$cfg" + fi +} + +# config_read → cat config.json to stdout (or an empty default if missing). +config_read() { + if config_exists; then + cat "$(config_json_path)" + else + echo '{}' + fi +} + +# config_get KEY → echo the JSON value (raw) at .KEY. Empty if missing. +# KEY may be a dotted path: e.g., "platforms.claude.config_dirs" +config_get() { + local key="$1" + config_read | jq -r --arg k "$key" ' + def get(p): reduce (p / ".")[] as $part (.; if . == null then null else .[$part] end); + get($k) // empty + ' +} + +# config_get_json KEY → echo the JSON value (preserving structure) at .KEY. +config_get_json() { + local key="$1" + config_read | jq --arg k "$key" ' + def get(p): reduce (p / ".")[] as $part (.; if . == null then null else .[$part] end); + get($k) + ' +} + +# config_set KEY VALUE → set .KEY to a STRING value (top-level or dotted path). +# Updates last_updated_at automatically. +config_set() { + local key="$1" value="$2" + local cfg now tmp + cfg="$(config_json_path)" + now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + tmp="$cfg.tmp.$$" + jq --arg k "$key" --arg v "$value" --arg now "$now" ' + def setpath_dotted(p; v): + (p / ".") as $parts | setpath($parts; v); + setpath_dotted($k; $v) | .last_updated_at = $now + ' < "$cfg" > "$tmp" && mv "$tmp" "$cfg" +} + +# config_set_json KEY JSON_VALUE → set .KEY to a JSON value (object/array/etc.) +config_set_json() { + local key="$1" value="$2" + local cfg now tmp + cfg="$(config_json_path)" + now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + tmp="$cfg.tmp.$$" + jq --arg k "$key" --argjson v "$value" --arg now "$now" ' + def setpath_dotted(p; v): + (p / ".") as $parts | setpath($parts; v); + setpath_dotted($k; $v) | .last_updated_at = $now + ' < "$cfg" > "$tmp" && mv "$tmp" "$cfg" +} + +# config_add_symlink PLATFORM SCOPE LINK SOURCE [BASE] +# Append a symlink record to the symlinks array. +# PLATFORM: claude | agents +# SCOPE: global | repo +# LINK: absolute path of the symlink we created +# SOURCE: absolute path of the skill directory the symlink points at +# BASE: for global → the config_dir; for repo → the repo path +# Skill name is inferred from basename(LINK). +config_add_symlink() { + local platform="$1" scope="$2" link="$3" source="$4" base="${5:-}" + local skill cfg now tmp + skill="$(basename "$link")" + cfg="$(config_json_path)" + now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + tmp="$cfg.tmp.$$" + local entry + if [ "$scope" = "global" ]; then + entry="$(jq -n \ + --arg platform "$platform" \ + --arg scope "$scope" \ + --arg config_dir "$base" \ + --arg skill "$skill" \ + --arg link "$link" \ + --arg source "$source" \ + '{platform:$platform, scope:$scope, config_dir:$config_dir, skill:$skill, link:$link, source:$source}')" + else + entry="$(jq -n \ + --arg platform "$platform" \ + --arg scope "$scope" \ + --arg repo "$base" \ + --arg skill "$skill" \ + --arg link "$link" \ + --arg source "$source" \ + '{platform:$platform, scope:$scope, repo:$repo, skill:$skill, link:$link, source:$source}')" + fi + jq --argjson entry "$entry" --arg now "$now" ' + .symlinks = ((.symlinks // []) | map(select(.link != $entry.link)) + [$entry]) + | .last_updated_at = $now + ' < "$cfg" > "$tmp" && mv "$tmp" "$cfg" +} + +# config_remove_symlink LINK → drop the entry whose .link matches. +config_remove_symlink() { + local link="$1" + local cfg now tmp + cfg="$(config_json_path)" + now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + tmp="$cfg.tmp.$$" + jq --arg link "$link" --arg now "$now" ' + .symlinks = ((.symlinks // []) | map(select(.link != $link))) + | .last_updated_at = $now + ' < "$cfg" > "$tmp" && mv "$tmp" "$cfg" +} + +# config_list_symlinks [SCOPE] [TARGET] +# Echo absolute symlink paths, one per line. +# SCOPE empty → all symlinks +# SCOPE=global → only global +# SCOPE=repo, TARGET=PATH → only symlinks whose .repo matches PATH +config_list_symlinks() { + local scope="${1:-}" target="${2:-}" + if [ -z "$scope" ]; then + config_read | jq -r '(.symlinks // [])[] | .link' + elif [ "$scope" = "global" ]; then + config_read | jq -r '(.symlinks // [])[] | select(.scope == "global") | .link' + elif [ "$scope" = "repo" ] && [ -n "$target" ]; then + config_read | jq -r --arg t "$target" '(.symlinks // [])[] | select(.scope == "repo" and .repo == $t) | .link' + elif [ "$scope" = "repo" ]; then + config_read | jq -r '(.symlinks // [])[] | select(.scope == "repo") | .link' + fi +} + +# config_list_symlink_records [JQ_FILTER] → echo full records as compact JSON, one per line. +# If JQ_FILTER is provided, use it as a select expression. +config_list_symlink_records() { + local filter="${1:-true}" + config_read | jq -c --argjson _ 0 "(.symlinks // [])[] | select($filter)" +} + +# config_set_platform_claude DIR... → record the list of claude config dirs. +config_set_platform_claude() { + local dirs_json + dirs_json="$(printf '%s\n' "$@" | jq -R . | jq -sc .)" + config_set_json "platforms.claude" "{\"config_dirs\": $dirs_json}" +} + +# config_set_platform_agents DIR → record the agents config dir. +config_set_platform_agents() { + local dir="$1" + config_set_json "platforms.agents" "{\"config_dir\": \"$dir\"}" +} + +# config_set_shell_rcs RC... → record the list of detected shell rcs. +config_set_shell_rcs() { + local rcs_json + rcs_json="$(printf '%s\n' "$@" | jq -R . | jq -sc .)" + config_set_json "shell_rc_detected" "$rcs_json" +} + +# config_clear → delete config.json and env.sh. +config_clear() { + rm -f "$(config_json_path)" "$(env_sh_path)" + rmdir "$(config_dir)" 2>/dev/null || true +} + +# write_env_sh → generate ~/.config/lfx-skills/env.sh from current config. +# Reads lfx_dev_root and canonical_clone from config.json. +write_env_sh() { + local clone devroot bin_path env_path + devroot="$(config_get lfx_dev_root)" + clone="$(config_get canonical_clone)" + bin_path="$clone/bin" + env_path="$(env_sh_path)" + mkdir -p "$(dirname "$env_path")" + cat > "$env_path" </dev/null)" ]); then + _emit pass clone-clean source_clone "Clone working tree clean" "$current" no + else + _emit warn clone-dirty source_clone \ + "Clone working tree has uncommitted changes" \ + "Run \`git status\` in $current" no + fi + fi +} + +# ─── Category 3: LFX dev root ──────────────────────────────────────────── +check_lfx_dev_root() { + local recorded env_path env_exists in_session + recorded="$(config_get lfx_dev_root)" + env_path="$(env_sh_path)" + env_exists=0 + [ -f "$env_path" ] && env_exists=1 + in_session="${LFX_DEV_ROOT:-}" + + if [ -z "$recorded" ]; then + _emit warn dev-root-not-recorded lfx_dev_root \ + "LFX_DEV_ROOT not recorded in config" \ + "Re-run \`lfx-skills install\` to capture it." no + elif [ ! -d "$recorded" ]; then + _emit fail dev-root-missing lfx_dev_root \ + "LFX_DEV_ROOT path does not exist" \ + "Recorded path: $recorded" no + else + local repo_count + repo_count="$(probe_count_repos_in "$recorded")" + if [ "$repo_count" -eq 0 ]; then + _emit warn dev-root-empty lfx_dev_root \ + "LFX_DEV_ROOT contains no lf* git repos" \ + "$recorded — clone some LFX repos here." no + else + _emit pass dev-root-ok lfx_dev_root \ + "LFX_DEV_ROOT has $repo_count lf* repo(s)" "$recorded" no + fi + fi + + if [ "$env_exists" -eq 0 ]; then + _emit fail env-sh-missing lfx_dev_root \ + "env.sh not found" \ + "Expected at $env_path. Run \`lfx-skills install\`." yes "$env_path" + else + _emit pass env-sh-exists lfx_dev_root "env.sh exists" "$env_path" no + fi + + if [ -z "$in_session" ]; then + _emit warn dev-root-not-in-session lfx_dev_root \ + "LFX_DEV_ROOT not set in current shell" \ + "Source $env_path or restart your shell after adding the snippet." no + elif [ -n "$recorded" ] && [ "$in_session" != "$recorded" ]; then + _emit warn dev-root-session-drift lfx_dev_root \ + "LFX_DEV_ROOT in session differs from config" \ + "Session: $in_session; config: $recorded" no + fi + + if [ "$env_exists" -eq 1 ]; then + local rc found=0 last_rc="" + while IFS= read -r rc; do + [ -z "$rc" ] && continue + last_rc="$rc" + if grep -F "$env_path" "$rc" >/dev/null 2>&1; then + found=1 + break + fi + done < <(probe_shell_rcs) + if [ "$found" -eq 1 ]; then + _emit pass env-sh-sourced lfx_dev_root "Shell rc sources env.sh" "$last_rc" no + else + _emit warn env-sh-not-sourced lfx_dev_root \ + "Shell rc does not source env.sh" \ + "Add: $(rc_snippet)" no + fi + fi +} + +# ─── Category 4: Platforms ─────────────────────────────────────────────── +check_platforms() { + local platforms_json + platforms_json="$(config_get_json platforms)" + if [ -z "$platforms_json" ] || [ "$platforms_json" = "null" ] || [ "$platforms_json" = "{}" ]; then + _emit warn platforms-none platforms "No platforms recorded" \ + "config.json has no platforms entry." no + return + fi + + local claude_dirs + claude_dirs="$(echo "$platforms_json" | jq -r '.claude.config_dirs[]?' 2>/dev/null)" + if [ -n "$claude_dirs" ]; then + if command -v claude >/dev/null 2>&1; then + _emit pass cli-on-path platforms "claude CLI on PATH" "$(command -v claude)" no + else + _emit warn cli-not-on-path platforms "claude CLI not on PATH" \ + "Install Claude Code or check your PATH." no + fi + local d + while IFS= read -r d; do + [ -z "$d" ] && continue + if [ -d "$d" ]; then + _emit pass platform-dir-ok platforms "Claude config dir exists" "$d" no + else + _emit fail platform-dir-missing platforms \ + "Claude config dir missing" "$d" no + fi + done </dev/null)" + if [ -n "$agents_dir" ]; then + if [ -d "$agents_dir" ]; then + _emit pass platform-dir-ok platforms "Agents config dir exists" "$agents_dir" no + else + _emit fail platform-dir-missing platforms \ + "Agents config dir missing" "$agents_dir" no + fi + fi +} + +# ─── Category 5: Frontmatter ───────────────────────────────────────────── +check_frontmatter() { + local clone + clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" + local skill_md skill_name name_in_md desc_in_md + for skill_md in "$clone"/lfx*/SKILL.md; do + [ -f "$skill_md" ] || continue + skill_name="$(basename "$(dirname "$skill_md")")" + if [ "$(head -1 "$skill_md")" != "---" ]; then + _emit fail frontmatter-missing frontmatter \ + "$skill_name: SKILL.md missing frontmatter" \ + "$skill_md does not start with ---" no + continue + fi + local fm + fm="$(awk 'NR==1 && /^---$/ {start=1; next} start && /^---$/ {exit} start {print}' "$skill_md")" + name_in_md="$(printf '%s\n' "$fm" | awk -F': *' '/^name: */ {print $2; exit}')" + desc_in_md="$(printf '%s\n' "$fm" | awk -F': *' '/^description: */ {print $2; exit}')" + + if [ -z "$name_in_md" ]; then + _emit fail frontmatter-no-name frontmatter \ + "$skill_name: missing required \`name\` field" "$skill_md" no + elif [ "$name_in_md" != "$skill_name" ]; then + _emit fail frontmatter-name-mismatch frontmatter \ + "$skill_name: name field is \`$name_in_md\`, expected \`$skill_name\`" \ + "$skill_md" no + fi + if [ -z "$desc_in_md" ]; then + if ! printf '%s\n' "$fm" | awk '/^description:/{f=1; next} f && /^[a-z_-]+:/{exit} f && /^ +./{print; exit}' | grep -q '.'; then + _emit warn frontmatter-no-description frontmatter \ + "$skill_name: missing or empty \`description\` field" "$skill_md" no + fi + fi + done +} + +# ─── Category 6: Routing ───────────────────────────────────────────────── +check_routing() { + local clone lfx_md + clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" + lfx_md="$clone/lfx/SKILL.md" + if [ ! -f "$lfx_md" ]; then + _emit warn routing-no-lfx routing "/lfx skill not found" \ + "$lfx_md missing — routing check skipped." no + return + fi + # Match /lfx-xxx only when the leading slash is at start-of-line or after a + # non-word, non-slash char. Excludes path references like apps/lfx-one/foo. + local routed s + routed="$(grep -oE '(^|[^a-zA-Z0-9_/])/lfx-[a-z0-9-]+' "$lfx_md" | sed 's|^[^/]*||' | sort -u)" + while IFS= read -r s; do + [ -z "$s" ] && continue + local skill_name="${s#/}" + if [ ! -d "$clone/$skill_name" ]; then + _emit warn routing-dangling routing \ + "/lfx mentions $s but skill directory missing" \ + "Expected $clone/$skill_name" no + fi + done </dev/null; then + _emit warn routing-uncovered routing \ + "/lfx routing table does not mention /$skill" \ + "Add an entry for /$skill in $lfx_md" no + fi + done < <(symlinks_eligible_skills "$clone") +} + +# ─── Category 7: MCP deps ──────────────────────────────────────────────── +check_mcp_deps() { + local clone + clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" + local skill_md skill_name + for skill_md in "$clone"/lfx*/SKILL.md; do + [ -f "$skill_md" ] || continue + if grep -qE 'mcp__[a-zA-Z_]+' "$skill_md" 2>/dev/null; then + skill_name="$(basename "$(dirname "$skill_md")")" + if grep -qE '^##? +Prerequisites' "$skill_md" 2>/dev/null; then + _emit pass mcp-documented mcp_deps \ + "$skill_name documents MCP prerequisites" "$skill_md" no + else + _emit warn mcp-undocumented mcp_deps \ + "$skill_name uses MCP tools but has no Prerequisites section" \ + "$skill_md" no + fi + fi + done +} + +# ─── Category 8: License headers ───────────────────────────────────────── +check_license_headers() { + local clone + clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" + local skill_md skill_name + for skill_md in "$clone"/lfx*/SKILL.md; do + [ -f "$skill_md" ] || continue + skill_name="$(basename "$(dirname "$skill_md")")" + if head -4 "$skill_md" | grep -qF "Copyright The Linux Foundation"; then + _emit pass license-ok license_headers "$skill_name has license header" "$skill_md" no + else + _emit fail license-missing license_headers \ + "$skill_name missing license header in first 4 lines" "$skill_md" no + fi + done +} + +# ─── Aggregator ────────────────────────────────────────────────────────── +# doctor_run_all → emit every check's records to stdout, in category order. +doctor_run_all() { + check_symlinks + check_source_clone + check_lfx_dev_root + check_platforms + check_frontmatter + check_routing + check_mcp_deps + check_license_headers +} + +# doctor_format_human → render results read from stdin as a colourised report. +# Single pass, buffering errors and warnings into shell variables before printing. +# Returns 0 if no failures, 1 otherwise. +doctor_format_human() { + local pass=0 warn=0 fail=0 + local errors_buf="" warnings_buf="" + local sev id cat title detail fixable payload line + while IFS='|' read -r sev id cat title detail fixable payload; do + case "$sev" in + pass) pass=$((pass + 1)) ;; + fail) + fail=$((fail + 1)) + line="$(printf ' %s✗%s [%s] %s' "$_UI_RED" "$_UI_RESET" "$id" "$title")" + errors_buf="${errors_buf}${line}"$'\n' + if [ -n "$detail" ]; then + errors_buf="${errors_buf}$(printf ' %s%s%s' "$_UI_DIM" "$detail" "$_UI_RESET")"$'\n' + fi + if [ "$fixable" = "yes" ]; then + errors_buf="${errors_buf}$(printf ' %s(fixable: lfx-skills doctor --fix)%s' "$_UI_DIM" "$_UI_RESET")"$'\n' + fi + ;; + warn) + warn=$((warn + 1)) + line="$(printf ' %s⚠%s [%s] %s' "$_UI_YELLOW" "$_UI_RESET" "$id" "$title")" + warnings_buf="${warnings_buf}${line}"$'\n' + if [ -n "$detail" ]; then + warnings_buf="${warnings_buf}$(printf ' %s%s%s' "$_UI_DIM" "$detail" "$_UI_RESET")"$'\n' + fi + if [ "$fixable" = "yes" ]; then + warnings_buf="${warnings_buf}$(printf ' %s(fixable: lfx-skills doctor --fix)%s' "$_UI_DIM" "$_UI_RESET")"$'\n' + fi + ;; + esac + done + + ui_section "LFX Skills Health Check" + printf '%s%d%s passed, %s%d%s warnings, %s%d%s errors\n\n' \ + "$_UI_GREEN" "$pass" "$_UI_RESET" \ + "$_UI_YELLOW" "$warn" "$_UI_RESET" \ + "$_UI_RED" "$fail" "$_UI_RESET" + + if [ "$fail" -gt 0 ]; then + ui_bold "Errors:" + printf '%s\n' "$errors_buf" + fi + if [ "$warn" -gt 0 ]; then + ui_bold "Warnings:" + printf '%s\n' "$warnings_buf" + fi + if [ "$fail" -eq 0 ] && [ "$warn" -eq 0 ]; then + ui_success "All checks passed." + fi + + [ "$fail" -eq 0 ] +} + +# doctor_format_json → render results read from stdin as a JSON array on stdout. +doctor_format_json() { + jq -Rsn ' + [inputs | split("\n")[] | select(length > 0) | split("|") | + {severity: .[0], id: .[1], category: .[2], title: .[3], detail: .[4], + fixable: (.[5] == "yes"), payload: (.[6] // "")}] + ' +} + +# doctor_fix_one ID PAYLOAD → apply the auto-fix for the given check. +# Returns 0 on success, 1 on failure or unknown ID. PAYLOAD comes straight from +# the structured 7th column of the result record — no string parsing required. +doctor_fix_one() { + local id="$1" payload="${2:-}" + case "$id" in + symlink-missing|symlink-broken) + local link="$payload" source + if [ -z "$link" ]; then + ui_error "Internal: $id record has no payload." + return 1 + fi + source="$(config_read | jq -r --arg l "$link" '(.symlinks // [])[] | select(.link == $l) | .source' | head -1)" + if [ -z "$source" ] || [ "$source" = "null" ]; then + ui_error "No manifest entry for $link — re-run \`lfx-skills install\` to recreate." + return 1 + fi + if [ ! -d "$source" ]; then + ui_error "Source skill missing on disk: $source" + return 1 + fi + [ -L "$link" ] && rm -f "$link" + mkdir -p "$(dirname "$link")" + ln -s "$source" "$link" + ui_success "Recreated symlink: $link → $source" + ;; + env-sh-missing) + write_env_sh + ui_success "Wrote $(env_sh_path)" + ;; + *) + ui_warn "No auto-fix available for: $id" + return 1 + ;; + esac +} diff --git a/lib/platforms.sh b/lib/platforms.sh new file mode 100644 index 0000000..69ff490 --- /dev/null +++ b/lib/platforms.sh @@ -0,0 +1,45 @@ +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +# +# Per-platform target path construction. Sourced; do not execute directly. + +# platform_target_dir PLATFORM SCOPE BASE +# PLATFORM: claude | agents +# SCOPE: global | repo +# BASE: for global → the config dir (e.g., $HOME/.claude) +# for repo → the repo path (e.g., $HOME/lf/lfx-v2-ui) +# Echoes the absolute target directory where skills should be symlinked. +platform_target_dir() { + local platform="$1" scope="$2" base="$3" + case "$platform:$scope" in + claude:global) printf '%s/skills\n' "$base" ;; + claude:repo) printf '%s/.claude/skills\n' "$base" ;; + agents:global) printf '%s/skills\n' "$base" ;; + agents:repo) printf '%s/.agents/skills\n' "$base" ;; + *) return 1 ;; + esac +} + +# platform_default_global_dir PLATFORM → echo the conventional global config dir. +# claude → $HOME/.claude +# agents → $HOME/.agents +platform_default_global_dir() { + case "$1" in + claude) printf '%s/.claude\n' "$HOME" ;; + agents) printf '%s/.agents\n' "$HOME" ;; + *) return 1 ;; + esac +} + +# platform_supported PLATFORM → return 0 if PLATFORM is known. +platform_supported() { + case "$1" in + claude|agents) return 0 ;; + *) return 1 ;; + esac +} + +# platform_all → echo every supported platform on its own line. +platform_all() { + printf 'claude\nagents\n' +} diff --git a/lib/probe.sh b/lib/probe.sh new file mode 100644 index 0000000..042c795 --- /dev/null +++ b/lib/probe.sh @@ -0,0 +1,93 @@ +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +# +# System probes for bin/lfx-skills. Sourced; do not execute directly. +# All probe_* functions have no side effects — they only inspect the system. + +# probe_clis → echo each detected CLI on its own line. +# Detects: claude, codex, gemini, opencode. +probe_clis() { + local cli + for cli in claude codex gemini opencode; do + if command -v "$cli" >/dev/null 2>&1; then + printf '%s\n' "$cli" + fi + done +} + +# probe_claude_config_dirs → echo each ~/.claude*/ directory that exists. +# Catches .claude, .claude-work, .claude-personal, etc. +probe_claude_config_dirs() { + local d + for d in "$HOME"/.claude*; do + [ -d "$d" ] || continue + printf '%s\n' "$d" + done +} + +# probe_agents_config_dirs → echo each ~/.agents*/ directory that exists. +probe_agents_config_dirs() { + local d + for d in "$HOME"/.agents*; do + [ -d "$d" ] || continue + printf '%s\n' "$d" + done +} + +# probe_dev_root_candidates → echo each existing path among common LFX dev root locations. +# Order matters: first match wins as the suggested default. +probe_dev_root_candidates() { + local d + for d in "$HOME/lf" "$HOME/lfx" "$HOME/code/lfx" "$HOME/work/lfx" "$HOME/dev/lfx"; do + [ -d "$d" ] || continue + printf '%s\n' "$d" + done +} + +# probe_repos_in DIR → echo each path under DIR that: +# - has a basename starting with "lf" +# - contains a .git directory or file (worktrees use .git as a file) +probe_repos_in() { + local dir="$1" + [ -d "$dir" ] || return 0 + local entry + for entry in "$dir"/lf*/; do + [ -d "$entry" ] || continue + if [ -e "${entry%/}/.git" ]; then + # strip trailing slash for display + printf '%s\n' "${entry%/}" + fi + done +} + +# probe_count_repos_in DIR → echo number of lf* git repos under DIR. +probe_count_repos_in() { + probe_repos_in "$1" | wc -l | tr -d ' ' +} + +# probe_shell_rcs → echo each shell rc file that exists. +# Used by the doctor "is env.sh sourced?" check. +probe_shell_rcs() { + local rc + for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile" "$HOME/.config/fish/config.fish"; do + [ -f "$rc" ] && printf '%s\n' "$rc" + done +} + +# probe_have_jq → return 0 if jq is on PATH, 1 otherwise. +probe_have_jq() { + command -v jq >/dev/null 2>&1 +} + +# probe_canonical_clone → echo the absolute path of the lfx-skills clone this script lives in. +# Caller passes the script's $0 (or any path inside the clone). +# Walks up from bin/ or lib/ to the clone root. +probe_canonical_clone() { + local script_path="$1" + local script_dir + script_dir="$(cd "$(dirname "$script_path")" && pwd)" + case "$(basename "$script_dir")" in + bin|lib) (cd "$script_dir/.." && pwd) ;; + *) printf '%s\n' "$script_dir" ;; + esac +} diff --git a/lib/symlinks.sh b/lib/symlinks.sh new file mode 100644 index 0000000..c9049c4 --- /dev/null +++ b/lib/symlinks.sh @@ -0,0 +1,173 @@ +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +# +# Symlink create/remove logic for bin/lfx-skills. Sourced; do not execute directly. +# 3-way create logic lifted from install.sh and battle-tested. + +# Skills that should NEVER be installed to user-level or per-repo targets. +# These are clone-only meta-skills: they live only inside the lfx-skills clone +# (auto-discovered via committed `.claude/skills/` and `.agents/skills/` symlinks). +SYMLINKS_CLONE_ONLY="lfx-install lfx-new-skill" + +# symlinks_eligible_skills CLONE → echo each installable skill directory name (one per line). +# A skill is eligible if: +# - basename matches lfx* (catches lfx + lfx-* variants) +# - directory contains SKILL.md +# - basename is NOT in SYMLINKS_CLONE_ONLY +symlinks_eligible_skills() { + local clone="$1" + local skill_path skill_name excluded + for skill_path in "$clone"/lfx*/; do + [ -d "$skill_path" ] || continue + [ -f "${skill_path}SKILL.md" ] || continue + skill_name="$(basename "${skill_path%/}")" + excluded=0 + for ex in $SYMLINKS_CLONE_ONLY; do + if [ "$skill_name" = "$ex" ]; then excluded=1; break; fi + done + [ "$excluded" -eq 1 ] && continue + printf '%s\n' "$skill_name" + done +} + +# symlinks_create_one SOURCE TARGET → create or update one symlink. +# Echoes one of: installed | updated | skipped | failed +# Stderr gets a one-line human-readable note. +# This is the lifted 3-way logic from the original install.sh (lines 31-46). +symlinks_create_one() { + local source="$1" target="$2" + if [ -L "$target" ]; then + rm "$target" + if ln -s "$source" "$target"; then + ui_dim " updated $(basename "$target") → $source" >&2 + printf 'updated\n' + else + ui_warn " failed $(basename "$target")" + printf 'failed\n' + fi + elif [ -e "$target" ]; then + ui_warn " skipped $(basename "$target") (non-symlink already exists at $target)" + printf 'skipped\n' + else + if ln -s "$source" "$target"; then + ui_dim " installed $(basename "$target") → $source" >&2 + printf 'installed\n' + else + ui_warn " failed $(basename "$target")" + printf 'failed\n' + fi + fi +} + +# symlinks_install_all CLONE PLATFORM SCOPE BASE +# CLONE: absolute path to lfx-skills clone +# PLATFORM: claude | agents +# SCOPE: global | repo +# BASE: for global → config dir; for repo → repo path +# Creates one symlink per eligible skill in CLONE into the platform target dir. +# Records each created symlink in config.json. +# Echoes a summary line: "///" +symlinks_install_all() { + local clone="$1" platform="$2" scope="$3" base="$4" + local target_dir + target_dir="$(platform_target_dir "$platform" "$scope" "$base")" || { + ui_error "Unknown platform/scope: $platform/$scope" + return 1 + } + mkdir -p "$target_dir" + + local n_installed=0 n_updated=0 n_skipped=0 n_total=0 + local skill source target outcome + + while IFS= read -r skill; do + [ -z "$skill" ] && continue + n_total=$((n_total + 1)) + source="$clone/$skill" + target="$target_dir/$skill" + outcome="$(symlinks_create_one "$source" "$target")" + case "$outcome" in + installed) + n_installed=$((n_installed + 1)) + config_add_symlink "$platform" "$scope" "$target" "$source" "$base" + ;; + updated) + n_updated=$((n_updated + 1)) + config_add_symlink "$platform" "$scope" "$target" "$source" "$base" + ;; + skipped) + n_skipped=$((n_skipped + 1)) + ;; + esac + done < <(symlinks_eligible_skills "$clone") + + printf '%d/%d/%d/%d\n' "$n_installed" "$n_updated" "$n_skipped" "$n_total" +} + +# symlinks_remove_one TARGET [EXPECTED_SOURCE] +# Safely remove one symlink. If EXPECTED_SOURCE is given, only remove if it matches. +# Echoes: removed | not-a-symlink | wrong-source | missing +symlinks_remove_one() { + local target="$1" expected="${2:-}" + if [ ! -e "$target" ] && [ ! -L "$target" ]; then + printf 'missing\n' + return 0 + fi + if [ ! -L "$target" ]; then + ui_warn " refused $target (not a symlink — skipping for safety)" + printf 'not-a-symlink\n' + return 0 + fi + if [ -n "$expected" ]; then + local actual + actual="$(readlink "$target" || true)" + if [ "$actual" != "$expected" ]; then + ui_warn " refused $target (points to $actual, not expected $expected)" + printf 'wrong-source\n' + return 0 + fi + fi + rm "$target" + ui_dim " removed $target" >&2 + printf 'removed\n' +} + +# symlinks_uninstall_all [SCOPE_FILTER] [TARGET_FILTER] +# Walk config.json's symlinks array and remove each. +# Filters mirror config_list_symlinks: empty=all, global, repo+target. +# Updates config.json to drop removed entries. +# Echoes summary: "///" +symlinks_uninstall_all() { + local scope_filter="${1:-}" target_filter="${2:-}" + local n_removed=0 n_refused=0 n_missing=0 n_total=0 + local link source outcome + + # Snapshot the list (config will mutate as we remove) + local snapshot + snapshot="$(config_list_symlinks "$scope_filter" "$target_filter")" + + while IFS= read -r link; do + [ -z "$link" ] && continue + n_total=$((n_total + 1)) + # Look up expected source from config (defensive) + source="$(config_read | jq -r --arg l "$link" '(.symlinks // [])[] | select(.link == $l) | .source' | head -1)" + outcome="$(symlinks_remove_one "$link" "$source")" + case "$outcome" in + removed) + n_removed=$((n_removed + 1)) + config_remove_symlink "$link" + ;; + missing) + n_missing=$((n_missing + 1)) + # Drop from config too — it's stale. + config_remove_symlink "$link" + ;; + *) + n_refused=$((n_refused + 1)) + ;; + esac + done <%s %s\n' "$_UI_BLUE" "$_UI_RESET" "$*"; } +ui_success() { printf '%s✓%s %s\n' "$_UI_GREEN" "$_UI_RESET" "$*"; } +ui_warn() { printf '%s⚠%s %s\n' "$_UI_YELLOW" "$_UI_RESET" "$*" >&2; } +ui_error() { printf '%s✗%s %s\n' "$_UI_RED" "$_UI_RESET" "$*" >&2; } +ui_dim() { printf '%s%s%s\n' "$_UI_DIM" "$*" "$_UI_RESET"; } +ui_bold() { printf '%s%s%s\n' "$_UI_BOLD" "$*" "$_UI_RESET"; } +ui_blank() { printf '\n'; } + +# ui_die MSG → print error and exit 1. +ui_die() { ui_error "$*"; exit 1; } + +# ui_confirm "Question?" [default=N] → returns 0 if yes, 1 if no. +# Auto-answers Y when LFX_SKILLS_YES=1 (set by --yes flag). +# default arg: "Y" or "N" (case-insensitive). +ui_confirm() { + local prompt="$1" + local default="${2:-N}" + local hint reply + case "$default" in + [Yy]*) hint="[Y/n]" ;; + *) hint="[y/N]" ;; + esac + if [ "${LFX_SKILLS_YES:-0}" = "1" ]; then + printf '%s %s (auto-yes)\n' "$prompt" "$hint" + return 0 + fi + printf '%s %s ' "$prompt" "$hint" >&2 + read -r reply || reply="" + reply="${reply:-$default}" + case "$reply" in + [Yy]|[Yy][Ee][Ss]) return 0 ;; + *) return 1 ;; + esac +} + +# ui_input "Question?" [default] → echo user input to stdout (default if empty). +# Auto-uses default when LFX_SKILLS_YES=1. +ui_input() { + local prompt="$1" + local default="${2:-}" + local reply + if [ "${LFX_SKILLS_YES:-0}" = "1" ]; then + printf '%s\n' "$default" + return 0 + fi + if [ -n "$default" ]; then + printf '%s [%s]: ' "$prompt" "$default" >&2 + else + printf '%s: ' "$prompt" >&2 + fi + read -r reply || reply="" + printf '%s\n' "${reply:-$default}" +} + +# ui_select "Question?" OPT1 OPT2 ... → echo chosen option to stdout. +# Auto-picks first option when LFX_SKILLS_YES=1. +ui_select() { + local prompt="$1"; shift + local n=$# + if [ "$n" -eq 0 ]; then + ui_die "ui_select: no options provided" + fi + if [ "${LFX_SKILLS_YES:-0}" = "1" ]; then + printf '%s\n' "$1" + return 0 + fi + printf '%s\n' "$prompt" >&2 + local i=1 + for opt in "$@"; do + printf ' %d) %s\n' "$i" "$opt" >&2 + i=$((i + 1)) + done + local reply + while true; do + printf 'Choose 1-%d: ' "$n" >&2 + read -r reply || reply="" + case "$reply" in + ''|*[!0-9]*) ui_warn "Enter a number 1-$n." ;; + *) + if [ "$reply" -ge 1 ] && [ "$reply" -le "$n" ]; then + # shift is 1-indexed, so use eval to grab nth arg + eval "printf '%s\n' \"\${$reply}\"" + return 0 + else + ui_warn "Out of range. Pick 1-$n." + fi + ;; + esac + done +} + +# ui_multiselect "Question?" "default-spec" OPT1 OPT2 ... +# default-spec: comma-separated 1-indexed positions selected by default +# (e.g., "1,3" or "" for none, "all" for everything). +# Echoes one selected option per line. +# Auto-picks defaults when LFX_SKILLS_YES=1. +ui_multiselect() { + local prompt="$1"; shift + local default_spec="$1"; shift + local n=$# + if [ "$n" -eq 0 ]; then + return 0 + fi + local -a opts + local i=1 + for opt in "$@"; do + opts[i]="$opt" + i=$((i + 1)) + done + + _expand_default_spec() { + case "$1" in + all) seq 1 "$n" | tr '\n' ',' | sed 's/,$//' ;; + none|"") echo "" ;; + *) echo "$1" ;; + esac + } + + if [ "${LFX_SKILLS_YES:-0}" = "1" ]; then + local resolved + resolved="$(_expand_default_spec "$default_spec")" + if [ -z "$resolved" ]; then + return 0 + fi + local IFS=',' + for idx in $resolved; do + printf '%s\n' "${opts[$idx]}" + done + return 0 + fi + + printf '%s\n' "$prompt" >&2 + i=1 + for opt in "$@"; do + printf ' %d) %s\n' "$i" "$opt" >&2 + i=$((i + 1)) + done + + local hint default_resolved + default_resolved="$(_expand_default_spec "$default_spec")" + if [ -n "$default_resolved" ]; then + hint="comma-separated, e.g. \"1,3\"; \"all\"; \"none\"; or Enter for default [$default_resolved]" + else + hint="comma-separated, e.g. \"1,3\"; \"all\"; \"none\"; or Enter for none" + fi + + local reply + while true; do + printf '%s: ' "$hint" >&2 + read -r reply || reply="" + if [ -z "$reply" ]; then + reply="$default_resolved" + fi + case "$reply" in + all) reply="$(seq 1 "$n" | tr '\n' ',' | sed 's/,$//')" ;; + none) return 0 ;; + esac + if [ -z "$reply" ]; then + return 0 + fi + # Validate each token is a number in range + local valid=1 token + local IFS=',' + for token in $reply; do + token="${token# }"; token="${token% }" + case "$token" in + ''|*[!0-9]*) valid=0; break ;; + *) + if [ "$token" -lt 1 ] || [ "$token" -gt "$n" ]; then + valid=0; break + fi + ;; + esac + done + if [ "$valid" -eq 1 ]; then + for token in $reply; do + token="${token# }"; token="${token% }" + printf '%s\n' "${opts[$token]}" + done + return 0 + fi + ui_warn "Invalid selection. Use comma-separated numbers in 1-$n, \"all\", or \"none\"." + done +} + +# ui_section "Title" → bold heading + underline. +ui_section() { + local title="$1" + local len=${#title} + printf '\n%s%s%s\n' "$_UI_BOLD" "$title" "$_UI_RESET" + printf '%s\n' "$(printf '%*s' "$len" '' | tr ' ' '─')" +} From 450eb81255c9357ce8b278d3b5f23f674685b6b5 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais Date: Tue, 5 May 2026 14:06:16 +0200 Subject: [PATCH 05/20] feat(skills): add lfx-doctor and lfx-skills-helper meta-skills MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two new conversational skills layered on top of the lfx-skills CLI. Both ship to every install location alongside the user-facing skills (only /lfx-install and /lfx-new-skill remain clone-only — those land in Phase 6). /lfx-doctor — diagnostic surface Wraps `lfx-skills doctor --json` with a conversational fix flow: parse the JSON, render a human report grouped by severity, ask the user per-issue whether to apply the available auto-fix, then re-verify. For content-level issues that the bash CLI can't repair (missing SKILL.md, license header gaps, routing-table coverage, draft-able description fields), the skill applies judgment the CLI can't — reading files, drafting fixes, asking before editing. references/fix-recipes.md gives per-issue narrative for every doctor check id, with copy-pasteable templates for the recipes the /lfx-doctor skill walks through. /lfx-skills-helper — CLI front-end Conversational layer over the management subcommands: install, uninstall, update, list, info, config, repos. Maps natural-language management intents to CLI invocations, confirms before any state-changing command, and reformats CLI output for human reading. Strict separation of responsibilities: - "Which skill should I use for X?" → hand off to /lfx (the plain-language router is the right surface; the helper does not invent recommendations). - Diagnostics → hand off to /lfx-doctor. - Authoring a new skill → hand off to /lfx-new-skill (Phase 6). references/intents.md documents the intent → CLI mapping for the management surface only. No backend/frontend/data taxonomy: that distinction isn't in the skill metadata, and forcing one on the user would mislead them about what each skill actually does. The CLI's lfx-skills list --available now picks both skills up automatically (eligible-skills filter checks for SKILL.md existence and excludes only the clone-only meta-skills). Co-Authored-By: Claude Opus 4.6 (1M context) Signed-off-by: Josep Garcia-Reyero Sais --- lfx-doctor/SKILL.md | 133 ++++++++++++ lfx-doctor/references/fix-recipes.md | 271 ++++++++++++++++++++++++ lfx-skills-helper/SKILL.md | 96 +++++++++ lfx-skills-helper/references/intents.md | 69 ++++++ 4 files changed, 569 insertions(+) create mode 100644 lfx-doctor/SKILL.md create mode 100644 lfx-doctor/references/fix-recipes.md create mode 100644 lfx-skills-helper/SKILL.md create mode 100644 lfx-skills-helper/references/intents.md diff --git a/lfx-doctor/SKILL.md b/lfx-doctor/SKILL.md new file mode 100644 index 0000000..798e957 --- /dev/null +++ b/lfx-doctor/SKILL.md @@ -0,0 +1,133 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-doctor +description: > + Diagnose problems with the LFX Skills installation: broken symlinks, missing + dev root, frontmatter errors, routing gaps, MCP setup. Use whenever a skill + isn't loading, when /lfx commands aren't appearing in autocomplete, when + newly installed skills don't show up, or when the user asks "what's wrong + with my install", "is my setup OK", or "check my lfx skills". Wraps + `lfx-skills doctor --json` with a conversational fix flow. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + + + +# LFX Skills Doctor + +You diagnose problems with a user's LFX Skills install and walk them through fixing the ones that need a human-in-the-loop. The bash CLI handles mechanical repairs; you handle everything that needs judgment (content gaps, scaffolding, file edits). + +## Step 1: Locate the CLI + +The `lfx-skills` CLI lives in the user's lfx-skills clone at `bin/lfx-skills`. Try, in order: + +1. **On PATH:** `command -v lfx-skills` — if found, use it directly. +2. **From the manifest:** `jq -r .canonical_clone ~/.config/lfx-skills/config.json 2>/dev/null` — if the file exists, append `/bin/lfx-skills`. +3. **Current dir:** if the user is inside the lfx-skills clone (a `bin/lfx-skills` exists relative to `pwd`), use `./bin/lfx-skills`. +4. **Last resort:** ask the user: "Where is your lfx-skills clone? (e.g., `~/lf/lfx-skills`)". + +If none of the above works, the install was never run: tell the user to clone the repo and run `./install.sh` (or use `/lfx-install` if they're inside the clone). Stop here. + +## Step 2: Run diagnostics + +```bash +"$LFX_SKILLS_CLI" doctor --json +``` + +This emits a JSON array of records. Parse it. Each record has: +`{severity, id, category, title, detail, fixable, payload}`. + +Group by severity (`pass` / `warn` / `fail`). + +## Step 3: Render the report + +Use this layout. Keep it scannable. + +``` +LFX Skills Health Check +═══════════════════════════════════════════ + +✓ checks passed. + +✗ errors: + + 1. + Why this matters: <plain-language consequence> + Fix: <action> + [auto-fixable] or [needs you to: <action>] + +⚠ <M> warnings: + + 1. <title> + Why: <consequence> + Fix: <action> +``` + +Don't dump every passing check. Summarise (`✓ 36 checks passed`) and let the user ask for the full list if they want it. + +For each error and warning, give plain-language context: not just the title from the JSON, but *why it matters* and *what to do about it*. Use `references/fix-recipes.md` (in this skill directory) to look up the per-issue narrative when the JSON record alone isn't enough. + +## Step 4: Offer fixes + +If there are fixable errors or warnings, ask: + +> "I can auto-fix N issue(s). Want me to walk through them?" + +Use `AskUserQuestion`. Wait for the answer. + +If yes: + +For each `fixable: true` record, ask per-issue (`AskUserQuestion`): "Fix `<id>`? — `<title>`". On yes, invoke: + +```bash +echo y | "$LFX_SKILLS_CLI" doctor --fix +``` + +(Or, more granularly, mark which issues to fix and answer the CLI's prompts. The CLI's `--fix` flow walks every fixable issue and asks per-issue too — you can let it drive, or you can pre-filter to the issues the user picked.) + +## Step 5: Handle the not-fixable cases + +For records with `fixable: false` that the user wants addressed, apply judgment. Common cases: + +| Issue ID | What you can offer | +|------------------------------|---------------------------------------------------------------------------------| +| `env-sh-not-sourced` | Print the exact line for the user to add to `~/.zshrc` (from `references/fix-recipes.md`). | +| `dev-root-not-in-session` | Suggest `source ~/.config/lfx-skills/env.sh` for this session, plus permanent fix. | +| `frontmatter-no-name` / `frontmatter-name-mismatch` | Read the SKILL.md, identify the line, offer to fix it via Edit. | +| `frontmatter-no-description` | Read the SKILL.md body, draft a one-paragraph description from it, offer to insert. | +| `license-missing` | Insert the YAML license-header lines (see `references/fix-recipes.md` template). | +| `mcp-undocumented` | Read the SKILL.md, find the `mcp__*` tool references, offer to draft a Prerequisites section. | +| `routing-uncovered` | Read `lfx/SKILL.md`, find the routing table, offer to add an entry for the missing skill. | +| `routing-dangling` | Either remove the dangling entry from `lfx/SKILL.md` or hand off to `/lfx-new-skill` to scaffold the missing skill. Ask the user. | +| `symlink-no-skillmd` | Hand off to `/lfx-new-skill` to scaffold the missing SKILL.md. | +| `clone-dirty` | Informational only. Mention that the user has uncommitted changes; don't act. | + +Always ask before editing files. Never edit `lfx/SKILL.md` (or anything else) silently. + +## Step 6: Re-verify + +After applying fixes, re-run: + +```bash +"$LFX_SKILLS_CLI" doctor --json +``` + +Show the new counts. Celebrate if everything's green; otherwise, note what's still outstanding and why. + +## Step 7: Close + +End with: + +> "Run `/lfx-doctor` anytime to recheck. For installation changes, `/lfx-install` or `/lfx-skills-helper` is the right entry point." + +## What this skill does NOT do + +- Install new skills or change install scope: that's `/lfx-install`. +- List, manage, or scaffold skills: `/lfx-skills-helper` and `/lfx-new-skill`. +- Modify `lfx/SKILL.md`'s routing table without asking the user first. +- Run destructive operations (force-removing real files, etc.) — only mechanical repairs the CLI considers safe. + +## Reference files + +- [`references/fix-recipes.md`](references/fix-recipes.md) — per-issue narrative, fix templates, and copy-pasteable snippets. diff --git a/lfx-doctor/references/fix-recipes.md b/lfx-doctor/references/fix-recipes.md new file mode 100644 index 0000000..11a6eee --- /dev/null +++ b/lfx-doctor/references/fix-recipes.md @@ -0,0 +1,271 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# Fix Recipes + +Per-issue narrative for `/lfx-doctor`. The CLI's `--fix` flag handles the mechanical cases; this file covers everything else, plus extra context the JSON output doesn't include. + +Each entry follows the same shape: + +- **What:** plain-language description of what's wrong +- **Why it matters:** consequence for the user +- **Fix this session:** quick one-shot +- **Fix permanently:** lasting change +- **Auto-fixable?** yes (CLI), yes (skill), or no + +--- + +## issue-id: no-config + +**What:** No `~/.config/lfx-skills/config.json`. +**Why it matters:** the CLI doesn't know what's installed where. Doctor can only check things relative to the manifest, so most other checks will be skipped. +**Fix:** run `lfx-skills install`. Or, inside the lfx-skills clone, run `/lfx-install` for a guided walkthrough. +**Auto-fixable?** no (requires user choices about platform / scope / dev root). + +--- + +## issue-id: no-symlinks + +**What:** Manifest exists but is empty (no symlinks recorded). +**Why it matters:** no LFX skills are actually installed. +**Fix:** run `lfx-skills install` to add them. +**Auto-fixable?** no. + +--- + +## issue-id: symlink-missing + +**What:** A symlink the manifest expects is gone from disk. +**Why it matters:** the corresponding `/lfx-...` command won't be available in your AI tool. +**Fix this session:** `lfx-skills doctor --fix` (CLI recreates from the manifest). +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: symlink-broken + +**What:** A symlink exists but points to a path that's no longer a directory. +**Why it matters:** same as above — the skill won't load. +**Common cause:** the lfx-skills clone moved or was deleted. +**Fix this session:** `lfx-skills doctor --fix`. +**Fix permanently:** if you moved the clone, run `lfx-skills install` again from the new location to re-record `canonical_clone`. +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: symlink-no-skillmd + +**What:** A symlink target exists, but the directory has no `SKILL.md`. +**Why it matters:** the skill won't load (the loader requires `SKILL.md`). Usually means someone created an empty `lfx-foo/` directory by accident, or pulled an in-progress branch. +**Fix:** either delete the empty directory, or scaffold a real skill via `/lfx-new-skill`. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can hand off to `/lfx-new-skill`. + +--- + +## issue-id: clone-not-recorded / clone-mismatch + +**What:** The CLI doesn't know which clone is canonical, or the recorded path doesn't match where the script ran from. +**Why it matters:** `lfx-skills update` and other commands rely on `canonical_clone`. Symlinks may also be pointing to a different clone than the one you think you're working in. +**Fix:** re-run `lfx-skills install` from the clone you want to be canonical. +**Auto-fixable?** no. + +--- + +## issue-id: clone-dirty + +**What:** Your `lfx-skills` clone has uncommitted changes. +**Why it matters:** purely informational. If you ran `lfx-skills update --pull`, that would fail with this state. Otherwise no impact. +**Fix:** commit, stash, or discard, depending on intent. +**Auto-fixable?** no. + +--- + +## issue-id: dev-root-not-recorded + +**What:** No `lfx_dev_root` in the manifest. +**Why it matters:** skills like `/lfx-test-journey` and `/lfx-coordinator` can't auto-discover your local LFX repos. +**Fix:** `lfx-skills config set lfx_dev_root=/path/to/your/lfx-clones`. Or re-run `lfx-skills install`. +**Auto-fixable?** no (requires user input). + +--- + +## issue-id: dev-root-missing + +**What:** The recorded `lfx_dev_root` path doesn't exist on disk. +**Common cause:** moved your clones to a new location. +**Fix:** `lfx-skills config set lfx_dev_root=/new/path`. The CLI will rewrite `env.sh` automatically. +**Auto-fixable?** no. + +--- + +## issue-id: dev-root-empty + +**What:** `LFX_DEV_ROOT` exists but contains no `lf*` git repos. +**Why it matters:** the skills will run, but they won't find any local repos to work on (they'll fall back to GitHub API calls when possible). +**Fix:** clone the LFX repos you work on into that directory. Examples: + +```bash +cd "$LFX_DEV_ROOT" +git clone https://github.com/linuxfoundation/lfx-v2-ui.git +git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git +``` + +**Auto-fixable?** no. + +--- + +## issue-id: env-sh-missing + +**What:** `~/.config/lfx-skills/env.sh` is gone. +**Why it matters:** sourcing your shell rc won't set `LFX_DEV_ROOT` or add `lfx-skills` to PATH. +**Fix this session:** `lfx-skills doctor --fix` (CLI regenerates from `config.json`). +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: dev-root-not-in-session + +**What:** `LFX_DEV_ROOT` isn't set in the current shell. +**Why it matters:** skills that read the env var directly will fall back to defaults (`~/lf`), which may not be where you keep your repos. +**Fix this session:** + +```bash +. ~/.config/lfx-skills/env.sh +``` + +**Fix permanently:** add to your `~/.zshrc` (or equivalent): + +```bash +[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" +``` + +**Auto-fixable?** no (we never auto-edit user shell rc). + +--- + +## issue-id: dev-root-session-drift + +**What:** Your shell's `LFX_DEV_ROOT` differs from the manifest's recorded value. +**Common cause:** you set the env var manually somewhere outside `env.sh`, or you re-ran `lfx-skills install` with a new dev root but haven't reloaded your shell. +**Fix:** decide which is right; align them. Either re-source `env.sh` or update the manifest with `lfx-skills config set lfx_dev_root=...`. +**Auto-fixable?** no. + +--- + +## issue-id: env-sh-not-sourced + +**What:** None of your shell rc files reference `~/.config/lfx-skills/env.sh`. +**Fix:** add this one-liner to your `~/.zshrc` (or `~/.bashrc`): + +```bash +[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" +``` + +**Auto-fixable?** no. + +--- + +## issue-id: cli-not-on-path + +**What:** The `claude` CLI isn't found on PATH but the manifest records a Claude install. +**Fix:** install Claude Code from <https://claude.com/code>, or check `which claude` from outside this shell. +**Auto-fixable?** no. + +--- + +## issue-id: platform-dir-missing + +**What:** A recorded config dir (e.g., `~/.claude`) no longer exists. +**Common cause:** you removed Claude Code, or moved its config. +**Fix:** if you still want skills installed there, recreate the dir (`mkdir -p ~/.claude/skills`) and run `lfx-skills doctor --fix`. Otherwise, run `lfx-skills uninstall --scope=global` and re-install with the new config dir. +**Auto-fixable?** no. + +--- + +## issue-id: frontmatter-missing + +**What:** A `SKILL.md` doesn't start with `---` on line 1. +**Why it matters:** the skill loader will refuse to load it. The loader requires frontmatter as the very first thing in the file (no blank lines, no comments above). +**Fix:** insert a frontmatter block at the top with the skill `name`, `description`, and `allowed-tools`. Use `/lfx-new-skill` as a template, or copy the shape from a sibling skill. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can guide the rewrite. + +--- + +## issue-id: frontmatter-no-name + +**What:** Frontmatter present but the `name:` field is missing or empty. +**Fix:** add `name: <skill-directory-basename>` to the frontmatter. Loader will fail without it. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch it via Edit. + +--- + +## issue-id: frontmatter-name-mismatch + +**What:** `name:` in the SKILL.md doesn't match the directory basename. +**Why it matters:** loaders use the directory name to register the slash command, but read the frontmatter for description and tools. A mismatch is confusing and may cause routing issues. +**Fix:** make `name:` equal `basename "$skill_dir"`. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. + +--- + +## issue-id: frontmatter-no-description + +**What:** No `description:` field, or it's empty. +**Why it matters:** the loader uses `description` to decide when to surface the skill. Missing description means the model has no context for *when* to invoke it. +**Fix:** write a one-paragraph description that includes 3–5 trigger phrases users might say. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can draft one from the SKILL.md body. + +--- + +## issue-id: license-missing + +**What:** A `SKILL.md` doesn't have the LFX copyright header in its first 4 lines. +**Why it matters:** CI's `license-header-check` job will fail. +**Fix:** add these lines as lines 2–3, immediately after the opening `---`: + +```yaml +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: ... +``` + +(The `#` comments are valid YAML comments. They satisfy the license check without breaking frontmatter parsing.) +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. + +--- + +## issue-id: routing-dangling + +**What:** `lfx/SKILL.md` mentions `/lfx-foo` but no `lfx-foo/` directory exists. +**Why it matters:** the user asks `/lfx` to route to `/lfx-foo` and gets a confused error. +**Fix:** either remove the dangling reference from `lfx/SKILL.md`, or create the missing skill via `/lfx-new-skill`. +**Auto-fixable?** no — needs your call on which. + +--- + +## issue-id: routing-uncovered + +**What:** A skill exists in the clone but `lfx/SKILL.md` doesn't route to it. +**Why it matters:** users typing `/lfx` won't find the skill via the plain-language router. They can still invoke it directly with `/lfx-foo`. +**Fix:** add an entry to `lfx/SKILL.md`'s routing table for the skill, including 1–2 example trigger phrases. +**Caveat:** internal-only skills (like `lfx-backend-builder` and `lfx-ui-builder`, which are only invoked by `/lfx-coordinator`) can legitimately stay out of the routing table. Use judgment. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. + +--- + +## issue-id: mcp-undocumented + +**What:** A SKILL.md uses `mcp__*` tool names but has no `## Prerequisites` section. +**Why it matters:** users without the MCP server configured will get cryptic errors when the skill tries to invoke a tool that doesn't exist. +**Fix:** add a Prerequisites section listing the MCP servers the skill depends on, with a link to setup instructions. +**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can grep the SKILL.md for `mcp__` references and draft a Prerequisites section. + +--- + +## issue-id: platforms-none + +**What:** Manifest exists but records no platforms. +**Common cause:** install was interrupted, or someone hand-edited the config. +**Fix:** run `lfx-skills install` again. +**Auto-fixable?** no. diff --git a/lfx-skills-helper/SKILL.md b/lfx-skills-helper/SKILL.md new file mode 100644 index 0000000..f117b39 --- /dev/null +++ b/lfx-skills-helper/SKILL.md @@ -0,0 +1,96 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-skills-helper +description: > + Manage your LFX Skills installation via the lfx-skills CLI: list what's + installed, install or uninstall in this repo or globally, update from + upstream, view or change config, look up what a specific skill does. Use for + "add lfx skills to this repo", "what's installed", "update lfx skills", + "show my lfx setup", "uninstall", "what does /lfx-foo do". For "which skill + should I use for X" or other plain-language routing questions, hand off to + /lfx. For health checks or repair, hand off to /lfx-doctor. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> + +# LFX Skills Helper + +You are the conversational front-end for the `lfx-skills` CLI: install, uninstall, update, list, info, config. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. + +## Step 1: Locate the CLI + +Same as `/lfx-doctor`. Try in order: + +1. `command -v lfx-skills` (on PATH). +2. `jq -r .canonical_clone ~/.config/lfx-skills/config.json 2>/dev/null` then append `/bin/lfx-skills`. +3. `./bin/lfx-skills` if you're inside the lfx-skills clone. +4. Ask the user. + +If none works: the install was never run. Tell the user to clone `linuxfoundation/lfx-skills` and run `./install.sh` (or invoke `/lfx-install` if they're already in the clone). Stop. + +## Step 2: Classify the request + +Decide which surface the request belongs to: + +| User asks about… | Surface | Action | +|--------------------------------------------|------------------|-------------------------------------------| +| Installing, updating, removing, config | This skill | Map to a CLI subcommand (Step 3) | +| What's installed / available / where | This skill | Map to a CLI subcommand (Step 3) | +| What a specific skill does | This skill | `lfx-skills info <name>` | +| **Which skill** to use for a task | Hand off to `/lfx` | Stop here; let the router pick. | +| **Diagnose** a problem / "why isn't X working" | Hand off to `/lfx-doctor` | Stop here. | +| **Scaffold a new skill** | Hand off to `/lfx-new-skill` | Stop here (clone-only). | + +Read `references/intents.md` once for the management-intent → CLI mapping. If the user's phrasing isn't in the table and doesn't fit your job either, say so and suggest the right entry point. + +## Step 3: Run the CLI + +Execute the chosen subcommand. Capture stdout. Use `--json` flags where available (currently `doctor --json`) when you need structured data. + +For commands that change state (`install`, `uninstall`, `update`, `config set`), **always confirm with the user first** via `AskUserQuestion`, showing exactly what you're about to run. The CLI's `--yes` flag is appropriate only after the user has confirmed in chat. + +## Step 4: Format the output + +The CLI output is structured but utilitarian. Reformat it for conversation: + +- **`lfx-skills list`** outputs `platform/scope<TAB>skill<TAB>link`. Render as a friendly grouped list: + + ``` + Globally installed (Claude): + /lfx + /lfx-coordinator + ... + + Per-repo (lfx-v2-meeting-service): + /lfx-coordinator + /lfx-pr-resolve + ... + ``` + +- **`lfx-skills info <skill>`** outputs frontmatter + install locations. Render the description in prose, list trigger phrases, summarise where it's installed. + +- **`lfx-skills config`** outputs raw JSON. Pretty-print it as a small table: dev root, canonical clone, platforms, total symlinks. + +- **`lfx-skills repos`** outputs one path per line. Group as a numbered list with sizes/last-modified if helpful. + +## Step 5: Hand-off rules + +If during the conversation the request shifts to something off your turf, hand off cleanly: + +- **Diagnostic questions** ("is my install OK?", "why isn't /lfx-foo working?", "fix my broken symlinks"): hand off to `/lfx-doctor`. +- **Routing / discovery** ("which skill should I use for backend work?", "I want to add a feature, where do I start?"): hand off to `/lfx`. +- **Creating a new skill**: hand off to `/lfx-new-skill` (only available inside the lfx-skills clone). + +## What this skill does NOT do + +- **Pick which skill the user should use**: that's `/lfx`'s job. This skill manages the install; it doesn't recommend skills. +- **Diagnose problems**: hand off to `/lfx-doctor`. +- **Scaffold new skills**: hand off to `/lfx-new-skill`. +- **Install/uninstall without confirmation**: always show the user the exact command first. +- **Invent categorisations**: skills aren't tagged backend/frontend/etc. anywhere in their metadata; don't pretend they are. + +## Reference files + +- [`references/intents.md`](references/intents.md) — management-intent → CLI command mapping. diff --git a/lfx-skills-helper/references/intents.md b/lfx-skills-helper/references/intents.md new file mode 100644 index 0000000..65f134e --- /dev/null +++ b/lfx-skills-helper/references/intents.md @@ -0,0 +1,69 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# Intent → CLI mapping + +Reference for `/lfx-skills-helper`. Maps natural-language **management** intents to the corresponding `lfx-skills` CLI invocation. + +This file is for skill *management*: install, uninstall, update, list, info, config. It is not a recommendation engine. Routing questions ("which skill should I use for X?") belong to `/lfx`. Diagnostic questions belong to `/lfx-doctor`. Authoring belongs to `/lfx-new-skill`. + +When the user's phrasing isn't an exact match, infer the closest intent and confirm the chosen command before running anything stateful. + +## Listing + +| User says | Run | Notes | +|-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| +| "What lfx skills do I have here?" | `lfx-skills list --scope=repo --repo="$(pwd)"` | Per-repo install only | +| "What lfx skills are installed globally?" | `lfx-skills list --scope=global` | Across every recorded global config dir | +| "What lfx skills do I have anywhere?" | `lfx-skills list` | Both scopes combined | +| "What lfx skills are available in the clone?" | `lfx-skills list --available` | All installable skills, regardless of install state | + +## Inspecting one skill + +| User says | Run | Notes | +|-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| +| "What does /lfx-coordinator do?" | `lfx-skills info lfx-coordinator` | Strip the leading slash; pass the bare name | +| "Where is /lfx-coordinator installed?" | `lfx-skills info lfx-coordinator` | The output includes install locations | +| "Show me my full setup" | `lfx-skills config` | Pretty-print the JSON for the user | +| "Where is my LFX dev root?" | `lfx-skills config get lfx_dev_root` | | +| "Which clone of lfx-skills am I using?" | `lfx-skills config get canonical_clone` | | +| "What repos are in my LFX dev root?" | `lfx-skills repos` | | + +## Installing / changing scope + +Always confirm via `AskUserQuestion` before running. Show the exact command first. + +| User says | Run | +|-------------------------------------------------|--------------------------------------------------------------------------------------| +| "Add lfx skills to this repo" | Confirm, then `lfx-skills install --yes --scope=repo --repos="$(pwd)"`. Suggest `/lfx-doctor` after. | +| "Remove lfx skills from this repo" | Confirm, then `lfx-skills uninstall --yes --scope=repo --repos="$(pwd)"` | +| "Install lfx skills globally for Claude" | Confirm, then `lfx-skills install --yes --platform=claude --scope=global` | +| "Add agents.md support" | Confirm, then `lfx-skills install --yes --platform=agents --scope=global` | +| "Install everything everywhere" | Confirm. Don't assume `--repos=`; ask the user which repos. | + +## Maintenance + +| User says | Run | Notes | +|-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| +| "Update lfx skills" | `lfx-skills update --pull` | Suggest `/lfx-doctor` after | +| "Re-apply my install" | `lfx-skills update` | No `--pull`; just refresh symlinks against the manifest | +| "Update my LFX dev root" | Confirm new path, `lfx-skills config set lfx_dev_root=NEW_PATH` | Rewrites `env.sh` automatically | +| "Switch my Claude config dir" | Suggest `lfx-skills uninstall` then `lfx-skills install --claude-config=NEW_DIR`. Don't try to mutate in place. | + +## Hand-offs (not your job) + +| User says | Hand off to | +|-------------------------------------------------|--------------------------------------------------------------------------------------| +| "Which skill should I use for X?" / task descriptions | `/lfx` | +| "Run a health check" / "is my install OK?" | `/lfx-doctor` | +| "Why isn't /lfx-foo working?" | `/lfx-doctor` | +| "Fix my broken symlinks" | `/lfx-doctor` | +| "How do I create a new lfx skill?" | `/lfx-new-skill` (only inside the lfx-skills clone) | +| "Scaffold a new skill called lfx-foo" | `/lfx-new-skill` | + +## Disambiguation rules + +- The leading slash in user phrasing (e.g., `/lfx-coordinator`) is conversational; strip it when passing to the CLI. +- If the user says "skill X" without specifying scope, assume per-repo when they're inside a repo, global otherwise. Confirm before acting. +- If the user gives a custom command-line flag combo you don't recognise, defer to the CLI: `lfx-skills help <subcommand>`. +- If the user describes a *task* rather than asking about install state, that's a routing question — hand off to `/lfx` even mid-conversation. From 2b199a67989b6033f4eabb1521f839bdbc8f36fd Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 14:18:56 +0200 Subject: [PATCH 06/20] feat(meta): clone-only meta-skills + CLAUDE.md/AGENTS.md + IFS-leak fix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The "fresh-clone, what now?" UX was the missing piece. After this commit a user can clone the repo, open Claude Code (or Codex / Gemini CLI / OpenCode via AGENTS.md) inside the clone, and ask for help installing, diagnosing, managing, or authoring skills — without anything being installed yet. The four meta-skills are auto-discovered via committed relative symlinks under .claude/skills/ and .agents/skills/. New skill directories: lfx-install/SKILL.md Conversational wrapper over the install wizard. Asks platform, scope, dev root, config dirs, repos in plain language; composes the non-interactive bin/lfx-skills install invocation; verifies via doctor; prints the rc snippet for the user to add manually. Strictly clone-only: never gets installed elsewhere. lfx-new-skill/SKILL.md + references/conventions-quickref.md Scaffolds a new lfx-<name>/ directory with valid frontmatter, the YAML license-header lines on rows 2-3, optional references/, then chains into `lfx-skills update` and `lfx-skills doctor` so the contributor can try /lfx-<name> immediately. Also clone-only. Auto-discovery wiring: .claude/skills/{lfx-install,lfx-new-skill,lfx-doctor,lfx-skills-helper} .agents/skills/{lfx-install,lfx-new-skill,lfx-doctor,lfx-skills-helper} Eight committed relative symlinks (../../lfx-foo). Survive any `git clone <repo> <any-path>` because they're relative. CLAUDE.md and AGENTS.md Repo-root agent guidance pointing at the four meta-skills, when to use which, and the layout. Same content; CLAUDE.md is for Claude Code's discovery, AGENTS.md for the agentskills.io ecosystem (Codex, Gemini CLI, OpenCode). .gitignore negation block: The repo's `.claude` was previously fully gitignored, which would block the four committed symlinks. Switched to `.claude/*` (the trailing-slash variant prevents negation) and added explicit un-ignore rules for the four meta-skill paths. Mirror block for .agents/. Other Claude/agents local state (settings.local.json, per-repo install symlinks created by the CLI in user repos) remains ignored. IFS-leak bug fix in cmd_install / cmd_uninstall: Three sites used `local IFS=','` to split comma-separated flag values. Bash's `local` is dynamically scoped, so the changed IFS leaked into nested calls — including `for ex in $SYMLINKS_CLONE_ONLY` inside symlinks_eligible_skills, which then failed to split the exclusion list on spaces and never excluded anything. Result: `lfx-skills install` symlinked lfx-install and lfx-new-skill into every target, defeating the "clone-only" boundary. Replaced `local IFS=','; for x in $var` with `while IFS= read -r x; do ... done < <(printf '%s\n' "$var" | tr ',' '\n')`. The trailing `\n` on printf is load-bearing: without it, `read` would silently drop the last comma-separated value because it had no terminator. Caught by the Phase 6 fresh-clone integration test, which expected 17 symlinks per target and got 19. End-to-end verification: 7-target install (2 Claude config dirs + 1 agents + 2 repos × 2 platforms) creates 119 manifest entries (7 × 17), with lfx-install and lfx-new-skill correctly excluded from every target. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- .agents/skills/lfx-doctor | 1 + .agents/skills/lfx-install | 1 + .agents/skills/lfx-new-skill | 1 + .agents/skills/lfx-skills-helper | 1 + .claude/skills/lfx-doctor | 1 + .claude/skills/lfx-install | 1 + .claude/skills/lfx-new-skill | 1 + .claude/skills/lfx-skills-helper | 1 + .gitignore | 33 +++- AGENTS.md | 40 ++++ CLAUDE.md | 36 ++++ bin/lfx-skills | 17 +- lfx-install/SKILL.md | 178 ++++++++++++++++++ lfx-new-skill/SKILL.md | 178 ++++++++++++++++++ .../references/conventions-quickref.md | 90 +++++++++ 15 files changed, 565 insertions(+), 15 deletions(-) create mode 120000 .agents/skills/lfx-doctor create mode 120000 .agents/skills/lfx-install create mode 120000 .agents/skills/lfx-new-skill create mode 120000 .agents/skills/lfx-skills-helper create mode 120000 .claude/skills/lfx-doctor create mode 120000 .claude/skills/lfx-install create mode 120000 .claude/skills/lfx-new-skill create mode 120000 .claude/skills/lfx-skills-helper create mode 100644 AGENTS.md create mode 100644 CLAUDE.md create mode 100644 lfx-install/SKILL.md create mode 100644 lfx-new-skill/SKILL.md create mode 100644 lfx-new-skill/references/conventions-quickref.md diff --git a/.agents/skills/lfx-doctor b/.agents/skills/lfx-doctor new file mode 120000 index 0000000..a241cc2 --- /dev/null +++ b/.agents/skills/lfx-doctor @@ -0,0 +1 @@ +../../lfx-doctor \ No newline at end of file diff --git a/.agents/skills/lfx-install b/.agents/skills/lfx-install new file mode 120000 index 0000000..44dd1a2 --- /dev/null +++ b/.agents/skills/lfx-install @@ -0,0 +1 @@ +../../lfx-install \ No newline at end of file diff --git a/.agents/skills/lfx-new-skill b/.agents/skills/lfx-new-skill new file mode 120000 index 0000000..86a14f3 --- /dev/null +++ b/.agents/skills/lfx-new-skill @@ -0,0 +1 @@ +../../lfx-new-skill \ No newline at end of file diff --git a/.agents/skills/lfx-skills-helper b/.agents/skills/lfx-skills-helper new file mode 120000 index 0000000..d463608 --- /dev/null +++ b/.agents/skills/lfx-skills-helper @@ -0,0 +1 @@ +../../lfx-skills-helper \ No newline at end of file diff --git a/.claude/skills/lfx-doctor b/.claude/skills/lfx-doctor new file mode 120000 index 0000000..a241cc2 --- /dev/null +++ b/.claude/skills/lfx-doctor @@ -0,0 +1 @@ +../../lfx-doctor \ No newline at end of file diff --git a/.claude/skills/lfx-install b/.claude/skills/lfx-install new file mode 120000 index 0000000..44dd1a2 --- /dev/null +++ b/.claude/skills/lfx-install @@ -0,0 +1 @@ +../../lfx-install \ No newline at end of file diff --git a/.claude/skills/lfx-new-skill b/.claude/skills/lfx-new-skill new file mode 120000 index 0000000..86a14f3 --- /dev/null +++ b/.claude/skills/lfx-new-skill @@ -0,0 +1 @@ +../../lfx-new-skill \ No newline at end of file diff --git a/.claude/skills/lfx-skills-helper b/.claude/skills/lfx-skills-helper new file mode 120000 index 0000000..d463608 --- /dev/null +++ b/.claude/skills/lfx-skills-helper @@ -0,0 +1 @@ +../../lfx-skills-helper \ No newline at end of file diff --git a/.gitignore b/.gitignore index ec1a76c..e4ee6ab 100644 --- a/.gitignore +++ b/.gitignore @@ -16,14 +16,31 @@ .Trashes Thumbs.db -# Claude AI assistant -.claude - -# Installed skill symlinks (created by per-repo installation). -# Phase 6 will commit a small set of relative symlinks under .claude/skills/ -# (lfx-install, lfx-doctor, lfx-skills-helper, lfx-new-skill); a negation -# block will be added there to un-ignore those specific paths. -.claude/skills/ +# Claude AI assistant — local state (settings.local.json, projects/, etc.) +# stays out of the repo. The negation block un-ignores the four committed +# meta-skill symlinks that make this clone self-discovering for /lfx-install, +# /lfx-new-skill, /lfx-doctor, /lfx-skills-helper. +# +# Important: we use `.claude/*` (not `.claude/` with trailing slash) because +# a fully-ignored directory can't be reopened with negation. The pattern below +# ignores everything UNDER .claude/ while leaving the directory itself +# inspectable, which is the only way `!` rules can take effect on its children. +.claude/* +!.claude/skills/ +.claude/skills/* +!.claude/skills/lfx-install +!.claude/skills/lfx-new-skill +!.claude/skills/lfx-doctor +!.claude/skills/lfx-skills-helper + +# Same negation pattern for the agents.md ecosystem mirror. +.agents/* +!.agents/skills/ +.agents/skills/* +!.agents/skills/lfx-install +!.agents/skills/lfx-new-skill +!.agents/skills/lfx-doctor +!.agents/skills/lfx-skills-helper # Legacy install manifest. Older clones of lfx-skills wrote this; the new CLI # stores its manifest at ~/.config/lfx-skills/config.json, so this file is no diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3676240 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,40 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# LFX Skills repo + +You are inside the LFX Skills source repository. Four meta-skills are auto-discovered here (via committed `.agents/skills/` symlinks) so you can help users without anything else being installed first: + +- **`/lfx-install`** — guides users through installing the skills (one-time setup after clone). Walks them through platform / config dirs / scope / repos in plain language, then runs the installer. +- **`/lfx-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. +- **`/lfx-skills-helper`** — manages the install: lists what's installed, installs/uninstalls in this repo, updates from upstream, shows config. Skill management only — *not* a router. +- **`/lfx-new-skill`** — scaffolds a new skill in this repo. Use when the contributor wants to add a new lfx skill or asks "how do I create a new skill". + +The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full command reference. + +## When to use which + +- "How do I install this?" / "I just cloned, what now?" → `/lfx-install` +- "Skills aren't loading" / "is my setup OK?" → `/lfx-doctor` +- "What's installed?" / "add to this repo" / "what does X do?" → `/lfx-skills-helper` +- "Create a new skill called …" → `/lfx-new-skill` +- Anything else (modifying an existing skill, writing docs, reviewing changes): proceed normally — use the existing user-facing skills (`/lfx`, `/lfx-coordinator`, `/lfx-preflight`, etc.) as you would in any LFX repo. + +## Repo layout + +- `bin/lfx-skills` — multi-subcommand bash CLI. +- `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, platforms). +- `lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. +- `.claude/skills/` and `.agents/skills/` — committed relative symlinks to the four meta-skills above (this is what makes them auto-discoverable when someone clones). +- `install.sh` — thin shim that execs `bin/lfx-skills install "$@"`. +- `~/.config/lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). + +## Tool-name compatibility + +The skill bodies in this repo use Claude Code's tool vocabulary by default (Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, Skill, WebFetch). When running under Codex, Gemini CLI, or OpenCode, treat them as the closest equivalent on your platform. See `docs/tool-mapping.md` if present. + +## Conventions + +- Every `SKILL.md` starts with `---` on line 1, with `# Copyright …` and `# SPDX-License-Identifier:` as YAML comments on lines 2–3. +- The `name:` frontmatter field equals the directory basename. +- DCO-signed commits required (`git commit -s`); GPG signing strongly preferred (`-S`). diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..a0f5244 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,36 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# LFX Skills repo + +You are inside the LFX Skills source repository. Four meta-skills are auto-discovered here (via committed `.claude/skills/` symlinks) so you can help users without anything else being installed first: + +- **`/lfx-install`** — guides users through installing the skills (one-time setup after clone). Walks them through platform / config dirs / scope / repos in plain language, then runs the installer. +- **`/lfx-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. +- **`/lfx-skills-helper`** — manages the install: lists what's installed, installs/uninstalls in this repo, updates from upstream, shows config. Skill management only — *not* a router. +- **`/lfx-new-skill`** — scaffolds a new skill in this repo. Use when the contributor wants to add a new lfx skill or asks "how do I create a new skill". + +The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full command reference. + +## When to use which + +- "How do I install this?" / "I just cloned, what now?" → `/lfx-install` +- "Skills aren't loading" / "is my setup OK?" → `/lfx-doctor` +- "What's installed?" / "add to this repo" / "what does X do?" → `/lfx-skills-helper` +- "Create a new skill called …" → `/lfx-new-skill` +- Anything else (modifying an existing skill, writing docs, reviewing changes): proceed normally — use the existing user-facing skills (`/lfx`, `/lfx-coordinator`, `/lfx-preflight`, etc.) as you would in any LFX repo. + +## Repo layout + +- `bin/lfx-skills` — multi-subcommand bash CLI. +- `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, platforms). +- `lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. +- `.claude/skills/` and `.agents/skills/` — committed relative symlinks to the four meta-skills above (this is what makes them auto-discoverable when someone clones). +- `install.sh` — thin shim that execs `bin/lfx-skills install "$@"`. +- `~/.config/lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). + +## Conventions + +- Every `SKILL.md` starts with `---` on line 1, with `# Copyright …` and `# SPDX-License-Identifier:` as YAML comments on lines 2–3. +- The `name:` frontmatter field equals the directory basename. +- DCO-signed commits required (`git commit -s`); GPG signing strongly preferred (`-S`). diff --git a/bin/lfx-skills b/bin/lfx-skills index b04db20..be03bfc 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -487,8 +487,11 @@ cmd_install() { if [ "$scope" = "global" ] || [ "$scope" = "both" ]; then if [ "$platform" = "claude" ] || [ "$platform" = "both" ]; then if [ -n "$claude_config" ]; then - local IFS=',' - for d in $claude_config; do chosen_claude_dirs+=("$d"); done + # Convert comma-separated to newline-separated and read line-by-line so + # we don't change IFS (which would leak into nested function calls and + # break, e.g. SYMLINKS_CLONE_ONLY iteration in symlinks_eligible_skills). + local d + while IFS= read -r d; do chosen_claude_dirs+=("$d"); done < <(printf '%s\n' "$claude_config" | tr ',' '\n') else if [ "${#claude_dirs_probed[@]}" -eq 0 ]; then chosen_claude_dirs=("$(platform_default_global_dir claude)") @@ -517,8 +520,8 @@ cmd_install() { local -a chosen_repos=() if [ "$scope" = "repo" ] || [ "$scope" = "both" ]; then if [ -n "$repos" ]; then - local IFS=',' - for r in $repos; do chosen_repos+=("$r"); done + local r + while IFS= read -r r; do chosen_repos+=("$r"); done < <(printf '%s\n' "$repos" | tr ',' '\n') else local -a candidate_repos=() while IFS= read -r r; do candidate_repos+=("$r"); done < <(probe_repos_in "$lfx_dev_root") @@ -692,11 +695,11 @@ cmd_uninstall() { fi if [ "$scope" = "repo" ] && [ -n "$repos" ]; then - local IFS=',' - for r in $repos; do + local r + while IFS= read -r r; do ui_step "Removing per-repo symlinks for $r…" symlinks_uninstall_all repo "$r" >/dev/null - done + done < <(printf '%s\n' "$repos" | tr ',' '\n') else local s s="$(symlinks_uninstall_all "$scope" "")" diff --git a/lfx-install/SKILL.md b/lfx-install/SKILL.md new file mode 100644 index 0000000..a21c255 --- /dev/null +++ b/lfx-install/SKILL.md @@ -0,0 +1,178 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-install +description: > + Install or set up LFX Skills. Walks the user through every choice in plain + language: which AI tools they use (Claude Code, agents.md tools, both), + where their LFX repos live, scope (global / per-repo / both), then runs the + installer and verifies. Use whenever the user says "I just cloned this — what + now?", "set up lfx skills", "install lfx skills", "I'm new to lfx-skills", + "first-time setup", or runs into the repo with no install manifest yet. Only + available inside the lfx-skills clone. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> + +# LFX Skills Install + +You guide the user through their first-time install. The bash CLI (`bin/lfx-skills install`) can do this non-interactively if every flag is supplied; this skill is the conversational layer that figures out what those flags should be by asking the user, in plain language, one question at a time. + +## Step 1: Verify you're in the clone + +This skill only works inside the `lfx-skills` clone (it's the only place where it's auto-discovered, via committed symlinks under `.claude/skills/`). Verify: + +```bash +[ -x ./bin/lfx-skills ] && echo OK || echo NOT_IN_CLONE +``` + +If `NOT_IN_CLONE`, tell the user: + +> "I only run inside the lfx-skills clone. `cd` to your clone of `linuxfoundation/lfx-skills` and ask again." + +Stop. + +## Step 2: Probe the system + +Run `./bin/lfx-skills` indirectly via its install command's PROBE step — but for the conversation, you also want the data yourself so you can ask informed questions. Do these one-shot probes: + +```bash +# CLIs available +for cli in claude codex gemini opencode; do + command -v "$cli" >/dev/null 2>&1 && echo "$cli" +done + +# Claude config dirs +ls -d "$HOME"/.claude* 2>/dev/null + +# Agents config dirs +ls -d "$HOME"/.agents* 2>/dev/null + +# Dev root candidates +for d in "$HOME/lf" "$HOME/lfx" "$HOME/code/lfx" "$HOME/work/lfx"; do + [ -d "$d" ] && echo "$d" +done +``` + +## Step 3: Q1 — Platform + +Use `AskUserQuestion`: + +> "Which AI coding tools do you use? (1) Claude Code, (2) an agents.md-compatible tool (Codex, Gemini CLI, OpenCode), (3) both." + +If you detected only one CLI installed, default to it but still confirm. + +## Step 4: Q2 — Scope + +> "Install scope? (1) **Global** — available in every session of your AI tool. (2) **Per-repo** — only in specific repos (their `.claude/skills/` or `.agents/skills/`). (3) **Both** — global plus pin into specific repos." + +This question goes second so subsequent questions can adapt. (Per-repo only? Skip the global config picker. Global only? Skip the repo picker.) + +## Step 5: Q3 — LFX dev root + +Show the candidates you probed with their repo counts: + +``` +Where do you keep your LFX repo clones? + 1. ~/lf (12 lf* repos) + 2. ~/code/lfx (3 lf* repos) + 3. Custom path… +``` + +Use `AskUserQuestion`. If the user already has `LFX_DEV_ROOT` set in the shell, mention it and ask whether to keep it. + +If the chosen path doesn't exist, ask whether to create it (`mkdir -p`). If it has zero `lf*` git repos, warn but proceed: the install will still work; the dev-root-empty doctor warning will trigger until they clone some. + +## Step 6: Q4 — Config dirs (only if scope includes Global) + +If you saw multiple Claude config dirs (e.g., `~/.claude`, `~/.claude-work`, `~/.claude-personal`), ask which to install into: + +> "I see these Claude config dirs: …. Install into all of them, or just one? (defaults to `~/.claude`)" + +Same for `~/.agents*` if applicable. (Most users have one each; this only matters for power users with multiple profiles.) + +If scope is Per-repo only, skip this step entirely. + +## Step 7: Q5 — Repos (only if scope includes Per-repo) + +List `lf*` git repos under the chosen dev root: + +> "Which repos? Pick numbers (e.g., `1, 3, 5`), `all`, or `none`." + +If scope is Global only, skip this step entirely. + +## Step 8: Show the plan + +Before running anything, summarise: + +``` +Plan: + Platform: claude + agents + Scope: global + per-repo + LFX_DEV_ROOT: ~/lf + Claude dirs: ~/.claude, ~/.claude-work + Agents dir: ~/.agents + Repos (4): lfx-v2-ui, lfx-v2-meeting-service, lfx-v2-committee-service, lfx-v2-query-service + Skills: 15 user-facing + lfx-doctor + lfx-skills-helper + +Will create approximately N symlinks. Proceed? +``` + +`AskUserQuestion`. If no, stop. + +## Step 9: Run the installer + +Compose the non-interactive flags from the user's answers: + +```bash +./bin/lfx-skills install --yes \ + --platform=<platform> \ + --scope=<scope> \ + --lfx-dev-root=<path> \ + --claude-config=<dir1,dir2,...> \ + --agents-config=<dir> \ + --repos=<repo1,repo2,...> +``` + +Stream the output to the user. + +## Step 10: Verify + +Run a quick verification: + +```bash +./bin/lfx-skills doctor +``` + +If errors, walk the user through the auto-fix: + +> "One or more checks failed. Want me to run `/lfx-doctor` to investigate?" + +## Step 11: Show the rc snippet + +The CLI prints the `env.sh` source line at the end. Repeat it explicitly: + +> "Add this one line to your `~/.zshrc` (or `~/.bashrc`) so `LFX_DEV_ROOT` and the `lfx-skills` command are available in every new shell: +> +> ```bash +> [ -f \"$HOME/.config/lfx-skills/env.sh\" ] && . \"$HOME/.config/lfx-skills/env.sh\" +> ``` +> +> I won't edit your shell rc for you — that's your call." + +## Step 12: Suggest next steps + +> "All set. Next: +> +> 1. Restart your AI coding assistant (or open a new session). +> 2. `cd` to any LFX repo and type `/lfx` — that's your plain-language entry point. +> 3. Run `/lfx-doctor` anytime to recheck the install. +> 4. Run `/lfx-skills-helper` to manage what's installed where." + +## What this skill does NOT do + +- **Edit your shell rc** — never. Always print the snippet for the user to paste. +- **Install only some skills** — v1 always installs everything (the full set per chosen target). Filtering is a v2 idea. +- **Run outside the clone** — bail in Step 1. +- **Re-run silently** — confirm at the plan step before doing anything stateful. diff --git a/lfx-new-skill/SKILL.md b/lfx-new-skill/SKILL.md new file mode 100644 index 0000000..c7b4bd4 --- /dev/null +++ b/lfx-new-skill/SKILL.md @@ -0,0 +1,178 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-new-skill +description: > + Scaffold a new skill in the lfx-skills repo. Walks the contributor through + picking a name, description, and tool list; generates the SKILL.md with the + correct frontmatter shape per docs/skill-authoring.md conventions; sets up + references/ if needed; chains into `lfx-skills update` to install the new + skill locally and `lfx-skills doctor` to verify so it can be tried + immediately. Use whenever someone wants to add a new lfx skill, scaffold a + new SKILL.md, or asks "how do I create a new skill". Only available inside + the lfx-skills clone. +allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion +--- + +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> + +# LFX New Skill — Scaffolder + +You help a contributor scaffold a new skill in the `lfx-skills` repo. Your job ends when there's a well-formed `lfx-<name>/SKILL.md` on disk, the new skill is installed locally, and `lfx-skills doctor` verifies it loads. You do not handle the rest of the contribution flow (DCO, PR, review) — for that, point at `CONTRIBUTING.md`. + +## Step 1: Verify you're in the clone + +This skill only works inside the `lfx-skills` clone. Verify: + +```bash +[ -x ./bin/lfx-skills ] && [ -d ./lfx ] && echo OK || echo NOT_IN_CLONE +``` + +If `NOT_IN_CLONE`: + +> "I only run inside the `lfx-skills` clone. `cd` to your clone and ask again." + +Stop. + +## Step 2: Q1 — Skill name + +Use `AskUserQuestion`: + +> "What's the skill name? It must: +> - start with `lfx-` +> - be lowercase with hyphens (kebab-case) +> - be a noun-phrase or verb-phrase short enough to type as `/lfx-<name>` +> +> Examples: `lfx-release-notes`, `lfx-onboarding-checklist`, `lfx-search-jira`." + +Validate the answer: +- Must match `^lfx-[a-z0-9-]+$` +- Must NOT collide with an existing directory: check `[ -d "lfx-<name>" ]` — if exists, ask for another. +- Must NOT collide with `lfx` itself or any reserved prefix. + +Re-ask until you get a valid name. + +## Step 3: Q2 — Description + +> "Describe the skill in one paragraph. The description is what your AI tool reads to decide *when* to invoke this skill — so include 3–5 trigger phrases users might say (e.g., 'Use for "release notes for X", "what changed in Y", "draft an announcement"'). +> +> Aim for 2–4 sentences." + +Don't auto-generate this. The contributor knows their intent better than you do. Wait for their answer. + +## Step 4: Q3 — Allowed tools + +> "Which tools should the skill have access to? +> +> The typical default is: `Bash, Read, Glob, Grep, AskUserQuestion`. +> +> Add `Write, Edit` if the skill will modify files. +> Add `Skill` if the skill will delegate to other lfx skills. +> Add `WebFetch` if the skill reads external URLs. +> Add specific MCP tool names (e.g., `mcp__atlassian__getJiraIssue`) if the skill depends on MCP servers. +> +> Type the comma-separated list, or just press Enter for the default." + +Validate: tools should be a comma-separated list of identifiers. If the user includes MCP tools, remind them they'll need a Prerequisites section in the body (see Step 7). + +## Step 5: Q4 — references/ directory? + +> "Will this skill have reference docs alongside `SKILL.md`? (e.g., a long table, a JSON config, a checklist that's too big to inline.) — yes / no." + +If yes: create `lfx-<name>/references/` with a `.gitkeep` so the directory is tracked even when empty. + +## Step 6: Generate SKILL.md + +Use the `Write` tool with the absolute path `<clone>/lfx-<name>/SKILL.md`. Template: + +```markdown +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: <skill-name> +description: > + <user's description, joined into a YAML folded scalar> +allowed-tools: <comma-separated list> +--- + +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> + +# <Title-cased name> + +<One-paragraph intro: who this skill helps and what it does. Pull from the description but expand.> + +## Step 1: <verb-noun> + +<Concrete instructions for the LLM running the skill. Use code fences for shell snippets and tool invocations. Be specific.> + +## Step 2: <verb-noun> + +<...> + +## What this skill does NOT do + +- <bound 1> +- <bound 2> + +## Reference files + +- (none yet — add as needed) +``` + +**Critical formatting rules** (mirror the rest of the lfx-skills repo): + +- Frontmatter MUST start at line 1 with `---`. No blank lines, no comments above. +- The license header is two YAML comment lines (`# Copyright…`, `# SPDX-License-Identifier:`) **inside** the frontmatter, lines 2–3. Plain `#` comments are valid YAML — they pass `head -4 | grep` for the CI license check without breaking frontmatter parsing. +- `name:` value must equal the directory basename (the `lfx-<name>` you picked in Step 2). +- `description:` uses YAML folded scalar (`>`) so it can wrap nicely in source while staying a single string at parse time. + +## Step 7: Prerequisites section (if MCP tools) + +If the user listed any `mcp__*` tools in Step 4, add a `## Prerequisites` section to the body listing them, with a one-line note about how to set up the MCP server (or a link to the right docs). The doctor's `mcp-undocumented` check looks for this — without it, the doctor will warn. + +## Step 8: Install locally + verify + +Chain into the CLI to make the new skill available immediately: + +```bash +./bin/lfx-skills update +``` + +This re-applies the manifest and detects the new `lfx-<name>/` directory. The CLI will list it as a new skill not in the manifest and prompt to install it everywhere already configured. + +Then verify: + +```bash +./bin/lfx-skills doctor +``` + +If the doctor flags `frontmatter-no-name`, `frontmatter-name-mismatch`, `license-missing`, `routing-uncovered`, or any other content issue with your new skill, fix it (use Edit) and re-run `doctor` until clean. + +The `routing-uncovered` warning is a reminder to add an entry to `lfx/SKILL.md` (the plain-language router) so `/lfx` knows when to route to your new skill. Decide whether your skill is user-facing (add to routing) or internal-only-invoked-by-another-skill (skip routing — the warning is acceptable). Ask the user if you're unsure. + +## Step 9: Tell the user how to try it + +> "All set. To try `/lfx-<name>`: +> +> 1. Restart your AI tool (or open a new session). +> 2. Type `/lfx-<name>` — it should show in autocomplete and load with the description you wrote. +> +> Iterate on the body as you go: every time you save the SKILL.md, your tool picks it up on the next invocation." + +## Step 10: Hand off to CONTRIBUTING + +> "When you're ready to ship this skill upstream: +> +> - Read `CONTRIBUTING.md` for the DCO, sign-off, and review flow. +> - Run `/lfx-preflight` to validate your changes. +> - Open a PR against `main`. +> +> Welcome to lfx-skills." + +## What this skill does NOT do + +- **Install or set up the lfx-skills install** — that's `/lfx-install`. +- **Diagnose existing install problems** — that's `/lfx-doctor`. +- **Manage the install (list, uninstall, update)** — that's `/lfx-skills-helper`. +- **Open PRs or push to a remote** — point at `CONTRIBUTING.md` and stop. +- **Author the body of the skill for the contributor** — you scaffold the structure, they write the substance. Don't fabricate steps that match a guess at intent; ask if you don't know. diff --git a/lfx-new-skill/references/conventions-quickref.md b/lfx-new-skill/references/conventions-quickref.md new file mode 100644 index 0000000..ccb2999 --- /dev/null +++ b/lfx-new-skill/references/conventions-quickref.md @@ -0,0 +1,90 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# Skill conventions — quick reference + +Cheat sheet for `/lfx-new-skill`. The full version is `docs/skill-authoring.md` (when present). When in doubt, copy a sibling skill that does something close to what you want. + +## Required frontmatter + +```yaml +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-<your-skill> +description: > + One paragraph. Include 3–5 trigger phrases users might say. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- +``` + +- The `---` MUST be line 1. No blank lines or comments above it. Skill loaders refuse anything else. +- The two `#` lines on lines 2–3 are valid YAML comments and satisfy the CI license-header check. +- `name:` MUST equal the directory basename. +- `description:` is YAML folded scalar (`>`), so newlines in source become spaces at parse time. + +## Allowed tools — common shapes + +| Use case | Tools | +|-------------------------------------------|-----------------------------------------------------------------------| +| Read-only research/exploration | `Bash, Read, Glob, Grep, AskUserQuestion` | +| Read + URLs | `Bash, Read, Glob, Grep, AskUserQuestion, WebFetch` | +| Code generation / file edits | `Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion` | +| Orchestrator that delegates to other skills | `Bash, Read, Glob, Grep, AskUserQuestion, Skill` | +| MCP-dependent (e.g. Snowflake, Atlassian) | Above + the specific `mcp__*` tool names | + +If you list any `mcp__*` tools, add a `## Prerequisites` section to the body so users without that MCP server know what to set up. The `lfx-skills doctor` check `mcp-undocumented` enforces this. + +## Body structure (suggested) + +```markdown +# <Title-cased name> + +<One paragraph: who this helps, what it does.> + +## Step 1: <verb-noun> + +<Concrete instructions. Use `AskUserQuestion` for any user input.> + +## Step 2: <verb-noun> + +... + +## What this skill does NOT do + +- <bound 1> +- <bound 2> + +## Reference files + +- (linked references/*.md) +``` + +## Naming the directory + +- All skill directories start with `lfx-`. The exception is `lfx/` itself (the plain-language router). +- The directory name becomes the slash command (`/lfx-foo`). Pick something the user will type. + +## When to use `references/` + +Use `references/` for content that's: + +- Too long to inline (tables, JSON config, checklists, recipe libraries). +- Read selectively by the LLM at runtime (rather than always-loaded with the skill). + +Reference files don't need frontmatter. Just markdown (or JSON / YAML / etc.). Add the LFX HTML license header at the top of markdown reference files for CI. + +## Routing + +`lfx/SKILL.md` is the plain-language router. If your new skill is **user-facing** (someone might describe a problem and want it routed), add a row to the routing table in `lfx/SKILL.md`. Internal skills (only invoked by another skill, like `/lfx-backend-builder` invoked by `/lfx-coordinator`) can stay out — `lfx-skills doctor`'s `routing-uncovered` warning for them is acceptable. + +## Validation + +After creating, run: + +```bash +./bin/lfx-skills update # install the new skill at every configured target +./bin/lfx-skills doctor # validate frontmatter, license header, routing +``` + +Fix anything the doctor flags before opening a PR. From 6a6b9a220c784574feb72f89183f320f57567918 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 14:27:15 +0200 Subject: [PATCH 07/20] =?UTF-8?q?chore:=20post-review=20polish=20=E2=80=94?= =?UTF-8?q?=20drop=20MCP=20doctor=20check=20+=20tighten=20routing=20scope?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three cleanups landed after the final review pass: 1. Drop the MCP-deps doctor category entirely. Per direct user instruction: remove the mcp_deps check from doctor and any references to it. The category was a false-positive magnet (any skill that even MENTIONED an mcp__* tool name in prose got flagged), and there's no clear v1 contract for the inverse: a skill genuinely depending on an MCP server is the user's setup problem, not a doctor concern. Removed: - check_mcp_deps() function and its call in doctor_run_all - the mcp-undocumented row from /lfx-doctor's per-issue table - the mcp-undocumented entry from references/fix-recipes.md - the "doctor warns" note in /lfx-new-skill (kept the recommendation to write a Prerequisites section — that's still good practice, it's just not enforced) Doctor is now 7 categories instead of 8 (help text updated). 2. Routing-uncovered: skip management surfaces and internal builders. The check used to flag /lfx-doctor and /lfx-skills-helper for not being in /lfx's routing table — but they're management surfaces reached by name, not via plain-language routing. Same for /lfx-backend-builder and /lfx-ui-builder, which are only invoked internally by /lfx-coordinator. Added a routing_exempt list in check_routing covering all four. 3. README autocomplete sample. The skill list under "Restart and verify" omitted /lfx-doctor and /lfx-skills-helper. Both auto-install globally as of this PR, so they'll appear in users' autocomplete; added them with one-line labels so the list is accurate. Doctor on the working tree post-fix: 19 pass / 6 warn / 0 fail (remaining warnings are real signal — uncommitted clone, env not sourced in current shell, etc.). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- README.md | 2 + bin/lfx-skills | 2 +- lfx-doctor/SKILL.md | 1 - lfx-doctor/references/fix-recipes.md | 9 ----- lfx-new-skill/SKILL.md | 4 +- .../references/conventions-quickref.md | 2 +- lib/doctor.sh | 39 +++++++------------ 7 files changed, 19 insertions(+), 40 deletions(-) diff --git a/README.md b/README.md index a8a4220..6ff17c7 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,8 @@ Restart your AI coding assistant (or open a new session) in any LFX repo and typ /lfx-intercom /lfx-snowflake-access /lfx-cdp-snowflake-connectors +/lfx-doctor ← health check + auto-fix for the install +/lfx-skills-helper ← manage what's installed where (install/uninstall/update/list) ``` ### Alternative: Per-repo installation diff --git a/bin/lfx-skills b/bin/lfx-skills index be03bfc..de06f30 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -77,7 +77,7 @@ COMMANDS uninstall Remove installed symlinks (recorded in config.json) update Re-apply the manifest; with --pull, git-pull the clone first check Lightweight verification: does config match filesystem? (exit 0/1) - doctor Full diagnostics across 8 categories; --fix to repair interactively + doctor Full diagnostics across 7 categories; --fix to repair interactively list List installed or available skills info SKILL Show frontmatter + install locations for a skill config Show or modify ~/.config/lfx-skills/config.json (get/set) diff --git a/lfx-doctor/SKILL.md b/lfx-doctor/SKILL.md index 798e957..ff0324b 100644 --- a/lfx-doctor/SKILL.md +++ b/lfx-doctor/SKILL.md @@ -97,7 +97,6 @@ For records with `fixable: false` that the user wants addressed, apply judgment. | `frontmatter-no-name` / `frontmatter-name-mismatch` | Read the SKILL.md, identify the line, offer to fix it via Edit. | | `frontmatter-no-description` | Read the SKILL.md body, draft a one-paragraph description from it, offer to insert. | | `license-missing` | Insert the YAML license-header lines (see `references/fix-recipes.md` template). | -| `mcp-undocumented` | Read the SKILL.md, find the `mcp__*` tool references, offer to draft a Prerequisites section. | | `routing-uncovered` | Read `lfx/SKILL.md`, find the routing table, offer to add an entry for the missing skill. | | `routing-dangling` | Either remove the dangling entry from `lfx/SKILL.md` or hand off to `/lfx-new-skill` to scaffold the missing skill. Ask the user. | | `symlink-no-skillmd` | Hand off to `/lfx-new-skill` to scaffold the missing SKILL.md. | diff --git a/lfx-doctor/references/fix-recipes.md b/lfx-doctor/references/fix-recipes.md index 11a6eee..00ba70e 100644 --- a/lfx-doctor/references/fix-recipes.md +++ b/lfx-doctor/references/fix-recipes.md @@ -254,15 +254,6 @@ name: ... --- -## issue-id: mcp-undocumented - -**What:** A SKILL.md uses `mcp__*` tool names but has no `## Prerequisites` section. -**Why it matters:** users without the MCP server configured will get cryptic errors when the skill tries to invoke a tool that doesn't exist. -**Fix:** add a Prerequisites section listing the MCP servers the skill depends on, with a link to setup instructions. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can grep the SKILL.md for `mcp__` references and draft a Prerequisites section. - ---- - ## issue-id: platforms-none **What:** Manifest exists but records no platforms. diff --git a/lfx-new-skill/SKILL.md b/lfx-new-skill/SKILL.md index c7b4bd4..a942f46 100644 --- a/lfx-new-skill/SKILL.md +++ b/lfx-new-skill/SKILL.md @@ -69,7 +69,7 @@ Don't auto-generate this. The contributor knows their intent better than you do. > Add `Write, Edit` if the skill will modify files. > Add `Skill` if the skill will delegate to other lfx skills. > Add `WebFetch` if the skill reads external URLs. -> Add specific MCP tool names (e.g., `mcp__atlassian__getJiraIssue`) if the skill depends on MCP servers. +> Add specific MCP tool names (formatted `mcp__SERVER__TOOL`) if the skill depends on MCP servers. > > Type the comma-separated list, or just press Enter for the default." @@ -128,7 +128,7 @@ allowed-tools: <comma-separated list> ## Step 7: Prerequisites section (if MCP tools) -If the user listed any `mcp__*` tools in Step 4, add a `## Prerequisites` section to the body listing them, with a one-line note about how to set up the MCP server (or a link to the right docs). The doctor's `mcp-undocumented` check looks for this — without it, the doctor will warn. +If the user listed any MCP tools in Step 4, add a `## Prerequisites` section to the body listing them, with a one-line note about how to set up the MCP server (or a link to the right docs). Without it, users without the MCP server configured will hit cryptic errors when the skill tries to invoke a tool that doesn't exist. ## Step 8: Install locally + verify diff --git a/lfx-new-skill/references/conventions-quickref.md b/lfx-new-skill/references/conventions-quickref.md index ccb2999..ecce3bb 100644 --- a/lfx-new-skill/references/conventions-quickref.md +++ b/lfx-new-skill/references/conventions-quickref.md @@ -33,7 +33,7 @@ allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion | Orchestrator that delegates to other skills | `Bash, Read, Glob, Grep, AskUserQuestion, Skill` | | MCP-dependent (e.g. Snowflake, Atlassian) | Above + the specific `mcp__*` tool names | -If you list any `mcp__*` tools, add a `## Prerequisites` section to the body so users without that MCP server know what to set up. The `lfx-skills doctor` check `mcp-undocumented` enforces this. +If you list any `mcp__*` tools, add a `## Prerequisites` section to the body so users without that MCP server configured know what to set up. Otherwise they'll hit cryptic errors when the skill tries to invoke a tool that doesn't exist. ## Body structure (suggested) diff --git a/lib/doctor.sh b/lib/doctor.sh index 1153431..f16dfa5 100644 --- a/lib/doctor.sh +++ b/lib/doctor.sh @@ -265,11 +265,20 @@ check_routing() { done <<EOF $routed EOF - # Inverse: every installable skill mentioned in /lfx? - local skill + # Inverse: every USER-FACING skill mentioned in /lfx? + # Skip /lfx itself and skills that intentionally don't belong in the user-task + # router: internal builders only invoked by /lfx-coordinator, and management + # surfaces (doctor / skills-helper) which the user reaches by name, not via + # plain-language routing. + local routing_exempt="lfx lfx-backend-builder lfx-ui-builder lfx-doctor lfx-skills-helper" + local skill skipped while IFS= read -r skill; do [ -z "$skill" ] && continue - [ "$skill" = "lfx" ] && continue + skipped=0 + for ex in $routing_exempt; do + if [ "$skill" = "$ex" ]; then skipped=1; break; fi + done + [ "$skipped" -eq 1 ] && continue if ! grep -qE "/${skill}([^a-z0-9-]|\$)" "$lfx_md" 2>/dev/null; then _emit warn routing-uncovered routing \ "/lfx routing table does not mention /$skill" \ @@ -278,28 +287,7 @@ EOF done < <(symlinks_eligible_skills "$clone") } -# ─── Category 7: MCP deps ──────────────────────────────────────────────── -check_mcp_deps() { - local clone - clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" - local skill_md skill_name - for skill_md in "$clone"/lfx*/SKILL.md; do - [ -f "$skill_md" ] || continue - if grep -qE 'mcp__[a-zA-Z_]+' "$skill_md" 2>/dev/null; then - skill_name="$(basename "$(dirname "$skill_md")")" - if grep -qE '^##? +Prerequisites' "$skill_md" 2>/dev/null; then - _emit pass mcp-documented mcp_deps \ - "$skill_name documents MCP prerequisites" "$skill_md" no - else - _emit warn mcp-undocumented mcp_deps \ - "$skill_name uses MCP tools but has no Prerequisites section" \ - "$skill_md" no - fi - fi - done -} - -# ─── Category 8: License headers ───────────────────────────────────────── +# ─── Category 7: License headers ───────────────────────────────────────── check_license_headers() { local clone clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" @@ -325,7 +313,6 @@ doctor_run_all() { check_platforms check_frontmatter check_routing - check_mcp_deps check_license_headers } From 12a15a57c8640dbe60607e34f401f459ef981264 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 14:41:46 +0200 Subject: [PATCH 08/20] feat(cli): replace number-prompt multiselects with arrow-key checkbox TUI MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The numbered comma-separated multiselect was bad UX: the user had to read a numbered list, decide which numbers to type, and rely on a free-text prompt that could mis-parse. Replaced with a real checkbox menu navigated by arrow keys. New helper in lib/ui.sh: ui_checkbox_select PROMPT DEFAULT_SPEC MIN_REQUIRED OPT1 OPT2 ... Renders an interactive checkbox menu on stderr; emits selected options on stdout (one per line). Navigation: ↑/↓ (or k/j) to move, space to toggle, enter to confirm, q or Esc to cancel. The cursor wraps top-to- bottom. Confirm is refused if fewer than MIN_REQUIRED items are selected (with an inline warning). Implementation notes: - Bash 3.2 compatible (no associative arrays, no mapfile, no process-substitution-only idioms in the inner loop). - Saves stty state up-front and restores via trap on EXIT/INT/TERM so a Ctrl-C never leaves the user's terminal in raw mode with the cursor hidden. - Reads keys one byte at a time via `read -rsn1`; arrow keys are detected as ESC '[' 'A'/'B' with a 50ms timeout to distinguish a plain Esc keypress from an arrow-key escape sequence. - Falls back to ui_multiselect (the legacy numbered prompt) when stdin or stderr is not a TTY (CI, piped input). - Honours LFX_SKILLS_YES=1 by emitting the resolved defaults immediately, no rendering. Wired into all four multiselect sites in cmd_install: Platform: 2 options ("Claude Code", "agents.md (Codex / Gemini CLI / OpenCode)"), prompt "Which AI platform(s)? (select one or both)". The old 3-option "claude / agents / both" was replaced — "both" was just selecting both checkboxes. Scope: 2 options ("global", "per-repo"), prompt "Install scope? (select one or both)". Same simplification. Claude config dirs: previously defaulted to "1" or asked for comma-separated numbers when more than one was probed; now shows a checkbox per dir. Repo picker: previously defaulted to "all" with comma-separated numbers; now a checkbox per lf* git repo found under LFX_DEV_ROOT. Internal refactor: cmd_install no longer threads $platform/$scope as single-value enums. Both can now be comma-joined ("claude,agents", "global,repo"). Compute boolean include_claude / include_agents / include_global / include_repo flags once after the picker, use those throughout the apply phase. The `--platform=both` and `--scope=both` flags still work as syntactic sugar for the comma-joined form. Manifest schema: agents.config_dir (singular) → agents.config_dirs (array), mirroring claude. The doctor's check_platforms iterates the array. This is a schema change from the v1-shape spec but lands in v1 since nothing has been released yet. Cancel handling: each picker returns an empty list on Esc/q. The caller checks `${#_picked[@]}` post-loop — process substitution exit codes don't propagate, so a `||` after `< <(picker)` would not fire. Caught during testing. End-to-end verified non-interactively: - --platform=both --scope=global with claude+agents multi-config (4 dirs total) creates 68 symlinks, manifest has parallel config_dirs arrays - --platform=both legacy sugar still works - --agents-config=a,b multi-value parses correctly - --yes skips all pickers and uses defaults The TUI itself is interactive-only; nothing in this change affects LFX_SKILLS_YES=1 or the piped-input fallback paths. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- bin/lfx-skills | 122 +++++++++++++++++++++++++++----------- lib/config.sh | 9 ++- lib/doctor.sh | 24 +++++--- lib/ui.sh | 156 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 266 insertions(+), 45 deletions(-) diff --git a/bin/lfx-skills b/bin/lfx-skills index de06f30..3268371 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -112,12 +112,12 @@ INTERACTIVE NON-INTERACTIVE FLAGS --yes Skip all confirmations (use defaults / supplied flags) - --platform=claude|agents|both - --scope=global|repo|both + --platform=claude|agents|claude,agents|both 'both' is sugar for 'claude,agents' + --scope=global|repo|global,repo|both 'both' is sugar for 'global,repo' --lfx-dev-root=PATH --claude-config=PATH[,PATH...] Multi-config: pass several Claude config dirs - --agents-config=PATH Single agents config dir (default: ~/.agents) - --repos=PATH[,PATH...] Required if --scope=repo|both + --agents-config=PATH[,PATH...] Multi-config: pass several agents config dirs + --repos=PATH[,PATH...] Required if --scope contains 'repo' --dry-run Show what would happen, then exit EXAMPLE @@ -450,17 +450,60 @@ cmd_install() { ui_dim " Agents config dirs: ${agents_dirs_probed[*]:-(none found)}" ui_dim " Dev root candidates: ${dev_root_probed[*]:-(none found)}" - # Step 2: PLATFORM + # Step 2: PLATFORM (multi-select: claude, agents, or both) if [ -z "$platform" ]; then - platform="$(ui_select "Which AI platform(s)?" "claude" "agents" "both")" + local -a _picked=() + local _line + while IFS= read -r _line; do _picked+=("$_line"); done < <( + ui_checkbox_select "Which AI platform(s)? (select one or both)" "1" 1 \ + "Claude Code" "agents.md (Codex / Gemini CLI / OpenCode)" + ) + if [ "${#_picked[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + local _p _parts="" + for _p in "${_picked[@]}"; do + case "$_p" in + "Claude Code") _parts="${_parts:+$_parts,}claude" ;; + "agents.md"*) _parts="${_parts:+$_parts,}agents" ;; + esac + done + platform="$_parts" fi - case "$platform" in claude|agents|both) ;; *) ui_die "--platform must be claude|agents|both" ;; esac + # Accept "both" as legacy sugar; normalise to comma-joined. + case "$platform" in + both) platform="claude,agents" ;; + claude|agents|claude,agents|agents,claude) ;; + *) ui_die "--platform must be a combination of claude,agents (or 'both')" ;; + esac + local include_claude=0 include_agents=0 + case ",$platform," in *,claude,*) include_claude=1 ;; esac + case ",$platform," in *,agents,*) include_agents=1 ;; esac - # Step 3: SCOPE + # Step 3: SCOPE (multi-select: global, per-repo, or both) if [ -z "$scope" ]; then - scope="$(ui_select "Install scope?" "global" "repo" "both")" + local -a _picked=() + local _line + while IFS= read -r _line; do _picked+=("$_line"); done < <( + ui_checkbox_select "Install scope? (select one or both)" "1" 1 \ + "global (available in every AI session)" "per-repo (only in selected repos)" + ) + if [ "${#_picked[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + local _s _parts="" + for _s in "${_picked[@]}"; do + case "$_s" in + global*) _parts="${_parts:+$_parts,}global" ;; + per-repo*) _parts="${_parts:+$_parts,}repo" ;; + esac + done + scope="$_parts" fi - case "$scope" in global|repo|both) ;; *) ui_die "--scope must be global|repo|both" ;; esac + case "$scope" in + both) scope="global,repo" ;; + global|repo|global,repo|repo,global) ;; + *) ui_die "--scope must be a combination of global,repo (or 'both')" ;; + esac + local include_global=0 include_repo=0 + case ",$scope," in *,global,*) include_global=1 ;; esac + case ",$scope," in *,repo,*) include_repo=1 ;; esac # Step 4: LFX DEV ROOT if [ -z "$lfx_dev_root" ]; then @@ -481,15 +524,15 @@ cmd_install() { repo_count="$(probe_count_repos_in "$lfx_dev_root")" ui_dim " $lfx_dev_root contains $repo_count lf* git repo(s)." - # Step 5a: CONFIG DIR PICKER (only if global) + # Step 5a: CONFIG DIR PICKER (only if global scope) # Empty defaults so `${arr[@]}` is safe under bash 3.2 + `set -u`. local -a chosen_claude_dirs=() chosen_agents_dirs=() - if [ "$scope" = "global" ] || [ "$scope" = "both" ]; then - if [ "$platform" = "claude" ] || [ "$platform" = "both" ]; then + if [ "$include_global" -eq 1 ]; then + if [ "$include_claude" -eq 1 ]; then if [ -n "$claude_config" ]; then - # Convert comma-separated to newline-separated and read line-by-line so - # we don't change IFS (which would leak into nested function calls and - # break, e.g. SYMLINKS_CLONE_ONLY iteration in symlinks_eligible_skills). + # Convert comma-separated to newline-separated; reading line-by-line + # avoids changing IFS (which dynamically scopes into nested calls and + # would break SYMLINKS_CLONE_ONLY iteration in symlinks_eligible_skills). local d while IFS= read -r d; do chosen_claude_dirs+=("$d"); done < <(printf '%s\n' "$claude_config" | tr ',' '\n') else @@ -500,25 +543,38 @@ cmd_install() { else local picked while IFS= read -r picked; do chosen_claude_dirs+=("$picked"); done < <( - ui_multiselect "Which Claude config dir(s) to install into?" "1" "${claude_dirs_probed[@]}" + ui_checkbox_select "Which Claude config dir(s)? (select one or more)" "1" 1 \ + "${claude_dirs_probed[@]}" ) + if [ "${#chosen_claude_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi fi fi fi - if [ "$platform" = "agents" ] || [ "$platform" = "both" ]; then + if [ "$include_agents" -eq 1 ]; then if [ -n "$agents_config" ]; then - chosen_agents_dirs=("$agents_config") + local d + while IFS= read -r d; do chosen_agents_dirs+=("$d"); done < <(printf '%s\n' "$agents_config" | tr ',' '\n') elif [ "${#agents_dirs_probed[@]}" -gt 0 ]; then - chosen_agents_dirs=("${agents_dirs_probed[0]}") + # If multiple, let the user pick. Single = silent default. + if [ "${#agents_dirs_probed[@]}" -eq 1 ]; then + chosen_agents_dirs=("${agents_dirs_probed[0]}") + else + local picked + while IFS= read -r picked; do chosen_agents_dirs+=("$picked"); done < <( + ui_checkbox_select "Which agents config dir(s)? (select one or more)" "1" 1 \ + "${agents_dirs_probed[@]}" + ) + if [ "${#chosen_agents_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + fi else chosen_agents_dirs=("$(platform_default_global_dir agents)") fi fi fi - # Step 5b: REPO PICKER (only if per-repo) + # Step 5b: REPO PICKER (only if per-repo scope) local -a chosen_repos=() - if [ "$scope" = "repo" ] || [ "$scope" = "both" ]; then + if [ "$include_repo" -eq 1 ]; then if [ -n "$repos" ]; then local r while IFS= read -r r; do chosen_repos+=("$r"); done < <(printf '%s\n' "$repos" | tr ',' '\n') @@ -527,14 +583,14 @@ cmd_install() { while IFS= read -r r; do candidate_repos+=("$r"); done < <(probe_repos_in "$lfx_dev_root") if [ "${#candidate_repos[@]}" -eq 0 ]; then ui_warn "No lf* git repos found under $lfx_dev_root." - if [ "$scope" = "repo" ]; then - ui_die "Per-repo install requires at least one repo. Aborting." - fi + ui_die "Per-repo install requires at least one repo. Aborting." else local picked while IFS= read -r picked; do chosen_repos+=("$picked"); done < <( - ui_multiselect "Which repos to install into?" "all" "${candidate_repos[@]}" + ui_checkbox_select "Which repos to install into? (select one or more)" "all" 1 \ + "${candidate_repos[@]}" ) + if [ "${#chosen_repos[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi fi fi fi @@ -583,7 +639,7 @@ cmd_install() { config_set_platform_claude "${chosen_claude_dirs[@]}" fi if [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then - config_set_platform_agents "${chosen_agents_dirs[0]}" + config_set_platform_agents "${chosen_agents_dirs[@]}" fi local -a rcs=() while IFS= read -r r; do rcs+=("$r"); done < <(probe_shell_rcs) @@ -593,27 +649,27 @@ cmd_install() { ui_step "Creating symlinks…" local total_installed=0 total_updated=0 total_skipped=0 - if [ "$scope" = "global" ] || [ "$scope" = "both" ]; then - if { [ "$platform" = "claude" ] || [ "$platform" = "both" ]; } && [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then + if [ "$include_global" -eq 1 ]; then + if [ "$include_claude" -eq 1 ] && [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then for d in "${chosen_claude_dirs[@]}"; do local s; s="$(symlinks_install_all "$CLONE_ROOT" claude global "$d")" _accumulate_install_summary "$s" done fi - if { [ "$platform" = "agents" ] || [ "$platform" = "both" ]; } && [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then + if [ "$include_agents" -eq 1 ] && [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then for d in "${chosen_agents_dirs[@]}"; do local s; s="$(symlinks_install_all "$CLONE_ROOT" agents global "$d")" _accumulate_install_summary "$s" done fi fi - if { [ "$scope" = "repo" ] || [ "$scope" = "both" ]; } && [ "${#chosen_repos[@]}" -gt 0 ]; then + if [ "$include_repo" -eq 1 ] && [ "${#chosen_repos[@]}" -gt 0 ]; then for r in "${chosen_repos[@]}"; do - if [ "$platform" = "claude" ] || [ "$platform" = "both" ]; then + if [ "$include_claude" -eq 1 ]; then local s; s="$(symlinks_install_all "$CLONE_ROOT" claude repo "$r")" _accumulate_install_summary "$s" fi - if [ "$platform" = "agents" ] || [ "$platform" = "both" ]; then + if [ "$include_agents" -eq 1 ]; then local s; s="$(symlinks_install_all "$CLONE_ROOT" agents repo "$r")" _accumulate_install_summary "$s" fi diff --git a/lib/config.sh b/lib/config.sh index 5b51473..3c8e9b8 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -187,10 +187,13 @@ config_set_platform_claude() { config_set_json "platforms.claude" "{\"config_dirs\": $dirs_json}" } -# config_set_platform_agents DIR → record the agents config dir. +# config_set_platform_agents DIR... → record the list of agents config dirs. +# Mirror of config_set_platform_claude — both use a `config_dirs` array so the +# manifest shape is symmetric across platforms. config_set_platform_agents() { - local dir="$1" - config_set_json "platforms.agents" "{\"config_dir\": \"$dir\"}" + local dirs_json + dirs_json="$(printf '%s\n' "$@" | jq -R . | jq -sc .)" + config_set_json "platforms.agents" "{\"config_dirs\": $dirs_json}" } # config_set_shell_rcs RC... → record the list of detected shell rcs. diff --git a/lib/doctor.sh b/lib/doctor.sh index f16dfa5..beaf55a 100644 --- a/lib/doctor.sh +++ b/lib/doctor.sh @@ -192,15 +192,21 @@ $claude_dirs EOF fi - local agents_dir - agents_dir="$(echo "$platforms_json" | jq -r '.agents.config_dir // empty' 2>/dev/null)" - if [ -n "$agents_dir" ]; then - if [ -d "$agents_dir" ]; then - _emit pass platform-dir-ok platforms "Agents config dir exists" "$agents_dir" no - else - _emit fail platform-dir-missing platforms \ - "Agents config dir missing" "$agents_dir" no - fi + local agents_dirs + agents_dirs="$(echo "$platforms_json" | jq -r '.agents.config_dirs[]?' 2>/dev/null)" + if [ -n "$agents_dirs" ]; then + local d + while IFS= read -r d; do + [ -z "$d" ] && continue + if [ -d "$d" ]; then + _emit pass platform-dir-ok platforms "Agents config dir exists" "$d" no + else + _emit fail platform-dir-missing platforms \ + "Agents config dir missing" "$d" no + fi + done <<EOF +$agents_dirs +EOF fi } diff --git a/lib/ui.sh b/lib/ui.sh index 38ca4aa..0ab7da3 100644 --- a/lib/ui.sh +++ b/lib/ui.sh @@ -209,6 +209,162 @@ ui_multiselect() { done } +# ui_checkbox_select PROMPT DEFAULT_SPEC MIN_REQUIRED OPT1 OPT2 ... +# PROMPT text shown above the menu +# DEFAULT_SPEC same shape as ui_multiselect: "1,2", "all", "none"/"" +# MIN_REQUIRED minimum number of items that must be selected before Enter +# is accepted (use 1 for "select one or both") +# OPT* option strings (also the values emitted to stdout on confirm) +# +# Renders an interactive checkbox menu on stderr; emits each selected option +# (one per line) on stdout. Navigation: +# ↑ / ↓ or k / j move the cursor (wraps top/bottom) +# space toggle the current item +# enter confirm (refused if fewer than MIN_REQUIRED selected) +# q or Esc cancel (returns 1, no output) +# +# When LFX_SKILLS_YES=1, emits the resolved defaults without rendering. +# When stdin or stderr is not a TTY, falls back to the number-prompt +# implementation in ui_multiselect (so CI / piped input keeps working). +ui_checkbox_select() { + local prompt="$1" default_spec="$2" min_required="$3" + shift 3 + local n=$# + [ "$n" -eq 0 ] && return 0 + + local i opt + local -a opts=() + for opt in "$@"; do opts[${#opts[@]}]="$opt"; done + + # Compute initial selection state from the default spec. + local -a selected=() + for ((i=0; i<n; i++)); do selected[i]=0; done + case "$default_spec" in + all) for ((i=0; i<n; i++)); do selected[i]=1; done ;; + none|"") ;; + *) + local _idx + for _idx in $(printf '%s' "$default_spec" | tr ',' ' '); do + case "$_idx" in + ''|*[!0-9]*) continue ;; + *) + if [ "$_idx" -ge 1 ] && [ "$_idx" -le "$n" ]; then + selected[_idx-1]=1 + fi + ;; + esac + done + ;; + esac + + # Auto-pick path: respect LFX_SKILLS_YES. + if [ "${LFX_SKILLS_YES:-0}" = "1" ]; then + for ((i=0; i<n; i++)); do + [ "${selected[i]}" = "1" ] && printf '%s\n' "${opts[i]}" + done + return 0 + fi + + # Non-TTY fallback: use the existing numbered ui_multiselect. + if ! [ -t 0 ] || ! [ -t 2 ]; then + ui_multiselect "$prompt" "$default_spec" "${opts[@]}" + return $? + fi + + # Save terminal state and ensure restore on any exit. + local _stty_saved + _stty_saved="$(stty -g 2>/dev/null || true)" + trap 'stty '"'$_stty_saved'"' 2>/dev/null; printf "\033[?25h" >&2' EXIT INT TERM + stty -icanon -echo 2>/dev/null + printf '\033[?25l' >&2 # hide cursor + + # Header (printed once). + printf '%s\n' "$prompt" >&2 + printf ' %s↑/↓ move • space toggle • enter confirm • q cancel%s\n' \ + "$_UI_DIM" "$_UI_RESET" >&2 + + # Render the option block (callable repeatedly). + _ui_cb_render() { + local _i marker arrow + for ((_i=0; _i<n; _i++)); do + marker=' ' + arrow=' ' + [ "${selected[_i]}" = "1" ] && marker='x' + if [ "$_i" -eq "$cursor" ]; then + arrow='›' + printf '\033[K %s%s%s [%s] %s\n' "$_UI_BOLD" "$arrow" "$_UI_RESET" "$marker" "${opts[_i]}" >&2 + else + printf '\033[K %s [%s] %s\n' "$arrow" "$marker" "${opts[_i]}" >&2 + fi + done + } + + local cursor=0 + _ui_cb_render + + # Event loop. + local key rest cancelled=0 count + while true; do + IFS= read -rsn1 key || break + case "$key" in + $'\e') + # Could be a 3-byte arrow-key escape (ESC '[' 'A'/'B'/'C'/'D') + # or a bare Esc (timeout fires, $rest stays empty). + IFS= read -rsn2 -t 0.05 rest || rest="" + case "$rest" in + '[A') cursor=$(( (cursor - 1 + n) % n )) ;; # Up + '[B') cursor=$(( (cursor + 1) % n )) ;; # Down + '') cancelled=1; break ;; # bare Esc + *) ;; # ignore other seqs + esac + ;; + 'k') cursor=$(( (cursor - 1 + n) % n )) ;; # vim up + 'j') cursor=$(( (cursor + 1) % n )) ;; # vim down + ' ') # toggle + if [ "${selected[cursor]}" = "1" ]; then + selected[cursor]=0 + else + selected[cursor]=1 + fi + ;; + '') # Enter + count=0 + for ((i=0; i<n; i++)); do + [ "${selected[i]}" = "1" ] && count=$((count + 1)) + done + if [ "$count" -ge "$min_required" ]; then + break + fi + # Below threshold: flash a warning and keep waiting. + printf '\033[K %s(select at least %d before pressing enter)%s\r' \ + "$_UI_YELLOW" "$min_required" "$_UI_RESET" >&2 + # The next render below will overwrite this line via the up-N-lines move. + ;; + q|Q) cancelled=1; break ;; + *) ;; + esac + # Move cursor up to start of options block, then redraw. + printf '\033[%dA' "$n" >&2 + _ui_cb_render + done + + # Restore terminal state. + stty "$_stty_saved" 2>/dev/null + printf '\033[?25h' >&2 + trap - EXIT INT TERM + + if [ "$cancelled" -eq 1 ]; then + printf '\n' >&2 + return 1 + fi + + # Emit selected options on stdout. + for ((i=0; i<n; i++)); do + [ "${selected[i]}" = "1" ] && printf '%s\n' "${opts[i]}" + done + return 0 +} + # ui_section "Title" → bold heading + underline. ui_section() { local title="$1" From d4abe94025862e6360a784d19dde0095f2f463ed Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 14:44:49 +0200 Subject: [PATCH 09/20] fix(cli): use integer read timeout for arrow-key detection (bash 3.2 compat) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bash 3.2 (stock macOS) rejects fractional `read -t` values with "invalid timeout specification". Arrow-key navigation in the new checkbox TUI was bombing out at the first keystroke as a result. Fixed by using `-t 1` instead of `-t 0.05`. Arrow keys still respond instantly because the terminal delivers the full escape sequence in one burst — the read returns as soon as the next two bytes are in the buffer (microseconds, not the full second). Only a bare Esc keypress now has a one-second debounce before registering as cancel; users have `q` as an instant alternative. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- lib/ui.sh | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/lib/ui.sh b/lib/ui.sh index 0ab7da3..98b80e8 100644 --- a/lib/ui.sh +++ b/lib/ui.sh @@ -308,9 +308,15 @@ ui_checkbox_select() { IFS= read -rsn1 key || break case "$key" in $'\e') - # Could be a 3-byte arrow-key escape (ESC '[' 'A'/'B'/'C'/'D') - # or a bare Esc (timeout fires, $rest stays empty). - IFS= read -rsn2 -t 0.05 rest || rest="" + # Could be a 3-byte arrow-key escape (ESC '[' 'A'/'B'/'C'/'D') or a + # bare Esc keypress. When you press an arrow, the terminal sends the + # whole sequence in one burst — the next two bytes are already in the + # input buffer, so the read returns instantly. A bare Esc has nothing + # behind it, so we have to wait for the timeout to confirm. + # Bash 3.2 (stock macOS) only supports integer `read -t` values, so + # we use 1 second. Arrow keys still respond instantly; only a bare Esc + # has a one-second debounce before it registers as cancel. + IFS= read -rsn2 -t 1 rest || rest="" case "$rest" in '[A') cursor=$(( (cursor - 1 + n) % n )) ;; # Up '[B') cursor=$(( (cursor + 1) % n )) ;; # Down From e90b152af369247d352f79b8f6144b6f8921197b Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 14:53:22 +0200 Subject: [PATCH 10/20] =?UTF-8?q?fix(cli):=20redesign=20checkbox=20TUI=20?= =?UTF-8?q?=E2=80=94=20nothing=20pre-selected,=20[continue]=20item?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three real issues from interactive testing: 1. Items came pre-selected, which was confusing. The user expected to start from a blank slate and explicitly opt in. 2. Enter confirmed the picker outright — the user expected enter on a checkbox to TOGGLE that item. There was no separate way to explicitly proceed. 3. As a knock-on of #2: pressing enter on the per-repo line confirmed the picker with whatever was pre-selected (i.e., global), so the wizard then asked for global config dirs even when the user had meant to switch to per-repo. Redesign: - Interactive path starts with EVERY checkbox unchecked. The user must explicitly toggle what they want. - Enter (and space) on a checkbox toggles that item. Neither confirms. - A new [continue] line appears at the bottom of every picker. Enter on it proceeds (refused with an inline warning if fewer than MIN_REQUIRED items are checked). - Added 'a' / 'n' shortcuts for select-all / clear-all. Helpful when the picker has 8+ items (e.g. 8 lf* repos under LFX_DEV_ROOT). - Added blank lines between wizard steps so the flow has visual breathing room. YES_DEFAULT_SPEC parameter (renamed from DEFAULT_SPEC to make intent explicit) now ONLY governs the LFX_SKILLS_YES=1 auto-pick path. Interactive callers ignore it. Non-interactive install / CI behaviour is unchanged. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- bin/lfx-skills | 6 ++ lib/ui.sh | 179 ++++++++++++++++++++++++++++--------------------- 2 files changed, 107 insertions(+), 78 deletions(-) diff --git a/bin/lfx-skills b/bin/lfx-skills index 3268371..e4fee1a 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -467,6 +467,7 @@ cmd_install() { esac done platform="$_parts" + ui_blank fi # Accept "both" as legacy sugar; normalise to comma-joined. case "$platform" in @@ -495,6 +496,7 @@ cmd_install() { esac done scope="$_parts" + ui_blank fi case "$scope" in both) scope="global,repo" ;; @@ -523,6 +525,7 @@ cmd_install() { local repo_count repo_count="$(probe_count_repos_in "$lfx_dev_root")" ui_dim " $lfx_dev_root contains $repo_count lf* git repo(s)." + ui_blank # Step 5a: CONFIG DIR PICKER (only if global scope) # Empty defaults so `${arr[@]}` is safe under bash 3.2 + `set -u`. @@ -547,6 +550,7 @@ cmd_install() { "${claude_dirs_probed[@]}" ) if [ "${#chosen_claude_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + ui_blank fi fi fi @@ -565,6 +569,7 @@ cmd_install() { "${agents_dirs_probed[@]}" ) if [ "${#chosen_agents_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + ui_blank fi else chosen_agents_dirs=("$(platform_default_global_dir agents)") @@ -591,6 +596,7 @@ cmd_install() { "${candidate_repos[@]}" ) if [ "${#chosen_repos[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + ui_blank fi fi fi diff --git a/lib/ui.sh b/lib/ui.sh index 98b80e8..34421dd 100644 --- a/lib/ui.sh +++ b/lib/ui.sh @@ -209,25 +209,33 @@ ui_multiselect() { done } -# ui_checkbox_select PROMPT DEFAULT_SPEC MIN_REQUIRED OPT1 OPT2 ... -# PROMPT text shown above the menu -# DEFAULT_SPEC same shape as ui_multiselect: "1,2", "all", "none"/"" -# MIN_REQUIRED minimum number of items that must be selected before Enter -# is accepted (use 1 for "select one or both") -# OPT* option strings (also the values emitted to stdout on confirm) +# ui_checkbox_select PROMPT YES_DEFAULT_SPEC MIN_REQUIRED OPT1 OPT2 ... +# PROMPT text shown above the menu +# YES_DEFAULT_SPEC selection used ONLY when LFX_SKILLS_YES=1 +# (interactive starts with nothing selected). +# Same format as ui_multiselect: "1,2", "all", "none"/"" +# MIN_REQUIRED minimum number of options that must be checked before +# the [continue] action is accepted (use 1 to require +# at least one selection) +# OPT* option strings (also the values emitted on confirm) # -# Renders an interactive checkbox menu on stderr; emits each selected option -# (one per line) on stdout. Navigation: -# ↑ / ↓ or k / j move the cursor (wraps top/bottom) -# space toggle the current item -# enter confirm (refused if fewer than MIN_REQUIRED selected) -# q or Esc cancel (returns 1, no output) +# Interactive UX: +# ↑ / ↓ or k / j move the cursor (wraps top↔continue) +# enter or space toggle the current checkbox +# enter on [continue] proceed (refused if fewer than MIN_REQUIRED checked) +# a select all +# n select none +# q or Esc cancel (returns 1, no output on stdout) # -# When LFX_SKILLS_YES=1, emits the resolved defaults without rendering. +# Nothing is pre-selected in the interactive path. The user must explicitly +# toggle each checkbox they want and then move to [continue] to proceed. +# +# When LFX_SKILLS_YES=1, emits whatever YES_DEFAULT_SPEC resolves to and +# returns immediately — no rendering, no prompt. # When stdin or stderr is not a TTY, falls back to the number-prompt # implementation in ui_multiselect (so CI / piped input keeps working). ui_checkbox_select() { - local prompt="$1" default_spec="$2" min_required="$3" + local prompt="$1" yes_default_spec="$2" min_required="$3" shift 3 local n=$# [ "$n" -eq 0 ] && return 0 @@ -236,41 +244,42 @@ ui_checkbox_select() { local -a opts=() for opt in "$@"; do opts[${#opts[@]}]="$opt"; done - # Compute initial selection state from the default spec. - local -a selected=() - for ((i=0; i<n; i++)); do selected[i]=0; done - case "$default_spec" in - all) for ((i=0; i<n; i++)); do selected[i]=1; done ;; - none|"") ;; - *) - local _idx - for _idx in $(printf '%s' "$default_spec" | tr ',' ' '); do - case "$_idx" in - ''|*[!0-9]*) continue ;; - *) - if [ "$_idx" -ge 1 ] && [ "$_idx" -le "$n" ]; then - selected[_idx-1]=1 - fi - ;; - esac - done - ;; - esac - - # Auto-pick path: respect LFX_SKILLS_YES. + # Auto-pick path: resolve YES_DEFAULT_SPEC, emit immediately. if [ "${LFX_SKILLS_YES:-0}" = "1" ]; then - for ((i=0; i<n; i++)); do - [ "${selected[i]}" = "1" ] && printf '%s\n' "${opts[i]}" - done + case "$yes_default_spec" in + all) for ((i=0; i<n; i++)); do printf '%s\n' "${opts[i]}"; done ;; + none|"") ;; + *) + local _idx + for _idx in $(printf '%s' "$yes_default_spec" | tr ',' ' '); do + case "$_idx" in + ''|*[!0-9]*) continue ;; + *) + if [ "$_idx" -ge 1 ] && [ "$_idx" -le "$n" ]; then + printf '%s\n' "${opts[_idx-1]}" + fi + ;; + esac + done + ;; + esac return 0 fi # Non-TTY fallback: use the existing numbered ui_multiselect. if ! [ -t 0 ] || ! [ -t 2 ]; then - ui_multiselect "$prompt" "$default_spec" "${opts[@]}" + ui_multiselect "$prompt" "$yes_default_spec" "${opts[@]}" return $? fi + # Interactive: nothing pre-selected. User toggles, then moves to [continue]. + local -a selected=() + for ((i=0; i<n; i++)); do selected[i]=0; done + + # Total visible items = n options + 1 [continue] item at index n. + local total=$((n + 1)) + local cursor=0 + # Save terminal state and ensure restore on any exit. local _stty_saved _stty_saved="$(stty -g 2>/dev/null || true)" @@ -280,12 +289,15 @@ ui_checkbox_select() { # Header (printed once). printf '%s\n' "$prompt" >&2 - printf ' %s↑/↓ move • space toggle • enter confirm • q cancel%s\n' \ + printf ' %s↑/↓ move • enter or space toggle • a all • n none • [continue] to proceed • q cancel%s\n' \ "$_UI_DIM" "$_UI_RESET" >&2 - # Render the option block (callable repeatedly). + # Render the option block + the [continue] action line. Called repeatedly. _ui_cb_render() { - local _i marker arrow + local _i marker arrow count=0 + for ((_i=0; _i<n; _i++)); do + [ "${selected[_i]}" = "1" ] && count=$((count + 1)) + done for ((_i=0; _i<n; _i++)); do marker=' ' arrow=' ' @@ -297,9 +309,16 @@ ui_checkbox_select() { printf '\033[K %s [%s] %s\n' "$arrow" "$marker" "${opts[_i]}" >&2 fi done + # Continue line at index n. + if [ "$cursor" -eq "$n" ]; then + printf '\033[K %s›%s %s→ continue%s %s(%d selected)%s\n' \ + "$_UI_BOLD" "$_UI_RESET" "$_UI_BOLD" "$_UI_RESET" "$_UI_DIM" "$count" "$_UI_RESET" >&2 + else + printf '\033[K %s→ continue%s %s(%d selected)%s\n' \ + "$_UI_DIM" "$_UI_RESET" "$_UI_DIM" "$count" "$_UI_RESET" >&2 + fi } - local cursor=0 _ui_cb_render # Event loop. @@ -308,49 +327,53 @@ ui_checkbox_select() { IFS= read -rsn1 key || break case "$key" in $'\e') - # Could be a 3-byte arrow-key escape (ESC '[' 'A'/'B'/'C'/'D') or a - # bare Esc keypress. When you press an arrow, the terminal sends the - # whole sequence in one burst — the next two bytes are already in the - # input buffer, so the read returns instantly. A bare Esc has nothing - # behind it, so we have to wait for the timeout to confirm. - # Bash 3.2 (stock macOS) only supports integer `read -t` values, so - # we use 1 second. Arrow keys still respond instantly; only a bare Esc - # has a one-second debounce before it registers as cancel. + # Arrow keys arrive as ESC '[' 'A'/'B'/'C'/'D' in one burst — the + # next two bytes are already in the buffer, so the read returns + # instantly. A bare Esc waits for the timeout. Bash 3.2 only supports + # integer `read -t`, so 1 second is the floor for bare Esc. IFS= read -rsn2 -t 1 rest || rest="" case "$rest" in - '[A') cursor=$(( (cursor - 1 + n) % n )) ;; # Up - '[B') cursor=$(( (cursor + 1) % n )) ;; # Down - '') cancelled=1; break ;; # bare Esc - *) ;; # ignore other seqs + '[A') cursor=$(( (cursor - 1 + total) % total )) ;; # Up + '[B') cursor=$(( (cursor + 1) % total )) ;; # Down + '') cancelled=1; break ;; # bare Esc + *) ;; # ignore other esac ;; - 'k') cursor=$(( (cursor - 1 + n) % n )) ;; # vim up - 'j') cursor=$(( (cursor + 1) % n )) ;; # vim down - ' ') # toggle - if [ "${selected[cursor]}" = "1" ]; then - selected[cursor]=0 + 'k') cursor=$(( (cursor - 1 + total) % total )) ;; # vim up + 'j') cursor=$(( (cursor + 1) % total )) ;; # vim down + ' '|'') + # Enter or space: on a checkbox toggles it; on [continue] proceeds. + if [ "$cursor" -lt "$n" ]; then + if [ "${selected[cursor]}" = "1" ]; then + selected[cursor]=0 + else + selected[cursor]=1 + fi else - selected[cursor]=1 - fi - ;; - '') # Enter - count=0 - for ((i=0; i<n; i++)); do - [ "${selected[i]}" = "1" ] && count=$((count + 1)) - done - if [ "$count" -ge "$min_required" ]; then - break + # Cursor on [continue] + count=0 + for ((i=0; i<n; i++)); do + [ "${selected[i]}" = "1" ] && count=$((count + 1)) + done + if [ "$count" -ge "$min_required" ]; then + break + fi + # Below threshold — render an inline warning beneath the menu and + # continue waiting. The next redraw overwrites it. + printf '\033[K %s(select at least %d before continuing)%s' \ + "$_UI_YELLOW" "$min_required" "$_UI_RESET" >&2 + # Sleep briefly so the user sees the warning before redraw clears it. + sleep 1 fi - # Below threshold: flash a warning and keep waiting. - printf '\033[K %s(select at least %d before pressing enter)%s\r' \ - "$_UI_YELLOW" "$min_required" "$_UI_RESET" >&2 - # The next render below will overwrite this line via the up-N-lines move. ;; - q|Q) cancelled=1; break ;; + 'a'|'A') for ((i=0; i<n; i++)); do selected[i]=1; done ;; + 'n'|'N') for ((i=0; i<n; i++)); do selected[i]=0; done ;; + 'q'|'Q') cancelled=1; break ;; *) ;; esac - # Move cursor up to start of options block, then redraw. - printf '\033[%dA' "$n" >&2 + # Move cursor up to start of options block (n options + continue line), + # then redraw the whole block. + printf '\033[%dA' "$total" >&2 _ui_cb_render done From 3abd7e9437003fd987357373773becc56c56937e Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 15:15:34 +0200 Subject: [PATCH 11/20] fix(cli): make install output disambiguate per-target batches MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The install summary previously showed each per-skill line as 'installed <name> → <source-path>' with no indication of which target directory the symlink was being created in. When the user picked multiple targets (e.g. claude + agents on the same repo), the same 17 skill names appeared twice in a row and looked like the loop had double-run. Three changes to make the structure obvious: 1. Per-skill output (lib/symlinks.sh) drops the source path. The source is always the canonical clone — repeating it on every line adds noise. Lines now read 'installed lfx-coordinator'. 2. Apply phase (cmd_install) prints a header before each symlinks_install_all call: ==> Claude · per-repo · /path/to/repo/.claude/skills/ installed lfx-coordinator ... so the per-skill lines have unambiguous context. 3. Preview shows the real symlink count, not just the count of unique skills. With 17 unique skills, 1 repo, and platform=both, the preview now reads: Skills: 17 unique Targets: 2 Symlinks: 34 total (skills × targets) instead of the misleading 'Skills to install: 17'. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- bin/lfx-skills | 33 +++++++++++++++++++++++++++++---- lib/symlinks.sh | 11 ++++++----- 2 files changed, 35 insertions(+), 9 deletions(-) diff --git a/bin/lfx-skills b/bin/lfx-skills index e4fee1a..d0586af 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -606,9 +606,27 @@ cmd_install() { ui_section "Plan" local skill_count skill_count="$(symlinks_eligible_skills "$CLONE_ROOT" | wc -l | tr -d ' ')" - printf ' Skills to install: %s\n' "$skill_count" - printf ' Platform: %s\n' "$platform" - printf ' Scope: %s\n' "$scope" + + # Compute number of install targets so the preview shows the actual symlink + # count (skills × targets), not just the number of unique skills. + local n_targets=0 + if [ "$include_global" -eq 1 ]; then + [ "$include_claude" -eq 1 ] && n_targets=$((n_targets + ${#chosen_claude_dirs[@]})) + [ "$include_agents" -eq 1 ] && n_targets=$((n_targets + ${#chosen_agents_dirs[@]})) + fi + if [ "$include_repo" -eq 1 ]; then + local _n_platforms=0 + [ "$include_claude" -eq 1 ] && _n_platforms=$((_n_platforms + 1)) + [ "$include_agents" -eq 1 ] && _n_platforms=$((_n_platforms + 1)) + n_targets=$((n_targets + ${#chosen_repos[@]} * _n_platforms)) + fi + local total_symlinks=$((skill_count * n_targets)) + + printf ' Skills: %s unique\n' "$skill_count" + printf ' Platform: %s\n' "$platform" + printf ' Scope: %s\n' "$scope" + printf ' Targets: %s\n' "$n_targets" + printf ' Symlinks: %s total (skills × targets)\n' "$total_symlinks" printf ' LFX_DEV_ROOT: %s\n' "$lfx_dev_root" if [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then printf ' Claude config dirs:\n' @@ -653,17 +671,20 @@ cmd_install() { config_set_shell_rcs "${rcs[@]}" fi - ui_step "Creating symlinks…" local total_installed=0 total_updated=0 total_skipped=0 if [ "$include_global" -eq 1 ]; then if [ "$include_claude" -eq 1 ] && [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then for d in "${chosen_claude_dirs[@]}"; do + ui_blank + ui_step "Claude · global · $d/skills/" local s; s="$(symlinks_install_all "$CLONE_ROOT" claude global "$d")" _accumulate_install_summary "$s" done fi if [ "$include_agents" -eq 1 ] && [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then for d in "${chosen_agents_dirs[@]}"; do + ui_blank + ui_step "agents · global · $d/skills/" local s; s="$(symlinks_install_all "$CLONE_ROOT" agents global "$d")" _accumulate_install_summary "$s" done @@ -672,10 +693,14 @@ cmd_install() { if [ "$include_repo" -eq 1 ] && [ "${#chosen_repos[@]}" -gt 0 ]; then for r in "${chosen_repos[@]}"; do if [ "$include_claude" -eq 1 ]; then + ui_blank + ui_step "Claude · per-repo · $r/.claude/skills/" local s; s="$(symlinks_install_all "$CLONE_ROOT" claude repo "$r")" _accumulate_install_summary "$s" fi if [ "$include_agents" -eq 1 ]; then + ui_blank + ui_step "agents · per-repo · $r/.agents/skills/" local s; s="$(symlinks_install_all "$CLONE_ROOT" agents repo "$r")" _accumulate_install_summary "$s" fi diff --git a/lib/symlinks.sh b/lib/symlinks.sh index c9049c4..e8b148c 100644 --- a/lib/symlinks.sh +++ b/lib/symlinks.sh @@ -36,24 +36,25 @@ symlinks_eligible_skills() { # This is the lifted 3-way logic from the original install.sh (lines 31-46). symlinks_create_one() { local source="$1" target="$2" + local name; name="$(basename "$target")" if [ -L "$target" ]; then rm "$target" if ln -s "$source" "$target"; then - ui_dim " updated $(basename "$target") → $source" >&2 + ui_dim " updated $name" >&2 printf 'updated\n' else - ui_warn " failed $(basename "$target")" + ui_warn " failed $name" printf 'failed\n' fi elif [ -e "$target" ]; then - ui_warn " skipped $(basename "$target") (non-symlink already exists at $target)" + ui_warn " skipped $name (non-symlink already exists at $target)" printf 'skipped\n' else if ln -s "$source" "$target"; then - ui_dim " installed $(basename "$target") → $source" >&2 + ui_dim " installed $name" >&2 printf 'installed\n' else - ui_warn " failed $(basename "$target")" + ui_warn " failed $name" printf 'failed\n' fi fi From 130022366aa352a1d2b8ef2dcef378200df35579 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 15:26:15 +0200 Subject: [PATCH 12/20] =?UTF-8?q?feat(skills):=20self-contained=20?= =?UTF-8?q?=E2=80=94=20no=20shell=20rc=20edit=20needed=20for=20skills=20to?= =?UTF-8?q?=20work?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mirrors the pattern Claude Code and Codex use: each tool owns its own config dir under $HOME and reads from it directly. The user never has to teach their shell about LFX. Previously, the install ended with a "Next steps" instruction to add a sourcing line to ~/.zshrc so that LFX_DEV_ROOT and the lfx-skills CLI would be on PATH. That had two problems: 1. It was annoying — most users skipped it and then wondered why skills couldn't find their repos. 2. It was fragile — env from shell rc only flows to Claude Code if Claude was launched from a terminal. GUI launches (Spotlight, Dock) don't source the rc, so LFX_DEV_ROOT wasn't there even when the user had done the rc edit. New architecture: - env.sh becomes idempotent. Uses `: "${LFX_DEV_ROOT:=...}"` instead of `export LFX_DEV_ROOT=...`, so an existing user-set value wins and the file is safe to source repeatedly. - The 3 skills that use LFX_DEV_ROOT (test-journey, coordinator, research) source ~/.config/lfx-skills/env.sh themselves at the top of their bash blocks. The user's shell is never involved. - Install "Next steps" simplified to "restart your AI tool, type /lfx". The rc-edit instruction is demoted to an optional follow-up for users who want to type `lfx-skills` directly from a terminal (rather than via /lfx-skills-helper from inside the AI tool). - Verified: skills find the configured dev root in a clean shell with no LFX_DEV_ROOT set; user override (`export LFX_DEV_ROOT=...`) still wins. Same pattern Claude Code uses with `~/.claude/`: the tool reads from its own config dir, the shell stays out of it. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- bin/lfx-skills | 18 +++++++++++++----- lfx-coordinator/SKILL.md | 7 ++++++- lfx-research/SKILL.md | 10 ++++++++-- lfx-test-journey/SKILL.md | 21 ++++++++++++++------- lib/config.sh | 13 ++++++++++--- 5 files changed, 51 insertions(+), 18 deletions(-) diff --git a/bin/lfx-skills b/bin/lfx-skills index d0586af..cb065eb 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -718,11 +718,11 @@ cmd_install() { ui_blank ui_bold "Next steps:" - printf ' 1. Add this line to your shell rc (~/.zshrc or equivalent):\n' - printf ' %s\n' "$(rc_snippet)" - printf ' 2. Restart your shell (or source the rc) to pick up LFX_DEV_ROOT and the lfx-skills CLI.\n' - printf ' 3. Restart your AI coding assistant.\n' - printf ' 4. Type /lfx in any LFX repo to get started.\n' + printf ' 1. Restart your AI coding assistant (or open a new session).\n' + printf ' 2. Type /lfx in any LFX repo to get started.\n' + printf '\n' + printf ' No shell rc edits needed: the skills resolve LFX_DEV_ROOT by sourcing\n' + printf ' %s themselves.\n' "$(env_sh_path)" if [ "${#chosen_repos[@]}" -gt 0 ]; then ui_blank @@ -733,6 +733,14 @@ cmd_install() { printf ' .agents/skills/lfx\n' printf ' %s(narrow patterns preserve any of your own committed skills)%s\n' "$_UI_DIM" "$_UI_RESET" fi + + ui_blank + printf '%sOptional:%s if you want to type %slfx-skills%s directly from your terminal,\n' \ + "$_UI_DIM" "$_UI_RESET" "$_UI_BOLD" "$_UI_RESET" + printf '%sadd this line to your shell rc (~/.zshrc or equivalent):%s\n' "$_UI_DIM" "$_UI_RESET" + printf ' %s\n' "$(rc_snippet)" + printf '%sOtherwise, manage everything via /lfx-skills-helper from inside Claude.%s\n' \ + "$_UI_DIM" "$_UI_RESET" } # Helper used inside cmd_install to sum `installed/updated/skipped/total` strings. diff --git a/lfx-coordinator/SKILL.md b/lfx-coordinator/SKILL.md index ab31635..e3e072d 100644 --- a/lfx-coordinator/SKILL.md +++ b/lfx-coordinator/SKILL.md @@ -79,10 +79,15 @@ Use your Read, Glob, Grep, and Bash tools to quickly check: - **Identify the upstream Go service from the code.** Read the Express proxy service file (e.g., `committee.service.ts`) and find which API paths it calls via `MicroserviceProxyService` (e.g., `/committees/...` means `lfx-v2-committee-service`). All services use `'LFX_V2_SERVICE'` as the env var — the API path prefix tells you which upstream Go repo owns the data. Then check if that repo exists locally: ```bash + # Source the LFX env file so $LFX_DEV_ROOT is set without depending on the + # user's shell rc. Falls back to $HOME/lf if the user hasn't run lfx-skills install. + [ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" + LFX_DEV_ROOT="${LFX_DEV_ROOT:-$HOME/lf}" + # Find the upstream service from the proxy code grep -r "proxyRequest" apps/lfx-one/src/server/services/<domain>.service.ts | head -5 # Check for local Go repos - ls -d "${LFX_DEV_ROOT:-$HOME/lf}"/lfx-v2-*-service 2>/dev/null + ls -d "$LFX_DEV_ROOT"/lfx-v2-*-service 2>/dev/null ``` - **Check the upstream Go service for the needed field.** Once you've identified the repo, check its domain model, Goa design, and conversions: ```bash diff --git a/lfx-research/SKILL.md b/lfx-research/SKILL.md index fbe732b..f612877 100644 --- a/lfx-research/SKILL.md +++ b/lfx-research/SKILL.md @@ -85,7 +85,13 @@ gh api repos/linuxfoundation/<repo-name>/contents/design/<file>.go \ | Surveys | `lfx-v2-survey-service` | | Members | `lfx-v2-member-service` | -**If the upstream Go repo exists locally** (check `${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-*-service`), read the files directly instead of using `gh api`. Local reads are faster and more reliable. +**If the upstream Go repo exists locally**, read the files directly instead of using `gh api`. Local reads are faster and more reliable. To resolve the path, source the LFX env file at the start of your bash commands so `$LFX_DEV_ROOT` is set without depending on the user's shell rc: + +```bash +[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" +LFX_DEV_ROOT="${LFX_DEV_ROOT:-$HOME/lf}" +ls -d "$LFX_DEV_ROOT"/lfx-v2-*-service 2>/dev/null # check what's local +``` **Report:** - Endpoint exists? (path, method, status codes) @@ -247,4 +253,4 @@ This keeps the user informed that exploration is happening and what's being chec - **Be specific** — include file paths, method names, field names - **Flag blockers** — if an upstream API doesn't exist, say so clearly - **Include example content** — read and include the key sections of example files -- **Prefer local reads** — if a Go repo exists at `${LFX_DEV_ROOT:-$HOME/lf}`, read it directly instead of using `gh api` +- **Prefer local reads** — if a Go repo exists at `$LFX_DEV_ROOT` (resolved by sourcing `~/.config/lfx-skills/env.sh` at the start of your bash commands), read it directly instead of using `gh api` diff --git a/lfx-test-journey/SKILL.md b/lfx-test-journey/SKILL.md index 89b3fb2..a870952 100644 --- a/lfx-test-journey/SKILL.md +++ b/lfx-test-journey/SKILL.md @@ -64,25 +64,32 @@ If a subcommand requires a journey name and the user didn't provide one, run **L ### Step 1: Discover Repos -Scan `${LFX_DEV_ROOT:-$HOME/lf}` for git repositories: +Resolve the LFX dev root and scan it for git repositories. The first line +sources the LFX env file written by `lfx-skills install`; this makes +`LFX_DEV_ROOT` available without depending on the user's shell rc. ```bash -for dir in "${LFX_DEV_ROOT:-$HOME/lf}"/*/; do +[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" +LFX_DEV_ROOT="${LFX_DEV_ROOT:-$HOME/lf}" + +for dir in "$LFX_DEV_ROOT"/*/; do if [ -d "$dir/.git" ]; then echo "$dir" fi done ``` -Present as a numbered list and **STOP — use `AskUserQuestion` and wait for the user to respond before continuing**: +Present as a numbered list (substitute the resolved `$LFX_DEV_ROOT` value +in the displayed paths) and **STOP — use `AskUserQuestion` and wait for the +user to respond before continuing**: ``` -Scanning ${LFX_DEV_ROOT:-$HOME/lf} for git repos... +Scanning $LFX_DEV_ROOT for git repos... Which repos are part of this journey? (type numbers, e.g. "1, 3") - 1. ${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-ui - 2. ${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-committee-service - 3. ${LFX_DEV_ROOT:-$HOME/lf}/lfx-v2-meeting-service + 1. $LFX_DEV_ROOT/lfx-v2-ui + 2. $LFX_DEV_ROOT/lfx-v2-committee-service + 3. $LFX_DEV_ROOT/lfx-v2-meeting-service ``` **⛔ GATE: You MUST call `AskUserQuestion` here and wait for the user's response. Do NOT continue to Step 2 until the user has selected repos.** Parse their response (comma-separated numbers or repo names). diff --git a/lib/config.sh b/lib/config.sh index 3c8e9b8..f4911ea 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -211,6 +211,11 @@ config_clear() { # write_env_sh → generate ~/.config/lfx-skills/env.sh from current config. # Reads lfx_dev_root and canonical_clone from config.json. +# +# The file is idempotent and respects existing values: LFX_DEV_ROOT is set +# only if not already set (so a user's session-level override wins), and +# PATH is appended only if not already on it. Safe to source repeatedly, +# both from the user's shell rc and inline at the top of skill bash blocks. write_env_sh() { local clone devroot bin_path env_path devroot="$(config_get lfx_dev_root)" @@ -219,9 +224,11 @@ write_env_sh() { env_path="$(env_sh_path)" mkdir -p "$(dirname "$env_path")" cat > "$env_path" <<EOF -# Generated by lfx-skills install. Source from your shell rc. -# DO NOT EDIT — re-run \`lfx-skills install\` or \`lfx-skills doctor --fix\` to regenerate. -export LFX_DEV_ROOT="$devroot" +# Generated by lfx-skills install. Idempotent: safe to source from shell rc +# AND inline from skill bash blocks (skills source it for LFX_DEV_ROOT). +# DO NOT EDIT — re-run \`lfx-skills install\` or \`lfx-skills config set\` to regenerate. +: "\${LFX_DEV_ROOT:=$devroot}" +export LFX_DEV_ROOT case ":\$PATH:" in *":$bin_path:"*) ;; *) export PATH="\$PATH:$bin_path" ;; From c26501be09f3f128c2c2e4bc55bc450e4a3a48fd Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Tue, 5 May 2026 15:46:13 +0200 Subject: [PATCH 13/20] feat(cli): drop env.sh, install lfx-skills CLI symlink into a PATH dir MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces the env.sh-sourced-from-shell-rc approach with two cleaner mechanisms that match what other dev tools (pipx, gh, brew) actually do: 1. ~/.lfx-skills/dev-root — single-line text file the 3 dev-root-aware skills read with `cat` to resolve LFX_DEV_ROOT. No env vars, no shell rc edits, no per-bash-block sourcing boilerplate. 2. ~/.local/bin/lfx-skills (or ~/bin, /opt/homebrew/bin, /usr/local/bin) — symlink the installer creates so `lfx-skills` is on PATH from any terminal. Probe picks the best writable PATH dir: ~/.local/bin → ~/bin → /opt/homebrew/bin → /usr/local/bin /opt/homebrew/bin specifically added per audit — Apple Silicon stock setup has no other user-writable PATH dir by default. env.sh, write_env_sh(), env_sh_path(), and rc_snippet() are gone. The doctor's env-sh-missing / env-sh-not-sourced / dev-root-not-in- session / dev-root-session-drift checks are gone — they were measuring something that no longer matters. New checks: dev-root-file-missing (fail; CLI auto-fixes) and dev-root-file-mismatch (warn; CLI rewrites from manifest). Safety holes plugged after a focused review subagent pass: - install_cli_symlink REFUSES to overwrite an existing symlink that doesn't already point into the canonical clone. Previously it silently `rm`-ed any pre-existing link, which would have wiped a user's own lfx-skills script. Now: idempotent re-install of our own link is fine; another lfx-skills clone gets a loud warning; any other foreign symlink is refused with instructions. - remove_cli_symlink refuses to act when canonical_clone is empty in the config (corrupted manifest). Previously the empty-prefix strip silently passed the safety check. - POSIX-correct quoting in `${actual#"$clone/"}` — protects against glob interpretation if a path ever contains * or ?. - Captured-stdout fix: install call no longer redirects stderr into the path variable, so warnings can flow to the user without contaminating the recorded value. - config_set_cli_symlink failure is detected and reported (was set -e fall-through previously). CLI-on-PATH is now installed by default; --no-cli-symlink opts out. If the probe finds no writable PATH dir, the install prints a copy-pasteable alias snippet pointing at <clone>/bin/lfx-skills, so the user always has a one-line escape hatch. End-to-end verified: foreign symlink survives a fresh install (with warn + alias fallback); corrupt-config uninstall refuses to remove the CLI symlink; multi-clone clobber emits "Replacing existing symlink…" warning instead of silent overwrite. Doc cascade: CLAUDE.md, AGENTS.md, lfx-doctor/SKILL.md, lfx-doctor/references/fix-recipes.md, lfx-install/SKILL.md, lfx-skills-helper/SKILL.md, and the 3 LFX_DEV_ROOT-using skills (test-journey, coordinator, research) all updated to point at the new shape. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com> Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- AGENTS.md | 4 +- CLAUDE.md | 4 +- bin/lfx-skills | 78 ++++++++++++++++++++-------- lfx-coordinator/SKILL.md | 7 ++- lfx-doctor/SKILL.md | 4 +- lfx-doctor/references/fix-recipes.md | 53 ++++--------------- lfx-install/SKILL.md | 16 +++--- lfx-research/SKILL.md | 7 ++- lfx-skills-helper/SKILL.md | 2 +- lfx-test-journey/SKILL.md | 9 ++-- lib/config.sh | 68 +++++++++++------------- lib/doctor.sh | 69 +++++++++--------------- lib/probe.sh | 20 +++++++ lib/symlinks.sh | 60 +++++++++++++++++++++ 14 files changed, 228 insertions(+), 173 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 3676240..7c32a4a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,7 +27,9 @@ The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full co - `lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. - `.claude/skills/` and `.agents/skills/` — committed relative symlinks to the four meta-skills above (this is what makes them auto-discoverable when someone clones). - `install.sh` — thin shim that execs `bin/lfx-skills install "$@"`. -- `~/.config/lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). +- `~/.lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). +- `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. +- `~/.local/bin/lfx-skills` (or `~/bin/...`, or `/usr/local/bin/...`) — symlink the installer creates so `lfx-skills` is on PATH everywhere. No shell rc edit needed. ## Tool-name compatibility diff --git a/CLAUDE.md b/CLAUDE.md index a0f5244..989c36b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -27,7 +27,9 @@ The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full co - `lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. - `.claude/skills/` and `.agents/skills/` — committed relative symlinks to the four meta-skills above (this is what makes them auto-discoverable when someone clones). - `install.sh` — thin shim that execs `bin/lfx-skills install "$@"`. -- `~/.config/lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). +- `~/.lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). +- `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. +- `~/.local/bin/lfx-skills` (or `~/bin/...`, or `/usr/local/bin/...`) — symlink the installer creates so `lfx-skills` is on PATH everywhere. No shell rc edit needed. ## Conventions diff --git a/bin/lfx-skills b/bin/lfx-skills index cb065eb..ad722de 100755 --- a/bin/lfx-skills +++ b/bin/lfx-skills @@ -80,7 +80,7 @@ COMMANDS doctor Full diagnostics across 7 categories; --fix to repair interactively list List installed or available skills info SKILL Show frontmatter + install locations for a skill - config Show or modify ~/.config/lfx-skills/config.json (get/set) + config Show or modify ~/.lfx-skills/config.json (get/set) repos List lf* git repos under \$LFX_DEV_ROOT version Print version of this lfx-skills clone help [CMD] Detailed help for CMD, or this overview @@ -94,8 +94,14 @@ EXAMPLES lfx-skills uninstall CONFIG - ~/.config/lfx-skills/config.json manifest (single source of truth) - ~/.config/lfx-skills/env.sh exports LFX_DEV_ROOT + adds bin/ to PATH + ~/.lfx-skills/config.json manifest (single source of truth) + ~/.lfx-skills/dev-root one-line text file: the resolved dev root, + read by skills via `cat` (no env var or + shell rc needed) + ~/.local/bin/lfx-skills (or symlink so you can type `lfx-skills` from + ~/bin, /usr/local/bin) anywhere — picked from a writable PATH + dir at install time. Recorded in + config.json.cli_symlink. Run \`lfx-skills help <command>\` for command-specific options. EOF @@ -118,6 +124,9 @@ NON-INTERACTIVE FLAGS --claude-config=PATH[,PATH...] Multi-config: pass several Claude config dirs --agents-config=PATH[,PATH...] Multi-config: pass several agents config dirs --repos=PATH[,PATH...] Required if --scope contains 'repo' + --no-cli-symlink Skip installing the `lfx-skills` CLI + symlink into a PATH dir (default: install + if a writable PATH dir is available) --dry-run Show what would happen, then exit EXAMPLE @@ -260,8 +269,8 @@ cmd_config() { [ "$key" = "$kv" ] && ui_die "Missing = in argument: $kv" config_set "$key" "$val" if [ "$key" = "lfx_dev_root" ] || [ "$key" = "canonical_clone" ]; then - write_env_sh - ui_success "Updated config and env.sh" + write_dev_root_file + ui_success "Updated config and dev-root" else ui_success "Updated config" fi @@ -419,6 +428,7 @@ cmd_install() { require_jq local platform="" scope="" lfx_dev_root="" claude_config="" agents_config="" repos="" dry_run=0 + local no_cli_symlink=0 while [ $# -gt 0 ]; do case "$1" in --yes) LFX_SKILLS_YES=1; export LFX_SKILLS_YES ;; @@ -428,6 +438,7 @@ cmd_install() { --claude-config=*) claude_config="${1#*=}" ;; --agents-config=*) agents_config="${1#*=}" ;; --repos=*) repos="${1#*=}" ;; + --no-cli-symlink) no_cli_symlink=1 ;; --dry-run) dry_run=1 ;; -h|--help) _help_install; return 0 ;; *) ui_error "Unknown flag: $1"; _help_install; exit 1 ;; @@ -707,8 +718,24 @@ cmd_install() { done fi - ui_step "Writing env.sh…" - write_env_sh + ui_step "Writing dev-root…" + write_dev_root_file + + # Install lfx-skills CLI into a writable PATH dir so the user can type + # `lfx-skills` from anywhere. Tracked in config.json for clean uninstall. + # Capture only stdout (warnings/errors flow to stderr, which the user sees + # directly and we don't want polluting our path variable). + local cli_symlink_path="" + if [ "$no_cli_symlink" = "0" ]; then + cli_symlink_path="$(install_cli_symlink "$CLONE_ROOT" || true)" + if [ -L "$cli_symlink_path" ]; then + if ! config_set_cli_symlink "$cli_symlink_path"; then + ui_warn "Created CLI symlink at $cli_symlink_path but failed to record it in config — uninstall won't clean it up automatically." + fi + else + cli_symlink_path="" + fi + fi # Step 8: VERIFY + summary ui_blank @@ -716,13 +743,20 @@ cmd_install() { printf ' %d installed, %d updated, %d skipped\n' \ "$total_installed" "$total_updated" "$total_skipped" + if [ -n "$cli_symlink_path" ]; then + ui_success "lfx-skills CLI installed to $cli_symlink_path" + elif [ "$no_cli_symlink" = "0" ]; then + ui_warn "Couldn't find a writable dir on your PATH for the CLI symlink." + printf ' To use %slfx-skills%s from anywhere, add an alias to your shell rc:\n' \ + "$_UI_BOLD" "$_UI_RESET" + printf ' alias lfx-skills='\''%s/bin/lfx-skills'\''\n' "$CLONE_ROOT" + printf ' Or extend PATH to include one of: ~/.local/bin, ~/bin, /usr/local/bin\n' + fi + ui_blank ui_bold "Next steps:" printf ' 1. Restart your AI coding assistant (or open a new session).\n' printf ' 2. Type /lfx in any LFX repo to get started.\n' - printf '\n' - printf ' No shell rc edits needed: the skills resolve LFX_DEV_ROOT by sourcing\n' - printf ' %s themselves.\n' "$(env_sh_path)" if [ "${#chosen_repos[@]}" -gt 0 ]; then ui_blank @@ -733,14 +767,6 @@ cmd_install() { printf ' .agents/skills/lfx\n' printf ' %s(narrow patterns preserve any of your own committed skills)%s\n' "$_UI_DIM" "$_UI_RESET" fi - - ui_blank - printf '%sOptional:%s if you want to type %slfx-skills%s directly from your terminal,\n' \ - "$_UI_DIM" "$_UI_RESET" "$_UI_BOLD" "$_UI_RESET" - printf '%sadd this line to your shell rc (~/.zshrc or equivalent):%s\n' "$_UI_DIM" "$_UI_RESET" - printf ' %s\n' "$(rc_snippet)" - printf '%sOtherwise, manage everything via /lfx-skills-helper from inside Claude.%s\n' \ - "$_UI_DIM" "$_UI_RESET" } # Helper used inside cmd_install to sum `installed/updated/skipped/total` strings. @@ -802,12 +828,22 @@ cmd_uninstall() { fi if [ "$keep_config" = "0" ] && [ -z "$scope" ]; then - if ui_confirm "Remove ~/.config/lfx-skills/ entirely?" N; then + # Remove the lfx-skills CLI symlink (e.g. ~/.local/bin/lfx-skills) if we + # installed it. Safe-by-default: only removed if it points into the + # canonical clone. + local cli_path canonical + cli_path="$(config_get cli_symlink)" + canonical="$(config_get canonical_clone)" + if [ -n "$cli_path" ] && [ -L "$cli_path" ]; then + if remove_cli_symlink "$cli_path" "$canonical"; then + ui_success "Removed lfx-skills CLI symlink at $cli_path" + fi + fi + if ui_confirm "Remove ~/.lfx-skills/ entirely?" N; then config_clear ui_success "Removed config dir." - ui_info "Remove the env.sh sourcing line from your shell rc when convenient." else - ui_info "Kept config dir (env.sh preserved)." + ui_info "Kept config dir." fi fi ui_success "Uninstall complete." diff --git a/lfx-coordinator/SKILL.md b/lfx-coordinator/SKILL.md index e3e072d..e7dedf7 100644 --- a/lfx-coordinator/SKILL.md +++ b/lfx-coordinator/SKILL.md @@ -79,10 +79,9 @@ Use your Read, Glob, Grep, and Bash tools to quickly check: - **Identify the upstream Go service from the code.** Read the Express proxy service file (e.g., `committee.service.ts`) and find which API paths it calls via `MicroserviceProxyService` (e.g., `/committees/...` means `lfx-v2-committee-service`). All services use `'LFX_V2_SERVICE'` as the env var — the API path prefix tells you which upstream Go repo owns the data. Then check if that repo exists locally: ```bash - # Source the LFX env file so $LFX_DEV_ROOT is set without depending on the - # user's shell rc. Falls back to $HOME/lf if the user hasn't run lfx-skills install. - [ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" - LFX_DEV_ROOT="${LFX_DEV_ROOT:-$HOME/lf}" + # Resolve the LFX dev root from the one-line file written by `lfx-skills install`, + # falling back to $HOME/lf when not installed. No shell rc / env var required. + LFX_DEV_ROOT="${LFX_DEV_ROOT:-$(cat ~/.lfx-skills/dev-root 2>/dev/null || echo "$HOME/lf")}" # Find the upstream service from the proxy code grep -r "proxyRequest" apps/lfx-one/src/server/services/<domain>.service.ts | head -5 diff --git a/lfx-doctor/SKILL.md b/lfx-doctor/SKILL.md index ff0324b..0e2ec65 100644 --- a/lfx-doctor/SKILL.md +++ b/lfx-doctor/SKILL.md @@ -23,7 +23,7 @@ You diagnose problems with a user's LFX Skills install and walk them through fix The `lfx-skills` CLI lives in the user's lfx-skills clone at `bin/lfx-skills`. Try, in order: 1. **On PATH:** `command -v lfx-skills` — if found, use it directly. -2. **From the manifest:** `jq -r .canonical_clone ~/.config/lfx-skills/config.json 2>/dev/null` — if the file exists, append `/bin/lfx-skills`. +2. **From the manifest:** `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` — if the file exists, append `/bin/lfx-skills`. 3. **Current dir:** if the user is inside the lfx-skills clone (a `bin/lfx-skills` exists relative to `pwd`), use `./bin/lfx-skills`. 4. **Last resort:** ask the user: "Where is your lfx-skills clone? (e.g., `~/lf/lfx-skills`)". @@ -92,8 +92,6 @@ For records with `fixable: false` that the user wants addressed, apply judgment. | Issue ID | What you can offer | |------------------------------|---------------------------------------------------------------------------------| -| `env-sh-not-sourced` | Print the exact line for the user to add to `~/.zshrc` (from `references/fix-recipes.md`). | -| `dev-root-not-in-session` | Suggest `source ~/.config/lfx-skills/env.sh` for this session, plus permanent fix. | | `frontmatter-no-name` / `frontmatter-name-mismatch` | Read the SKILL.md, identify the line, offer to fix it via Edit. | | `frontmatter-no-description` | Read the SKILL.md body, draft a one-paragraph description from it, offer to insert. | | `license-missing` | Insert the YAML license-header lines (see `references/fix-recipes.md` template). | diff --git a/lfx-doctor/references/fix-recipes.md b/lfx-doctor/references/fix-recipes.md index 00ba70e..84b0422 100644 --- a/lfx-doctor/references/fix-recipes.md +++ b/lfx-doctor/references/fix-recipes.md @@ -17,7 +17,7 @@ Each entry follows the same shape: ## issue-id: no-config -**What:** No `~/.config/lfx-skills/config.json`. +**What:** No `~/.lfx-skills/config.json`. **Why it matters:** the CLI doesn't know what's installed where. Doctor can only check things relative to the manifest, so most other checks will be skipped. **Fix:** run `lfx-skills install`. Or, inside the lfx-skills clone, run `/lfx-install` for a guided walkthrough. **Auto-fixable?** no (requires user choices about platform / scope / dev root). @@ -114,54 +114,21 @@ git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git --- -## issue-id: env-sh-missing +## issue-id: dev-root-file-missing -**What:** `~/.config/lfx-skills/env.sh` is gone. -**Why it matters:** sourcing your shell rc won't set `LFX_DEV_ROOT` or add `lfx-skills` to PATH. -**Fix this session:** `lfx-skills doctor --fix` (CLI regenerates from `config.json`). +**What:** `~/.lfx-skills/dev-root` is gone. +**Why it matters:** the 3 skills that need a local LFX path (`/lfx-coordinator`, `/lfx-research`, `/lfx-test-journey`) read this file via `cat` to resolve the dev root without depending on env vars. Without it, they fall back to `~/lf`, which may not match your setup. +**Fix:** `lfx-skills doctor --fix` regenerates it from the manifest. Or `lfx-skills config set lfx_dev_root=/your/path` if you also need to change the path. **Auto-fixable?** yes (CLI). --- -## issue-id: dev-root-not-in-session +## issue-id: dev-root-file-mismatch -**What:** `LFX_DEV_ROOT` isn't set in the current shell. -**Why it matters:** skills that read the env var directly will fall back to defaults (`~/lf`), which may not be where you keep your repos. -**Fix this session:** - -```bash -. ~/.config/lfx-skills/env.sh -``` - -**Fix permanently:** add to your `~/.zshrc` (or equivalent): - -```bash -[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" -``` - -**Auto-fixable?** no (we never auto-edit user shell rc). - ---- - -## issue-id: dev-root-session-drift - -**What:** Your shell's `LFX_DEV_ROOT` differs from the manifest's recorded value. -**Common cause:** you set the env var manually somewhere outside `env.sh`, or you re-ran `lfx-skills install` with a new dev root but haven't reloaded your shell. -**Fix:** decide which is right; align them. Either re-source `env.sh` or update the manifest with `lfx-skills config set lfx_dev_root=...`. -**Auto-fixable?** no. - ---- - -## issue-id: env-sh-not-sourced - -**What:** None of your shell rc files reference `~/.config/lfx-skills/env.sh`. -**Fix:** add this one-liner to your `~/.zshrc` (or `~/.bashrc`): - -```bash -[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" -``` - -**Auto-fixable?** no. +**What:** The contents of `~/.lfx-skills/dev-root` differ from `lfx_dev_root` in `config.json`. +**Common cause:** the file was hand-edited, or an old `lfx-skills` version wrote one source but not the other. +**Fix:** `lfx-skills doctor --fix` rewrites the file from `config.json` (the source of truth). +**Auto-fixable?** yes (CLI). --- diff --git a/lfx-install/SKILL.md b/lfx-install/SKILL.md index a21c255..db1c61e 100644 --- a/lfx-install/SKILL.md +++ b/lfx-install/SKILL.md @@ -149,17 +149,19 @@ If errors, walk the user through the auto-fix: > "One or more checks failed. Want me to run `/lfx-doctor` to investigate?" -## Step 11: Show the rc snippet +## Step 11: Confirm the CLI is on PATH -The CLI prints the `env.sh` source line at the end. Repeat it explicitly: +The installer creates a symlink at a writable PATH dir (`~/.local/bin/lfx-skills`, `~/bin/lfx-skills`, or `/usr/local/bin/lfx-skills`) so the user can type `lfx-skills` from anywhere — no shell rc edit. Read the install output to see which path was used and tell the user: -> "Add this one line to your `~/.zshrc` (or `~/.bashrc`) so `LFX_DEV_ROOT` and the `lfx-skills` command are available in every new shell: -> +> "`lfx-skills` is now at `<reported path>` and ready to use from any terminal." + +If the installer reported it couldn't find a writable PATH dir, share the alias snippet it printed: + +> "I couldn't find a writable PATH dir to drop the CLI into. Add this alias to your shell rc to use `lfx-skills` from anywhere: > ```bash -> [ -f \"$HOME/.config/lfx-skills/env.sh\" ] && . \"$HOME/.config/lfx-skills/env.sh\" +> alias lfx-skills='<clone>/bin/lfx-skills' > ``` -> -> I won't edit your shell rc for you — that's your call." +> Or extend PATH to include `~/.local/bin`, `~/bin`, or `/usr/local/bin`." ## Step 12: Suggest next steps diff --git a/lfx-research/SKILL.md b/lfx-research/SKILL.md index f612877..df77dc9 100644 --- a/lfx-research/SKILL.md +++ b/lfx-research/SKILL.md @@ -85,11 +85,10 @@ gh api repos/linuxfoundation/<repo-name>/contents/design/<file>.go \ | Surveys | `lfx-v2-survey-service` | | Members | `lfx-v2-member-service` | -**If the upstream Go repo exists locally**, read the files directly instead of using `gh api`. Local reads are faster and more reliable. To resolve the path, source the LFX env file at the start of your bash commands so `$LFX_DEV_ROOT` is set without depending on the user's shell rc: +**If the upstream Go repo exists locally**, read the files directly instead of using `gh api`. Local reads are faster and more reliable. Resolve the path at the start of your bash commands by reading the dev-root file written by `lfx-skills install`: ```bash -[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" -LFX_DEV_ROOT="${LFX_DEV_ROOT:-$HOME/lf}" +LFX_DEV_ROOT="${LFX_DEV_ROOT:-$(cat ~/.lfx-skills/dev-root 2>/dev/null || echo "$HOME/lf")}" ls -d "$LFX_DEV_ROOT"/lfx-v2-*-service 2>/dev/null # check what's local ``` @@ -253,4 +252,4 @@ This keeps the user informed that exploration is happening and what's being chec - **Be specific** — include file paths, method names, field names - **Flag blockers** — if an upstream API doesn't exist, say so clearly - **Include example content** — read and include the key sections of example files -- **Prefer local reads** — if a Go repo exists at `$LFX_DEV_ROOT` (resolved by sourcing `~/.config/lfx-skills/env.sh` at the start of your bash commands), read it directly instead of using `gh api` +- **Prefer local reads** — if a Go repo exists at `$LFX_DEV_ROOT` (resolved via `cat ~/.lfx-skills/dev-root` at the start of your bash commands), read it directly instead of using `gh api` diff --git a/lfx-skills-helper/SKILL.md b/lfx-skills-helper/SKILL.md index f117b39..e7c958c 100644 --- a/lfx-skills-helper/SKILL.md +++ b/lfx-skills-helper/SKILL.md @@ -24,7 +24,7 @@ You are the conversational front-end for the `lfx-skills` CLI: install, uninstal Same as `/lfx-doctor`. Try in order: 1. `command -v lfx-skills` (on PATH). -2. `jq -r .canonical_clone ~/.config/lfx-skills/config.json 2>/dev/null` then append `/bin/lfx-skills`. +2. `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` then append `/bin/lfx-skills`. 3. `./bin/lfx-skills` if you're inside the lfx-skills clone. 4. Ask the user. diff --git a/lfx-test-journey/SKILL.md b/lfx-test-journey/SKILL.md index a870952..b643618 100644 --- a/lfx-test-journey/SKILL.md +++ b/lfx-test-journey/SKILL.md @@ -64,13 +64,12 @@ If a subcommand requires a journey name and the user didn't provide one, run **L ### Step 1: Discover Repos -Resolve the LFX dev root and scan it for git repositories. The first line -sources the LFX env file written by `lfx-skills install`; this makes -`LFX_DEV_ROOT` available without depending on the user's shell rc. +Scan the LFX dev root for git repositories. The first line resolves +`LFX_DEV_ROOT` from a one-line text file written by `lfx-skills install`, +falling back to `~/lf` when not installed. No shell rc / env var required. ```bash -[ -f "$HOME/.config/lfx-skills/env.sh" ] && . "$HOME/.config/lfx-skills/env.sh" -LFX_DEV_ROOT="${LFX_DEV_ROOT:-$HOME/lf}" +LFX_DEV_ROOT="${LFX_DEV_ROOT:-$(cat ~/.lfx-skills/dev-root 2>/dev/null || echo "$HOME/lf")}" for dir in "$LFX_DEV_ROOT"/*/; do if [ -d "$dir/.git" ]; then diff --git a/lib/config.sh b/lib/config.sh index f4911ea..985ae1c 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -4,14 +4,24 @@ # Config + env.sh I/O for bin/lfx-skills. Sourced; do not execute directly. # Requires: jq (caller validates with probe_have_jq). -CONFIG_DIR_DEFAULT="${XDG_CONFIG_HOME:-$HOME/.config}/lfx-skills" +# Config dir lives at $HOME/.lfx-skills (sibling of $HOME/.claude, $HOME/.codex, +# etc. — the convention the user-facing AI tools follow). Each config artefact +# is a sibling file inside that dir: +# config.json manifest of every symlink the CLI installed (incl. cli_symlink) +# dev-root single-line text file containing the resolved LFX_DEV_ROOT. +# Skills `cat` this to get the dev root without depending on +# env vars or shell rc edits. +# Nothing in $HOME/.lfx-skills/ is sourced by the user's shell. The CLI is made +# available system-wide via a symlink installed into a writable PATH dir +# (~/.local/bin, ~/bin, or /usr/local/bin) — see install_cli_symlink. +CONFIG_DIR_DEFAULT="$HOME/.lfx-skills" CONFIG_JSON_DEFAULT="$CONFIG_DIR_DEFAULT/config.json" -ENV_SH_DEFAULT="$CONFIG_DIR_DEFAULT/env.sh" +DEV_ROOT_FILE_DEFAULT="$CONFIG_DIR_DEFAULT/dev-root" # Allow overrides via env (used by tests). -config_dir() { printf '%s\n' "${LFX_SKILLS_CONFIG_DIR:-$CONFIG_DIR_DEFAULT}"; } -config_json_path(){ printf '%s\n' "${LFX_SKILLS_CONFIG_JSON:-$(config_dir)/config.json}"; } -env_sh_path() { printf '%s\n' "${LFX_SKILLS_ENV_SH:-$(config_dir)/env.sh}"; } +config_dir() { printf '%s\n' "${LFX_SKILLS_CONFIG_DIR:-$CONFIG_DIR_DEFAULT}"; } +config_json_path() { printf '%s\n' "${LFX_SKILLS_CONFIG_JSON:-$(config_dir)/config.json}"; } +dev_root_path() { printf '%s\n' "${LFX_SKILLS_DEV_ROOT_FILE:-$(config_dir)/dev-root}"; } # config_exists → return 0 if config.json exists, 1 otherwise. config_exists() { [ -f "$(config_json_path)" ]; } @@ -203,42 +213,24 @@ config_set_shell_rcs() { config_set_json "shell_rc_detected" "$rcs_json" } -# config_clear → delete config.json and env.sh. +# config_clear → delete config.json and dev-root. config_clear() { - rm -f "$(config_json_path)" "$(env_sh_path)" + rm -f "$(config_json_path)" "$(dev_root_path)" rmdir "$(config_dir)" 2>/dev/null || true } -# write_env_sh → generate ~/.config/lfx-skills/env.sh from current config. -# Reads lfx_dev_root and canonical_clone from config.json. -# -# The file is idempotent and respects existing values: LFX_DEV_ROOT is set -# only if not already set (so a user's session-level override wins), and -# PATH is appended only if not already on it. Safe to source repeatedly, -# both from the user's shell rc and inline at the top of skill bash blocks. -write_env_sh() { - local clone devroot bin_path env_path +# write_dev_root_file → write the resolved LFX_DEV_ROOT path to +# $HOME/.lfx-skills/dev-root as a single line. Skills read this with `cat` so +# they don't have to source env.sh, parse JSON, or depend on env vars. +write_dev_root_file() { + local devroot path devroot="$(config_get lfx_dev_root)" - clone="$(config_get canonical_clone)" - bin_path="$clone/bin" - env_path="$(env_sh_path)" - mkdir -p "$(dirname "$env_path")" - cat > "$env_path" <<EOF -# Generated by lfx-skills install. Idempotent: safe to source from shell rc -# AND inline from skill bash blocks (skills source it for LFX_DEV_ROOT). -# DO NOT EDIT — re-run \`lfx-skills install\` or \`lfx-skills config set\` to regenerate. -: "\${LFX_DEV_ROOT:=$devroot}" -export LFX_DEV_ROOT -case ":\$PATH:" in - *":$bin_path:"*) ;; - *) export PATH="\$PATH:$bin_path" ;; -esac -EOF -} - -# rc_snippet → echo the one-liner the user should add to their shell rc. -rc_snippet() { - local env_path - env_path="$(env_sh_path)" - printf '[ -f "%s" ] && . "%s"\n' "$env_path" "$env_path" + path="$(dev_root_path)" + mkdir -p "$(dirname "$path")" + printf '%s\n' "$devroot" > "$path" +} + +# config_set_cli_symlink PATH → record the lfx-skills CLI symlink path. +config_set_cli_symlink() { + config_set cli_symlink "$1" } diff --git a/lib/doctor.sh b/lib/doctor.sh index beaf55a..602f530 100644 --- a/lib/doctor.sh +++ b/lib/doctor.sh @@ -93,68 +93,47 @@ check_source_clone() { # ─── Category 3: LFX dev root ──────────────────────────────────────────── check_lfx_dev_root() { - local recorded env_path env_exists in_session + local recorded dev_root_file recorded="$(config_get lfx_dev_root)" - env_path="$(env_sh_path)" - env_exists=0 - [ -f "$env_path" ] && env_exists=1 - in_session="${LFX_DEV_ROOT:-}" + dev_root_file="$(dev_root_path)" + # The recorded path itself if [ -z "$recorded" ]; then _emit warn dev-root-not-recorded lfx_dev_root \ - "LFX_DEV_ROOT not recorded in config" \ + "lfx_dev_root not recorded in config" \ "Re-run \`lfx-skills install\` to capture it." no elif [ ! -d "$recorded" ]; then _emit fail dev-root-missing lfx_dev_root \ - "LFX_DEV_ROOT path does not exist" \ - "Recorded path: $recorded" no + "Recorded LFX dev root path does not exist" \ + "Recorded: $recorded" no else local repo_count repo_count="$(probe_count_repos_in "$recorded")" if [ "$repo_count" -eq 0 ]; then _emit warn dev-root-empty lfx_dev_root \ - "LFX_DEV_ROOT contains no lf* git repos" \ + "LFX dev root contains no lf* git repos" \ "$recorded — clone some LFX repos here." no else _emit pass dev-root-ok lfx_dev_root \ - "LFX_DEV_ROOT has $repo_count lf* repo(s)" "$recorded" no + "LFX dev root has $repo_count lf* repo(s)" "$recorded" no fi fi - if [ "$env_exists" -eq 0 ]; then - _emit fail env-sh-missing lfx_dev_root \ - "env.sh not found" \ - "Expected at $env_path. Run \`lfx-skills install\`." yes "$env_path" + # The dev-root cache file that skills `cat` to resolve LFX_DEV_ROOT + if [ ! -f "$dev_root_file" ]; then + _emit fail dev-root-file-missing lfx_dev_root \ + "Skills' dev-root cache file is missing" \ + "Expected at $dev_root_file. Run \`lfx-skills config set lfx_dev_root=...\` to regenerate." yes "$dev_root_file" else - _emit pass env-sh-exists lfx_dev_root "env.sh exists" "$env_path" no - fi - - if [ -z "$in_session" ]; then - _emit warn dev-root-not-in-session lfx_dev_root \ - "LFX_DEV_ROOT not set in current shell" \ - "Source $env_path or restart your shell after adding the snippet." no - elif [ -n "$recorded" ] && [ "$in_session" != "$recorded" ]; then - _emit warn dev-root-session-drift lfx_dev_root \ - "LFX_DEV_ROOT in session differs from config" \ - "Session: $in_session; config: $recorded" no - fi - - if [ "$env_exists" -eq 1 ]; then - local rc found=0 last_rc="" - while IFS= read -r rc; do - [ -z "$rc" ] && continue - last_rc="$rc" - if grep -F "$env_path" "$rc" >/dev/null 2>&1; then - found=1 - break - fi - done < <(probe_shell_rcs) - if [ "$found" -eq 1 ]; then - _emit pass env-sh-sourced lfx_dev_root "Shell rc sources env.sh" "$last_rc" no + local file_value + file_value="$(cat "$dev_root_file" 2>/dev/null)" + if [ "$file_value" != "$recorded" ]; then + _emit warn dev-root-file-mismatch lfx_dev_root \ + "dev-root file out of sync with config.json" \ + "File: $file_value; config: $recorded" yes "$dev_root_file" else - _emit warn env-sh-not-sourced lfx_dev_root \ - "Shell rc does not source env.sh" \ - "Add: $(rc_snippet)" no + _emit pass dev-root-file-ok lfx_dev_root \ + "dev-root cache file in sync with config" "$dev_root_file" no fi fi } @@ -413,9 +392,9 @@ doctor_fix_one() { ln -s "$source" "$link" ui_success "Recreated symlink: $link → $source" ;; - env-sh-missing) - write_env_sh - ui_success "Wrote $(env_sh_path)" + dev-root-file-missing|dev-root-file-mismatch) + write_dev_root_file + ui_success "Wrote $(dev_root_path)" ;; *) ui_warn "No auto-fix available for: $id" diff --git a/lib/probe.sh b/lib/probe.sh index 042c795..b1d9e51 100644 --- a/lib/probe.sh +++ b/lib/probe.sh @@ -79,6 +79,26 @@ probe_have_jq() { command -v jq >/dev/null 2>&1 } +# probe_writable_path_dir → echo the best dir on the user's PATH where we can +# create a symlink to `lfx-skills` without sudo. Returns empty if none qualifies. +# Preference (user-owned first): +# ~/.local/bin pipx convention; many Linux distros add this to PATH +# ~/bin older convention; some users still use it +# /opt/homebrew/bin Apple Silicon Homebrew; user-writable since brew chowns it +# /usr/local/bin Intel Macs and Linux; often root-owned, but writable for some setups +# A dir qualifies if it exists, is writable by the current user, and is on PATH. +probe_writable_path_dir() { + local d + for d in "$HOME/.local/bin" "$HOME/bin" "/opt/homebrew/bin" "/usr/local/bin"; do + [ -d "$d" ] || continue + [ -w "$d" ] || continue + case ":$PATH:" in + *":$d:"*) printf '%s\n' "$d"; return 0 ;; + esac + done + return 1 +} + # probe_canonical_clone → echo the absolute path of the lfx-skills clone this script lives in. # Caller passes the script's $0 (or any path inside the clone). # Walks up from bin/ or lib/ to the clone root. diff --git a/lib/symlinks.sh b/lib/symlinks.sh index e8b148c..fc14790 100644 --- a/lib/symlinks.sh +++ b/lib/symlinks.sh @@ -172,3 +172,63 @@ EOF printf '%d/%d/%d/%d\n' "$n_removed" "$n_refused" "$n_missing" "$n_total" } + +# install_cli_symlink CLONE [TARGET_DIR] +# Symlink <CLONE>/bin/lfx-skills into a writable PATH dir so the user can type +# `lfx-skills` from anywhere — no shell rc edit required. If TARGET_DIR is not +# given, picks the best candidate via probe_writable_path_dir. +# Echoes the symlink path on success (e.g. "/Users/x/.local/bin/lfx-skills"). +# Returns 1 if no writable PATH dir is available, OR if the target is occupied +# by something we don't own. Never silently clobbers a user's own script / +# foreign symlink. The caller should print fallback instructions. +install_cli_symlink() { + local clone="$1" target_dir="${2:-}" + local source target + source="$clone/bin/lfx-skills" + if [ -z "$target_dir" ]; then + target_dir="$(probe_writable_path_dir)" || return 1 + fi + target="$target_dir/lfx-skills" + if [ -L "$target" ]; then + # An existing symlink at the target. Only safe to replace if it already + # points where WE want it to point (idempotent re-install) or into the + # canonical clone (e.g. another lfx-skills clone — mark the clobber loudly). + local actual; actual="$(readlink "$target" 2>/dev/null || true)" + if [ "$actual" = "$source" ]; then + : # already correct — fall through to ln (will recreate identically) + rm "$target" + elif [ -n "$clone" ] && [ "${actual#"$clone/"}" != "$actual" ]; then + ui_warn "Replacing existing symlink at $target (was $actual, replacing with $source)" + rm "$target" + else + ui_warn "Refused to overwrite $target → $actual (not owned by this lfx-skills clone)" + return 1 + fi + elif [ -e "$target" ]; then + ui_warn "Refused to overwrite existing non-symlink at $target" + return 1 + fi + if ln -s "$source" "$target"; then + printf '%s\n' "$target" + return 0 + fi + return 1 +} + +# remove_cli_symlink TARGET CLONE → remove TARGET only if it's a symlink that +# points into the canonical clone we passed in. Safe-by-default: refuses to +# delete anything that doesn't look like our install. Refuses on empty CLONE. +remove_cli_symlink() { + local target="$1" clone="${2:-}" + [ -L "$target" ] || return 0 + if [ -z "$clone" ]; then + ui_warn "Refused to remove $target (no canonical_clone in config — can't verify ownership)" + return 1 + fi + local actual; actual="$(readlink "$target" 2>/dev/null || true)" + if [ "${actual#"$clone/"}" = "$actual" ]; then + ui_warn "Refused to remove $target (points to $actual, outside $clone)" + return 1 + fi + rm -f "$target" +} From 3c126279a0c213eef5bd9a1ef03a6d1ce967bd17 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Wed, 6 May 2026 16:20:27 +0200 Subject: [PATCH 14/20] feat: split Claude plugin from agents.md CLI Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- .agents/skills/lfx-doctor | 1 - .agents/skills/lfx-install | 1 - .agents/skills/lfx-new-skill | 1 - .agents/skills/lfx-skills-helper | 1 - .claude-plugin/plugin.json | 27 ++ .claude/skills/lfx-doctor | 1 - .claude/skills/lfx-install | 1 - .claude/skills/lfx-new-skill | 1 - .claude/skills/lfx-skills-helper | 1 - .gitignore | 29 +- AGENTS.md | 70 +++- CLAUDE.md | 70 +++- README.md | 243 +++++++------ {bin => cli}/lfx-skills | 322 +++++++++--------- docs/platform-install.md | 98 ++++-- install.sh | 4 +- lib/config.sh | 49 ++- lib/doctor.sh | 70 +--- lib/platforms.sh | 45 --- lib/probe.sh | 24 +- lib/symlinks.sh | 159 +++++++-- lib/targets.sh | 21 ++ lib/ui.sh | 2 +- .../lfx-backend-builder}/SKILL.md | 0 .../references/backend-endpoint.md | 0 .../references/fga-patterns.md | 0 .../references/getting-started.md | 0 .../references/goa-patterns.md | 0 .../references/helm-chart.md | 0 .../references/indexer-patterns.md | 0 .../references/nats-messaging.md | 0 .../references/new-service.md | 0 .../references/query-service.md | 0 .../references/service-types.md | 0 .../lfx-cdp-snowflake-connectors}/SKILL.md | 0 .../lfx-coordinator}/SKILL.md | 0 .../references/fga-protected-types.md | 0 .../references/indexed-data-types.md | 0 .../references/shared-types.md | 0 {lfx-doctor => skills/lfx-doctor}/SKILL.md | 6 +- .../lfx-doctor}/references/fix-recipes.md | 30 +- .../lfx-git-setup}/SKILL.md | 0 .../lfx-git-setup}/references/linux.md | 0 .../lfx-git-setup}/references/mac.md | 0 .../lfx-git-setup}/references/windows.md | 0 {lfx-install => skills/lfx-install}/SKILL.md | 63 ++-- .../lfx-intercom}/SKILL.md | 0 .../lfx-new-skill}/SKILL.md | 47 ++- .../references/conventions-quickref.md | 4 +- .../lfx-pr-catchup}/SKILL.md | 0 .../lfx-pr-resolve}/SKILL.md | 0 .../lfx-pr-resolve}/evals/evals.json | 0 .../lfx-preflight}/SKILL.md | 0 .../lfx-product-architect}/SKILL.md | 0 .../lfx-research}/SKILL.md | 0 {lfx-setup => skills/lfx-setup}/SKILL.md | 0 .../lfx-skills-helper}/SKILL.md | 30 +- .../lfx-skills-helper}/references/intents.md | 13 +- .../lfx-snowflake-access}/SKILL.md | 0 .../lfx-test-journey}/SKILL.md | 0 .../lfx-ui-builder}/SKILL.md | 0 .../references/frontend-component.md | 0 .../references/frontend-service.md | 0 {lfx => skills/lfx}/SKILL.md | 0 {lfx => skills/lfx}/references/glossary.md | 0 {lfx => skills/lfx}/references/quickstart.md | 0 66 files changed, 814 insertions(+), 620 deletions(-) delete mode 120000 .agents/skills/lfx-doctor delete mode 120000 .agents/skills/lfx-install delete mode 120000 .agents/skills/lfx-new-skill delete mode 120000 .agents/skills/lfx-skills-helper create mode 100644 .claude-plugin/plugin.json delete mode 120000 .claude/skills/lfx-doctor delete mode 120000 .claude/skills/lfx-install delete mode 120000 .claude/skills/lfx-new-skill delete mode 120000 .claude/skills/lfx-skills-helper rename {bin => cli}/lfx-skills (74%) delete mode 100644 lib/platforms.sh create mode 100644 lib/targets.sh rename {lfx-backend-builder => skills/lfx-backend-builder}/SKILL.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/backend-endpoint.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/fga-patterns.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/getting-started.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/goa-patterns.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/helm-chart.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/indexer-patterns.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/nats-messaging.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/new-service.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/query-service.md (100%) rename {lfx-backend-builder => skills/lfx-backend-builder}/references/service-types.md (100%) rename {lfx-cdp-snowflake-connectors => skills/lfx-cdp-snowflake-connectors}/SKILL.md (100%) rename {lfx-coordinator => skills/lfx-coordinator}/SKILL.md (100%) rename {lfx-coordinator => skills/lfx-coordinator}/references/fga-protected-types.md (100%) rename {lfx-coordinator => skills/lfx-coordinator}/references/indexed-data-types.md (100%) rename {lfx-coordinator => skills/lfx-coordinator}/references/shared-types.md (100%) rename {lfx-doctor => skills/lfx-doctor}/SKILL.md (96%) rename {lfx-doctor => skills/lfx-doctor}/references/fix-recipes.md (89%) rename {lfx-git-setup => skills/lfx-git-setup}/SKILL.md (100%) rename {lfx-git-setup => skills/lfx-git-setup}/references/linux.md (100%) rename {lfx-git-setup => skills/lfx-git-setup}/references/mac.md (100%) rename {lfx-git-setup => skills/lfx-git-setup}/references/windows.md (100%) rename {lfx-install => skills/lfx-install}/SKILL.md (67%) rename {lfx-intercom => skills/lfx-intercom}/SKILL.md (100%) rename {lfx-new-skill => skills/lfx-new-skill}/SKILL.md (74%) rename {lfx-new-skill => skills/lfx-new-skill}/references/conventions-quickref.md (96%) rename {lfx-pr-catchup => skills/lfx-pr-catchup}/SKILL.md (100%) rename {lfx-pr-resolve => skills/lfx-pr-resolve}/SKILL.md (100%) rename {lfx-pr-resolve => skills/lfx-pr-resolve}/evals/evals.json (100%) rename {lfx-preflight => skills/lfx-preflight}/SKILL.md (100%) rename {lfx-product-architect => skills/lfx-product-architect}/SKILL.md (100%) rename {lfx-research => skills/lfx-research}/SKILL.md (100%) rename {lfx-setup => skills/lfx-setup}/SKILL.md (100%) rename {lfx-skills-helper => skills/lfx-skills-helper}/SKILL.md (70%) rename {lfx-skills-helper => skills/lfx-skills-helper}/references/intents.md (83%) rename {lfx-snowflake-access => skills/lfx-snowflake-access}/SKILL.md (100%) rename {lfx-test-journey => skills/lfx-test-journey}/SKILL.md (100%) rename {lfx-ui-builder => skills/lfx-ui-builder}/SKILL.md (100%) rename {lfx-ui-builder => skills/lfx-ui-builder}/references/frontend-component.md (100%) rename {lfx-ui-builder => skills/lfx-ui-builder}/references/frontend-service.md (100%) rename {lfx => skills/lfx}/SKILL.md (100%) rename {lfx => skills/lfx}/references/glossary.md (100%) rename {lfx => skills/lfx}/references/quickstart.md (100%) diff --git a/.agents/skills/lfx-doctor b/.agents/skills/lfx-doctor deleted file mode 120000 index a241cc2..0000000 --- a/.agents/skills/lfx-doctor +++ /dev/null @@ -1 +0,0 @@ -../../lfx-doctor \ No newline at end of file diff --git a/.agents/skills/lfx-install b/.agents/skills/lfx-install deleted file mode 120000 index 44dd1a2..0000000 --- a/.agents/skills/lfx-install +++ /dev/null @@ -1 +0,0 @@ -../../lfx-install \ No newline at end of file diff --git a/.agents/skills/lfx-new-skill b/.agents/skills/lfx-new-skill deleted file mode 120000 index 86a14f3..0000000 --- a/.agents/skills/lfx-new-skill +++ /dev/null @@ -1 +0,0 @@ -../../lfx-new-skill \ No newline at end of file diff --git a/.agents/skills/lfx-skills-helper b/.agents/skills/lfx-skills-helper deleted file mode 120000 index d463608..0000000 --- a/.agents/skills/lfx-skills-helper +++ /dev/null @@ -1 +0,0 @@ -../../lfx-skills-helper \ No newline at end of file diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..2fccb4c --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,27 @@ +{ + "name": "lfx-skills", + "description": "LFX Self-Service development skills for Claude Code", + "author": { + "name": "The Linux Foundation" + }, + "homepage": "https://github.com/linuxfoundation/lfx-skills", + "repository": "https://github.com/linuxfoundation/lfx-skills", + "license": "MIT", + "skills": [ + "./skills/lfx/", + "./skills/lfx-backend-builder/", + "./skills/lfx-cdp-snowflake-connectors/", + "./skills/lfx-coordinator/", + "./skills/lfx-git-setup/", + "./skills/lfx-intercom/", + "./skills/lfx-pr-catchup/", + "./skills/lfx-pr-resolve/", + "./skills/lfx-preflight/", + "./skills/lfx-product-architect/", + "./skills/lfx-research/", + "./skills/lfx-setup/", + "./skills/lfx-snowflake-access/", + "./skills/lfx-test-journey/", + "./skills/lfx-ui-builder/" + ] +} diff --git a/.claude/skills/lfx-doctor b/.claude/skills/lfx-doctor deleted file mode 120000 index a241cc2..0000000 --- a/.claude/skills/lfx-doctor +++ /dev/null @@ -1 +0,0 @@ -../../lfx-doctor \ No newline at end of file diff --git a/.claude/skills/lfx-install b/.claude/skills/lfx-install deleted file mode 120000 index 44dd1a2..0000000 --- a/.claude/skills/lfx-install +++ /dev/null @@ -1 +0,0 @@ -../../lfx-install \ No newline at end of file diff --git a/.claude/skills/lfx-new-skill b/.claude/skills/lfx-new-skill deleted file mode 120000 index 86a14f3..0000000 --- a/.claude/skills/lfx-new-skill +++ /dev/null @@ -1 +0,0 @@ -../../lfx-new-skill \ No newline at end of file diff --git a/.claude/skills/lfx-skills-helper b/.claude/skills/lfx-skills-helper deleted file mode 120000 index d463608..0000000 --- a/.claude/skills/lfx-skills-helper +++ /dev/null @@ -1 +0,0 @@ -../../lfx-skills-helper \ No newline at end of file diff --git a/.gitignore b/.gitignore index e4ee6ab..5b8c163 100644 --- a/.gitignore +++ b/.gitignore @@ -17,33 +17,16 @@ Thumbs.db # Claude AI assistant — local state (settings.local.json, projects/, etc.) -# stays out of the repo. The negation block un-ignores the four committed -# meta-skill symlinks that make this clone self-discovering for /lfx-install, -# /lfx-new-skill, /lfx-doctor, /lfx-skills-helper. -# -# Important: we use `.claude/*` (not `.claude/` with trailing slash) because -# a fully-ignored directory can't be reopened with negation. The pattern below -# ignores everything UNDER .claude/ while leaving the directory itself -# inspectable, which is the only way `!` rules can take effect on its children. +# stays out of the repo. Claude Code distribution now goes through the +# .claude-plugin manifest; this repo does not commit .claude/skills symlinks. .claude/* -!.claude/skills/ -.claude/skills/* -!.claude/skills/lfx-install -!.claude/skills/lfx-new-skill -!.claude/skills/lfx-doctor -!.claude/skills/lfx-skills-helper -# Same negation pattern for the agents.md ecosystem mirror. +# agents.md-compatible tools can still use the CLI installer; no bootstrap +# symlinks are committed in this repo for now. .agents/* -!.agents/skills/ -.agents/skills/* -!.agents/skills/lfx-install -!.agents/skills/lfx-new-skill -!.agents/skills/lfx-doctor -!.agents/skills/lfx-skills-helper -# Legacy install manifest. Older clones of lfx-skills wrote this; the new CLI -# stores its manifest at ~/.config/lfx-skills/config.json, so this file is no +# Legacy install manifest. Older clones of lfx-skills wrote this; the CLI +# stores its manifest at ~/.lfx-skills/config.json, so this file is no # longer used. Ignored in case any clone still has one lying around. .install-manifest.json diff --git a/AGENTS.md b/AGENTS.md index 7c32a4a..47c97df 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,14 +3,22 @@ # LFX Skills repo -You are inside the LFX Skills source repository. Four meta-skills are auto-discovered here (via committed `.agents/skills/` symlinks) so you can help users without anything else being installed first: +You are inside the LFX Skills source repository. agents.md-compatible tools use the `cli/lfx-skills` CLI to install skills globally or per repo. Claude Code uses the `.claude-plugin/plugin.json` plugin manifest and does not use the CLI installer. The four helper skills live under `skills/` for clone/agent workflows and are intentionally not in the Claude plugin. -- **`/lfx-install`** — guides users through installing the skills (one-time setup after clone). Walks them through platform / config dirs / scope / repos in plain language, then runs the installer. +- **`/lfx-install`** — guides users through agents.md setup after clone. Walks them through scope, config dirs, repos, and dev root, then runs the CLI installer. - **`/lfx-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. - **`/lfx-skills-helper`** — manages the install: lists what's installed, installs/uninstalls in this repo, updates from upstream, shows config. Skill management only — *not* a router. - **`/lfx-new-skill`** — scaffolds a new skill in this repo. Use when the contributor wants to add a new lfx skill or asks "how do I create a new skill". -The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full command reference. +The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full command reference. + +## Claude vs agents.md + +- Claude Code install/update happens through the plugin marketplace: `/plugin marketplace add linuxfoundation/lfx-plugins`, then `/plugin install lfx-skills@lfx`. +- agents.md install/update happens through `cli/lfx-skills`. +- The CLI has no platform picker anymore. It installs agents.md symlinks only. +- To remove old Claude symlink installs from before the plugin pivot, run `cli/lfx-skills uninstall --legacy-claude-only`. +- To remove the complete local agents.md install, CLI symlink, config, and legacy Claude symlinks owned by this clone, run `cli/lfx-skills uninstall --all`. ## When to use which @@ -22,12 +30,12 @@ The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full co ## Repo layout -- `bin/lfx-skills` — multi-subcommand bash CLI. -- `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, platforms). -- `lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. -- `.claude/skills/` and `.agents/skills/` — committed relative symlinks to the four meta-skills above (this is what makes them auto-discoverable when someone clones). -- `install.sh` — thin shim that execs `bin/lfx-skills install "$@"`. -- `~/.lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). +- `cli/lfx-skills` — multi-subcommand bash CLI. +- `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). +- `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. +- `.claude-plugin/plugin.json` — Claude Code plugin manifest. It explicitly lists Claude-facing runtime skills and intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. +- `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. +- `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. - `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. - `~/.local/bin/lfx-skills` (or `~/bin/...`, or `/usr/local/bin/...`) — symlink the installer creates so `lfx-skills` is on PATH everywhere. No shell rc edit needed. @@ -39,4 +47,46 @@ The skill bodies in this repo use Claude Code's tool vocabulary by default (Bash - Every `SKILL.md` starts with `---` on line 1, with `# Copyright …` and `# SPDX-License-Identifier:` as YAML comments on lines 2–3. - The `name:` frontmatter field equals the directory basename. -- DCO-signed commits required (`git commit -s`); GPG signing strongly preferred (`-S`). +- DCO-signed and cryptographically signed manual commits are required (`git commit -s -S`). + +## Releases + +- Releases use GitHub Releases with `vMAJOR.MINOR.PATCH`, like `lfx-mcp`. +- One skill change or a batch of skill changes can ship in the same release. +- Create releases through GitHub Releases or `gh release create`; GitHub creates the tag. +- After release, manually update `../lfx-plugins/.claude-plugin/marketplace.json` so `lfx-skills` points at the new tag. +- `.claude-plugin/plugin.json` must not contain `version`; the `lfx-plugins` marketplace entry owns the published plugin version. + +Version bump guidelines: + +| Change type | Version component | +|---|---| +| Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | **patch** | +| New skills, substantial skill behavior updates, new supported platform behavior | **minor** | +| Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | + +Release command shape: + +```bash +LATEST=$(git tag --sort=-v:refname | head -1) +echo "Latest tag: $LATEST" +NEXT=v0.1.0 + +gh release create "$NEXT" \ + --generate-notes \ + --latest +``` + +After creating the GitHub Release, update the sibling marketplace repo: + +```bash +cd ../lfx-plugins +# Edit .claude-plugin/marketplace.json: +# - plugins[].source.ref = "$NEXT" +# - plugins[].version = "${NEXT#v}" +git add .claude-plugin/marketplace.json +git commit -s -S -m "chore: publish lfx-skills plugin $NEXT" +git push +``` + +After the marketplace update, Claude users update with `/plugin marketplace update lfx` and `/plugin update lfx-skills@lfx`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. diff --git a/CLAUDE.md b/CLAUDE.md index 989c36b..6e07f33 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,14 +3,22 @@ # LFX Skills repo -You are inside the LFX Skills source repository. Four meta-skills are auto-discovered here (via committed `.claude/skills/` symlinks) so you can help users without anything else being installed first: +You are inside the LFX Skills source repository. Claude Code users install the runtime skills through the `.claude-plugin/plugin.json` plugin manifest; agents.md-compatible tools use the `cli/lfx-skills` CLI. Claude Code does not use the CLI installer. The four helper skills live under `skills/` for clone/agent workflows and are intentionally not in the Claude plugin. -- **`/lfx-install`** — guides users through installing the skills (one-time setup after clone). Walks them through platform / config dirs / scope / repos in plain language, then runs the installer. +- **`/lfx-install`** — guides users through agents.md setup after clone. Walks them through scope, config dirs, repos, and dev root, then runs the CLI installer. - **`/lfx-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. - **`/lfx-skills-helper`** — manages the install: lists what's installed, installs/uninstalls in this repo, updates from upstream, shows config. Skill management only — *not* a router. - **`/lfx-new-skill`** — scaffolds a new skill in this repo. Use when the contributor wants to add a new lfx skill or asks "how do I create a new skill". -The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full command reference. +The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full command reference. + +## Claude vs agents.md + +- Claude Code install/update happens through the plugin marketplace: `/plugin marketplace add linuxfoundation/lfx-plugins`, then `/plugin install lfx-skills@lfx`. +- agents.md install/update happens through `cli/lfx-skills`. +- The CLI has no platform picker anymore. It installs agents.md symlinks only. +- To remove old Claude symlink installs from before the plugin pivot, run `cli/lfx-skills uninstall --legacy-claude-only`. +- To remove the complete local agents.md install, CLI symlink, config, and legacy Claude symlinks owned by this clone, run `cli/lfx-skills uninstall --all`. ## When to use which @@ -22,12 +30,12 @@ The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full co ## Repo layout -- `bin/lfx-skills` — multi-subcommand bash CLI. -- `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, platforms). -- `lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. -- `.claude/skills/` and `.agents/skills/` — committed relative symlinks to the four meta-skills above (this is what makes them auto-discoverable when someone clones). -- `install.sh` — thin shim that execs `bin/lfx-skills install "$@"`. -- `~/.lfx-skills/config.json` — user-level manifest written by the CLI (not in this repo). +- `cli/lfx-skills` — multi-subcommand bash CLI. +- `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). +- `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. +- `.claude-plugin/plugin.json` — Claude Code plugin manifest. It explicitly lists Claude-facing runtime skills and intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. +- `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. +- `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. - `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. - `~/.local/bin/lfx-skills` (or `~/bin/...`, or `/usr/local/bin/...`) — symlink the installer creates so `lfx-skills` is on PATH everywhere. No shell rc edit needed. @@ -35,4 +43,46 @@ The CLI itself is at `bin/lfx-skills`. Run `bin/lfx-skills help` for the full co - Every `SKILL.md` starts with `---` on line 1, with `# Copyright …` and `# SPDX-License-Identifier:` as YAML comments on lines 2–3. - The `name:` frontmatter field equals the directory basename. -- DCO-signed commits required (`git commit -s`); GPG signing strongly preferred (`-S`). +- DCO-signed and cryptographically signed manual commits are required (`git commit -s -S`). + +## Releases + +- Releases use GitHub Releases with `vMAJOR.MINOR.PATCH`, like `lfx-mcp`. +- One skill change or a batch of skill changes can ship in the same release. +- Create releases through GitHub Releases or `gh release create`; GitHub creates the tag. +- After release, manually update `../lfx-plugins/.claude-plugin/marketplace.json` so `lfx-skills` points at the new tag. +- `.claude-plugin/plugin.json` must not contain `version`; the `lfx-plugins` marketplace entry owns the published plugin version. + +Version bump guidelines: + +| Change type | Version component | +|---|---| +| Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | **patch** | +| New skills, substantial skill behavior updates, new supported platform behavior | **minor** | +| Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | + +Release command shape: + +```bash +LATEST=$(git tag --sort=-v:refname | head -1) +echo "Latest tag: $LATEST" +NEXT=v0.1.0 + +gh release create "$NEXT" \ + --generate-notes \ + --latest +``` + +After creating the GitHub Release, update the sibling marketplace repo: + +```bash +cd ../lfx-plugins +# Edit .claude-plugin/marketplace.json: +# - plugins[].source.ref = "$NEXT" +# - plugins[].version = "${NEXT#v}" +git add .claude-plugin/marketplace.json +git commit -s -S -m "chore: publish lfx-skills plugin $NEXT" +git push +``` + +After the marketplace update, Claude users update with `/plugin marketplace update lfx` and `/plugin update lfx-skills@lfx`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. diff --git a/README.md b/README.md index 6ff17c7..c65313a 100644 --- a/README.md +++ b/README.md @@ -7,12 +7,50 @@ A collection of AI coding skills that encode the full development workflow for t ## Quick Install +### Claude Code + +Install the Claude Code plugin from the LF marketplace: + +```text +/plugin marketplace add linuxfoundation/lfx-plugins +/plugin install lfx-skills@lfx +``` + +For local plugin development from this checkout: + ```bash git clone https://github.com/linuxfoundation/lfx-skills.git cd lfx-skills +claude --plugin-dir . +``` + +Claude Code plugin commands are namespaced by the plugin name, for example `/lfx-skills:lfx`. The plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. + +### agents.md-Compatible Tools + +For Codex, Gemini CLI, OpenCode, and other agents.md-compatible tools, the setup is agent-first: + +```bash +git clone https://github.com/linuxfoundation/lfx-skills.git +cd lfx-skills +``` + +Start your coding agent in the cloned repo and ask it to set up LFX Skills. The repo includes four helper skills under `skills/` for this flow: + +- `lfx-install` — guided first-time setup +- `lfx-doctor` — install health checks and repair guidance +- `lfx-skills-helper` — list, update, uninstall, and inspect the setup +- `lfx-new-skill` — scaffold a new skill in this repo + +If you prefer to run the installer manually: + +```bash ./install.sh ``` +The CLI installs skill symlinks into agents.md skill directories and records the install in `~/.lfx-skills/config.json`. +agents.md installs include the 15 runtime skills plus `/lfx-doctor` and `/lfx-skills-helper`. `/lfx-install` and `/lfx-new-skill` stay clone-only. + Then restart your AI coding assistant, open any LFX repo, and type `/lfx` to get started. ## How It Works @@ -29,39 +67,19 @@ New to LFX development? Type `/lfx` and say **"show me an example"** for a walkt ## Prerequisites -- An AI coding assistant that supports skill-based workflows (e.g., Claude Code, Gemini CLI). See [docs/platform-install.md](docs/platform-install.md) for setup instructions. +- An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. See [docs/platform-install.md](docs/platform-install.md) for details. - Access to LFX repositories (for the skills to operate on) -- **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. Set it to your preferred location (e.g., `~/code/lfx`, `~/work/lfx`) and the skills will discover your local repos there. Add `export LFX_DEV_ROOT=...` to your shell rc to make it persistent. - -## Manual Installation - -> **Note:** These manual instructions are for Claude Code. For other platforms, see [docs/platform-install.md](docs/platform-install.md). - -If you prefer to install manually instead of using `./install.sh`: +- **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. The CLI records the chosen path in `~/.lfx-skills/dev-root` so skills can discover local repos without shell rc edits. -### Step 1: Clone this repo +## CLI Safety -```bash -git clone https://github.com/linuxfoundation/lfx-skills.git -``` - -### Step 2: Install the skills - -Claude Code auto-discovers skills from `~/.claude/skills/`. Symlink each skill: - -```bash -# From the cloned repo directory -mkdir -p ~/.claude/skills -for skill in lfx-*/ lfx/; do - ln -sf "$(pwd)/$skill" ~/.claude/skills/"$(basename "$skill")" -done -``` +`./install.sh` is a thin wrapper around `cli/lfx-skills install`. It does not edit your shell rc or rewrite your `PATH`. -This makes all `/lfx*` skills available globally. +The only PATH-related write it attempts is creating an `lfx-skills` symlink in an existing writable directory already on your `PATH` (`~/.local/bin`, `~/bin`, `/opt/homebrew/bin`, or `/usr/local/bin`). It refuses to overwrite existing non-symlinks or symlinks it does not own. If no safe directory is available, it prints an alias fallback instead. -### Step 3: Verify +## Verify -Restart your AI coding assistant (or open a new session) in any LFX repo and type `/lfx` — you should see all skills in the autocomplete list: +Restart your AI coding assistant (or open a new session) in any LFX repo and type `/lfx` — agents.md installs should see these commands in autocomplete: ``` /lfx ← start here (plain-language entry point) @@ -83,28 +101,90 @@ Restart your AI coding assistant (or open a new session) in any LFX repo and typ /lfx-skills-helper ← manage what's installed where (install/uninstall/update/list) ``` -### Alternative: Per-repo installation +## Releases and Updates + +Releases follow the same GitHub Release pattern used by LFX service repos such as `lfx-mcp`. + +You can ship one skill change or a batch of skill changes in the same release. Merge the skill changes first, then create a GitHub Release using `vMAJOR.MINOR.PATCH`; GitHub creates the tag. + +### Version bump guidelines + +| Change type | Version component | +|---|---| +| Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | **patch** | +| New skills, substantial skill behavior updates, new supported platform behavior | **minor** | +| Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | + +```bash +LATEST=$(git tag --sort=-v:refname | head -1) +echo "Latest tag: $LATEST" +NEXT=v0.1.0 + +gh release create "$NEXT" \ + --generate-notes \ + --latest +``` + +Then update the self-hosted Claude marketplace in the sibling `lfx-plugins` repo. The marketplace entry must point at the released `lfx-skills` tag, and `version` must be the same tag without the leading `v`: + +```bash +cd ../lfx-plugins +``` + +Update `.claude-plugin/marketplace.json`: + +```json +{ + "source": { + "source": "github", + "repo": "linuxfoundation/lfx-skills", + "ref": "v0.1.0" + }, + "version": "0.1.0" +} +``` + +Commit the marketplace bump with DCO and cryptographic signing: + +```bash +git add .claude-plugin/marketplace.json +git commit -s -S -m "chore: publish lfx-skills plugin v0.1.0" +git push +``` + +Do not put `version` in `.claude-plugin/plugin.json`; Claude resolves manifest versions before marketplace versions, so the marketplace repo owns the published plugin version. -If you prefer skills scoped to a specific repo instead of global: +Claude Code users then update from Claude: + +```text +/plugin marketplace update lfx +/plugin update lfx-skills@lfx +``` + +For agents.md-compatible installs, update from the terminal or ask your coding agent to update LFX Skills: ```bash -# From inside a target repo (e.g., lfx-v2-ui) -mkdir -p .claude/skills -for skill in /path/to/skills/lfx-*/ /path/to/skills/lfx/; do - ln -sf "$skill" .claude/skills/"$(basename "$skill")" -done +lfx-skills update --pull +lfx-skills doctor +``` -# Keep symlinks out of version control -echo '.claude/skills/' >> .gitignore +If `lfx-skills` is not on `PATH`, run the CLI from the clone: + +```bash +/path/to/lfx-skills/cli/lfx-skills update --pull +/path/to/lfx-skills/cli/lfx-skills doctor ``` -### Uninstall +To remove old Claude symlink installs from before the plugin pivot, use the CLI's legacy cleanup mode: + +```bash +lfx-skills uninstall --legacy-claude-only +``` -> **Note:** These instructions are for Claude Code. For other platforms, remove the skill references from your tool's configuration. +To remove the whole agents.md installation, CLI symlink, config, and any legacy Claude symlinks owned by this clone: ```bash -rm -f ~/.claude/skills/lfx-* -rm -f ~/.claude/skills/lfx +lfx-skills uninstall --all ``` ## Architecture @@ -447,63 +527,32 @@ An **interactive setup guide** that walks through environment configuration step ## Project Structure ``` -├── lfx/ -│ ├── SKILL.md # Entry point — plain-language router -│ └── references/ -│ ├── glossary.md # LFX terms explained in plain language -│ └── quickstart.md # Example workflow transcripts -├── lfx-coordinator/ -│ ├── SKILL.md # Orchestrator — plans, delegates, validates -│ └── references/ -│ ├── fga-protected-types.md # FGA-protected resource types (read-restricted) -│ ├── indexed-data-types.md # Queryable resource types and indexing -│ └── shared-types.md # Shared package conventions -├── lfx-research/ -│ └── SKILL.md # Read-only exploration and API validation -├── lfx-backend-builder/ -│ ├── SKILL.md # Express.js proxy + Go microservice codegen -│ └── references/ -│ ├── backend-endpoint.md # Three-file pattern for Express endpoints -│ ├── fga-patterns.md # OpenFGA access control patterns -│ ├── getting-started.md # Repo map and deployment overview -│ ├── goa-patterns.md # Goa v3 DSL conventions -│ ├── helm-chart.md # Service Helm chart structure -│ ├── indexer-patterns.md # OpenSearch indexing patterns -│ ├── nats-messaging.md # NATS subject naming and messaging -│ ├── new-service.md # New resource service checklist -│ ├── query-service.md # Query service API reference -│ └── service-types.md # Native vs wrapper service types -├── lfx-ui-builder/ -│ ├── SKILL.md # Angular 20 frontend codegen -│ └── references/ -│ ├── frontend-component.md # Component patterns and conventions -│ └── frontend-service.md # Service patterns and state management -├── lfx-product-architect/ -│ └── SKILL.md # Architecture guidance and decision trees -├── lfx-preflight/ -│ └── SKILL.md # Pre-PR validation and auto-fix -├── lfx-pr-catchup/ -│ └── SKILL.md # Morning PR catch-up dashboard -├── lfx-pr-resolve/ -│ ├── SKILL.md # Address PR review feedback end-to-end -│ └── evals/ -│ └── evals.json # Skill behavior assertions -├── lfx-setup/ -│ └── SKILL.md # Environment setup guide -├── lfx-test-journey/ -│ └── SKILL.md # Multi-branch journey testing -├── lfx-git-setup/ -│ ├── SKILL.md # DCO sign-off and GPG-signed commit setup -│ └── references/ -│ ├── linux.md # Linux-specific GPG/DCO instructions -│ ├── mac.md # macOS-specific GPG/DCO instructions -│ └── windows.md # Windows-specific GPG/DCO instructions -├── lfx-intercom/ -│ └── SKILL.md # Intercom integration — add or fix to LFX standard -├── lfx-snowflake-access/ -│ └── SKILL.md # Request Snowflake access via Terraform PR -└── lfx-cdp-snowflake-connectors/ - └── SKILL.md # Snowflake connector scaffolding for CDP +├── .claude-plugin/ +│ └── plugin.json # Claude Code plugin manifest +├── cli/ +│ └── lfx-skills # CLI installer and management command +├── lib/ +│ └── *.sh # CLI support libraries +└── skills/ + ├── lfx/ # Entry point — plain-language router + ├── lfx-coordinator/ # Orchestrator — plans, delegates, validates + ├── lfx-research/ # Read-only exploration and API validation + ├── lfx-backend-builder/ # Express.js proxy + Go microservice codegen + ├── lfx-ui-builder/ # Angular 20 frontend codegen + ├── lfx-product-architect/ # Architecture guidance and decision trees + ├── lfx-preflight/ # Pre-PR validation and auto-fix + ├── lfx-pr-catchup/ # Morning PR catch-up dashboard + ├── lfx-pr-resolve/ # Address PR review feedback end-to-end + ├── lfx-setup/ # Environment setup guide + ├── lfx-test-journey/ # Multi-branch journey testing + ├── lfx-git-setup/ # DCO sign-off and GPG-signed commit setup + ├── lfx-intercom/ # Intercom integration — add or fix + ├── lfx-snowflake-access/ # Request Snowflake access via Terraform PR + ├── lfx-cdp-snowflake-connectors/# Snowflake connector scaffolding for CDP + ├── lfx-doctor/ # Install health checks and repair guidance + ├── lfx-skills-helper/ # CLI management front-end + ├── lfx-install/ # CLI install guide for agents.md tools + └── lfx-new-skill/ # Contributor scaffolder ``` ## License diff --git a/bin/lfx-skills b/cli/lfx-skills similarity index 74% rename from bin/lfx-skills rename to cli/lfx-skills index ad722de..c43f487 100755 --- a/bin/lfx-skills +++ b/cli/lfx-skills @@ -7,7 +7,16 @@ set -euo pipefail -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPT_PATH="${BASH_SOURCE[0]}" +while [ -L "$SCRIPT_PATH" ]; do + SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_PATH")" && pwd)" + SCRIPT_PATH="$(readlink "$SCRIPT_PATH")" + case "$SCRIPT_PATH" in + /*) ;; + *) SCRIPT_PATH="$SCRIPT_DIR/$SCRIPT_PATH" ;; + esac +done +SCRIPT_DIR="$(cd -P "$(dirname "$SCRIPT_PATH")" && pwd)" CLONE_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" export CLONE_ROOT @@ -15,8 +24,8 @@ export CLONE_ROOT . "$CLONE_ROOT/lib/ui.sh" # shellcheck source=../lib/probe.sh . "$CLONE_ROOT/lib/probe.sh" -# shellcheck source=../lib/platforms.sh -. "$CLONE_ROOT/lib/platforms.sh" +# shellcheck source=../lib/targets.sh +. "$CLONE_ROOT/lib/targets.sh" # shellcheck source=../lib/config.sh . "$CLONE_ROOT/lib/config.sh" # shellcheck source=../lib/symlinks.sh @@ -66,14 +75,14 @@ cmd_help() { } _help_root() { - cat <<EOF + cat <<'EOF' lfx-skills — install, manage, and diagnose the LFX Skills collection USAGE lfx-skills <command> [options] COMMANDS - install Install LFX skills (interactive wizard or non-interactive flags) + install Install LFX skills for agents.md-compatible tools uninstall Remove installed symlinks (recorded in config.json) update Re-apply the manifest; with --pull, git-pull the clone first check Lightweight verification: does config match filesystem? (exit 0/1) @@ -81,13 +90,13 @@ COMMANDS list List installed or available skills info SKILL Show frontmatter + install locations for a skill config Show or modify ~/.lfx-skills/config.json (get/set) - repos List lf* git repos under \$LFX_DEV_ROOT + repos List lf* git repos under $LFX_DEV_ROOT version Print version of this lfx-skills clone help [CMD] Detailed help for CMD, or this overview EXAMPLES lfx-skills install # interactive wizard - lfx-skills install --yes --platform=claude --scope=global + lfx-skills install --yes --scope=global lfx-skills doctor lfx-skills doctor --fix lfx-skills list --available @@ -103,7 +112,7 @@ CONFIG dir at install time. Recorded in config.json.cli_symlink. -Run \`lfx-skills help <command>\` for command-specific options. +Run `lfx-skills help <command>` for command-specific options. EOF } @@ -113,15 +122,14 @@ USAGE lfx-skills install [flags] INTERACTIVE - Run with no flags to step through: platform → scope → dev root → config - dirs (if global) → repos (if per-repo) → preview → apply → verify. + Run with no flags to step through: scope → dev root → config dirs + (if global) → repos (if per-repo) → preview → apply → verify. + This CLI creates symlinks only for agents.md-compatible tools. NON-INTERACTIVE FLAGS --yes Skip all confirmations (use defaults / supplied flags) - --platform=claude|agents|claude,agents|both 'both' is sugar for 'claude,agents' --scope=global|repo|global,repo|both 'both' is sugar for 'global,repo' --lfx-dev-root=PATH - --claude-config=PATH[,PATH...] Multi-config: pass several Claude config dirs --agents-config=PATH[,PATH...] Multi-config: pass several agents config dirs --repos=PATH[,PATH...] Required if --scope contains 'repo' --no-cli-symlink Skip installing the `lfx-skills` CLI @@ -131,9 +139,8 @@ NON-INTERACTIVE FLAGS EXAMPLE lfx-skills install --yes \ - --platform=both --scope=both \ + --scope=both \ --lfx-dev-root=$HOME/lf \ - --claude-config=$HOME/.claude,$HOME/.claude-work \ --repos=$HOME/lf/lfx-v2-ui,$HOME/lf/lfx-v2-meeting-service EOF } @@ -143,13 +150,16 @@ _help_uninstall() { USAGE lfx-skills uninstall [flags] - Removes every symlink listed in config.json. Asks before deleting env.sh. + Removes recorded agents.md symlinks by default. FLAGS --yes Skip confirmations --scope=global|repo Limit removal to a scope --repos=PATH[,PATH...] With --scope=repo: only remove from these repos - --keep-config Keep config.json + env.sh on disk + --legacy-claude-only Only remove legacy Claude symlinks owned by this clone + --all Remove the whole installation: recorded symlinks, + legacy Claude symlinks, CLI symlink, and config + --keep-config Keep config.json + dev-root on disk EOF } @@ -160,9 +170,8 @@ USAGE Re-apply the install manifest. With --pull, git-pull the canonical clone first. Detects skills present in the clone but missing from the manifest - (newly added upstream OR locally created via /lfx-new-skill) and asks - whether to install them. Detects manifest entries whose source skill is - gone and offers cleanup. + (newly added upstream OR locally created via /lfx-new-skill) and reports + them so you can re-run install. EOF } @@ -183,7 +192,7 @@ USAGE lfx-skills doctor [--json] [--fix] FLAGS - --json Emit results as a JSON array (for scripts / Claude skills) + --json Emit results as a JSON array (for scripts / skills) --fix Interactive fix: walk through fixable issues and prompt per issue EOF } @@ -214,7 +223,7 @@ _help_config() { USAGE lfx-skills config Show full config.json (pretty-printed) lfx-skills config get KEY Print one value - lfx-skills config set KEY=VAL Update a value (re-writes env.sh if relevant) + lfx-skills config set KEY=VAL Update a value (re-writes dev-root if relevant) EOF } @@ -311,7 +320,7 @@ cmd_list() { jq_filter="$jq_filter and .repo == \"$repo\"" fi fi - config_read | jq -r --argjson _ 0 "(.symlinks // [])[] | select($jq_filter) | \"\(.platform)/\(.scope)\t\(.skill)\t\(.link)\"" | sort -u + config_read | jq -r --argjson _ 0 "(.symlinks // [])[] | select($jq_filter) | \"\(.scope)\t\(.skill)\t\(.link)\"" | sort -u } # ─── info ──────────────────────────────────────────────────────────────── @@ -319,13 +328,13 @@ cmd_info() { require_jq local skill="${1:-}" [ -z "$skill" ] && ui_die "Usage: lfx-skills info SKILL" - local skill_md="$CLONE_ROOT/$skill/SKILL.md" + local skill_md="$CLONE_ROOT/skills/$skill/SKILL.md" if [ ! -f "$skill_md" ]; then - ui_error "No SKILL.md for \`$skill\` in $CLONE_ROOT" + ui_error "No SKILL.md for \`$skill\` in $CLONE_ROOT/skills" exit 1 fi ui_section "$skill" - printf 'Source: %s\n' "$CLONE_ROOT/$skill" + printf 'Source: %s\n' "$CLONE_ROOT/skills/$skill" printf '\n' ui_bold "Frontmatter:" awk 'NR==1 && /^---$/ {start=1; next} start && /^---$/ {exit} start {print " " $0}' "$skill_md" @@ -337,7 +346,7 @@ cmd_info() { if [ "$n_locations" -eq 0 ]; then printf ' (not installed anywhere)\n' else - config_read | jq -r --arg s "$skill" '(.symlinks // [])[] | select(.skill == $s) | " \(.platform)/\(.scope) \(.link)"' + config_read | jq -r --arg s "$skill" '(.symlinks // [])[] | select(.skill == $s) | " \(.scope) \(.link)"' fi fi } @@ -427,15 +436,15 @@ EOF cmd_install() { require_jq - local platform="" scope="" lfx_dev_root="" claude_config="" agents_config="" repos="" dry_run=0 + local scope="" lfx_dev_root="" agents_config="" repos="" dry_run=0 local no_cli_symlink=0 while [ $# -gt 0 ]; do case "$1" in --yes) LFX_SKILLS_YES=1; export LFX_SKILLS_YES ;; - --platform=*) platform="${1#*=}" ;; + --platform=*) ui_die "--platform is no longer supported; the CLI installs agents.md skills only." ;; --scope=*) scope="${1#*=}" ;; --lfx-dev-root=*) lfx_dev_root="${1#*=}" ;; - --claude-config=*) claude_config="${1#*=}" ;; + --claude-config=*) ui_die "--claude-config is no longer supported; Claude Code uses the lfx-skills plugin." ;; --agents-config=*) agents_config="${1#*=}" ;; --repos=*) repos="${1#*=}" ;; --no-cli-symlink) no_cli_symlink=1 ;; @@ -452,45 +461,14 @@ cmd_install() { ui_step "Probing your system…" # Initialize all dynamic arrays to empty up-front so `${arr[@]}` is safe under # bash 3.2 with `set -u` even when the probe yields nothing. - local -a claude_dirs_probed=() agents_dirs_probed=() dev_root_probed=() - while IFS= read -r d; do claude_dirs_probed+=("$d"); done < <(probe_claude_config_dirs) + local -a agents_dirs_probed=() dev_root_probed=() while IFS= read -r d; do agents_dirs_probed+=("$d"); done < <(probe_agents_config_dirs) while IFS= read -r d; do dev_root_probed+=("$d"); done < <(probe_dev_root_candidates) - ui_dim " Claude config dirs: ${claude_dirs_probed[*]:-(none found)}" ui_dim " Agents config dirs: ${agents_dirs_probed[*]:-(none found)}" ui_dim " Dev root candidates: ${dev_root_probed[*]:-(none found)}" - # Step 2: PLATFORM (multi-select: claude, agents, or both) - if [ -z "$platform" ]; then - local -a _picked=() - local _line - while IFS= read -r _line; do _picked+=("$_line"); done < <( - ui_checkbox_select "Which AI platform(s)? (select one or both)" "1" 1 \ - "Claude Code" "agents.md (Codex / Gemini CLI / OpenCode)" - ) - if [ "${#_picked[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi - local _p _parts="" - for _p in "${_picked[@]}"; do - case "$_p" in - "Claude Code") _parts="${_parts:+$_parts,}claude" ;; - "agents.md"*) _parts="${_parts:+$_parts,}agents" ;; - esac - done - platform="$_parts" - ui_blank - fi - # Accept "both" as legacy sugar; normalise to comma-joined. - case "$platform" in - both) platform="claude,agents" ;; - claude|agents|claude,agents|agents,claude) ;; - *) ui_die "--platform must be a combination of claude,agents (or 'both')" ;; - esac - local include_claude=0 include_agents=0 - case ",$platform," in *,claude,*) include_claude=1 ;; esac - case ",$platform," in *,agents,*) include_agents=1 ;; esac - - # Step 3: SCOPE (multi-select: global, per-repo, or both) + # Step 2: SCOPE (multi-select: global, per-repo, or both) if [ -z "$scope" ]; then local -a _picked=() local _line @@ -518,7 +496,7 @@ cmd_install() { case ",$scope," in *,global,*) include_global=1 ;; esac case ",$scope," in *,repo,*) include_repo=1 ;; esac - # Step 4: LFX DEV ROOT + # Step 3: LFX DEV ROOT if [ -z "$lfx_dev_root" ]; then if [ -n "${LFX_DEV_ROOT:-}" ]; then lfx_dev_root="$LFX_DEV_ROOT" @@ -529,66 +507,41 @@ cmd_install() { lfx_dev_root="$(ui_input "Where do you keep your LFX repos?" "$HOME/lf")" fi fi - if [ ! -d "$lfx_dev_root" ]; then - ui_warn "$lfx_dev_root does not exist. Creating…" - mkdir -p "$lfx_dev_root" - fi local repo_count - repo_count="$(probe_count_repos_in "$lfx_dev_root")" + if [ -d "$lfx_dev_root" ]; then + repo_count="$(probe_count_repos_in "$lfx_dev_root")" + else + repo_count=0 + fi ui_dim " $lfx_dev_root contains $repo_count lf* git repo(s)." ui_blank - # Step 5a: CONFIG DIR PICKER (only if global scope) + # Step 4a: CONFIG DIR PICKER (only if global scope) # Empty defaults so `${arr[@]}` is safe under bash 3.2 + `set -u`. - local -a chosen_claude_dirs=() chosen_agents_dirs=() + local -a chosen_agents_dirs=() if [ "$include_global" -eq 1 ]; then - if [ "$include_claude" -eq 1 ]; then - if [ -n "$claude_config" ]; then - # Convert comma-separated to newline-separated; reading line-by-line - # avoids changing IFS (which dynamically scopes into nested calls and - # would break SYMLINKS_CLONE_ONLY iteration in symlinks_eligible_skills). - local d - while IFS= read -r d; do chosen_claude_dirs+=("$d"); done < <(printf '%s\n' "$claude_config" | tr ',' '\n') + if [ -n "$agents_config" ]; then + local d + while IFS= read -r d; do chosen_agents_dirs+=("$d"); done < <(printf '%s\n' "$agents_config" | tr ',' '\n') + elif [ "${#agents_dirs_probed[@]}" -gt 0 ]; then + # If multiple, let the user pick. Single = silent default. + if [ "${#agents_dirs_probed[@]}" -eq 1 ]; then + chosen_agents_dirs=("${agents_dirs_probed[0]}") else - if [ "${#claude_dirs_probed[@]}" -eq 0 ]; then - chosen_claude_dirs=("$(platform_default_global_dir claude)") - elif [ "${#claude_dirs_probed[@]}" -eq 1 ]; then - chosen_claude_dirs=("${claude_dirs_probed[0]}") - else - local picked - while IFS= read -r picked; do chosen_claude_dirs+=("$picked"); done < <( - ui_checkbox_select "Which Claude config dir(s)? (select one or more)" "1" 1 \ - "${claude_dirs_probed[@]}" - ) - if [ "${#chosen_claude_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi - ui_blank - fi - fi - fi - if [ "$include_agents" -eq 1 ]; then - if [ -n "$agents_config" ]; then - local d - while IFS= read -r d; do chosen_agents_dirs+=("$d"); done < <(printf '%s\n' "$agents_config" | tr ',' '\n') - elif [ "${#agents_dirs_probed[@]}" -gt 0 ]; then - # If multiple, let the user pick. Single = silent default. - if [ "${#agents_dirs_probed[@]}" -eq 1 ]; then - chosen_agents_dirs=("${agents_dirs_probed[0]}") - else - local picked - while IFS= read -r picked; do chosen_agents_dirs+=("$picked"); done < <( - ui_checkbox_select "Which agents config dir(s)? (select one or more)" "1" 1 \ - "${agents_dirs_probed[@]}" - ) - if [ "${#chosen_agents_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi - ui_blank - fi - else - chosen_agents_dirs=("$(platform_default_global_dir agents)") + local picked + while IFS= read -r picked; do chosen_agents_dirs+=("$picked"); done < <( + ui_checkbox_select "Which agents config dir(s)? (select one or more)" "1" 1 \ + "${agents_dirs_probed[@]}" + ) + if [ "${#chosen_agents_dirs[@]}" -eq 0 ]; then ui_info "Aborted."; return 0; fi + ui_blank fi + else + chosen_agents_dirs=("$(default_agents_config_dir)") fi fi - # Step 5b: REPO PICKER (only if per-repo scope) + # Step 4b: REPO PICKER (only if per-repo scope) local -a chosen_repos=() if [ "$include_repo" -eq 1 ]; then if [ -n "$repos" ]; then @@ -612,7 +565,7 @@ cmd_install() { fi fi - # Step 6: PREVIEW + # Step 5: PREVIEW ui_blank ui_section "Plan" local skill_count @@ -622,26 +575,20 @@ cmd_install() { # count (skills × targets), not just the number of unique skills. local n_targets=0 if [ "$include_global" -eq 1 ]; then - [ "$include_claude" -eq 1 ] && n_targets=$((n_targets + ${#chosen_claude_dirs[@]})) - [ "$include_agents" -eq 1 ] && n_targets=$((n_targets + ${#chosen_agents_dirs[@]})) + n_targets=$((n_targets + ${#chosen_agents_dirs[@]})) fi if [ "$include_repo" -eq 1 ]; then - local _n_platforms=0 - [ "$include_claude" -eq 1 ] && _n_platforms=$((_n_platforms + 1)) - [ "$include_agents" -eq 1 ] && _n_platforms=$((_n_platforms + 1)) - n_targets=$((n_targets + ${#chosen_repos[@]} * _n_platforms)) + n_targets=$((n_targets + ${#chosen_repos[@]})) fi local total_symlinks=$((skill_count * n_targets)) printf ' Skills: %s unique\n' "$skill_count" - printf ' Platform: %s\n' "$platform" printf ' Scope: %s\n' "$scope" printf ' Targets: %s\n' "$n_targets" printf ' Symlinks: %s total (skills × targets)\n' "$total_symlinks" printf ' LFX_DEV_ROOT: %s\n' "$lfx_dev_root" - if [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then - printf ' Claude config dirs:\n' - for d in "${chosen_claude_dirs[@]}"; do printf ' %s\n' "$d"; done + if [ ! -d "$lfx_dev_root" ]; then + printf ' Will create LFX_DEV_ROOT directory.\n' fi if [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then printf ' Agents config dirs:\n' @@ -664,18 +611,18 @@ cmd_install() { return 0 fi - # Step 7: APPLY + # Step 6: APPLY ui_blank + if [ ! -d "$lfx_dev_root" ]; then + ui_step "Creating $lfx_dev_root…" + mkdir -p "$lfx_dev_root" + fi + ui_step "Writing config…" config_init "$CLONE_ROOT" "$lfx_dev_root" + config_prune_obsolete_platforms config_set canonical_clone "$CLONE_ROOT" config_set lfx_dev_root "$lfx_dev_root" - if [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then - config_set_platform_claude "${chosen_claude_dirs[@]}" - fi - if [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then - config_set_platform_agents "${chosen_agents_dirs[@]}" - fi local -a rcs=() while IFS= read -r r; do rcs+=("$r"); done < <(probe_shell_rcs) if [ "${#rcs[@]}" -gt 0 ]; then @@ -684,37 +631,21 @@ cmd_install() { local total_installed=0 total_updated=0 total_skipped=0 if [ "$include_global" -eq 1 ]; then - if [ "$include_claude" -eq 1 ] && [ "${#chosen_claude_dirs[@]}" -gt 0 ]; then - for d in "${chosen_claude_dirs[@]}"; do - ui_blank - ui_step "Claude · global · $d/skills/" - local s; s="$(symlinks_install_all "$CLONE_ROOT" claude global "$d")" - _accumulate_install_summary "$s" - done - fi - if [ "$include_agents" -eq 1 ] && [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then + if [ "${#chosen_agents_dirs[@]}" -gt 0 ]; then for d in "${chosen_agents_dirs[@]}"; do ui_blank ui_step "agents · global · $d/skills/" - local s; s="$(symlinks_install_all "$CLONE_ROOT" agents global "$d")" + local s; s="$(symlinks_install_all "$CLONE_ROOT" global "$d")" _accumulate_install_summary "$s" done fi fi if [ "$include_repo" -eq 1 ] && [ "${#chosen_repos[@]}" -gt 0 ]; then for r in "${chosen_repos[@]}"; do - if [ "$include_claude" -eq 1 ]; then - ui_blank - ui_step "Claude · per-repo · $r/.claude/skills/" - local s; s="$(symlinks_install_all "$CLONE_ROOT" claude repo "$r")" - _accumulate_install_summary "$s" - fi - if [ "$include_agents" -eq 1 ]; then - ui_blank - ui_step "agents · per-repo · $r/.agents/skills/" - local s; s="$(symlinks_install_all "$CLONE_ROOT" agents repo "$r")" - _accumulate_install_summary "$s" - fi + ui_blank + ui_step "agents · per-repo · $r/.agents/skills/" + local s; s="$(symlinks_install_all "$CLONE_ROOT" repo "$r")" + _accumulate_install_summary "$s" done fi @@ -737,7 +668,7 @@ cmd_install() { fi fi - # Step 8: VERIFY + summary + # Step 7: VERIFY + summary ui_blank ui_section "Done" printf ' %d installed, %d updated, %d skipped\n' \ @@ -746,10 +677,10 @@ cmd_install() { if [ -n "$cli_symlink_path" ]; then ui_success "lfx-skills CLI installed to $cli_symlink_path" elif [ "$no_cli_symlink" = "0" ]; then - ui_warn "Couldn't find a writable dir on your PATH for the CLI symlink." + ui_warn "Couldn't safely install the CLI symlink into a writable dir on your PATH." printf ' To use %slfx-skills%s from anywhere, add an alias to your shell rc:\n' \ "$_UI_BOLD" "$_UI_RESET" - printf ' alias lfx-skills='\''%s/bin/lfx-skills'\''\n' "$CLONE_ROOT" + printf ' alias lfx-skills='\''%s/cli/lfx-skills'\''\n' "$CLONE_ROOT" printf ' Or extend PATH to include one of: ~/.local/bin, ~/bin, /usr/local/bin\n' fi @@ -761,8 +692,6 @@ cmd_install() { if [ "${#chosen_repos[@]}" -gt 0 ]; then ui_blank ui_bold "Per-repo install: add to each repo's .gitignore" - printf ' .claude/skills/lfx-*\n' - printf ' .claude/skills/lfx\n' printf ' .agents/skills/lfx-*\n' printf ' .agents/skills/lfx\n' printf ' %s(narrow patterns preserve any of your own committed skills)%s\n' "$_UI_DIM" "$_UI_RESET" @@ -784,12 +713,14 @@ EOF # ─── uninstall ─────────────────────────────────────────────────────────── cmd_uninstall() { require_jq - local scope="" repos="" keep_config=0 + local scope="" repos="" keep_config=0 legacy_only=0 remove_all=0 while [ $# -gt 0 ]; do case "$1" in --yes) LFX_SKILLS_YES=1; export LFX_SKILLS_YES ;; --scope=*) scope="${1#*=}" ;; --repos=*) repos="${1#*=}" ;; + --legacy-claude-only) legacy_only=1 ;; + --all) remove_all=1 ;; --keep-config) keep_config=1 ;; -h|--help) _help_uninstall; return 0 ;; *) ui_error "Unknown flag: $1"; _help_uninstall; exit 1 ;; @@ -797,12 +728,39 @@ cmd_uninstall() { shift done + if [ "$legacy_only" = "1" ] && [ "$remove_all" = "1" ]; then + ui_die "--legacy-claude-only and --all cannot be combined." + fi + if [ "$remove_all" = "1" ] && { [ -n "$scope" ] || [ -n "$repos" ] || [ "$keep_config" = "1" ]; }; then + ui_die "--all cannot be combined with --scope, --repos, or --keep-config." + fi + if [ "$legacy_only" = "1" ]; then + ui_section "Legacy Claude Cleanup" + if ! ui_confirm "Remove legacy lfx-skills Claude symlinks only?" N; then + ui_info "Aborted." + return 0 + fi + _cleanup_legacy_claude + config_prune_obsolete_platforms + ui_success "Legacy Claude cleanup complete." + return 0 + fi + if ! config_exists; then - ui_warn "No config.json — nothing to uninstall." + ui_warn "No config.json — no recorded agents.md install to uninstall." + ui_section "Legacy Claude Cleanup" + if ui_confirm "Check ~/.claude/skills and remove lfx-skills symlinks that point into this clone?" N; then + local legacy_summary + legacy_summary="$(symlinks_uninstall_legacy_claude_root "$CLONE_ROOT")" + ui_dim " $legacy_summary removed/refused/missing/total" + fi return 0 fi ui_section "LFX Skills Uninstall" + if [ "$remove_all" = "1" ]; then + printf ' Mode: remove whole installation\n' + fi if [ -n "$scope" ]; then printf ' Scope filter: %s\n' "$scope" fi @@ -810,7 +768,11 @@ cmd_uninstall() { printf ' Repo filter: %s\n' "$repos" fi - if ! ui_confirm "Remove the recorded symlinks?" Y; then + local confirm_msg="Remove the recorded agents.md symlinks?" + if [ "$remove_all" = "1" ]; then + confirm_msg="Remove the whole lfx-skills installation?" + fi + if ! ui_confirm "$confirm_msg" N; then ui_info "Aborted." return 0 fi @@ -827,7 +789,15 @@ cmd_uninstall() { ui_dim " $s removed/refused/missing/total" fi - if [ "$keep_config" = "0" ] && [ -z "$scope" ]; then + if [ "$remove_all" = "1" ]; then + _cleanup_legacy_claude + elif [ -z "$scope" ] || [ "$scope" = "global" ]; then + _cleanup_legacy_claude + fi + + config_prune_obsolete_platforms + + if { [ "$keep_config" = "0" ] && [ -z "$scope" ]; } || [ "$remove_all" = "1" ]; then # Remove the lfx-skills CLI symlink (e.g. ~/.local/bin/lfx-skills) if we # installed it. Safe-by-default: only removed if it points into the # canonical clone. @@ -839,7 +809,7 @@ cmd_uninstall() { ui_success "Removed lfx-skills CLI symlink at $cli_path" fi fi - if ui_confirm "Remove ~/.lfx-skills/ entirely?" N; then + if [ "$remove_all" = "1" ] || ui_confirm "Remove ~/.lfx-skills/ entirely?" N; then config_clear ui_success "Removed config dir." else @@ -849,6 +819,16 @@ cmd_uninstall() { ui_success "Uninstall complete." } +_cleanup_legacy_claude() { + local recorded_summary root_summary + ui_step "Removing legacy Claude symlinks recorded in config…" + recorded_summary="$(symlinks_uninstall_legacy_claude_recorded)" + ui_dim " $recorded_summary removed/refused/missing/total" + ui_step "Checking legacy Claude root symlinks in ~/.claude/skills…" + root_summary="$(symlinks_uninstall_legacy_claude_root "$CLONE_ROOT")" + ui_dim " $root_summary removed/refused/missing/total" +} + # ─── update ────────────────────────────────────────────────────────────── cmd_update() { require_jq @@ -869,24 +849,26 @@ cmd_update() { if [ "$do_pull" = "1" ]; then ui_step "git pull in $CLONE_ROOT…" - (cd "$CLONE_ROOT" && git pull --ff-only) || ui_warn "git pull failed — proceeding with current state." + (cd "$CLONE_ROOT" && git pull --ff-only) || ui_die "git pull failed." fi - # Re-apply: walk every recorded (platform, scope, base) tuple and re-install. + # Re-apply: walk every recorded agents.md (scope, base) tuple and re-install. ui_step "Re-applying manifest…" local rec config_read | jq -c ' - [(.symlinks // [])[] | {platform, scope, base: (if .scope == "global" then .config_dir else .repo end)}] - | unique_by(.platform + "/" + .scope + "/" + .base) + [(.symlinks // [])[] + | select((.platform // "agents") != "claude") + | {scope, base: (if .scope == "global" then .config_dir else .repo end)}] + | unique_by(.scope + "/" + .base) | .[] ' | while IFS= read -r rec; do - local platform scope base - platform="$(echo "$rec" | jq -r .platform)" + local scope base scope="$(echo "$rec" | jq -r .scope)" base="$(echo "$rec" | jq -r .base)" - ui_dim " → $platform/$scope at $base" - symlinks_install_all "$CLONE_ROOT" "$platform" "$scope" "$base" >/dev/null + ui_dim " → $scope at $base" + symlinks_install_all "$CLONE_ROOT" "$scope" "$base" >/dev/null done + config_prune_obsolete_platforms # Detect new skills (in clone, not in manifest) local installed_set new_skills diff --git a/docs/platform-install.md b/docs/platform-install.md index efcfa54..f8155af 100644 --- a/docs/platform-install.md +++ b/docs/platform-install.md @@ -7,57 +7,113 @@ LFX Skills work with any AI coding assistant that can load context from Markdown ## Claude Code -Claude Code is the reference implementation. Skills are auto-discovered from `~/.claude/skills/`. +Claude Code uses the plugin system. The plugin manifest lives at `.claude-plugin/plugin.json`, and the Claude-facing skills live under `skills/`. -**Automatic installation:** +**Install from the LF marketplace:** + +```text +/plugin marketplace add linuxfoundation/lfx-plugins +/plugin install lfx-skills@lfx +``` + +**Test this checkout locally:** ```bash git clone https://github.com/linuxfoundation/lfx-skills.git cd lfx-skills -./install.sh +claude --plugin-dir . ``` -**Manual installation:** +Plugin skills are namespaced by the plugin name, for example `/lfx-skills:lfx`. + +The plugin manifest explicitly lists Claude-facing runtime skills and excludes the four helper skills: `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. + +The plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. The agents.md/manual installer owns the user-level CLI setup. + +**Verify:** Restart Claude Code (or open a new session) and type `/lfx-skills:lfx`. + +## agents.md-Compatible Tools + +Codex, Gemini CLI, OpenCode, and similar tools use an agent-first setup flow. ```bash -mkdir -p ~/.claude/skills -for skill in lfx-*/ lfx/; do - ln -sf "$(pwd)/$skill" ~/.claude/skills/"$(basename "$skill")" -done +git clone https://github.com/linuxfoundation/lfx-skills.git +cd lfx-skills ``` -**Verify:** Restart Claude Code (or open a new session) and type `/lfx`. +Start your coding agent in the cloned repo and ask it to set up LFX Skills. The repo includes four helper skills under `skills/`: + +- `lfx-install` — guided first-time setup +- `lfx-doctor` — install health checks and repair guidance +- `lfx-skills-helper` — list, update, uninstall, and inspect the setup +- `lfx-new-skill` — scaffold a new skill in this repo -**Per-repo installation** (scoped to a single repo instead of global): +Manual fallback: ```bash -# From inside a target repo (e.g., lfx-v2-ui) -mkdir -p .claude/skills -for skill in /path/to/skills/lfx-*/ /path/to/skills/lfx/; do - ln -sf "$skill" .claude/skills/"$(basename "$skill")" -done -echo '.claude/skills/' >> .gitignore +./install.sh ``` -**Uninstall:** +The installer can target global agents.md skill directories, selected repos, or both. It records the manifest in `~/.lfx-skills/config.json` and the chosen LFX repo root in `~/.lfx-skills/dev-root`. + +The CLI is agents.md-only. Claude Code uses the plugin path above. To remove old Claude symlink installs from before the plugin pivot, run: ```bash -rm -f ~/.claude/skills/lfx-* -rm -f ~/.claude/skills/lfx +lfx-skills uninstall --legacy-claude-only ``` +To remove the complete agents.md install, CLI symlink, config, and any legacy Claude symlinks owned by this clone: + +```bash +lfx-skills uninstall --all +``` +agents.md installs include the 15 runtime skills plus `lfx-doctor` and `lfx-skills-helper`. `lfx-install` and `lfx-new-skill` stay clone-only. + +The CLI does not edit your shell rc or rewrite `PATH`. It only creates an `lfx-skills` symlink in an existing writable directory already on `PATH`, and it refuses to overwrite files or foreign symlinks. + ## Gemini CLI -Reference the SKILL.md files in your project's `GEMINI.md` configuration. Consult [Gemini CLI documentation](https://github.com/google-gemini/gemini-cli) for details on loading external context files. +Use the agents.md-compatible install path above, or reference the SKILL.md files in your project's `GEMINI.md` configuration. Consult [Gemini CLI documentation](https://github.com/google-gemini/gemini-cli) for details on loading external context files. ## Other Platforms Most AI coding tools support loading context from Markdown files. To use LFX Skills with your tool: 1. Clone this repository -2. Point your tool at the SKILL.md files in each skill directory +2. Point your tool at the SKILL.md files in each directory under `skills/` 3. Consult [docs/tool-mapping.md](tool-mapping.md) to translate tool names used in SKILL.md files to your platform's equivalents ## Contributing To add installation instructions for a new platform, submit a PR updating this file and the capability table in [docs/tool-mapping.md](tool-mapping.md). + +## Claude Plugin Releases + +Claude plugin releases follow the same GitHub Release pattern as `lfx-mcp`: + +```bash +LATEST=$(git tag --sort=-v:refname | head -1) +echo "Latest tag: $LATEST" +NEXT=v0.1.0 + +gh release create "$NEXT" \ + --generate-notes \ + --latest +``` + +Use `vMAJOR.MINOR.PATCH` release names. GitHub creates the tag, and the tag is the canonical release version. After the `lfx-skills` release, manually update the sibling `lfx-plugins` marketplace repo so the marketplace entry points at that tag: + +```json +{ + "source": { + "source": "github", + "repo": "linuxfoundation/lfx-skills", + "ref": "v0.1.0" + }, + "version": "0.1.0" +} +``` + +Commit that marketplace change with `git commit -s -S`. + +Do not put `version` in `.claude-plugin/plugin.json`; the `lfx-plugins` marketplace entry owns the published version. diff --git a/install.sh b/install.sh index 9ac0a52..ca5cdb5 100755 --- a/install.sh +++ b/install.sh @@ -2,6 +2,6 @@ # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT # -# Thin shim: the real installer lives at bin/lfx-skills. +# Thin shim: the real installer lives at cli/lfx-skills. # Preserved as `./install.sh` because that's the documented entry point. -exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/bin/lfx-skills" install "$@" +exec "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/cli/lfx-skills" install "$@" diff --git a/lib/config.sh b/lib/config.sh index 985ae1c..3998a33 100644 --- a/lib/config.sh +++ b/lib/config.sh @@ -1,12 +1,11 @@ # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT # -# Config + env.sh I/O for bin/lfx-skills. Sourced; do not execute directly. +# Config + dev-root I/O for cli/lfx-skills. Sourced; do not execute directly. # Requires: jq (caller validates with probe_have_jq). -# Config dir lives at $HOME/.lfx-skills (sibling of $HOME/.claude, $HOME/.codex, -# etc. — the convention the user-facing AI tools follow). Each config artefact -# is a sibling file inside that dir: +# Config dir lives at $HOME/.lfx-skills. Each config artefact is a sibling file +# inside that dir: # config.json manifest of every symlink the CLI installed (incl. cli_symlink) # dev-root single-line text file containing the resolved LFX_DEV_ROOT. # Skills `cat` this to get the dev root without depending on @@ -48,7 +47,6 @@ config_init() { shell_rc_detected: [], installed_at: $now, last_updated_at: $now, - platforms: {}, symlinks: [] }' > "$cfg" fi @@ -64,7 +62,7 @@ config_read() { } # config_get KEY → echo the JSON value (raw) at .KEY. Empty if missing. -# KEY may be a dotted path: e.g., "platforms.claude.config_dirs" +# KEY may be a dotted path: e.g., "shell_rc_detected" config_get() { local key="$1" config_read | jq -r --arg k "$key" ' @@ -111,16 +109,15 @@ config_set_json() { ' < "$cfg" > "$tmp" && mv "$tmp" "$cfg" } -# config_add_symlink PLATFORM SCOPE LINK SOURCE [BASE] +# config_add_symlink SCOPE LINK SOURCE [BASE] # Append a symlink record to the symlinks array. -# PLATFORM: claude | agents # SCOPE: global | repo # LINK: absolute path of the symlink we created # SOURCE: absolute path of the skill directory the symlink points at # BASE: for global → the config_dir; for repo → the repo path # Skill name is inferred from basename(LINK). config_add_symlink() { - local platform="$1" scope="$2" link="$3" source="$4" base="${5:-}" + local scope="$1" link="$2" source="$3" base="${4:-}" local skill cfg now tmp skill="$(basename "$link")" cfg="$(config_json_path)" @@ -129,22 +126,20 @@ config_add_symlink() { local entry if [ "$scope" = "global" ]; then entry="$(jq -n \ - --arg platform "$platform" \ --arg scope "$scope" \ --arg config_dir "$base" \ --arg skill "$skill" \ --arg link "$link" \ --arg source "$source" \ - '{platform:$platform, scope:$scope, config_dir:$config_dir, skill:$skill, link:$link, source:$source}')" + '{scope:$scope, config_dir:$config_dir, skill:$skill, link:$link, source:$source}')" else entry="$(jq -n \ - --arg platform "$platform" \ --arg scope "$scope" \ --arg repo "$base" \ --arg skill "$skill" \ --arg link "$link" \ --arg source "$source" \ - '{platform:$platform, scope:$scope, repo:$repo, skill:$skill, link:$link, source:$source}')" + '{scope:$scope, repo:$repo, skill:$skill, link:$link, source:$source}')" fi jq --argjson entry "$entry" --arg now "$now" ' .symlinks = ((.symlinks // []) | map(select(.link != $entry.link)) + [$entry]) @@ -190,22 +185,6 @@ config_list_symlink_records() { config_read | jq -c --argjson _ 0 "(.symlinks // [])[] | select($filter)" } -# config_set_platform_claude DIR... → record the list of claude config dirs. -config_set_platform_claude() { - local dirs_json - dirs_json="$(printf '%s\n' "$@" | jq -R . | jq -sc .)" - config_set_json "platforms.claude" "{\"config_dirs\": $dirs_json}" -} - -# config_set_platform_agents DIR... → record the list of agents config dirs. -# Mirror of config_set_platform_claude — both use a `config_dirs` array so the -# manifest shape is symmetric across platforms. -config_set_platform_agents() { - local dirs_json - dirs_json="$(printf '%s\n' "$@" | jq -R . | jq -sc .)" - config_set_json "platforms.agents" "{\"config_dirs\": $dirs_json}" -} - # config_set_shell_rcs RC... → record the list of detected shell rcs. config_set_shell_rcs() { local rcs_json @@ -219,9 +198,19 @@ config_clear() { rmdir "$(config_dir)" 2>/dev/null || true } +# config_prune_obsolete_platforms → remove legacy platform metadata. +config_prune_obsolete_platforms() { + config_exists || return 0 + local cfg now tmp + cfg="$(config_json_path)" + now="$(date -u +%Y-%m-%dT%H:%M:%SZ)" + tmp="$cfg.tmp.$$" + jq --arg now "$now" 'del(.platforms) | .last_updated_at = $now' < "$cfg" > "$tmp" && mv "$tmp" "$cfg" +} + # write_dev_root_file → write the resolved LFX_DEV_ROOT path to # $HOME/.lfx-skills/dev-root as a single line. Skills read this with `cat` so -# they don't have to source env.sh, parse JSON, or depend on env vars. +# they don't have to source shell config, parse JSON, or depend on env vars. write_dev_root_file() { local devroot path devroot="$(config_get lfx_dev_root)" diff --git a/lib/doctor.sh b/lib/doctor.sh index 602f530..d813f1d 100644 --- a/lib/doctor.sh +++ b/lib/doctor.sh @@ -1,7 +1,7 @@ # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT # -# Diagnostic checks for bin/lfx-skills doctor. Sourced; do not execute directly. +# Diagnostic checks for cli/lfx-skills doctor. Sourced; do not execute directly. # # Architecture: records flow as text. Each check writes pipe-delimited records # to stdout; aggregators concatenate; formatters consume stdin and render either @@ -138,63 +138,12 @@ check_lfx_dev_root() { fi } -# ─── Category 4: Platforms ─────────────────────────────────────────────── -check_platforms() { - local platforms_json - platforms_json="$(config_get_json platforms)" - if [ -z "$platforms_json" ] || [ "$platforms_json" = "null" ] || [ "$platforms_json" = "{}" ]; then - _emit warn platforms-none platforms "No platforms recorded" \ - "config.json has no platforms entry." no - return - fi - - local claude_dirs - claude_dirs="$(echo "$platforms_json" | jq -r '.claude.config_dirs[]?' 2>/dev/null)" - if [ -n "$claude_dirs" ]; then - if command -v claude >/dev/null 2>&1; then - _emit pass cli-on-path platforms "claude CLI on PATH" "$(command -v claude)" no - else - _emit warn cli-not-on-path platforms "claude CLI not on PATH" \ - "Install Claude Code or check your PATH." no - fi - local d - while IFS= read -r d; do - [ -z "$d" ] && continue - if [ -d "$d" ]; then - _emit pass platform-dir-ok platforms "Claude config dir exists" "$d" no - else - _emit fail platform-dir-missing platforms \ - "Claude config dir missing" "$d" no - fi - done <<EOF -$claude_dirs -EOF - fi - - local agents_dirs - agents_dirs="$(echo "$platforms_json" | jq -r '.agents.config_dirs[]?' 2>/dev/null)" - if [ -n "$agents_dirs" ]; then - local d - while IFS= read -r d; do - [ -z "$d" ] && continue - if [ -d "$d" ]; then - _emit pass platform-dir-ok platforms "Agents config dir exists" "$d" no - else - _emit fail platform-dir-missing platforms \ - "Agents config dir missing" "$d" no - fi - done <<EOF -$agents_dirs -EOF - fi -} - -# ─── Category 5: Frontmatter ───────────────────────────────────────────── +# ─── Category 4: Frontmatter ───────────────────────────────────────────── check_frontmatter() { local clone clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" local skill_md skill_name name_in_md desc_in_md - for skill_md in "$clone"/lfx*/SKILL.md; do + for skill_md in "$clone"/skills/lfx*/SKILL.md; do [ -f "$skill_md" ] || continue skill_name="$(basename "$(dirname "$skill_md")")" if [ "$(head -1 "$skill_md")" != "---" ]; then @@ -225,11 +174,11 @@ check_frontmatter() { done } -# ─── Category 6: Routing ───────────────────────────────────────────────── +# ─── Category 5: Routing ───────────────────────────────────────────────── check_routing() { local clone lfx_md clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" - lfx_md="$clone/lfx/SKILL.md" + lfx_md="$clone/skills/lfx/SKILL.md" if [ ! -f "$lfx_md" ]; then _emit warn routing-no-lfx routing "/lfx skill not found" \ "$lfx_md missing — routing check skipped." no @@ -242,10 +191,10 @@ check_routing() { while IFS= read -r s; do [ -z "$s" ] && continue local skill_name="${s#/}" - if [ ! -d "$clone/$skill_name" ]; then + if [ ! -d "$clone/skills/$skill_name" ]; then _emit warn routing-dangling routing \ "/lfx mentions $s but skill directory missing" \ - "Expected $clone/$skill_name" no + "Expected $clone/skills/$skill_name" no fi done <<EOF $routed @@ -272,12 +221,12 @@ EOF done < <(symlinks_eligible_skills "$clone") } -# ─── Category 7: License headers ───────────────────────────────────────── +# ─── Category 6: License headers ───────────────────────────────────────── check_license_headers() { local clone clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" local skill_md skill_name - for skill_md in "$clone"/lfx*/SKILL.md; do + for skill_md in "$clone"/skills/lfx*/SKILL.md; do [ -f "$skill_md" ] || continue skill_name="$(basename "$(dirname "$skill_md")")" if head -4 "$skill_md" | grep -qF "Copyright The Linux Foundation"; then @@ -295,7 +244,6 @@ doctor_run_all() { check_symlinks check_source_clone check_lfx_dev_root - check_platforms check_frontmatter check_routing check_license_headers diff --git a/lib/platforms.sh b/lib/platforms.sh deleted file mode 100644 index 69ff490..0000000 --- a/lib/platforms.sh +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -# -# Per-platform target path construction. Sourced; do not execute directly. - -# platform_target_dir PLATFORM SCOPE BASE -# PLATFORM: claude | agents -# SCOPE: global | repo -# BASE: for global → the config dir (e.g., $HOME/.claude) -# for repo → the repo path (e.g., $HOME/lf/lfx-v2-ui) -# Echoes the absolute target directory where skills should be symlinked. -platform_target_dir() { - local platform="$1" scope="$2" base="$3" - case "$platform:$scope" in - claude:global) printf '%s/skills\n' "$base" ;; - claude:repo) printf '%s/.claude/skills\n' "$base" ;; - agents:global) printf '%s/skills\n' "$base" ;; - agents:repo) printf '%s/.agents/skills\n' "$base" ;; - *) return 1 ;; - esac -} - -# platform_default_global_dir PLATFORM → echo the conventional global config dir. -# claude → $HOME/.claude -# agents → $HOME/.agents -platform_default_global_dir() { - case "$1" in - claude) printf '%s/.claude\n' "$HOME" ;; - agents) printf '%s/.agents\n' "$HOME" ;; - *) return 1 ;; - esac -} - -# platform_supported PLATFORM → return 0 if PLATFORM is known. -platform_supported() { - case "$1" in - claude|agents) return 0 ;; - *) return 1 ;; - esac -} - -# platform_all → echo every supported platform on its own line. -platform_all() { - printf 'claude\nagents\n' -} diff --git a/lib/probe.sh b/lib/probe.sh index b1d9e51..c3700ae 100644 --- a/lib/probe.sh +++ b/lib/probe.sh @@ -1,30 +1,20 @@ # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT # -# System probes for bin/lfx-skills. Sourced; do not execute directly. +# System probes for cli/lfx-skills. Sourced; do not execute directly. # All probe_* functions have no side effects — they only inspect the system. -# probe_clis → echo each detected CLI on its own line. -# Detects: claude, codex, gemini, opencode. +# probe_clis → echo each detected agents.md-compatible CLI on its own line. +# Detects: codex, gemini, opencode. probe_clis() { local cli - for cli in claude codex gemini opencode; do + for cli in codex gemini opencode; do if command -v "$cli" >/dev/null 2>&1; then printf '%s\n' "$cli" fi done } -# probe_claude_config_dirs → echo each ~/.claude*/ directory that exists. -# Catches .claude, .claude-work, .claude-personal, etc. -probe_claude_config_dirs() { - local d - for d in "$HOME"/.claude*; do - [ -d "$d" ] || continue - printf '%s\n' "$d" - done -} - # probe_agents_config_dirs → echo each ~/.agents*/ directory that exists. probe_agents_config_dirs() { local d @@ -66,7 +56,7 @@ probe_count_repos_in() { } # probe_shell_rcs → echo each shell rc file that exists. -# Used by the doctor "is env.sh sourced?" check. +# Kept for manifest context; install does not edit shell rc files. probe_shell_rcs() { local rc for rc in "$HOME/.zshrc" "$HOME/.bashrc" "$HOME/.bash_profile" "$HOME/.profile" "$HOME/.config/fish/config.fish"; do @@ -101,13 +91,13 @@ probe_writable_path_dir() { # probe_canonical_clone → echo the absolute path of the lfx-skills clone this script lives in. # Caller passes the script's $0 (or any path inside the clone). -# Walks up from bin/ or lib/ to the clone root. +# Walks up from cli/ or lib/ to the clone root. probe_canonical_clone() { local script_path="$1" local script_dir script_dir="$(cd "$(dirname "$script_path")" && pwd)" case "$(basename "$script_dir")" in - bin|lib) (cd "$script_dir/.." && pwd) ;; + cli|lib) (cd "$script_dir/.." && pwd) ;; *) printf '%s\n' "$script_dir" ;; esac } diff --git a/lib/symlinks.sh b/lib/symlinks.sh index fc14790..8e9c412 100644 --- a/lib/symlinks.sh +++ b/lib/symlinks.sh @@ -1,12 +1,12 @@ # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT # -# Symlink create/remove logic for bin/lfx-skills. Sourced; do not execute directly. +# Symlink create/remove logic for cli/lfx-skills. Sourced; do not execute directly. # 3-way create logic lifted from install.sh and battle-tested. -# Skills that should NEVER be installed to user-level or per-repo targets. -# These are clone-only meta-skills: they live only inside the lfx-skills clone -# (auto-discovered via committed `.claude/skills/` and `.agents/skills/` symlinks). +# Skills that should NEVER be installed to user-level or per-repo targets by +# the CLI. These are authoring/install helpers for this clone, not runtime LFX +# workflow skills. SYMLINKS_CLONE_ONLY="lfx-install lfx-new-skill" # symlinks_eligible_skills CLONE → echo each installable skill directory name (one per line). @@ -16,8 +16,9 @@ SYMLINKS_CLONE_ONLY="lfx-install lfx-new-skill" # - basename is NOT in SYMLINKS_CLONE_ONLY symlinks_eligible_skills() { local clone="$1" + local skills_dir="$clone/skills" local skill_path skill_name excluded - for skill_path in "$clone"/lfx*/; do + for skill_path in "$skills_dir"/lfx*/; do [ -d "$skill_path" ] || continue [ -f "${skill_path}SKILL.md" ] || continue skill_name="$(basename "${skill_path%/}")" @@ -30,14 +31,22 @@ symlinks_eligible_skills() { done } -# symlinks_create_one SOURCE TARGET → create or update one symlink. +# symlinks_create_one SOURCE TARGET [PREVIOUS_SOURCE] → create or update one symlink. # Echoes one of: installed | updated | skipped | failed # Stderr gets a one-line human-readable note. -# This is the lifted 3-way logic from the original install.sh (lines 31-46). +# Existing symlinks are updated only when they already point to SOURCE, or when +# config.json records the existing source as one this CLI previously installed. symlinks_create_one() { - local source="$1" target="$2" + local source="$1" target="$2" previous="${3:-}" local name; name="$(basename "$target")" if [ -L "$target" ]; then + local actual + actual="$(readlink "$target" 2>/dev/null || true)" + if [ "$actual" != "$source" ] && { [ -z "$previous" ] || [ "$actual" != "$previous" ]; }; then + ui_warn " skipped $name (symlink points to $actual, not this lfx-skills install)" + printf 'skipped\n' + return 0 + fi rm "$target" if ln -s "$source" "$target"; then ui_dim " updated $name" >&2 @@ -60,19 +69,18 @@ symlinks_create_one() { fi } -# symlinks_install_all CLONE PLATFORM SCOPE BASE +# symlinks_install_all CLONE SCOPE BASE # CLONE: absolute path to lfx-skills clone -# PLATFORM: claude | agents # SCOPE: global | repo # BASE: for global → config dir; for repo → repo path -# Creates one symlink per eligible skill in CLONE into the platform target dir. +# Creates one symlink per eligible skill in CLONE into the agents.md target dir. # Records each created symlink in config.json. # Echoes a summary line: "<installed>/<updated>/<skipped>/<total>" symlinks_install_all() { - local clone="$1" platform="$2" scope="$3" base="$4" + local clone="$1" scope="$2" base="$3" local target_dir - target_dir="$(platform_target_dir "$platform" "$scope" "$base")" || { - ui_error "Unknown platform/scope: $platform/$scope" + target_dir="$(target_dir_for_scope "$scope" "$base")" || { + ui_error "Unknown scope: $scope" return 1 } mkdir -p "$target_dir" @@ -83,17 +91,22 @@ symlinks_install_all() { while IFS= read -r skill; do [ -z "$skill" ] && continue n_total=$((n_total + 1)) - source="$clone/$skill" + source="$clone/skills/$skill" target="$target_dir/$skill" - outcome="$(symlinks_create_one "$source" "$target")" + local previous_source="" + if config_exists; then + previous_source="$(config_read | jq -r --arg l "$target" '(.symlinks // [])[] | select(.link == $l) | .source' | head -1)" + [ "$previous_source" = "null" ] && previous_source="" + fi + outcome="$(symlinks_create_one "$source" "$target" "$previous_source")" case "$outcome" in installed) n_installed=$((n_installed + 1)) - config_add_symlink "$platform" "$scope" "$target" "$source" "$base" + config_add_symlink "$scope" "$target" "$source" "$base" ;; updated) n_updated=$((n_updated + 1)) - config_add_symlink "$platform" "$scope" "$target" "$source" "$base" + config_add_symlink "$scope" "$target" "$source" "$base" ;; skipped) n_skipped=$((n_skipped + 1)) @@ -173,35 +186,123 @@ EOF printf '%d/%d/%d/%d\n' "$n_removed" "$n_refused" "$n_missing" "$n_total" } +# symlinks_uninstall_legacy_claude_recorded +# Remove legacy config-recorded Claude symlinks, and drop stale records for +# missing links. Refuses links that no longer point to the recorded source. +symlinks_uninstall_legacy_claude_recorded() { + config_exists || { printf '0/0/0/0\n'; return 0; } + local n_removed=0 n_refused=0 n_missing=0 n_total=0 + local snapshot link source outcome + snapshot="$(config_read | jq -r '(.symlinks // [])[] | select(.platform == "claude") | .link')" + + while IFS= read -r link; do + [ -z "$link" ] && continue + n_total=$((n_total + 1)) + source="$(config_read | jq -r --arg l "$link" '(.symlinks // [])[] | select(.link == $l) | .source' | head -1)" + outcome="$(symlinks_remove_one "$link" "$source")" + case "$outcome" in + removed) + n_removed=$((n_removed + 1)) + config_remove_symlink "$link" + ;; + missing) + n_missing=$((n_missing + 1)) + config_remove_symlink "$link" + ;; + *) + n_refused=$((n_refused + 1)) + ;; + esac + done <<EOF +$snapshot +EOF + + printf '%d/%d/%d/%d\n' "$n_removed" "$n_refused" "$n_missing" "$n_total" +} + +# symlinks_uninstall_legacy_claude_root CLONE +# Remove old root ~/.claude/skills links only when they are LFX skill links and +# their target points into this lfx-skills clone. This intentionally does not +# touch arbitrary Claude skills or non-symlink files. +symlinks_uninstall_legacy_claude_root() { + local clone="$1" + local target_dir="$HOME/.claude/skills" + local n_removed=0 n_refused=0 n_missing=0 n_total=0 + [ -d "$target_dir" ] || { printf '0/0/0/0\n'; return 0; } + + local link name actual resolved dir base + for link in "$target_dir"/lfx "$target_dir"/lfx-*; do + [ -e "$link" ] || [ -L "$link" ] || continue + n_total=$((n_total + 1)) + name="$(basename "$link")" + case "$name" in + lfx|lfx-*) ;; + *) n_refused=$((n_refused + 1)); continue ;; + esac + if [ ! -L "$link" ]; then + ui_warn " refused $link (not a symlink — skipping for safety)" + n_refused=$((n_refused + 1)) + continue + fi + actual="$(readlink "$link" || true)" + case "$actual" in + /*) resolved="$actual" ;; + *) + dir="$(dirname "$link")/$(dirname "$actual")" + base="$(basename "$actual")" + if [ -d "$dir" ]; then + resolved="$(cd "$dir" && pwd -P)/$base" + else + resolved="$actual" + fi + ;; + esac + case "$resolved" in + "$clone"/*) + rm "$link" + ui_dim " removed $link" >&2 + n_removed=$((n_removed + 1)) + ;; + *) + ui_warn " refused $link (points to $actual, not this lfx-skills install)" + n_refused=$((n_refused + 1)) + ;; + esac + done + + printf '%d/%d/%d/%d\n' "$n_removed" "$n_refused" "$n_missing" "$n_total" +} + # install_cli_symlink CLONE [TARGET_DIR] -# Symlink <CLONE>/bin/lfx-skills into a writable PATH dir so the user can type +# Symlink <CLONE>/cli/lfx-skills into a writable PATH dir so the user can type # `lfx-skills` from anywhere — no shell rc edit required. If TARGET_DIR is not # given, picks the best candidate via probe_writable_path_dir. # Echoes the symlink path on success (e.g. "/Users/x/.local/bin/lfx-skills"). # Returns 1 if no writable PATH dir is available, OR if the target is occupied -# by something we don't own. Never silently clobbers a user's own script / -# foreign symlink. The caller should print fallback instructions. +# by anything other than the exact symlink this clone owns. Never edits PATH, +# and never silently clobbers a user's own script or a foreign symlink. The +# caller should print fallback instructions. install_cli_symlink() { local clone="$1" target_dir="${2:-}" local source target - source="$clone/bin/lfx-skills" + source="$clone/cli/lfx-skills" + [ -x "$source" ] || return 1 if [ -z "$target_dir" ]; then target_dir="$(probe_writable_path_dir)" || return 1 fi + [ -d "$target_dir" ] || return 1 + [ -w "$target_dir" ] || return 1 target="$target_dir/lfx-skills" if [ -L "$target" ]; then - # An existing symlink at the target. Only safe to replace if it already - # points where WE want it to point (idempotent re-install) or into the - # canonical clone (e.g. another lfx-skills clone — mark the clobber loudly). + # An existing symlink at the target is only safe to replace when it already + # points exactly where this install wants it to point. Anything else may be + # user-managed, from another checkout, or from another tool. local actual; actual="$(readlink "$target" 2>/dev/null || true)" if [ "$actual" = "$source" ]; then : # already correct — fall through to ln (will recreate identically) rm "$target" - elif [ -n "$clone" ] && [ "${actual#"$clone/"}" != "$actual" ]; then - ui_warn "Replacing existing symlink at $target (was $actual, replacing with $source)" - rm "$target" else - ui_warn "Refused to overwrite $target → $actual (not owned by this lfx-skills clone)" + ui_warn "Refused to overwrite existing symlink at $target → $actual" return 1 fi elif [ -e "$target" ]; then diff --git a/lib/targets.sh b/lib/targets.sh new file mode 100644 index 0000000..bd166ec --- /dev/null +++ b/lib/targets.sh @@ -0,0 +1,21 @@ +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +# +# agents.md target path construction. Sourced; do not execute directly. + +# target_dir_for_scope SCOPE BASE +# SCOPE: global | repo +# BASE: for global → the agents config dir (e.g., $HOME/.agents) +# for repo → the repo path (e.g., $HOME/lf/lfx-v2-ui) +# Echoes the absolute target directory where skills should be symlinked. +target_dir_for_scope() { + local scope="$1" base="$2" + case "$scope" in + global) printf '%s/skills\n' "$base" ;; + repo) printf '%s/.agents/skills\n' "$base" ;; + *) return 1 ;; + esac +} + +# default_agents_config_dir → echo the conventional global agents config dir. +default_agents_config_dir() { printf '%s/.agents\n' "$HOME"; } diff --git a/lib/ui.sh b/lib/ui.sh index 34421dd..a043097 100644 --- a/lib/ui.sh +++ b/lib/ui.sh @@ -1,7 +1,7 @@ # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT # -# UI helpers for bin/lfx-skills. Sourced; do not execute directly. +# UI helpers for cli/lfx-skills. Sourced; do not execute directly. # Targets bash 3.2+ (stock macOS). # Color codes — only emitted when stdout is a TTY and NO_COLOR is unset. diff --git a/lfx-backend-builder/SKILL.md b/skills/lfx-backend-builder/SKILL.md similarity index 100% rename from lfx-backend-builder/SKILL.md rename to skills/lfx-backend-builder/SKILL.md diff --git a/lfx-backend-builder/references/backend-endpoint.md b/skills/lfx-backend-builder/references/backend-endpoint.md similarity index 100% rename from lfx-backend-builder/references/backend-endpoint.md rename to skills/lfx-backend-builder/references/backend-endpoint.md diff --git a/lfx-backend-builder/references/fga-patterns.md b/skills/lfx-backend-builder/references/fga-patterns.md similarity index 100% rename from lfx-backend-builder/references/fga-patterns.md rename to skills/lfx-backend-builder/references/fga-patterns.md diff --git a/lfx-backend-builder/references/getting-started.md b/skills/lfx-backend-builder/references/getting-started.md similarity index 100% rename from lfx-backend-builder/references/getting-started.md rename to skills/lfx-backend-builder/references/getting-started.md diff --git a/lfx-backend-builder/references/goa-patterns.md b/skills/lfx-backend-builder/references/goa-patterns.md similarity index 100% rename from lfx-backend-builder/references/goa-patterns.md rename to skills/lfx-backend-builder/references/goa-patterns.md diff --git a/lfx-backend-builder/references/helm-chart.md b/skills/lfx-backend-builder/references/helm-chart.md similarity index 100% rename from lfx-backend-builder/references/helm-chart.md rename to skills/lfx-backend-builder/references/helm-chart.md diff --git a/lfx-backend-builder/references/indexer-patterns.md b/skills/lfx-backend-builder/references/indexer-patterns.md similarity index 100% rename from lfx-backend-builder/references/indexer-patterns.md rename to skills/lfx-backend-builder/references/indexer-patterns.md diff --git a/lfx-backend-builder/references/nats-messaging.md b/skills/lfx-backend-builder/references/nats-messaging.md similarity index 100% rename from lfx-backend-builder/references/nats-messaging.md rename to skills/lfx-backend-builder/references/nats-messaging.md diff --git a/lfx-backend-builder/references/new-service.md b/skills/lfx-backend-builder/references/new-service.md similarity index 100% rename from lfx-backend-builder/references/new-service.md rename to skills/lfx-backend-builder/references/new-service.md diff --git a/lfx-backend-builder/references/query-service.md b/skills/lfx-backend-builder/references/query-service.md similarity index 100% rename from lfx-backend-builder/references/query-service.md rename to skills/lfx-backend-builder/references/query-service.md diff --git a/lfx-backend-builder/references/service-types.md b/skills/lfx-backend-builder/references/service-types.md similarity index 100% rename from lfx-backend-builder/references/service-types.md rename to skills/lfx-backend-builder/references/service-types.md diff --git a/lfx-cdp-snowflake-connectors/SKILL.md b/skills/lfx-cdp-snowflake-connectors/SKILL.md similarity index 100% rename from lfx-cdp-snowflake-connectors/SKILL.md rename to skills/lfx-cdp-snowflake-connectors/SKILL.md diff --git a/lfx-coordinator/SKILL.md b/skills/lfx-coordinator/SKILL.md similarity index 100% rename from lfx-coordinator/SKILL.md rename to skills/lfx-coordinator/SKILL.md diff --git a/lfx-coordinator/references/fga-protected-types.md b/skills/lfx-coordinator/references/fga-protected-types.md similarity index 100% rename from lfx-coordinator/references/fga-protected-types.md rename to skills/lfx-coordinator/references/fga-protected-types.md diff --git a/lfx-coordinator/references/indexed-data-types.md b/skills/lfx-coordinator/references/indexed-data-types.md similarity index 100% rename from lfx-coordinator/references/indexed-data-types.md rename to skills/lfx-coordinator/references/indexed-data-types.md diff --git a/lfx-coordinator/references/shared-types.md b/skills/lfx-coordinator/references/shared-types.md similarity index 100% rename from lfx-coordinator/references/shared-types.md rename to skills/lfx-coordinator/references/shared-types.md diff --git a/lfx-doctor/SKILL.md b/skills/lfx-doctor/SKILL.md similarity index 96% rename from lfx-doctor/SKILL.md rename to skills/lfx-doctor/SKILL.md index 0e2ec65..a4052d7 100644 --- a/lfx-doctor/SKILL.md +++ b/skills/lfx-doctor/SKILL.md @@ -20,11 +20,11 @@ You diagnose problems with a user's LFX Skills install and walk them through fix ## Step 1: Locate the CLI -The `lfx-skills` CLI lives in the user's lfx-skills clone at `bin/lfx-skills`. Try, in order: +The `lfx-skills` CLI lives in the user's lfx-skills clone at `cli/lfx-skills`. Try, in order: 1. **On PATH:** `command -v lfx-skills` — if found, use it directly. -2. **From the manifest:** `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` — if the file exists, append `/bin/lfx-skills`. -3. **Current dir:** if the user is inside the lfx-skills clone (a `bin/lfx-skills` exists relative to `pwd`), use `./bin/lfx-skills`. +2. **From the manifest:** `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` — if the file exists, append `/cli/lfx-skills`. +3. **Current dir:** if the user is inside the lfx-skills clone (a `cli/lfx-skills` exists relative to `pwd`), use `./cli/lfx-skills`. 4. **Last resort:** ask the user: "Where is your lfx-skills clone? (e.g., `~/lf/lfx-skills`)". If none of the above works, the install was never run: tell the user to clone the repo and run `./install.sh` (or use `/lfx-install` if they're inside the clone). Stop here. diff --git a/lfx-doctor/references/fix-recipes.md b/skills/lfx-doctor/references/fix-recipes.md similarity index 89% rename from lfx-doctor/references/fix-recipes.md rename to skills/lfx-doctor/references/fix-recipes.md index 84b0422..0dce472 100644 --- a/lfx-doctor/references/fix-recipes.md +++ b/skills/lfx-doctor/references/fix-recipes.md @@ -20,7 +20,7 @@ Each entry follows the same shape: **What:** No `~/.lfx-skills/config.json`. **Why it matters:** the CLI doesn't know what's installed where. Doctor can only check things relative to the manifest, so most other checks will be skipped. **Fix:** run `lfx-skills install`. Or, inside the lfx-skills clone, run `/lfx-install` for a guided walkthrough. -**Auto-fixable?** no (requires user choices about platform / scope / dev root). +**Auto-fixable?** no (requires user choices about scope / dev root). --- @@ -93,7 +93,7 @@ Each entry follows the same shape: **What:** The recorded `lfx_dev_root` path doesn't exist on disk. **Common cause:** moved your clones to a new location. -**Fix:** `lfx-skills config set lfx_dev_root=/new/path`. The CLI will rewrite `env.sh` automatically. +**Fix:** `lfx-skills config set lfx_dev_root=/new/path`. The CLI will rewrite `~/.lfx-skills/dev-root` automatically. **Auto-fixable?** no. --- @@ -132,23 +132,6 @@ git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git --- -## issue-id: cli-not-on-path - -**What:** The `claude` CLI isn't found on PATH but the manifest records a Claude install. -**Fix:** install Claude Code from <https://claude.com/code>, or check `which claude` from outside this shell. -**Auto-fixable?** no. - ---- - -## issue-id: platform-dir-missing - -**What:** A recorded config dir (e.g., `~/.claude`) no longer exists. -**Common cause:** you removed Claude Code, or moved its config. -**Fix:** if you still want skills installed there, recreate the dir (`mkdir -p ~/.claude/skills`) and run `lfx-skills doctor --fix`. Otherwise, run `lfx-skills uninstall --scope=global` and re-install with the new config dir. -**Auto-fixable?** no. - ---- - ## issue-id: frontmatter-missing **What:** A `SKILL.md` doesn't start with `---` on line 1. @@ -218,12 +201,3 @@ name: ... **Fix:** add an entry to `lfx/SKILL.md`'s routing table for the skill, including 1–2 example trigger phrases. **Caveat:** internal-only skills (like `lfx-backend-builder` and `lfx-ui-builder`, which are only invoked by `/lfx-coordinator`) can legitimately stay out of the routing table. Use judgment. **Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. - ---- - -## issue-id: platforms-none - -**What:** Manifest exists but records no platforms. -**Common cause:** install was interrupted, or someone hand-edited the config. -**Fix:** run `lfx-skills install` again. -**Auto-fixable?** no. diff --git a/lfx-git-setup/SKILL.md b/skills/lfx-git-setup/SKILL.md similarity index 100% rename from lfx-git-setup/SKILL.md rename to skills/lfx-git-setup/SKILL.md diff --git a/lfx-git-setup/references/linux.md b/skills/lfx-git-setup/references/linux.md similarity index 100% rename from lfx-git-setup/references/linux.md rename to skills/lfx-git-setup/references/linux.md diff --git a/lfx-git-setup/references/mac.md b/skills/lfx-git-setup/references/mac.md similarity index 100% rename from lfx-git-setup/references/mac.md rename to skills/lfx-git-setup/references/mac.md diff --git a/lfx-git-setup/references/windows.md b/skills/lfx-git-setup/references/windows.md similarity index 100% rename from lfx-git-setup/references/windows.md rename to skills/lfx-git-setup/references/windows.md diff --git a/lfx-install/SKILL.md b/skills/lfx-install/SKILL.md similarity index 67% rename from lfx-install/SKILL.md rename to skills/lfx-install/SKILL.md index db1c61e..674b17f 100644 --- a/lfx-install/SKILL.md +++ b/skills/lfx-install/SKILL.md @@ -3,13 +3,12 @@ # SPDX-License-Identifier: MIT name: lfx-install description: > - Install or set up LFX Skills. Walks the user through every choice in plain - language: which AI tools they use (Claude Code, agents.md tools, both), - where their LFX repos live, scope (global / per-repo / both), then runs the - installer and verifies. Use whenever the user says "I just cloned this — what - now?", "set up lfx skills", "install lfx skills", "I'm new to lfx-skills", - "first-time setup", or runs into the repo with no install manifest yet. Only - available inside the lfx-skills clone. + Install or set up LFX Skills for agents.md-compatible tools via the + lfx-skills CLI, and point Claude Code-only users to the Claude plugin path. + Walks through the choices in plain language: where their LFX repos live, + scope, agents config dirs, then runs the installer and verifies. Use + whenever the user says "I just cloned this — what now?", "set up lfx skills", + "install lfx skills", "I'm new to lfx-skills", or "first-time setup". allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- @@ -17,14 +16,14 @@ allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion # LFX Skills Install -You guide the user through their first-time install. The bash CLI (`bin/lfx-skills install`) can do this non-interactively if every flag is supplied; this skill is the conversational layer that figures out what those flags should be by asking the user, in plain language, one question at a time. +You guide the user through their first-time install. The bash CLI (`cli/lfx-skills install`) can do this non-interactively if every flag is supplied; this skill is the conversational layer that figures out what those flags should be by asking the user, in plain language, one question at a time. ## Step 1: Verify you're in the clone -This skill only works inside the `lfx-skills` clone (it's the only place where it's auto-discovered, via committed symlinks under `.claude/skills/`). Verify: +This skill only works inside the `lfx-skills` clone. Verify: ```bash -[ -x ./bin/lfx-skills ] && echo OK || echo NOT_IN_CLONE +[ -x ./cli/lfx-skills ] && echo OK || echo NOT_IN_CLONE ``` If `NOT_IN_CLONE`, tell the user: @@ -35,17 +34,14 @@ Stop. ## Step 2: Probe the system -Run `./bin/lfx-skills` indirectly via its install command's PROBE step — but for the conversation, you also want the data yourself so you can ask informed questions. Do these one-shot probes: +Run `./cli/lfx-skills` indirectly via its install command's PROBE step — but for the conversation, you also want the data yourself so you can ask informed questions. Do these one-shot probes: ```bash -# CLIs available -for cli in claude codex gemini opencode; do +# agents.md-compatible CLIs available +for cli in codex gemini opencode; do command -v "$cli" >/dev/null 2>&1 && echo "$cli" done -# Claude config dirs -ls -d "$HOME"/.claude* 2>/dev/null - # Agents config dirs ls -d "$HOME"/.agents* 2>/dev/null @@ -55,17 +51,28 @@ for d in "$HOME/lf" "$HOME/lfx" "$HOME/code/lfx" "$HOME/work/lfx"; do done ``` -## Step 3: Q1 — Platform +## Step 3: Q1 — Install route Use `AskUserQuestion`: -> "Which AI coding tools do you use? (1) Claude Code, (2) an agents.md-compatible tool (Codex, Gemini CLI, OpenCode), (3) both." +> "Which setup do you need? (1) agents.md-compatible tool (Codex, Gemini CLI, OpenCode), (2) Claude Code only, (3) both." If you detected only one CLI installed, default to it but still confirm. +If the user picks Claude Code only, explain that Claude installs this repo as a plugin and does not use the CLI symlink installer: + +```text +/plugin marketplace add linuxfoundation/lfx-plugins +/plugin install lfx-skills@lfx +``` + +If they are testing from a local checkout, tell them to run Claude Code with the local plugin directory or add the local marketplace per the Claude Code plugin docs. Stop after explaining the plugin path; do not run `./cli/lfx-skills install` for Claude-only installs. + +If the user picks both, use the plugin path for Claude Code and continue with the CLI flow below for agents.md-compatible tools. The CLI itself remains agents.md-only. + ## Step 4: Q2 — Scope -> "Install scope? (1) **Global** — available in every session of your AI tool. (2) **Per-repo** — only in specific repos (their `.claude/skills/` or `.agents/skills/`). (3) **Both** — global plus pin into specific repos." +> "Install scope? (1) **Global** — available in every session of your agents.md-compatible tool. (2) **Per-repo** — only in specific repos (their `.agents/skills/`). (3) **Both** — global plus pin into specific repos." This question goes second so subsequent questions can adapt. (Per-repo only? Skip the global config picker. Global only? Skip the repo picker.) @@ -84,13 +91,13 @@ Use `AskUserQuestion`. If the user already has `LFX_DEV_ROOT` set in the shell, If the chosen path doesn't exist, ask whether to create it (`mkdir -p`). If it has zero `lf*` git repos, warn but proceed: the install will still work; the dev-root-empty doctor warning will trigger until they clone some. -## Step 6: Q4 — Config dirs (only if scope includes Global) +## Step 6: Q4 — Agents config dirs (only if scope includes Global) -If you saw multiple Claude config dirs (e.g., `~/.claude`, `~/.claude-work`, `~/.claude-personal`), ask which to install into: +If you saw multiple `~/.agents*` config dirs, ask which to install into: -> "I see these Claude config dirs: …. Install into all of them, or just one? (defaults to `~/.claude`)" +> "I see these agents config dirs: …. Install into all of them, or just one? (defaults to `~/.agents`)" -Same for `~/.agents*` if applicable. (Most users have one each; this only matters for power users with multiple profiles.) +Most users have one; this only matters for power users with multiple profiles. If scope is Per-repo only, skip this step entirely. @@ -108,10 +115,8 @@ Before running anything, summarise: ``` Plan: - Platform: claude + agents Scope: global + per-repo LFX_DEV_ROOT: ~/lf - Claude dirs: ~/.claude, ~/.claude-work Agents dir: ~/.agents Repos (4): lfx-v2-ui, lfx-v2-meeting-service, lfx-v2-committee-service, lfx-v2-query-service Skills: 15 user-facing + lfx-doctor + lfx-skills-helper @@ -126,11 +131,9 @@ Will create approximately N symlinks. Proceed? Compose the non-interactive flags from the user's answers: ```bash -./bin/lfx-skills install --yes \ - --platform=<platform> \ +./cli/lfx-skills install --yes \ --scope=<scope> \ --lfx-dev-root=<path> \ - --claude-config=<dir1,dir2,...> \ --agents-config=<dir> \ --repos=<repo1,repo2,...> ``` @@ -142,7 +145,7 @@ Stream the output to the user. Run a quick verification: ```bash -./bin/lfx-skills doctor +./cli/lfx-skills doctor ``` If errors, walk the user through the auto-fix: @@ -159,7 +162,7 @@ If the installer reported it couldn't find a writable PATH dir, share the alias > "I couldn't find a writable PATH dir to drop the CLI into. Add this alias to your shell rc to use `lfx-skills` from anywhere: > ```bash -> alias lfx-skills='<clone>/bin/lfx-skills' +> alias lfx-skills='<clone>/cli/lfx-skills' > ``` > Or extend PATH to include `~/.local/bin`, `~/bin`, or `/usr/local/bin`." diff --git a/lfx-intercom/SKILL.md b/skills/lfx-intercom/SKILL.md similarity index 100% rename from lfx-intercom/SKILL.md rename to skills/lfx-intercom/SKILL.md diff --git a/lfx-new-skill/SKILL.md b/skills/lfx-new-skill/SKILL.md similarity index 74% rename from lfx-new-skill/SKILL.md rename to skills/lfx-new-skill/SKILL.md index a942f46..b54c790 100644 --- a/lfx-new-skill/SKILL.md +++ b/skills/lfx-new-skill/SKILL.md @@ -3,14 +3,12 @@ # SPDX-License-Identifier: MIT name: lfx-new-skill description: > - Scaffold a new skill in the lfx-skills repo. Walks the contributor through - picking a name, description, and tool list; generates the SKILL.md with the - correct frontmatter shape per docs/skill-authoring.md conventions; sets up - references/ if needed; chains into `lfx-skills update` to install the new - skill locally and `lfx-skills doctor` to verify so it can be tried + Scaffold a new skill in the lfx-skills repo under skills/. Walks the + contributor through picking a name, description, and tool list; generates the + SKILL.md with the correct frontmatter shape; sets up references/ if needed; + chains into `lfx-skills update` and `lfx-skills doctor` so it can be tried immediately. Use whenever someone wants to add a new lfx skill, scaffold a - new SKILL.md, or asks "how do I create a new skill". Only available inside - the lfx-skills clone. + new SKILL.md, or asks "how do I create a new skill". allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- @@ -25,7 +23,7 @@ You help a contributor scaffold a new skill in the `lfx-skills` repo. Your job e This skill only works inside the `lfx-skills` clone. Verify: ```bash -[ -x ./bin/lfx-skills ] && [ -d ./lfx ] && echo OK || echo NOT_IN_CLONE +[ -x ./cli/lfx-skills ] && [ -d ./skills/lfx ] && echo OK || echo NOT_IN_CLONE ``` If `NOT_IN_CLONE`: @@ -47,7 +45,7 @@ Use `AskUserQuestion`: Validate the answer: - Must match `^lfx-[a-z0-9-]+$` -- Must NOT collide with an existing directory: check `[ -d "lfx-<name>" ]` — if exists, ask for another. +- Must NOT collide with an existing directory: check `[ -d "skills/lfx-<name>" ]` — if exists, ask for another. - Must NOT collide with `lfx` itself or any reserved prefix. Re-ask until you get a valid name. @@ -79,11 +77,11 @@ Validate: tools should be a comma-separated list of identifiers. If the user inc > "Will this skill have reference docs alongside `SKILL.md`? (e.g., a long table, a JSON config, a checklist that's too big to inline.) — yes / no." -If yes: create `lfx-<name>/references/` with a `.gitkeep` so the directory is tracked even when empty. +If yes: create `skills/lfx-<name>/references/` with a `.gitkeep` so the directory is tracked even when empty. ## Step 6: Generate SKILL.md -Use the `Write` tool with the absolute path `<clone>/lfx-<name>/SKILL.md`. Template: +Use the `Write` tool with the absolute path `<clone>/skills/lfx-<name>/SKILL.md`. Template: ```markdown --- @@ -135,15 +133,15 @@ If the user listed any MCP tools in Step 4, add a `## Prerequisites` section to Chain into the CLI to make the new skill available immediately: ```bash -./bin/lfx-skills update +./cli/lfx-skills update ``` -This re-applies the manifest and detects the new `lfx-<name>/` directory. The CLI will list it as a new skill not in the manifest and prompt to install it everywhere already configured. +This re-applies the manifest and detects the new `skills/lfx-<name>/` directory. The CLI will list it as a new skill not in the manifest and prompt to install it everywhere already configured. Then verify: ```bash -./bin/lfx-skills doctor +./cli/lfx-skills doctor ``` If the doctor flags `frontmatter-no-name`, `frontmatter-name-mismatch`, `license-missing`, `routing-uncovered`, or any other content issue with your new skill, fix it (use Edit) and re-run `doctor` until clean. @@ -159,13 +157,32 @@ The `routing-uncovered` warning is a reminder to add an entry to `lfx/SKILL.md` > > Iterate on the body as you go: every time you save the SKILL.md, your tool picks it up on the next invocation." -## Step 10: Hand off to CONTRIBUTING +## Step 10: Explain release/update flow > "When you're ready to ship this skill upstream: > > - Read `CONTRIBUTING.md` for the DCO, sign-off, and review flow. > - Run `/lfx-preflight` to validate your changes. > - Open a PR against `main`. +> - After the PR is merged, ship either this skill alone or a batch of skill changes with a GitHub Release: +> +> | Change type | Version component | +> |---|---| +> | Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | patch | +> | New skills, substantial skill behavior updates, new supported platform behavior | minor | +> | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | major (only when explicitly instructed) | +> +> ```bash +> LATEST=$(git tag --sort=-v:refname | head -1) +> echo "Latest tag: $LATEST" +> NEXT=v0.1.0 +> +> gh release create "$NEXT" \ +> --generate-notes \ +> --latest +> ``` +> +> The release tag is the canonical version. After creating the GitHub Release, manually update `../lfx-plugins/.claude-plugin/marketplace.json` so `lfx-skills` points at the new tag and the marketplace `version` is the tag without the leading `v`; commit that marketplace bump with `git commit -s -S`. Claude users update with `/plugin marketplace update lfx` and `/plugin update lfx-skills@lfx`; agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. > > Welcome to lfx-skills." diff --git a/lfx-new-skill/references/conventions-quickref.md b/skills/lfx-new-skill/references/conventions-quickref.md similarity index 96% rename from lfx-new-skill/references/conventions-quickref.md rename to skills/lfx-new-skill/references/conventions-quickref.md index ecce3bb..413ae1e 100644 --- a/lfx-new-skill/references/conventions-quickref.md +++ b/skills/lfx-new-skill/references/conventions-quickref.md @@ -83,8 +83,8 @@ Reference files don't need frontmatter. Just markdown (or JSON / YAML / etc.). A After creating, run: ```bash -./bin/lfx-skills update # install the new skill at every configured target -./bin/lfx-skills doctor # validate frontmatter, license header, routing +./cli/lfx-skills update # install the new skill at every configured target +./cli/lfx-skills doctor # validate frontmatter, license header, routing ``` Fix anything the doctor flags before opening a PR. diff --git a/lfx-pr-catchup/SKILL.md b/skills/lfx-pr-catchup/SKILL.md similarity index 100% rename from lfx-pr-catchup/SKILL.md rename to skills/lfx-pr-catchup/SKILL.md diff --git a/lfx-pr-resolve/SKILL.md b/skills/lfx-pr-resolve/SKILL.md similarity index 100% rename from lfx-pr-resolve/SKILL.md rename to skills/lfx-pr-resolve/SKILL.md diff --git a/lfx-pr-resolve/evals/evals.json b/skills/lfx-pr-resolve/evals/evals.json similarity index 100% rename from lfx-pr-resolve/evals/evals.json rename to skills/lfx-pr-resolve/evals/evals.json diff --git a/lfx-preflight/SKILL.md b/skills/lfx-preflight/SKILL.md similarity index 100% rename from lfx-preflight/SKILL.md rename to skills/lfx-preflight/SKILL.md diff --git a/lfx-product-architect/SKILL.md b/skills/lfx-product-architect/SKILL.md similarity index 100% rename from lfx-product-architect/SKILL.md rename to skills/lfx-product-architect/SKILL.md diff --git a/lfx-research/SKILL.md b/skills/lfx-research/SKILL.md similarity index 100% rename from lfx-research/SKILL.md rename to skills/lfx-research/SKILL.md diff --git a/lfx-setup/SKILL.md b/skills/lfx-setup/SKILL.md similarity index 100% rename from lfx-setup/SKILL.md rename to skills/lfx-setup/SKILL.md diff --git a/lfx-skills-helper/SKILL.md b/skills/lfx-skills-helper/SKILL.md similarity index 70% rename from lfx-skills-helper/SKILL.md rename to skills/lfx-skills-helper/SKILL.md index e7c958c..3f8d1d6 100644 --- a/lfx-skills-helper/SKILL.md +++ b/skills/lfx-skills-helper/SKILL.md @@ -3,13 +3,15 @@ # SPDX-License-Identifier: MIT name: lfx-skills-helper description: > - Manage your LFX Skills installation via the lfx-skills CLI: list what's - installed, install or uninstall in this repo or globally, update from - upstream, view or change config, look up what a specific skill does. Use for - "add lfx skills to this repo", "what's installed", "update lfx skills", - "show my lfx setup", "uninstall", "what does /lfx-foo do". For "which skill - should I use for X" or other plain-language routing questions, hand off to - /lfx. For health checks or repair, hand off to /lfx-doctor. + Manage the agents.md LFX Skills installation via the lfx-skills CLI: list + what's installed, install or uninstall in this repo or globally, update from + upstream, view or change config, look up what a specific skill does, and + remove legacy Claude symlink installs. Use for "add lfx skills to this repo", + "what's installed", "update lfx skills", "show my lfx setup", "uninstall", + "remove old Claude symlinks", "what does /lfx-foo do". For Claude plugin + installs, explain the plugin marketplace path. For "which skill should I use + for X" or other plain-language routing questions, hand off to /lfx. For health + checks or repair, hand off to /lfx-doctor. allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- @@ -17,15 +19,17 @@ allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion # LFX Skills Helper -You are the conversational front-end for the `lfx-skills` CLI: install, uninstall, update, list, info, config. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. +You are the conversational front-end for the agents.md-only `lfx-skills` CLI: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. + +Claude Code is separate: it installs LFX Skills as a plugin with `/plugin marketplace add linuxfoundation/lfx-plugins` and `/plugin install lfx-skills@lfx`. Do not use the CLI to install Claude Code skills. The CLI can only remove old Claude symlink installs via `lfx-skills uninstall --legacy-claude-only` or as part of `lfx-skills uninstall --all`. ## Step 1: Locate the CLI Same as `/lfx-doctor`. Try in order: 1. `command -v lfx-skills` (on PATH). -2. `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` then append `/bin/lfx-skills`. -3. `./bin/lfx-skills` if you're inside the lfx-skills clone. +2. `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` then append `/cli/lfx-skills`. +3. `./cli/lfx-skills` if you're inside the lfx-skills clone. 4. Ask the user. If none works: the install was never run. Tell the user to clone `linuxfoundation/lfx-skills` and run `./install.sh` (or invoke `/lfx-install` if they're already in the clone). Stop. @@ -55,10 +59,10 @@ For commands that change state (`install`, `uninstall`, `update`, `config set`), The CLI output is structured but utilitarian. Reformat it for conversation: -- **`lfx-skills list`** outputs `platform/scope<TAB>skill<TAB>link`. Render as a friendly grouped list: +- **`lfx-skills list`** outputs `scope<TAB>skill<TAB>link`. Render as a friendly grouped list: ``` - Globally installed (Claude): + Globally installed (agents.md): /lfx /lfx-coordinator ... @@ -71,7 +75,7 @@ The CLI output is structured but utilitarian. Reformat it for conversation: - **`lfx-skills info <skill>`** outputs frontmatter + install locations. Render the description in prose, list trigger phrases, summarise where it's installed. -- **`lfx-skills config`** outputs raw JSON. Pretty-print it as a small table: dev root, canonical clone, platforms, total symlinks. +- **`lfx-skills config`** outputs raw JSON. Pretty-print it as a small table: dev root, canonical clone, total symlinks, and CLI symlink. - **`lfx-skills repos`** outputs one path per line. Group as a numbered list with sizes/last-modified if helpful. diff --git a/lfx-skills-helper/references/intents.md b/skills/lfx-skills-helper/references/intents.md similarity index 83% rename from lfx-skills-helper/references/intents.md rename to skills/lfx-skills-helper/references/intents.md index 65f134e..a7b9cf3 100644 --- a/lfx-skills-helper/references/intents.md +++ b/skills/lfx-skills-helper/references/intents.md @@ -5,7 +5,7 @@ Reference for `/lfx-skills-helper`. Maps natural-language **management** intents to the corresponding `lfx-skills` CLI invocation. -This file is for skill *management*: install, uninstall, update, list, info, config. It is not a recommendation engine. Routing questions ("which skill should I use for X?") belong to `/lfx`. Diagnostic questions belong to `/lfx-doctor`. Authoring belongs to `/lfx-new-skill`. +This file is for agents.md skill *management*: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. It is not a recommendation engine. Routing questions ("which skill should I use for X?") belong to `/lfx`. Diagnostic questions belong to `/lfx-doctor`. Authoring belongs to `/lfx-new-skill`. When the user's phrasing isn't an exact match, infer the closest intent and confirm the chosen command before running anything stateful. @@ -37,9 +37,10 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs |-------------------------------------------------|--------------------------------------------------------------------------------------| | "Add lfx skills to this repo" | Confirm, then `lfx-skills install --yes --scope=repo --repos="$(pwd)"`. Suggest `/lfx-doctor` after. | | "Remove lfx skills from this repo" | Confirm, then `lfx-skills uninstall --yes --scope=repo --repos="$(pwd)"` | -| "Install lfx skills globally for Claude" | Confirm, then `lfx-skills install --yes --platform=claude --scope=global` | -| "Add agents.md support" | Confirm, then `lfx-skills install --yes --platform=agents --scope=global` | +| "Install lfx skills globally for Claude" | Explain the Claude Code plugin path: `/plugin marketplace add linuxfoundation/lfx-plugins`, then `/plugin install lfx-skills@lfx` | +| "Add agents.md support" | Confirm, then `lfx-skills install --yes --scope=global` | | "Install everything everywhere" | Confirm. Don't assume `--repos=`; ask the user which repos. | +| "Uninstall lfx skills" | Confirm, then `lfx-skills uninstall --yes --all` | ## Maintenance @@ -47,8 +48,10 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs |-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| | "Update lfx skills" | `lfx-skills update --pull` | Suggest `/lfx-doctor` after | | "Re-apply my install" | `lfx-skills update` | No `--pull`; just refresh symlinks against the manifest | -| "Update my LFX dev root" | Confirm new path, `lfx-skills config set lfx_dev_root=NEW_PATH` | Rewrites `env.sh` automatically | -| "Switch my Claude config dir" | Suggest `lfx-skills uninstall` then `lfx-skills install --claude-config=NEW_DIR`. Don't try to mutate in place. | +| "Remove old Claude symlinks" | Confirm, then `lfx-skills uninstall --yes --legacy-claude-only` | Removes only lfx-skills-owned legacy Claude symlinks | +| "Remove lfx-skills completely" | Confirm, then `lfx-skills uninstall --yes --all` | Removes agents.md symlinks, legacy Claude symlinks, CLI symlink, config | +| "Update my LFX dev root" | Confirm new path, `lfx-skills config set lfx_dev_root=NEW_PATH` | Rewrites `~/.lfx-skills/dev-root` automatically | +| "Switch my Claude config dir" | Explain that Claude Code plugin scope is managed by Claude Code settings, not this CLI. | ## Hand-offs (not your job) diff --git a/lfx-snowflake-access/SKILL.md b/skills/lfx-snowflake-access/SKILL.md similarity index 100% rename from lfx-snowflake-access/SKILL.md rename to skills/lfx-snowflake-access/SKILL.md diff --git a/lfx-test-journey/SKILL.md b/skills/lfx-test-journey/SKILL.md similarity index 100% rename from lfx-test-journey/SKILL.md rename to skills/lfx-test-journey/SKILL.md diff --git a/lfx-ui-builder/SKILL.md b/skills/lfx-ui-builder/SKILL.md similarity index 100% rename from lfx-ui-builder/SKILL.md rename to skills/lfx-ui-builder/SKILL.md diff --git a/lfx-ui-builder/references/frontend-component.md b/skills/lfx-ui-builder/references/frontend-component.md similarity index 100% rename from lfx-ui-builder/references/frontend-component.md rename to skills/lfx-ui-builder/references/frontend-component.md diff --git a/lfx-ui-builder/references/frontend-service.md b/skills/lfx-ui-builder/references/frontend-service.md similarity index 100% rename from lfx-ui-builder/references/frontend-service.md rename to skills/lfx-ui-builder/references/frontend-service.md diff --git a/lfx/SKILL.md b/skills/lfx/SKILL.md similarity index 100% rename from lfx/SKILL.md rename to skills/lfx/SKILL.md diff --git a/lfx/references/glossary.md b/skills/lfx/references/glossary.md similarity index 100% rename from lfx/references/glossary.md rename to skills/lfx/references/glossary.md diff --git a/lfx/references/quickstart.md b/skills/lfx/references/quickstart.md similarity index 100% rename from lfx/references/quickstart.md rename to skills/lfx/references/quickstart.md From e89be8c5ba8204826f53ec9346a1596a9a561a0e Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Wed, 6 May 2026 17:50:50 +0200 Subject: [PATCH 15/20] docs: clarify LFX Skills distribution Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- .claude-plugin/plugin.json | 2 +- README.md | 18 +++- docs/overview.md | 190 +++++++++++++++++++++++++++++++++++++ docs/platform-install.md | 119 ----------------------- 4 files changed, 205 insertions(+), 124 deletions(-) create mode 100644 docs/overview.md delete mode 100644 docs/platform-install.md diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index 2fccb4c..d8fd307 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "lfx-skills", - "description": "LFX Self-Service development skills for Claude Code", + "description": "Claude Code skills for LFX. Start with the lfx skill and describe what you want in plain language.", "author": { "name": "The Linux Foundation" }, diff --git a/README.md b/README.md index c65313a..e5aed04 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,17 @@ A collection of AI coding skills that encode the full development workflow for t ### Claude Code -Install the Claude Code plugin from the LF marketplace: +Install the Claude Code plugin from the LFX marketplace: ```text /plugin marketplace add linuxfoundation/lfx-plugins /plugin install lfx-skills@lfx ``` +After installation, start with the `lfx` skill. In Claude Code that command is namespaced as `/lfx-skills:lfx`; describe what you want in plain language and it will route you to the right workflow. + +The marketplace metadata lives in the separate `linuxfoundation/lfx-plugins` repo. That lets LFX publish multiple Claude Code plugins from one marketplace. This repo owns the `lfx-skills` plugin source; the marketplace repo owns the published plugin version and source tag. + For local plugin development from this checkout: ```bash @@ -24,7 +28,13 @@ cd lfx-skills claude --plugin-dir . ``` -Claude Code plugin commands are namespaced by the plugin name, for example `/lfx-skills:lfx`. The plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. +The Claude Code plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. + +If you previously installed LFX Skills into Claude Code with symlinks, clone this repo, start your coding agent here, and ask it to uninstall the legacy Claude setup. Manual fallback: + +```bash +./cli/lfx-skills uninstall --legacy-claude-only +``` ### agents.md-Compatible Tools @@ -67,7 +77,7 @@ New to LFX development? Type `/lfx` and say **"show me an example"** for a walkt ## Prerequisites -- An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. See [docs/platform-install.md](docs/platform-install.md) for details. +- An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. See [docs/overview.md](docs/overview.md) for details. - Access to LFX repositories (for the skills to operate on) - **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. The CLI records the chosen path in `~/.lfx-skills/dev-root` so skills can discover local repos without shell rc edits. @@ -125,7 +135,7 @@ gh release create "$NEXT" \ --latest ``` -Then update the self-hosted Claude marketplace in the sibling `lfx-plugins` repo. The marketplace entry must point at the released `lfx-skills` tag, and `version` must be the same tag without the leading `v`: +Then update the self-hosted Claude marketplace in the sibling `lfx-plugins` repo. The marketplace can host multiple LFX plugins; update only the `lfx-skills` entry for an LFX Skills release. The entry must point at the released `lfx-skills` tag, and `version` must be the same tag without the leading `v`: ```bash cd ../lfx-plugins diff --git a/docs/overview.md b/docs/overview.md new file mode 100644 index 0000000..f050e3b --- /dev/null +++ b/docs/overview.md @@ -0,0 +1,190 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# LFX Skills Distribution + +LFX Skills has two supported installation paths: + +- **Claude Code:** use the Claude Code plugin. +- **agents.md-compatible tools:** use the LFX Skills clone plus the `lfx-skills` CLI installer and agent first usage. + +Start with the `lfx` skill in either setup. It is the plain-language entry point and routes users to the right workflow. + +## Claude Code Plugin + +Claude Code users should install LFX Skills from the LFX plugin marketplace: + +```text +/plugin marketplace add linuxfoundation/lfx-plugins +/plugin install lfx-skills@lfx +``` + +After installation, start with: + +```text +/lfx-skills:lfx +``` + +The Claude Code plugin is only for Claude Code. It does not install the CLI and does not use the agents.md installer. + +## LFX Plugin Marketplace + +The marketplace lives separately from LFX Skills: + +```text +linuxfoundation/lfx-plugins +``` + +That separate marketplace lets LFX publish multiple Claude Code plugins from one place. Today it publishes the `lfx-skills` plugin, and future LFX Claude Code plugins can be added there as separate plugin entries. + +LFX Skills owns the skill source. The marketplace owns the published plugin listing, version, and release tag reference. + +## Legacy Claude Symlink Cleanup + +Before the plugin split, some local setups may have installed LFX Skills into Claude Code with symlinks. Those installs should be removed before using the plugin. + +Recommended flow: + +```bash +git clone https://github.com/linuxfoundation/lfx-skills.git +cd lfx-skills +``` + +Start your coding agent in the cloned LFX Skills directory and ask: + +```text +Uninstall the legacy Claude setup for LFX Skills. +``` + +Manual fallback: + +```bash +./cli/lfx-skills uninstall --legacy-claude-only +``` + +The cleanup only removes legacy Claude links owned by the local LFX Skills clone. It does not remove unrelated Claude skills or unrelated files. + +## agents.md-Compatible Tools + +Codex, Gemini CLI, OpenCode, and similar tools use the agents.md path. + +Clone LFX Skills: + +```bash +git clone https://github.com/linuxfoundation/lfx-skills.git +cd lfx-skills +``` + +Then start your coding agent in the cloned LFX Skills directory and ask it to set up LFX Skills. + +LFX Skills includes four helper skills for this flow: + +- `lfx-install` — guided first-time setup +- `lfx-doctor` — install health checks and repair guidance +- `lfx-skills-helper` — list, update, uninstall, and inspect the setup +- `lfx-new-skill` — scaffold a new LFX skill + +Manual fallback: + +```bash +./install.sh +``` + +After installation, restart your coding agent, open any LFX repo, and start with: + +```text +/lfx +``` + +The CLI installer is only for agents.md-compatible tools. It installs the skills into agents.md skill locations, keeps track of what it owns, and avoids editing shell startup files. + +## Updating + +Claude Code users update from Claude: + +```text +/plugin marketplace update lfx +/plugin update lfx-skills@lfx +``` + +Auto-updating can also be enabled in claude code. + +agents.md-compatible users can ask their coding agent: + +```text +Update LFX Skills and run the doctor. +``` + +Manual fallback: + +```bash +lfx-skills update --pull +lfx-skills doctor +``` + +If `lfx-skills` is not on `PATH`, run it from the local LFX Skills clone: + +```bash +/path/to/lfx-skills/cli/lfx-skills update --pull +/path/to/lfx-skills/cli/lfx-skills doctor +``` + +## Releases + +Skill releases use GitHub Release tags in `linuxfoundation/lfx-skills`. + +Use SemVer tags: + +```text +vMAJOR.MINOR.PATCH +``` + +Version bump guide: + +| Change type | Version component | +| ----------------------------------------------------------------------------------------------- | ----------------- | +| Docs, prompt wording, installer fixes, small bug fixes | **patch** | +| New skills or substantial skill behavior updates | **minor** | +| Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** | + +Create tag releases with the same `gh release create`: + +```bash +LATEST=$(git tag --sort=-v:refname | head -1) +echo "Latest tag: $LATEST" +NEXT=v0.1.0 + +gh release create "$NEXT" \ + --generate-notes \ + --latest +``` + +The `gh release create` command creates the release tag. The tag is the canonical LFX Skills version. + +Then update the `lfx-skills` entry in the LFX plugin marketplace so it points at the new release tag and version. + +Commit the marketplace update with DCO and cryptographic signing: + +```bash +git add .claude-plugin/marketplace.json +git commit -s -S -m "chore: publish lfx-skills plugin v0.1.0" +git push +``` + +Do not add a version to the LFX Skills Claude plugin manifest. The marketplace owns the published Claude plugin version. + +## Removing agents.md Installs + +To remove the complete agents.md installation, CLI symlink, config, and any legacy Claude symlinks owned by the local LFX Skills clone: + +```bash +lfx-skills uninstall --all +``` + +## Other Tools + +For tools that do not support Claude plugins or agents.md skill directories directly: + +1. Clone LFX Skills. +2. Point the tool at the `SKILL.md` files under `skills/`. +3. Use [tool-mapping.md](tool-mapping.md) to translate tool names to the closest available tools on that platform. diff --git a/docs/platform-install.md b/docs/platform-install.md deleted file mode 100644 index f8155af..0000000 --- a/docs/platform-install.md +++ /dev/null @@ -1,119 +0,0 @@ -<!-- Copyright The Linux Foundation and each contributor to LFX. --> -<!-- SPDX-License-Identifier: MIT --> - -# Platform Installation Guide - -LFX Skills work with any AI coding assistant that can load context from Markdown files. This guide covers installation for specific platforms. - -## Claude Code - -Claude Code uses the plugin system. The plugin manifest lives at `.claude-plugin/plugin.json`, and the Claude-facing skills live under `skills/`. - -**Install from the LF marketplace:** - -```text -/plugin marketplace add linuxfoundation/lfx-plugins -/plugin install lfx-skills@lfx -``` - -**Test this checkout locally:** - -```bash -git clone https://github.com/linuxfoundation/lfx-skills.git -cd lfx-skills -claude --plugin-dir . -``` - -Plugin skills are namespaced by the plugin name, for example `/lfx-skills:lfx`. - -The plugin manifest explicitly lists Claude-facing runtime skills and excludes the four helper skills: `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. - -The plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. The agents.md/manual installer owns the user-level CLI setup. - -**Verify:** Restart Claude Code (or open a new session) and type `/lfx-skills:lfx`. - -## agents.md-Compatible Tools - -Codex, Gemini CLI, OpenCode, and similar tools use an agent-first setup flow. - -```bash -git clone https://github.com/linuxfoundation/lfx-skills.git -cd lfx-skills -``` - -Start your coding agent in the cloned repo and ask it to set up LFX Skills. The repo includes four helper skills under `skills/`: - -- `lfx-install` — guided first-time setup -- `lfx-doctor` — install health checks and repair guidance -- `lfx-skills-helper` — list, update, uninstall, and inspect the setup -- `lfx-new-skill` — scaffold a new skill in this repo - -Manual fallback: - -```bash -./install.sh -``` - -The installer can target global agents.md skill directories, selected repos, or both. It records the manifest in `~/.lfx-skills/config.json` and the chosen LFX repo root in `~/.lfx-skills/dev-root`. - -The CLI is agents.md-only. Claude Code uses the plugin path above. To remove old Claude symlink installs from before the plugin pivot, run: - -```bash -lfx-skills uninstall --legacy-claude-only -``` - -To remove the complete agents.md install, CLI symlink, config, and any legacy Claude symlinks owned by this clone: - -```bash -lfx-skills uninstall --all -``` -agents.md installs include the 15 runtime skills plus `lfx-doctor` and `lfx-skills-helper`. `lfx-install` and `lfx-new-skill` stay clone-only. - -The CLI does not edit your shell rc or rewrite `PATH`. It only creates an `lfx-skills` symlink in an existing writable directory already on `PATH`, and it refuses to overwrite files or foreign symlinks. - -## Gemini CLI - -Use the agents.md-compatible install path above, or reference the SKILL.md files in your project's `GEMINI.md` configuration. Consult [Gemini CLI documentation](https://github.com/google-gemini/gemini-cli) for details on loading external context files. - -## Other Platforms - -Most AI coding tools support loading context from Markdown files. To use LFX Skills with your tool: - -1. Clone this repository -2. Point your tool at the SKILL.md files in each directory under `skills/` -3. Consult [docs/tool-mapping.md](tool-mapping.md) to translate tool names used in SKILL.md files to your platform's equivalents - -## Contributing - -To add installation instructions for a new platform, submit a PR updating this file and the capability table in [docs/tool-mapping.md](tool-mapping.md). - -## Claude Plugin Releases - -Claude plugin releases follow the same GitHub Release pattern as `lfx-mcp`: - -```bash -LATEST=$(git tag --sort=-v:refname | head -1) -echo "Latest tag: $LATEST" -NEXT=v0.1.0 - -gh release create "$NEXT" \ - --generate-notes \ - --latest -``` - -Use `vMAJOR.MINOR.PATCH` release names. GitHub creates the tag, and the tag is the canonical release version. After the `lfx-skills` release, manually update the sibling `lfx-plugins` marketplace repo so the marketplace entry points at that tag: - -```json -{ - "source": { - "source": "github", - "repo": "linuxfoundation/lfx-skills", - "ref": "v0.1.0" - }, - "version": "0.1.0" -} -``` - -Commit that marketplace change with `git commit -s -S`. - -Do not put `version` in `.claude-plugin/plugin.json`; the `lfx-plugins` marketplace entry owns the published version. From dbb00d82cb31613998176f2856ab268ebc4811c7 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Thu, 7 May 2026 10:36:48 +0200 Subject: [PATCH 16/20] feat: split Claude plugin and agents.md CLI Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- .claude-plugin/marketplace.json | 19 ++ AGENTS.md | 15 +- CLAUDE.md | 15 +- README.md | 180 +++++++------- cli/lfx-skills | 33 ++- docs/overview.md | 48 ++-- lib/doctor.sh | 23 +- skills/lfx-coordinator/SKILL.md | 26 ++ skills/lfx-install/SKILL.md | 4 +- skills/lfx-new-skill/SKILL.md | 222 +++++++----------- .../references/conventions-quickref.md | 132 +++++++---- skills/lfx-research/SKILL.md | 26 ++ skills/lfx-skills-helper/SKILL.md | 2 +- .../lfx-skills-helper/references/intents.md | 2 +- skills/lfx-test-journey/SKILL.md | 28 ++- 15 files changed, 462 insertions(+), 313 deletions(-) create mode 100644 .claude-plugin/marketplace.json diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json new file mode 100644 index 0000000..25bc53b --- /dev/null +++ b/.claude-plugin/marketplace.json @@ -0,0 +1,19 @@ +{ + "name": "lfx-skills", + "owner": { + "name": "The Linux Foundation" + }, + "description": "Claude Code marketplace for LFX Skills.", + "plugins": [ + { + "name": "lfx-skills", + "source": { + "source": "github", + "repo": "linuxfoundation/lfx-skills", + "ref": "v0.1.0" + }, + "version": "0.1.0", + "description": "Claude Code skills for LFX. Start with the lfx skill and describe what you want in plain language." + } + ] +} diff --git a/AGENTS.md b/AGENTS.md index 47c97df..ecd9efb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -14,7 +14,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co ## Claude vs agents.md -- Claude Code install/update happens through the plugin marketplace: `/plugin marketplace add linuxfoundation/lfx-plugins`, then `/plugin install lfx-skills@lfx`. +- Claude Code install/update happens through the in-repo plugin marketplace: `/plugin marketplace add linuxfoundation/lfx-skills`, then `/plugin install lfx-skills@lfx-skills`. - agents.md install/update happens through `cli/lfx-skills`. - The CLI has no platform picker anymore. It installs agents.md symlinks only. - To remove old Claude symlink installs from before the plugin pivot, run `cli/lfx-skills uninstall --legacy-claude-only`. @@ -34,6 +34,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). - `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. - `.claude-plugin/plugin.json` — Claude Code plugin manifest. It explicitly lists Claude-facing runtime skills and intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. +- `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin, points its source at the released `linuxfoundation/lfx-skills` tag, and carries the published plugin version. - `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. - `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. - `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. @@ -54,8 +55,8 @@ The skill bodies in this repo use Claude Code's tool vocabulary by default (Bash - Releases use GitHub Releases with `vMAJOR.MINOR.PATCH`, like `lfx-mcp`. - One skill change or a batch of skill changes can ship in the same release. - Create releases through GitHub Releases or `gh release create`; GitHub creates the tag. -- After release, manually update `../lfx-plugins/.claude-plugin/marketplace.json` so `lfx-skills` points at the new tag. -- `.claude-plugin/plugin.json` must not contain `version`; the `lfx-plugins` marketplace entry owns the published plugin version. +- Before release, update `.claude-plugin/marketplace.json` so `plugins[].source.ref` equals the release tag and `plugins[].version` equals the release without the leading `v`. +- `.claude-plugin/plugin.json` must not contain `version`; Anthropic resolves `plugin.json` version before marketplace version, so keep the explicit version in the marketplace only. Version bump guidelines: @@ -77,16 +78,14 @@ gh release create "$NEXT" \ --latest ``` -After creating the GitHub Release, update the sibling marketplace repo: +Before creating the GitHub Release, update and commit the plugin version plus marketplace tag reference: ```bash -cd ../lfx-plugins # Edit .claude-plugin/marketplace.json: # - plugins[].source.ref = "$NEXT" # - plugins[].version = "${NEXT#v}" git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: publish lfx-skills plugin $NEXT" -git push +git commit -s -S -m "chore: release lfx-skills plugin $NEXT" ``` -After the marketplace update, Claude users update with `/plugin marketplace update lfx` and `/plugin update lfx-skills@lfx`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. +After the release, Claude users update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. diff --git a/CLAUDE.md b/CLAUDE.md index 6e07f33..857a45b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -14,7 +14,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co ## Claude vs agents.md -- Claude Code install/update happens through the plugin marketplace: `/plugin marketplace add linuxfoundation/lfx-plugins`, then `/plugin install lfx-skills@lfx`. +- Claude Code install/update happens through the in-repo plugin marketplace: `/plugin marketplace add linuxfoundation/lfx-skills`, then `/plugin install lfx-skills@lfx-skills`. - agents.md install/update happens through `cli/lfx-skills`. - The CLI has no platform picker anymore. It installs agents.md symlinks only. - To remove old Claude symlink installs from before the plugin pivot, run `cli/lfx-skills uninstall --legacy-claude-only`. @@ -34,6 +34,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). - `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. - `.claude-plugin/plugin.json` — Claude Code plugin manifest. It explicitly lists Claude-facing runtime skills and intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. +- `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin, points its source at the released `linuxfoundation/lfx-skills` tag, and carries the published plugin version. - `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. - `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. - `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. @@ -50,8 +51,8 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - Releases use GitHub Releases with `vMAJOR.MINOR.PATCH`, like `lfx-mcp`. - One skill change or a batch of skill changes can ship in the same release. - Create releases through GitHub Releases or `gh release create`; GitHub creates the tag. -- After release, manually update `../lfx-plugins/.claude-plugin/marketplace.json` so `lfx-skills` points at the new tag. -- `.claude-plugin/plugin.json` must not contain `version`; the `lfx-plugins` marketplace entry owns the published plugin version. +- Before release, update `.claude-plugin/marketplace.json` so `plugins[].source.ref` equals the release tag and `plugins[].version` equals the release without the leading `v`. +- `.claude-plugin/plugin.json` must not contain `version`; Anthropic resolves `plugin.json` version before marketplace version, so keep the explicit version in the marketplace only. Version bump guidelines: @@ -73,16 +74,14 @@ gh release create "$NEXT" \ --latest ``` -After creating the GitHub Release, update the sibling marketplace repo: +Before creating the GitHub Release, update and commit the plugin version plus marketplace tag reference: ```bash -cd ../lfx-plugins # Edit .claude-plugin/marketplace.json: # - plugins[].source.ref = "$NEXT" # - plugins[].version = "${NEXT#v}" git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: publish lfx-skills plugin $NEXT" -git push +git commit -s -S -m "chore: release lfx-skills plugin $NEXT" ``` -After the marketplace update, Claude users update with `/plugin marketplace update lfx` and `/plugin update lfx-skills@lfx`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. +After the release, Claude users update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. diff --git a/README.md b/README.md index e5aed04..143ab8d 100644 --- a/README.md +++ b/README.md @@ -12,21 +12,13 @@ A collection of AI coding skills that encode the full development workflow for t Install the Claude Code plugin from the LFX marketplace: ```text -/plugin marketplace add linuxfoundation/lfx-plugins -/plugin install lfx-skills@lfx +/plugin marketplace add linuxfoundation/lfx-skills +/plugin install lfx-skills@lfx-skills ``` After installation, start with the `lfx` skill. In Claude Code that command is namespaced as `/lfx-skills:lfx`; describe what you want in plain language and it will route you to the right workflow. -The marketplace metadata lives in the separate `linuxfoundation/lfx-plugins` repo. That lets LFX publish multiple Claude Code plugins from one marketplace. This repo owns the `lfx-skills` plugin source; the marketplace repo owns the published plugin version and source tag. - -For local plugin development from this checkout: - -```bash -git clone https://github.com/linuxfoundation/lfx-skills.git -cd lfx-skills -claude --plugin-dir . -``` +The marketplace metadata lives in `.claude-plugin/marketplace.json` in LFX Skills. The marketplace points the plugin source at the released `linuxfoundation/lfx-skills` tag and carries the published plugin version. The Claude Code plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. @@ -79,7 +71,7 @@ New to LFX development? Type `/lfx` and say **"show me an example"** for a walkt - An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. See [docs/overview.md](docs/overview.md) for details. - Access to LFX repositories (for the skills to operate on) -- **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. The CLI records the chosen path in `~/.lfx-skills/dev-root` so skills can discover local repos without shell rc edits. +- **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. The CLI records the chosen path in `~/.lfx-skills/dev-root` so skills can discover local repos without shell rc edits. Skills that use this will prompt the user to either set it or use default. ## CLI Safety @@ -89,7 +81,7 @@ The only PATH-related write it attempts is creating an `lfx-skills` symlink in a ## Verify -Restart your AI coding assistant (or open a new session) in any LFX repo and type `/lfx` — agents.md installs should see these commands in autocomplete: +Restart your AI coding assistant (or open a new session) in any LFX repo and type `/lfx`. ``` /lfx ← start here (plain-language entry point) @@ -113,35 +105,19 @@ Restart your AI coding assistant (or open a new session) in any LFX repo and typ ## Releases and Updates -Releases follow the same GitHub Release pattern used by LFX service repos such as `lfx-mcp`. +Releases follow the same tag-first GitHub Release pattern used by LFX service repos such as `lfx-mcp`. -You can ship one skill change or a batch of skill changes in the same release. Merge the skill changes first, then create a GitHub Release using `vMAJOR.MINOR.PATCH`; GitHub creates the tag. +You can ship one skill change or a batch of skill changes in the same release. Merge the skill changes first, bump the plugin version and marketplace tag reference, then create a GitHub Release using `vMAJOR.MINOR.PATCH`; GitHub creates the tag. ### Version bump guidelines -| Change type | Version component | -|---|---| -| Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | **patch** | -| New skills, substantial skill behavior updates, new supported platform behavior | **minor** | +| Change type | Version component | +| ----------------------------------------------------------------------------------------------- | ------------------------------------------- | +| Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | **patch** | +| New skills, substantial skill behavior updates, new supported platform behavior | **minor** | | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | -```bash -LATEST=$(git tag --sort=-v:refname | head -1) -echo "Latest tag: $LATEST" -NEXT=v0.1.0 - -gh release create "$NEXT" \ - --generate-notes \ - --latest -``` - -Then update the self-hosted Claude marketplace in the sibling `lfx-plugins` repo. The marketplace can host multiple LFX plugins; update only the `lfx-skills` entry for an LFX Skills release. The entry must point at the released `lfx-skills` tag, and `version` must be the same tag without the leading `v`: - -```bash -cd ../lfx-plugins -``` - -Update `.claude-plugin/marketplace.json`: +Before creating the release, update the marketplace entry in `.claude-plugin/marketplace.json`: ```json { @@ -154,21 +130,32 @@ Update `.claude-plugin/marketplace.json`: } ``` -Commit the marketplace bump with DCO and cryptographic signing: +Commit the version bump with DCO and cryptographic signing before creating the GitHub Release: ```bash git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: publish lfx-skills plugin v0.1.0" -git push +git commit -s -S -m "chore: release lfx-skills plugin v0.1.0" ``` -Do not put `version` in `.claude-plugin/plugin.json`; Claude resolves manifest versions before marketplace versions, so the marketplace repo owns the published plugin version. +Do not set `version` in `.claude-plugin/plugin.json`. Anthropic's version-resolution order checks `plugin.json` first, then marketplace `version`, then the git commit SHA. Keeping the explicit version only in the marketplace avoids duplicate version sources while still pinning the plugin source to the release tag. + +Create the GitHub Release after the version bump commit is merged: + +```bash +LATEST=$(git tag --sort=-v:refname | head -1) +echo "Latest tag: $LATEST" +NEXT=v0.1.0 + +gh release create "$NEXT" \ + --generate-notes \ + --latest +``` Claude Code users then update from Claude: ```text -/plugin marketplace update lfx -/plugin update lfx-skills@lfx +/plugin marketplace update lfx-skills +/plugin update lfx-skills@lfx-skills ``` For agents.md-compatible installs, update from the terminal or ask your coding agent to update LFX Skills: @@ -222,23 +209,23 @@ The skills form a layered system where each skill has a clear responsibility and ## Skill Overview -| Skill | Purpose | Mode | Tools | -|-------|---------|------|-------| -| **`/lfx`** | **Start here.** Describe what you want in plain language — auto-detects context and routes to the right skill | Router | Bash, Read, Glob, Grep, AskUserQuestion, **Skill** | -| `/lfx-coordinator` | Orchestrates full feature development — researches, plans, delegates to builders in parallel, validates | Read + delegate | Bash, Read, Glob, Grep, AskUserQuestion, **Skill** | -| `/lfx-research` | Explores upstream APIs, discovers code patterns, reads architecture docs, validates contracts via MCP | Read-only | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | -| `/lfx-backend-builder` | Generates Express.js proxy endpoints, Go microservice code, shared types. Encodes three-file pattern, logging, Goa DSL, NATS messaging | Code gen | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | -| `/lfx-ui-builder` | Generates Angular 20 components, services, drawers, pagination UI, styling. Encodes signal patterns, PrimeNG wrappers | Code gen | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | -| `/lfx-product-architect` | Answers "where should this go?", traces data flows, makes placement decisions, explains design patterns | Read-only | Bash, Read, Glob, Grep, AskUserQuestion | -| `/lfx-preflight` | Pre-PR validation — Phase 1 auto-fixes (format, license, lint, build), Phase 2 code review (15 report-only checks for Angular). Pass `--skip-review` to skip Phase 2 | Validate + review | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | -| `/lfx-pr-catchup` | Morning PR dashboard — unresolved comments, status changes, stale PRs, approved-but-not-merged across all your open PRs | Read-only | Bash, Read, Glob, Grep, AskUserQuestion | -| `/lfx-pr-resolve` | Address PR review feedback end-to-end — fetches threads, makes changes, commits, responds per-thread, resolves, posts summary | Audit + fix | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion, **Skill** | -| `/lfx-setup` | Environment setup — prerequisites, clone, install, env vars, dev server. Adapts to Angular or Go repos | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion | -| `/lfx-test-journey` | Combine branches from multiple repos into worktrees for journey testing | Interactive | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | -| `/lfx-git-setup` | Interactive setup for DCO sign-off and GPG-signed commits. Use when commits aren't showing as Verified or onboarding to LFX | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | -| `/lfx-intercom` | Add or fix Intercom integration against the LFX canonical pattern — audits JWT setup, shutdown, Auth0 claim, app IDs, CSP | Audit + fix | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | -| `/lfx-snowflake-access` | Guide users requesting Snowflake access — generates Terraform HCL for `users.tf` or `service_accounts.tf` and walks through the PR | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | -| `/lfx-cdp-snowflake-connectors` | Streamlines adding a new Snowflake connector to CDP — requires knowledge of the source specs. Requires LFX BI Layer MCP server | Interactive and guided | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, **MCP (LFX BI Layer)** | +| Skill | Purpose | Mode | Tools | +| ------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------- | ---------------------------------------------------------------------------- | +| **`/lfx`** | **Start here.** Describe what you want in plain language — auto-detects context and routes to the right skill | Router | Bash, Read, Glob, Grep, AskUserQuestion, **Skill** | +| `/lfx-coordinator` | Orchestrates full feature development — researches, plans, delegates to builders in parallel, validates | Read + delegate | Bash, Read, Glob, Grep, AskUserQuestion, **Skill** | +| `/lfx-research` | Explores upstream APIs, discovers code patterns, reads architecture docs, validates contracts via MCP | Read-only | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | +| `/lfx-backend-builder` | Generates Express.js proxy endpoints, Go microservice code, shared types. Encodes three-file pattern, logging, Goa DSL, NATS messaging | Code gen | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | +| `/lfx-ui-builder` | Generates Angular 20 components, services, drawers, pagination UI, styling. Encodes signal patterns, PrimeNG wrappers | Code gen | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | +| `/lfx-product-architect` | Answers "where should this go?", traces data flows, makes placement decisions, explains design patterns | Read-only | Bash, Read, Glob, Grep, AskUserQuestion | +| `/lfx-preflight` | Pre-PR validation — Phase 1 auto-fixes (format, license, lint, build), Phase 2 code review (15 report-only checks for Angular). Pass `--skip-review` to skip Phase 2 | Validate + review | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion | +| `/lfx-pr-catchup` | Morning PR dashboard — unresolved comments, status changes, stale PRs, approved-but-not-merged across all your open PRs | Read-only | Bash, Read, Glob, Grep, AskUserQuestion | +| `/lfx-pr-resolve` | Address PR review feedback end-to-end — fetches threads, makes changes, commits, responds per-thread, resolves, posts summary | Audit + fix | Bash, Read, **Write, Edit**, Glob, Grep, AskUserQuestion, **Skill** | +| `/lfx-setup` | Environment setup — prerequisites, clone, install, env vars, dev server. Adapts to Angular or Go repos | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion | +| `/lfx-test-journey` | Combine branches from multiple repos into worktrees for journey testing | Interactive | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | +| `/lfx-git-setup` | Interactive setup for DCO sign-off and GPG-signed commits. Use when commits aren't showing as Verified or onboarding to LFX | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | +| `/lfx-intercom` | Add or fix Intercom integration against the LFX canonical pattern — audits JWT setup, shutdown, Auth0 claim, app IDs, CSP | Audit + fix | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion | +| `/lfx-snowflake-access` | Guide users requesting Snowflake access — generates Terraform HCL for `users.tf` or `service_accounts.tf` and walks through the PR | Interactive guide | Bash, Read, Glob, Grep, AskUserQuestion, **WebFetch** | +| `/lfx-cdp-snowflake-connectors` | Streamlines adding a new Snowflake connector to CDP — requires knowledge of the source specs. Requires LFX BI Layer MCP server | Interactive and guided | Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion, **MCP (LFX BI Layer)** | > **Note:** Tool names in the table above follow Claude Code conventions. See [docs/tool-mapping.md](docs/tool-mapping.md) for equivalents on other platforms. @@ -251,6 +238,7 @@ The skills form a layered system where each skill has a clear responsibility and The top-level orchestrator for any feature development. It **never writes code directly** — instead, it researches the codebase, builds a delegation plan, and invokes `/lfx-backend-builder` and `/lfx-ui-builder` in parallel. **Workflow:** + 1. **Setup** — detects repo type (Angular or Go), checks/creates feature branch 2. **Plan** — determines scope, build order (upstream Go → shared types → Express proxy → frontend) 3. **Research** — inline exploration (5–10 tool calls) to find existing patterns, upstream APIs, file paths @@ -260,6 +248,7 @@ The top-level orchestrator for any feature development. It **never writes code d 7. **Summary** — structured completion report with files changed, validation results, and next steps **Key behaviors:** + - Identifies the upstream Go service by reading Express proxy code API paths (e.g., `/committees/...` → `lfx-v2-committee-service`) - Includes upstream Go service changes when the data model needs modification - Handles validation failures by re-invoking only the skill that owns the broken file @@ -272,6 +261,7 @@ The top-level orchestrator for any feature development. It **never writes code d A **read-only** exploration agent that gathers all context needed before code generation. Returns structured, compact findings (under 30 lines) that the coordinator consumes. **Research tasks:** + - **Upstream API validation** — reads OpenAPI specs via `gh api` or local files to check if endpoints/fields exist - **Codebase exploration** — finds existing services, components, controllers, domain models - **Architecture doc reading** — checks placement rules, protected files, dependencies @@ -280,16 +270,16 @@ A **read-only** exploration agent that gathers all context needed before code ge **Upstream service mapping:** -| Domain | Repo | -|--------|------| -| Committees | `lfx-v2-committee-service` | -| Meetings | `lfx-v2-meeting-service` | -| Voting | `lfx-v2-voting-service` | +| Domain | Repo | +| ------------- | ----------------------------- | +| Committees | `lfx-v2-committee-service` | +| Meetings | `lfx-v2-meeting-service` | +| Voting | `lfx-v2-voting-service` | | Mailing Lists | `lfx-v2-mailing-list-service` | -| Members | `lfx-v2-member-service` | -| Projects | `lfx-v2-project-service` | -| Surveys | `lfx-v2-survey-service` | -| Queries | `lfx-v2-query-service` | +| Members | `lfx-v2-member-service` | +| Projects | `lfx-v2-project-service` | +| Surveys | `lfx-v2-survey-service` | +| Queries | `lfx-v2-query-service` | --- @@ -298,6 +288,7 @@ A **read-only** exploration agent that gathers all context needed before code ge Generates **PR-ready backend code** for both the Express.js proxy layer (in `lfx-v2-ui`) and Go microservices (in `lfx-v2-*-service` repos). Always reads target files before generating code — never works from memory alone. **Express.js proxy (Angular repo):** + - Follows the **three-file pattern**: service → controller → route - Services use `MicroserviceProxyService` for all upstream calls (never raw `fetch`/`axios`) - Controllers use `logger.startOperation()` / `logger.success()` / `logger.error()` lifecycle @@ -305,6 +296,7 @@ Generates **PR-ready backend code** for both the Express.js proxy layer (in `lfx - Encodes logging conventions, error handling (`next(error)`, never `res.status(500)`), pagination (`page_size`), and auth defaults (user bearer token) **Go microservices:** + - Goa v3 DSL for API design (`cmd/{service}/design/`) with `make apigen` for code generation - Domain models in `internal/domain/model/` with `Tags()` method for OpenSearch indexing - NATS messaging — publish index + access messages on every write operation @@ -313,18 +305,18 @@ Generates **PR-ready backend code** for both the Express.js proxy layer (in `lfx **Reference docs included:** -| Reference | Content | -|-----------|---------| -| `getting-started.md` | Repo map, deployment overview, local dev setup | -| `goa-patterns.md` | Goa DSL conventions, `make apigen`, ETag/If-Match optimistic locking | -| `nats-messaging.md` | Subject naming, service-to-service communication, KV storage | +| Reference | Content | +| --------------------- | --------------------------------------------------------------------- | +| `getting-started.md` | Repo map, deployment overview, local dev setup | +| `goa-patterns.md` | Goa DSL conventions, `make apigen`, ETag/If-Match optimistic locking | +| `nats-messaging.md` | Subject naming, service-to-service communication, KV storage | | `indexer-patterns.md` | IndexerMessageEnvelope, IndexingConfig, OpenSearch document structure | -| `fga-patterns.md` | OpenFGA tuples, permission inheritance, debugging access | -| `service-types.md` | Native vs wrapper services, which template to follow | -| `query-service.md` | Query service API, OpenSearch queries, FGA-based filtering | -| `helm-chart.md` | Deployment, HTTPRoute, Heimdall rules, KV buckets, secrets | -| `new-service.md` | End-to-end checklist for building a new resource service | -| `backend-endpoint.md` | Three-file pattern, authentication, pagination, error handling | +| `fga-patterns.md` | OpenFGA tuples, permission inheritance, debugging access | +| `service-types.md` | Native vs wrapper services, which template to follow | +| `query-service.md` | Query service API, OpenSearch queries, FGA-based filtering | +| `helm-chart.md` | Deployment, HTTPRoute, Heimdall rules, KV buckets, secrets | +| `new-service.md` | End-to-end checklist for building a new resource service | +| `backend-endpoint.md` | Three-file pattern, authentication, pagination, error handling | --- @@ -333,6 +325,7 @@ Generates **PR-ready backend code** for both the Express.js proxy layer (in `lfx Generates **PR-ready Angular 20 frontend code** — components, services, drawers, pagination, and styling. Only activates in Angular repos. **Components:** + - Standalone with direct imports (no barrel exports) - Strict 11-section class structure: injections → inputs → forms → model signals → writable signals → computed/toSignal → constructor → public methods → protected methods → private init functions → private helpers - Signal-based reactivity: `signal()`, `input()`, `output()`, `computed()`, `model()`, `toSignal()` @@ -340,25 +333,28 @@ Generates **PR-ready Angular 20 frontend code** — components, services, drawer - PrimeNG components wrapped with `lfx-` prefix and `descendants: false` on `@ContentChild` **Services:** + - `@Injectable({ providedIn: 'root' })` with `inject(HttpClient)` - GET requests: `catchError(() => of(default))` for graceful degradation - POST/PUT/DELETE: `take(1)`, let errors propagate - Interfaces from `@lfx-one/shared/interfaces`, relative API paths (`/api/...`) **Drawers:** + - `model<boolean>(false)` for visibility - Lazy data loading via `toObservable(visible).pipe(skip(1), switchMap(...))` - `forkJoin` for parallel API calls, responsive width classes **Pagination:** + - Infinite scroll with `page_token`, `scan()` accumulator, separate first-page and next-page streams **Reference docs included:** -| Reference | Content | -|-----------|---------| +| Reference | Content | +| ----------------------- | -------------------------------------------------------------------------------------- | | `frontend-component.md` | Component placement, class structure, signal types, template rules, drawer conventions | -| `frontend-service.md` | Service patterns, state management, signals vs RxJS guidance | +| `frontend-service.md` | Service patterns, state management, signals vs RxJS guidance | --- @@ -367,6 +363,7 @@ Generates **PR-ready Angular 20 frontend code** — components, services, drawer A **read-only** advisory skill that answers architectural questions without generating code. Provides decision trees, data flow traces, and placement recommendations. **Decision trees:** + - "Where does my component go?" — route vs module-specific vs shared vs PrimeNG wrapper - "Do I need a new module?" — distinct domain + own routes + enough isolation - "Where does my type go?" — shared package vs local definition @@ -375,6 +372,7 @@ A **read-only** advisory skill that answers architectural questions without gene - "User token or M2M token?" — default to user bearer, M2M only for public/privileged calls **Data flow tracing:** + - Frontend → Backend → Upstream: Angular component → HttpClient → Express proxy → MicroserviceProxyService → Go microservice - Write flow: HTTP → Heimdall auth → Goa handler → Storage → concurrent NATS publish (index + FGA) - Read flow: query-service → OpenSearch → batch FGA check → filtered results @@ -388,6 +386,7 @@ A **read-only** advisory skill that answers architectural questions without gene Runs a two-phase **pre-PR validation**. Adapts all checks to the repo type. **Phase 1: Validation (auto-fix):** + 1. **Working tree status** — uncommitted changes, commits ahead of main, JIRA references, `--signoff` 2. **License headers** — verifies and auto-fixes missing headers on `.ts`, `.html`, `.scss`, `.go` files 3. **Formatting** — `yarn format` (Angular) or `gofmt -w .` (Go), reports which files changed @@ -410,6 +409,7 @@ Runs a two-phase **pre-PR validation**. Adapts all checks to the repo type. A **read-only** morning dashboard that shows all your open PRs across repos, classified by urgency. **Workflow:** + 1. **Auth check** — verifies `gh auth status` 2. **Config** — uses defaults immediately (org filter and stale threshold can be passed inline, e.g., `/lfx-pr-catchup linuxfoundation`) 3. **Fetch** — `gh search prs --author=@me --state=open` (up to 50) @@ -427,16 +427,20 @@ A **read-only** morning dashboard that shows all your open PRs across repos, cla Combines feature branches from one or more repos into isolated git worktrees for end-to-end journey testing. Use this when you have a user journey spread across multiple PRs/branches and need to test everything together before merging. **Quick start:** + ``` /lfx-test-journey ``` + This starts the interactive create flow: + 1. Select which repos are involved (auto-discovers repos in `$LFX_DEV_ROOT`, defaults to `~/lf/`) 2. Pick branches to include per repo (shows your unmerged branches) 3. Name the journey 4. The skill creates worktrees, merges branches, and tells you exactly where to `cd` and how to run the app **After creating a journey:** + ```bash # Go to the worktree and run the app as usual cd ~/.lfx-journeys/<journey-name>/<repo-name>/ @@ -444,6 +448,7 @@ yarn start # (Angular) or go run cmd/*/main.go (Go) ``` **Managing journeys:** + ``` /lfx-test-journey list # See all active journeys /lfx-test-journey status # Check if branches have new commits @@ -461,6 +466,7 @@ yarn start # (Angular) or go run cmd/*/main.go (Go) An **interactive setup guide** that walks through environment configuration step by step, verifying each step before proceeding. **Angular repo setup (lfx-v2-ui):** + 1. Prerequisites: Node.js v22+, Yarn v4.9.2+, Git 2. Clone the repository 3. Environment variables from `.env.example` + 1Password credentials @@ -469,6 +475,7 @@ An **interactive setup guide** that walks through environment configuration step 6. Verification with HTTP status check **Go microservice setup:** + 1. Prerequisites: Go 1.22+, Git, Make (optional: Helm, Docker) 2. Clone the repository 3. Environment variables for local or shared dev stack @@ -484,6 +491,7 @@ An **interactive setup guide** that walks through environment configuration step ## Typical Workflows ### Start here — just describe what you want + ``` /lfx → "Add a bio field to committee members" → auto-routes to coordinator → builds → validates → PR /lfx → "How does meeting data work?" → auto-routes to product architect → explains architecture @@ -491,32 +499,38 @@ An **interactive setup guide** that walks through environment configuration step ``` ### Build a new feature end-to-end + ``` /lfx-coordinator → researches → plans → delegates to /lfx-backend-builder + /lfx-ui-builder → validates → /lfx-preflight → PR ``` ### Understand the architecture before coding + ``` /lfx-product-architect → "where should this component go?" / "how does the data flow?" ``` ### Explore what exists before planning + ``` /lfx-research → upstream API contract + codebase patterns + example files ``` ### Quick backend-only or frontend-only change + ``` /lfx-backend-builder → generates Express proxy + shared types /lfx-ui-builder → generates Angular component + service ``` ### Morning PR catch-up + ``` /lfx-pr-catchup → fetches open PRs → enriches via GraphQL → renders attention dashboard → drill-down ``` ### Test a multi-branch user journey + ``` /lfx-test-journey → pick repos → pick branches → creates worktrees → cd into worktree → yarn start /lfx-test-journey status → shows which branches have new commits → /lfx-test-journey refresh <name> @@ -524,12 +538,14 @@ An **interactive setup guide** that walks through environment configuration step ``` ### Validate before submitting a PR + ``` /lfx-preflight → Phase 1 (license, format, lint, build, protected files) → Phase 2 (15 code review checks, Angular only) → PR /lfx-preflight --skip-review → Phase 1 only (useful during dev) ``` ### Set up a new developer environment + ``` /lfx-setup → prerequisites → clone → env vars → install → dev server ``` diff --git a/cli/lfx-skills b/cli/lfx-skills index c43f487..bec3a02 100755 --- a/cli/lfx-skills +++ b/cli/lfx-skills @@ -190,10 +190,13 @@ _help_doctor() { cat <<'EOF' USAGE lfx-skills doctor [--json] [--fix] + lfx-skills doctor --skill-formatting-only --skill=lfx-name [--json] FLAGS - --json Emit results as a JSON array (for scripts / skills) - --fix Interactive fix: walk through fixable issues and prompt per issue + --json Emit results as a JSON array (for scripts / skills) + --fix Interactive fix: walk through fixable issues + --skill-formatting-only Check only SKILL.md frontmatter and license header + --skill=NAME Limit formatting checks to one skill directory EOF } @@ -378,19 +381,41 @@ cmd_check() { # ─── doctor ────────────────────────────────────────────────────────────── cmd_doctor() { require_jq - local emit_json=0 do_fix=0 + local emit_json=0 do_fix=0 skill_formatting_only=0 skill_filter="" while [ $# -gt 0 ]; do case "$1" in --json) emit_json=1 ;; --fix) do_fix=1 ;; + --skill-formatting-only) skill_formatting_only=1 ;; + --skill=*) skill_filter="${1#--skill=}" ;; + --skill) + shift + [ $# -gt 0 ] || { ui_error "--skill requires a value"; exit 1; } + skill_filter="$1" + ;; *) ui_error "Unknown flag: $1"; exit 1 ;; esac shift done + if [ "$skill_formatting_only" = "1" ]; then + [ "$do_fix" = "0" ] || { ui_error "--fix is not supported with --skill-formatting-only"; exit 1; } + [ -n "$skill_filter" ] || { ui_error "--skill-formatting-only requires --skill=NAME"; exit 1; } + [ -d "$CLONE_ROOT/skills/$skill_filter" ] || { ui_error "Unknown skill: $skill_filter"; exit 1; } + elif [ -n "$skill_filter" ]; then + ui_error "--skill can only be used with --skill-formatting-only" + exit 1 + fi + # Run every check once; records flow through a shell variable. local results - results="$(doctor_run_all)" + if [ "$skill_formatting_only" = "1" ]; then + DOCTOR_SKILL_FILTER="$skill_filter" + results="$(doctor_run_skill_formatting)" + unset DOCTOR_SKILL_FILTER + else + results="$(doctor_run_all)" + fi if [ "$emit_json" = "1" ]; then printf '%s\n' "$results" | doctor_format_json diff --git a/docs/overview.md b/docs/overview.md index f050e3b..a1cab22 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -15,8 +15,8 @@ Start with the `lfx` skill in either setup. It is the plain-language entry point Claude Code users should install LFX Skills from the LFX plugin marketplace: ```text -/plugin marketplace add linuxfoundation/lfx-plugins -/plugin install lfx-skills@lfx +/plugin marketplace add linuxfoundation/lfx-skills +/plugin install lfx-skills@lfx-skills ``` After installation, start with: @@ -29,15 +29,15 @@ The Claude Code plugin is only for Claude Code. It does not install the CLI and ## LFX Plugin Marketplace -The marketplace lives separately from LFX Skills: +The marketplace lives in LFX Skills: ```text -linuxfoundation/lfx-plugins +linuxfoundation/lfx-skills ``` -That separate marketplace lets LFX publish multiple Claude Code plugins from one place. Today it publishes the `lfx-skills` plugin, and future LFX Claude Code plugins can be added there as separate plugin entries. +The marketplace publishes the `lfx-skills` Claude Code plugin. Its source points at the released LFX Skills tag, so Claude Code users install a tagged release rather than an arbitrary branch commit. -LFX Skills owns the skill source. The marketplace owns the published plugin listing, version, and release tag reference. +LFX Skills also owns the plugin manifest. The marketplace carries both the published plugin version and the release tag reference. ## Legacy Claude Symlink Cleanup @@ -103,11 +103,11 @@ The CLI installer is only for agents.md-compatible tools. It installs the skills Claude Code users update from Claude: ```text -/plugin marketplace update lfx -/plugin update lfx-skills@lfx +/plugin marketplace update lfx-skills +/plugin update lfx-skills@lfx-skills ``` -Auto-updating can also be enabled in claude code. +Auto-updating can also be enabled in Claude Code. agents.md-compatible users can ask their coding agent: @@ -147,7 +147,23 @@ Version bump guide: | New skills or substantial skill behavior updates | **minor** | | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** | -Create tag releases with the same `gh release create`: +Before creating the release, update the marketplace entry in `.claude-plugin/marketplace.json`. + +Do not set `version` in `.claude-plugin/plugin.json`. Anthropic's version-resolution order checks `plugin.json` first, then marketplace `version`, then the git commit SHA. Keeping the explicit version only in the marketplace avoids duplicate version sources while still pinning the plugin source to the release tag. + +This follows Anthropic's Claude Code marketplace guidance: + +- [Create and distribute a plugin marketplace](https://code.claude.com/docs/en/plugin-marketplaces) +- [Plugins reference](https://code.claude.com/docs/en/plugins-reference) + +Commit the version bump with DCO and cryptographic signing: + +```bash +git add .claude-plugin/marketplace.json +git commit -s -S -m "chore: release lfx-skills plugin v0.1.0" +``` + +After the version bump commit is merged, create the tag release with the same `gh release create` flow used by LFX MCP: ```bash LATEST=$(git tag --sort=-v:refname | head -1) @@ -161,18 +177,6 @@ gh release create "$NEXT" \ The `gh release create` command creates the release tag. The tag is the canonical LFX Skills version. -Then update the `lfx-skills` entry in the LFX plugin marketplace so it points at the new release tag and version. - -Commit the marketplace update with DCO and cryptographic signing: - -```bash -git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: publish lfx-skills plugin v0.1.0" -git push -``` - -Do not add a version to the LFX Skills Claude plugin manifest. The marketplace owns the published Claude plugin version. - ## Removing agents.md Installs To remove the complete agents.md installation, CLI symlink, config, and any legacy Claude symlinks owned by the local LFX Skills clone: diff --git a/lib/doctor.sh b/lib/doctor.sh index d813f1d..f9a3fc2 100644 --- a/lib/doctor.sh +++ b/lib/doctor.sh @@ -19,6 +19,11 @@ _emit() { printf '%s|%s|%s|%s|%s|%s|%s\n' "$1" "$2" "$3" "$4" "$5" "$6" "${7:-}" } +_doctor_skill_filter_matches() { + local skill_name="$1" + [ -z "${DOCTOR_SKILL_FILTER:-}" ] || [ "$skill_name" = "$DOCTOR_SKILL_FILTER" ] +} + # ─── Category 1: Symlinks ──────────────────────────────────────────────── check_symlinks() { if ! config_exists; then @@ -142,10 +147,12 @@ check_lfx_dev_root() { check_frontmatter() { local clone clone="${CLONE_ROOT:-$(probe_canonical_clone "${BASH_SOURCE[0]:-$0}")}" - local skill_md skill_name name_in_md desc_in_md + local skill_md skill_name name_in_md desc_in_md skill_issue for skill_md in "$clone"/skills/lfx*/SKILL.md; do [ -f "$skill_md" ] || continue skill_name="$(basename "$(dirname "$skill_md")")" + _doctor_skill_filter_matches "$skill_name" || continue + skill_issue=0 if [ "$(head -1 "$skill_md")" != "---" ]; then _emit fail frontmatter-missing frontmatter \ "$skill_name: SKILL.md missing frontmatter" \ @@ -158,19 +165,25 @@ check_frontmatter() { desc_in_md="$(printf '%s\n' "$fm" | awk -F': *' '/^description: */ {print $2; exit}')" if [ -z "$name_in_md" ]; then + skill_issue=1 _emit fail frontmatter-no-name frontmatter \ "$skill_name: missing required \`name\` field" "$skill_md" no elif [ "$name_in_md" != "$skill_name" ]; then + skill_issue=1 _emit fail frontmatter-name-mismatch frontmatter \ "$skill_name: name field is \`$name_in_md\`, expected \`$skill_name\`" \ "$skill_md" no fi if [ -z "$desc_in_md" ]; then if ! printf '%s\n' "$fm" | awk '/^description:/{f=1; next} f && /^[a-z_-]+:/{exit} f && /^ +./{print; exit}' | grep -q '.'; then + skill_issue=1 _emit warn frontmatter-no-description frontmatter \ "$skill_name: missing or empty \`description\` field" "$skill_md" no fi fi + if [ "${DOCTOR_EMIT_FORMATTING_PASS:-0}" = "1" ] && [ "$skill_issue" -eq 0 ]; then + _emit pass frontmatter-ok frontmatter "$skill_name frontmatter OK" "$skill_md" no + fi done } @@ -229,6 +242,7 @@ check_license_headers() { for skill_md in "$clone"/skills/lfx*/SKILL.md; do [ -f "$skill_md" ] || continue skill_name="$(basename "$(dirname "$skill_md")")" + _doctor_skill_filter_matches "$skill_name" || continue if head -4 "$skill_md" | grep -qF "Copyright The Linux Foundation"; then _emit pass license-ok license_headers "$skill_name has license header" "$skill_md" no else @@ -249,6 +263,13 @@ doctor_run_all() { check_license_headers } +# doctor_run_skill_formatting → only content-format checks. Used by +# /lfx-new-skill so scaffolding validation does not require an agents.md install. +doctor_run_skill_formatting() { + DOCTOR_EMIT_FORMATTING_PASS=1 check_frontmatter + check_license_headers +} + # doctor_format_human → render results read from stdin as a colourised report. # Single pass, buffering errors and warnings into shell variables before printing. # Returns 0 if no failures, 1 otherwise. diff --git a/skills/lfx-coordinator/SKILL.md b/skills/lfx-coordinator/SKILL.md index e7dedf7..052a18d 100644 --- a/skills/lfx-coordinator/SKILL.md +++ b/skills/lfx-coordinator/SKILL.md @@ -73,6 +73,32 @@ Step 7: Summary — report results, suggest /lfx-preflight Read the relevant reference before planning work that involves query service usage, indexing changes, or adding fields to indexed resources. +## Dev Root Setup + +Before relying on local upstream service repos, resolve the LFX dev root: + +```bash +if [ -f "$HOME/.lfx-skills/dev-root" ]; then + LFX_DEV_ROOT="$(cat "$HOME/.lfx-skills/dev-root")" +else + LFX_DEV_ROOT="$HOME/lf" +fi +printf '%s\n' "$LFX_DEV_ROOT" +``` + +If `~/.lfx-skills/dev-root` already exists, use it without asking and continue. Do not list or scan repos during setup. + +If `~/.lfx-skills/dev-root` is missing, ask a quick question: whether the user wants to set an LFX dev root now or use the default `~/lf`. Explain that setting it lets skills read local LFX repos directly, which is faster and more reliable than falling back to GitHub API calls. Suggest likely locations: `~/lf`, `~/lfx`, `~/src/lfx`, or the parent directory containing their LFX clones. + +If they accept, ask for the path, then write it: + +```bash +mkdir -p ~/.lfx-skills +printf '%s\n' "<chosen-dev-root>" > ~/.lfx-skills/dev-root +``` + +If they choose the default, continue with `~/lf` and use GitHub fallbacks when local repos are unavailable. + ## Step 3: Research (do this inline — NOT via Skill delegation) Use your Read, Glob, Grep, and Bash tools to quickly check: diff --git a/skills/lfx-install/SKILL.md b/skills/lfx-install/SKILL.md index 674b17f..b15b05c 100644 --- a/skills/lfx-install/SKILL.md +++ b/skills/lfx-install/SKILL.md @@ -62,8 +62,8 @@ If you detected only one CLI installed, default to it but still confirm. If the user picks Claude Code only, explain that Claude installs this repo as a plugin and does not use the CLI symlink installer: ```text -/plugin marketplace add linuxfoundation/lfx-plugins -/plugin install lfx-skills@lfx +/plugin marketplace add linuxfoundation/lfx-skills +/plugin install lfx-skills@lfx-skills ``` If they are testing from a local checkout, tell them to run Claude Code with the local plugin directory or add the local marketplace per the Claude Code plugin docs. Stop after explaining the plugin path; do not run `./cli/lfx-skills install` for Claude-only installs. diff --git a/skills/lfx-new-skill/SKILL.md b/skills/lfx-new-skill/SKILL.md index b54c790..d722b85 100644 --- a/skills/lfx-new-skill/SKILL.md +++ b/skills/lfx-new-skill/SKILL.md @@ -3,12 +3,9 @@ # SPDX-License-Identifier: MIT name: lfx-new-skill description: > - Scaffold a new skill in the lfx-skills repo under skills/. Walks the - contributor through picking a name, description, and tool list; generates the - SKILL.md with the correct frontmatter shape; sets up references/ if needed; - chains into `lfx-skills update` and `lfx-skills doctor` so it can be tried - immediately. Use whenever someone wants to add a new lfx skill, scaffold a - new SKILL.md, or asks "how do I create a new skill". + Scaffold a new skill in the lfx-skills repo under skills/. Use when someone + wants to add a new lfx skill, provides a complete SKILL.md to import, has an + idea for a skill and wants help drafting it, or asks how to create a skill. allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- @@ -16,180 +13,127 @@ allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion # LFX New Skill — Scaffolder -You help a contributor scaffold a new skill in the `lfx-skills` repo. Your job ends when there's a well-formed `lfx-<name>/SKILL.md` on disk, the new skill is installed locally, and `lfx-skills doctor` verifies it loads. You do not handle the rest of the contribution flow (DCO, PR, review) — for that, point at `CONTRIBUTING.md`. +You help contributors add a skill under `skills/lfx-<name>/`. Read [`references/conventions-quickref.md`](references/conventions-quickref.md) before writing files. -## Step 1: Verify you're in the clone +Use two modes: -This skill only works inside the `lfx-skills` clone. Verify: +- **Complete skill provided:** if the user pastes or points to a complete `SKILL.md`, use it as the source of truth. Preserve the body. Only normalize frontmatter, license header, directory name, and repo-specific placeholders when needed. +- **Draft from idea:** if the user only has an idea, help write the body. Ask targeted questions about trigger phrases, inputs, steps, tools, outputs, and boundaries. Draft concrete step-by-step instructions from the answers. + +## Step 1: Verify Location + +Run: ```bash [ -x ./cli/lfx-skills ] && [ -d ./skills/lfx ] && echo OK || echo NOT_IN_CLONE ``` -If `NOT_IN_CLONE`: - -> "I only run inside the `lfx-skills` clone. `cd` to your clone and ask again." +If `NOT_IN_CLONE`, tell the user to `cd` to the `lfx-skills` clone and stop. -Stop. +## Step 2: Gather Inputs -## Step 2: Q1 — Skill name +Ask whether the user has a complete `SKILL.md` or wants help drafting one. -Use `AskUserQuestion`: +For a complete skill: -> "What's the skill name? It must: -> - start with `lfx-` -> - be lowercase with hyphens (kebab-case) -> - be a noun-phrase or verb-phrase short enough to type as `/lfx-<name>` -> -> Examples: `lfx-release-notes`, `lfx-onboarding-checklist`, `lfx-search-jira`." +- Read the supplied file/content. +- Extract `name`, `description`, and `allowed-tools` when present. +- Ask only for missing required fields. -Validate the answer: -- Must match `^lfx-[a-z0-9-]+$` -- Must NOT collide with an existing directory: check `[ -d "skills/lfx-<name>" ]` — if exists, ask for another. -- Must NOT collide with `lfx` itself or any reserved prefix. +For a draft: -Re-ask until you get a valid name. +- Ask for the skill name. It must match `^lfx-[a-z0-9-]+$` and not already exist under `skills/`. +- Ask for a frontmatter description with 3-5 trigger phrases. +- Ask which tools it needs. Default: `Bash, Read, Glob, Grep, AskUserQuestion`. +- Ask what the skill should do, what inputs it needs, what it may inspect or modify, what output it should produce, and what it must not do. -## Step 3: Q2 — Description +Ask whether it needs `references/`. -> "Describe the skill in one paragraph. The description is what your AI tool reads to decide *when* to invoke this skill — so include 3–5 trigger phrases users might say (e.g., 'Use for "release notes for X", "what changed in Y", "draft an announcement"'). -> -> Aim for 2–4 sentences." +## Step 3: Write Files -Don't auto-generate this. The contributor knows their intent better than you do. Wait for their answer. +Create: -## Step 4: Q3 — Allowed tools - -> "Which tools should the skill have access to? -> -> The typical default is: `Bash, Read, Glob, Grep, AskUserQuestion`. -> -> Add `Write, Edit` if the skill will modify files. -> Add `Skill` if the skill will delegate to other lfx skills. -> Add `WebFetch` if the skill reads external URLs. -> Add specific MCP tool names (formatted `mcp__SERVER__TOOL`) if the skill depends on MCP servers. -> -> Type the comma-separated list, or just press Enter for the default." +```text +skills/lfx-<name>/SKILL.md +``` -Validate: tools should be a comma-separated list of identifiers. If the user includes MCP tools, remind them they'll need a Prerequisites section in the body (see Step 7). +If requested, also create: -## Step 5: Q4 — references/ directory? +```text +skills/lfx-<name>/references/.gitkeep +``` -> "Will this skill have reference docs alongside `SKILL.md`? (e.g., a long table, a JSON config, a checklist that's too big to inline.) — yes / no." +Ensure: -If yes: create `skills/lfx-<name>/references/` with a `.gitkeep` so the directory is tracked even when empty. +- frontmatter starts on line 1 +- license comments are lines 2-3 inside the frontmatter +- `name:` equals the directory basename +- `description:` uses a YAML folded scalar +- `allowed-tools:` is present +- MCP-dependent skills include a `## Prerequisites` section -## Step 6: Generate SKILL.md +If the skill is user-facing, ask whether to add routing guidance to `skills/lfx/SKILL.md`. Internal-only skills can remain unrouted. -Use the `Write` tool with the absolute path `<clone>/skills/lfx-<name>/SKILL.md`. Template: +## Step 4: Validate -```markdown ---- -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -name: <skill-name> -description: > - <user's description, joined into a YAML folded scalar> -allowed-tools: <comma-separated list> ---- +Run only the new skill formatting check: -<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> - -# <Title-cased name> - -<One-paragraph intro: who this skill helps and what it does. Pull from the description but expand.> +```bash +./cli/lfx-skills doctor --skill-formatting-only --skill=lfx-<name> +``` -## Step 1: <verb-noun> +Fix `frontmatter-*` and `license-missing` issues, then rerun until clean. Do not run the full doctor for this scaffolding check; full doctor includes agents.md install/setup checks. -<Concrete instructions for the LLM running the skill. Use code fences for shell snippets and tool invocations. Be specific.> +## Step 5: Explain Local Testing -## Step 2: <verb-noun> +Ask which runtime they want to test: Claude Code plugin, agents.md, or both. -<...> +For Claude Code plugin testing: -## What this skill does NOT do +- If the skill should ship in the Claude plugin, add it to `.claude-plugin/plugin.json`. +- Give the user this validation command to run from their normal terminal: -- <bound 1> -- <bound 2> + ```bash + cd "<absolute-path-to-lfx-skills>" + claude plugin validate . + ``` -## Reference files +- Ask which target LFX repo they want to test in. Resolve a repo name under `~/.lfx-skills/dev-root` or use the absolute path they provide. +- Give the user a ready-to-run command: -- (none yet — add as needed) -``` + ```bash + LFX_SKILLS_CLONE="<absolute-path-to-lfx-skills>" + cd "<resolved-target-repo-path>" + claude --plugin-dir "$LFX_SKILLS_CLONE" + ``` -**Critical formatting rules** (mirror the rest of the lfx-skills repo): +- Tell them to run `/lfx-skills:lfx-<name>` in that Claude Code session. -- Frontmatter MUST start at line 1 with `---`. No blank lines, no comments above. -- The license header is two YAML comment lines (`# Copyright…`, `# SPDX-License-Identifier:`) **inside** the frontmatter, lines 2–3. Plain `#` comments are valid YAML — they pass `head -4 | grep` for the CI license check without breaking frontmatter parsing. -- `name:` value must equal the directory basename (the `lfx-<name>` you picked in Step 2). -- `description:` uses YAML folded scalar (`>`) so it can wrap nicely in source while staying a single string at parse time. +For agents.md testing: -## Step 7: Prerequisites section (if MCP tools) +- Run `./cli/lfx-skills update`. +- Tell the user to restart their agents.md-compatible coding agent and run `/lfx-<name>`. +- If LFX Skills is not installed for agents.md, point them to `/lfx-install` or `./install.sh`. -If the user listed any MCP tools in Step 4, add a `## Prerequisites` section to the body listing them, with a one-line note about how to set up the MCP server (or a link to the right docs). Without it, users without the MCP server configured will hit cryptic errors when the skill tries to invoke a tool that doesn't exist. +Keep the two paths separate: the CLI is for agents.md installs; Claude Code uses the plugin path. -## Step 8: Install locally + verify +## Step 6: Offer Commit And Release Help -Chain into the CLI to make the new skill available immediately: +After validation, ask whether the user wants help committing the scaffolded skill. -```bash -./cli/lfx-skills update -``` +If they say yes: -This re-applies the manifest and detects the new `skills/lfx-<name>/` directory. The CLI will list it as a new skill not in the manifest and prompt to install it everywhere already configured. +- Review `git diff` and `git status`. +- Commit only the intended files. +- Use `git commit -s -S`. +- Do not add co-author trailers. +- Do not push unless explicitly asked. -Then verify: +For releases, explain that after the PR merges they can ask their coding agent to help choose the next SemVer version, update `.claude-plugin/marketplace.json`, run validation, and draft the `gh release create` command. The user should review before committing or creating the release. -```bash -./cli/lfx-skills doctor -``` +## Boundaries -If the doctor flags `frontmatter-no-name`, `frontmatter-name-mismatch`, `license-missing`, `routing-uncovered`, or any other content issue with your new skill, fix it (use Edit) and re-run `doctor` until clean. - -The `routing-uncovered` warning is a reminder to add an entry to `lfx/SKILL.md` (the plain-language router) so `/lfx` knows when to route to your new skill. Decide whether your skill is user-facing (add to routing) or internal-only-invoked-by-another-skill (skip routing — the warning is acceptable). Ask the user if you're unsure. - -## Step 9: Tell the user how to try it - -> "All set. To try `/lfx-<name>`: -> -> 1. Restart your AI tool (or open a new session). -> 2. Type `/lfx-<name>` — it should show in autocomplete and load with the description you wrote. -> -> Iterate on the body as you go: every time you save the SKILL.md, your tool picks it up on the next invocation." - -## Step 10: Explain release/update flow - -> "When you're ready to ship this skill upstream: -> -> - Read `CONTRIBUTING.md` for the DCO, sign-off, and review flow. -> - Run `/lfx-preflight` to validate your changes. -> - Open a PR against `main`. -> - After the PR is merged, ship either this skill alone or a batch of skill changes with a GitHub Release: -> -> | Change type | Version component | -> |---|---| -> | Typo fixes, prompt wording tweaks, docs, installer fixes, CI fixes | patch | -> | New skills, substantial skill behavior updates, new supported platform behavior | minor | -> | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | major (only when explicitly instructed) | -> -> ```bash -> LATEST=$(git tag --sort=-v:refname | head -1) -> echo "Latest tag: $LATEST" -> NEXT=v0.1.0 -> -> gh release create "$NEXT" \ -> --generate-notes \ -> --latest -> ``` -> -> The release tag is the canonical version. After creating the GitHub Release, manually update `../lfx-plugins/.claude-plugin/marketplace.json` so `lfx-skills` points at the new tag and the marketplace `version` is the tag without the leading `v`; commit that marketplace bump with `git commit -s -S`. Claude users update with `/plugin marketplace update lfx` and `/plugin update lfx-skills@lfx`; agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. -> -> Welcome to lfx-skills." - -## What this skill does NOT do - -- **Install or set up the lfx-skills install** — that's `/lfx-install`. -- **Diagnose existing install problems** — that's `/lfx-doctor`. -- **Manage the install (list, uninstall, update)** — that's `/lfx-skills-helper`. -- **Open PRs or push to a remote** — point at `CONTRIBUTING.md` and stop. -- **Author the body of the skill for the contributor** — you scaffold the structure, they write the substance. Don't fabricate steps that match a guess at intent; ask if you don't know. +- Do not install or repair the user's LFX Skills setup; route that to `/lfx-install` or `/lfx-doctor`. +- Do not use the CLI to install Claude Code skills. +- Do not run Claude Code plugin commands for agents.md testing. +- Do not invent unclear behavior. Ask when scope, inputs, outputs, or safety boundaries are unclear. diff --git a/skills/lfx-new-skill/references/conventions-quickref.md b/skills/lfx-new-skill/references/conventions-quickref.md index 413ae1e..f2b2223 100644 --- a/skills/lfx-new-skill/references/conventions-quickref.md +++ b/skills/lfx-new-skill/references/conventions-quickref.md @@ -1,90 +1,134 @@ <!-- Copyright The Linux Foundation and each contributor to LFX. --> <!-- SPDX-License-Identifier: MIT --> -# Skill conventions — quick reference +# New Skill Quick Reference -Cheat sheet for `/lfx-new-skill`. The full version is `docs/skill-authoring.md` (when present). When in doubt, copy a sibling skill that does something close to what you want. - -## Required frontmatter +## Frontmatter ```yaml --- # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT -name: lfx-<your-skill> +name: lfx-<name> description: > - One paragraph. Include 3–5 trigger phrases users might say. + One paragraph with 3-5 trigger phrases users might say. allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- ``` -- The `---` MUST be line 1. No blank lines or comments above it. Skill loaders refuse anything else. -- The two `#` lines on lines 2–3 are valid YAML comments and satisfy the CI license-header check. -- `name:` MUST equal the directory basename. -- `description:` is YAML folded scalar (`>`), so newlines in source become spaces at parse time. +- `---` must be line 1. +- License comments must be lines 2-3 inside frontmatter. +- `name:` must match the directory basename. +- `description:` should use `>`. + +## Input Modes + +- **Complete skill:** preserve the supplied body; normalize only frontmatter, license, path, and repo-specific placeholders. +- **Idea:** ask enough questions to draft concrete runtime instructions. Cover triggers, inputs, steps, allowed tools, output, and explicit non-goals. + +## Tool Defaults -## Allowed tools — common shapes +| Use case | Tools | +|---|---| +| Read-only research | `Bash, Read, Glob, Grep, AskUserQuestion` | +| Reads external URLs | `Bash, Read, Glob, Grep, AskUserQuestion, WebFetch` | +| Edits files | `Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion` | +| Delegates to skills | `Bash, Read, Glob, Grep, AskUserQuestion, Skill` | +| MCP-dependent | Add the specific `mcp__*` tools | -| Use case | Tools | -|-------------------------------------------|-----------------------------------------------------------------------| -| Read-only research/exploration | `Bash, Read, Glob, Grep, AskUserQuestion` | -| Read + URLs | `Bash, Read, Glob, Grep, AskUserQuestion, WebFetch` | -| Code generation / file edits | `Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion` | -| Orchestrator that delegates to other skills | `Bash, Read, Glob, Grep, AskUserQuestion, Skill` | -| MCP-dependent (e.g. Snowflake, Atlassian) | Above + the specific `mcp__*` tool names | +MCP-dependent skills should include `## Prerequisites`. -If you list any `mcp__*` tools, add a `## Prerequisites` section to the body so users without that MCP server configured know what to set up. Otherwise they'll hit cryptic errors when the skill tries to invoke a tool that doesn't exist. +## Body Shape -## Body structure (suggested) +Use this as the default body structure when drafting: ```markdown -# <Title-cased name> +# <Title> -<One paragraph: who this helps, what it does.> +<Who this helps and what it does.> -## Step 1: <verb-noun> +## Step 1: <Action> -<Concrete instructions. Use `AskUserQuestion` for any user input.> +<Concrete instructions. Ask for user input when needed.> -## Step 2: <verb-noun> +## Step 2: <Action> -... +<Concrete instructions.> ## What this skill does NOT do -- <bound 1> -- <bound 2> +- <Boundary> ## Reference files -- (linked references/*.md) +- (none yet) ``` -## Naming the directory +## References -- All skill directories start with `lfx-`. The exception is `lfx/` itself (the plain-language router). -- The directory name becomes the slash command (`/lfx-foo`). Pick something the user will type. +Use `references/` for long tables, checklists, examples, JSON/YAML snippets, or content the model should load only when needed. -## When to use `references/` +## Routing -Use `references/` for content that's: +User-facing skills should usually be mentioned in `skills/lfx/SKILL.md` so `/lfx` can route to them. Internal-only skills can stay unrouted. -- Too long to inline (tables, JSON config, checklists, recipe libraries). -- Read selectively by the LLM at runtime (rather than always-loaded with the skill). +## Validation -Reference files don't need frontmatter. Just markdown (or JSON / YAML / etc.). Add the LFX HTML license header at the top of markdown reference files for CI. +Run: -## Routing +```bash +./cli/lfx-skills doctor --skill-formatting-only --skill=lfx-<name> +``` -`lfx/SKILL.md` is the plain-language router. If your new skill is **user-facing** (someone might describe a problem and want it routed), add a row to the routing table in `lfx/SKILL.md`. Internal skills (only invoked by another skill, like `/lfx-backend-builder` invoked by `/lfx-coordinator`) can stay out — `lfx-skills doctor`'s `routing-uncovered` warning for them is acceptable. +This checks only the new skill's frontmatter and license header. -## Validation +## Claude Code Local Test + +If the skill should ship in the Claude plugin, add it to `.claude-plugin/plugin.json`. + +Give the user: + +```bash +cd "<absolute-path-to-lfx-skills>" +claude plugin validate . +``` -After creating, run: +Then ask which LFX repo they want to test in, resolve it under `~/.lfx-skills/dev-root` if they gave a repo name, and give: ```bash -./cli/lfx-skills update # install the new skill at every configured target -./cli/lfx-skills doctor # validate frontmatter, license header, routing +LFX_SKILLS_CLONE="<absolute-path-to-lfx-skills>" +cd "<resolved-target-repo-path>" +claude --plugin-dir "$LFX_SKILLS_CLONE" ``` -Fix anything the doctor flags before opening a PR. +They should test: + +```text +/lfx-skills:lfx-<name> +``` + +## agents.md Local Test + +Run: + +```bash +./cli/lfx-skills update +``` + +Then tell the user to restart their agents.md-compatible coding agent and run: + +```text +/lfx-<name> +``` + +## Commit And Release + +After validation, ask if the user wants help committing. If yes: + +- Review `git diff` and `git status`. +- Commit only intended files. +- Use `git commit -s -S`. +- Do not add co-author trailers. +- Do not push unless explicitly asked. + +For releases, the user can ask an agent to prepare the release bump: choose SemVer, update `.claude-plugin/marketplace.json`, run validation, and draft `gh release create`. diff --git a/skills/lfx-research/SKILL.md b/skills/lfx-research/SKILL.md index df77dc9..f9b93e8 100644 --- a/skills/lfx-research/SKILL.md +++ b/skills/lfx-research/SKILL.md @@ -85,6 +85,32 @@ gh api repos/linuxfoundation/<repo-name>/contents/design/<file>.go \ | Surveys | `lfx-v2-survey-service` | | Members | `lfx-v2-member-service` | +**Dev root setup:** + +Before checking local repos, resolve the LFX dev root: + +```bash +if [ -f "$HOME/.lfx-skills/dev-root" ]; then + LFX_DEV_ROOT="$(cat "$HOME/.lfx-skills/dev-root")" +else + LFX_DEV_ROOT="$HOME/lf" +fi +printf '%s\n' "$LFX_DEV_ROOT" +``` + +If `~/.lfx-skills/dev-root` already exists, use it without asking and continue. Do not list or scan repos during setup. + +If `~/.lfx-skills/dev-root` is missing, ask a quick question: whether the user wants to set an LFX dev root now or use the default `~/lf`. Explain that setting it lets research read local LFX repos directly, which is faster and more reliable than falling back to `gh api`. Suggest likely locations: `~/lf`, `~/lfx`, `~/src/lfx`, or the parent directory containing their LFX clones. + +If they accept, ask for the path, then write it: + +```bash +mkdir -p ~/.lfx-skills +printf '%s\n' "<chosen-dev-root>" > ~/.lfx-skills/dev-root +``` + +If they choose the default, continue with `~/lf` and use GitHub fallbacks when local repos are unavailable. + **If the upstream Go repo exists locally**, read the files directly instead of using `gh api`. Local reads are faster and more reliable. Resolve the path at the start of your bash commands by reading the dev-root file written by `lfx-skills install`: ```bash diff --git a/skills/lfx-skills-helper/SKILL.md b/skills/lfx-skills-helper/SKILL.md index 3f8d1d6..8f5a5e5 100644 --- a/skills/lfx-skills-helper/SKILL.md +++ b/skills/lfx-skills-helper/SKILL.md @@ -21,7 +21,7 @@ allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion You are the conversational front-end for the agents.md-only `lfx-skills` CLI: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. -Claude Code is separate: it installs LFX Skills as a plugin with `/plugin marketplace add linuxfoundation/lfx-plugins` and `/plugin install lfx-skills@lfx`. Do not use the CLI to install Claude Code skills. The CLI can only remove old Claude symlink installs via `lfx-skills uninstall --legacy-claude-only` or as part of `lfx-skills uninstall --all`. +Claude Code is separate: it installs LFX Skills as a plugin with `/plugin marketplace add linuxfoundation/lfx-skills` and `/plugin install lfx-skills@lfx-skills`. Do not use the CLI to install Claude Code skills. The CLI can only remove old Claude symlink installs via `lfx-skills uninstall --legacy-claude-only` or as part of `lfx-skills uninstall --all`. ## Step 1: Locate the CLI diff --git a/skills/lfx-skills-helper/references/intents.md b/skills/lfx-skills-helper/references/intents.md index a7b9cf3..22f2bb7 100644 --- a/skills/lfx-skills-helper/references/intents.md +++ b/skills/lfx-skills-helper/references/intents.md @@ -37,7 +37,7 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs |-------------------------------------------------|--------------------------------------------------------------------------------------| | "Add lfx skills to this repo" | Confirm, then `lfx-skills install --yes --scope=repo --repos="$(pwd)"`. Suggest `/lfx-doctor` after. | | "Remove lfx skills from this repo" | Confirm, then `lfx-skills uninstall --yes --scope=repo --repos="$(pwd)"` | -| "Install lfx skills globally for Claude" | Explain the Claude Code plugin path: `/plugin marketplace add linuxfoundation/lfx-plugins`, then `/plugin install lfx-skills@lfx` | +| "Install lfx skills globally for Claude" | Explain the Claude Code plugin path: `/plugin marketplace add linuxfoundation/lfx-skills`, then `/plugin install lfx-skills@lfx-skills` | | "Add agents.md support" | Confirm, then `lfx-skills install --yes --scope=global` | | "Install everything everywhere" | Confirm. Don't assume `--repos=`; ask the user which repos. | | "Uninstall lfx skills" | Confirm, then `lfx-skills uninstall --yes --all` | diff --git a/skills/lfx-test-journey/SKILL.md b/skills/lfx-test-journey/SKILL.md index b643618..2c4c813 100644 --- a/skills/lfx-test-journey/SKILL.md +++ b/skills/lfx-test-journey/SKILL.md @@ -62,10 +62,36 @@ If a subcommand requires a journey name and the user didn't provide one, run **L ## Create Journey +### Dev Root + +Resolve the LFX dev root: + +```bash +if [ -f "$HOME/.lfx-skills/dev-root" ]; then + LFX_DEV_ROOT="$(cat "$HOME/.lfx-skills/dev-root")" +else + LFX_DEV_ROOT="$HOME/lf" +fi +printf '%s\n' "$LFX_DEV_ROOT" +``` + +If `~/.lfx-skills/dev-root` already exists, use it without asking and continue. Do not list or scan repos during setup. + +If `~/.lfx-skills/dev-root` is missing, ask a quick question: whether the user wants to set an LFX dev root now or use the default `~/lf`. Explain that journey testing needs a parent directory containing local LFX repos so it can create worktrees from their branches. Suggest likely locations: `~/lf`, `~/lfx`, `~/src/lfx`, or the parent directory containing their LFX clones. + +If they accept, ask for the path, then write it: + +```bash +mkdir -p ~/.lfx-skills +printf '%s\n' "<chosen-dev-root>" > ~/.lfx-skills/dev-root +``` + +If they choose the default, continue with `~/lf`. If no repos are found there, stop and tell them journey testing needs local LFX repo clones. + ### Step 1: Discover Repos Scan the LFX dev root for git repositories. The first line resolves -`LFX_DEV_ROOT` from a one-line text file written by `lfx-skills install`, +`LFX_DEV_ROOT` from a one-line text file written by `lfx-skills install` in `~/.lfx-skills/dev-root`, falling back to `~/lf` when not installed. No shell rc / env var required. ```bash From 8952e883ed4370f7f52f4023ac89b10c78b2809d Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Thu, 7 May 2026 12:00:32 +0200 Subject: [PATCH 17/20] docs: clarify plugin versioning and repo bootstrap skills Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- .agents/skills/lfx-doctor/SKILL.md | 21 +++++ .agents/skills/lfx-install/SKILL.md | 21 +++++ .agents/skills/lfx-new-skill/SKILL.md | 20 +++++ .agents/skills/lfx-skills-helper/SKILL.md | 22 ++++++ .claude-plugin/marketplace.json | 7 +- .claude-plugin/plugin.json | 1 + .claude/skills/lfx-doctor/SKILL.md | 21 +++++ .claude/skills/lfx-install/SKILL.md | 21 +++++ .claude/skills/lfx-new-skill/SKILL.md | 20 +++++ .claude/skills/lfx-skills-helper/SKILL.md | 22 ++++++ .gitignore | 31 +++++++- AGENTS.md | 38 +++------- CLAUDE.md | 38 +++------- README.md | 76 ++++++++++--------- docs/overview.md | 42 ++++------ skills/lfx-doctor/SKILL.md | 20 +++-- skills/lfx-new-skill/SKILL.md | 5 +- .../references/conventions-quickref.md | 4 +- .../lfx-skills-helper/references/intents.md | 3 +- 19 files changed, 295 insertions(+), 138 deletions(-) create mode 100644 .agents/skills/lfx-doctor/SKILL.md create mode 100644 .agents/skills/lfx-install/SKILL.md create mode 100644 .agents/skills/lfx-new-skill/SKILL.md create mode 100644 .agents/skills/lfx-skills-helper/SKILL.md create mode 100644 .claude/skills/lfx-doctor/SKILL.md create mode 100644 .claude/skills/lfx-install/SKILL.md create mode 100644 .claude/skills/lfx-new-skill/SKILL.md create mode 100644 .claude/skills/lfx-skills-helper/SKILL.md diff --git a/.agents/skills/lfx-doctor/SKILL.md b/.agents/skills/lfx-doctor/SKILL.md new file mode 100644 index 0000000..1d94ec9 --- /dev/null +++ b/.agents/skills/lfx-doctor/SKILL.md @@ -0,0 +1,21 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-doctor +description: > + Bootstrap wrapper for the LFX Skills doctor helper in this source repo. Use to + diagnose agents.md installs and legacy Claude symlink installs from inside the + lfx-skills clone. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +# LFX Doctor Bootstrap + +This is a repository-local bootstrap skill. Read `skills/lfx-doctor/SKILL.md` +from the LFX Skills repo root and follow that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-doctor/SKILL.md +``` diff --git a/.agents/skills/lfx-install/SKILL.md b/.agents/skills/lfx-install/SKILL.md new file mode 100644 index 0000000..a870d00 --- /dev/null +++ b/.agents/skills/lfx-install/SKILL.md @@ -0,0 +1,21 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-install +description: > + Bootstrap wrapper for the LFX Skills install helper in this source repo. Use + when the user has just cloned lfx-skills and wants to set up agents.md-compatible + tools, or needs the Claude Code plugin install path explained. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +# LFX Install Bootstrap + +This is a repository-local bootstrap skill. Read `skills/lfx-install/SKILL.md` +from the LFX Skills repo root and follow that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-install/SKILL.md +``` diff --git a/.agents/skills/lfx-new-skill/SKILL.md b/.agents/skills/lfx-new-skill/SKILL.md new file mode 100644 index 0000000..638f209 --- /dev/null +++ b/.agents/skills/lfx-new-skill/SKILL.md @@ -0,0 +1,20 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-new-skill +description: > + Bootstrap wrapper for the LFX Skills authoring helper in this source repo. Use + when a contributor wants to create a new lfx skill or asks how to author one. +allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion +--- + +# LFX New Skill Bootstrap + +This is a repository-local bootstrap skill. Read `skills/lfx-new-skill/SKILL.md` +from the LFX Skills repo root and follow that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-new-skill/SKILL.md +``` diff --git a/.agents/skills/lfx-skills-helper/SKILL.md b/.agents/skills/lfx-skills-helper/SKILL.md new file mode 100644 index 0000000..c9c01d0 --- /dev/null +++ b/.agents/skills/lfx-skills-helper/SKILL.md @@ -0,0 +1,22 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-skills-helper +description: > + Bootstrap wrapper for the LFX Skills management helper in this source repo. + Use to list, update, uninstall, inspect, or manage agents.md installs and + legacy Claude symlink cleanup. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +# LFX Skills Helper Bootstrap + +This is a repository-local bootstrap skill. Read +`skills/lfx-skills-helper/SKILL.md` from the LFX Skills repo root and follow +that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-skills-helper/SKILL.md +``` diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 25bc53b..507b6f5 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -7,12 +7,7 @@ "plugins": [ { "name": "lfx-skills", - "source": { - "source": "github", - "repo": "linuxfoundation/lfx-skills", - "ref": "v0.1.0" - }, - "version": "0.1.0", + "source": "./", "description": "Claude Code skills for LFX. Start with the lfx skill and describe what you want in plain language." } ] diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index d8fd307..efe3897 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,6 +1,7 @@ { "name": "lfx-skills", "description": "Claude Code skills for LFX. Start with the lfx skill and describe what you want in plain language.", + "version": "0.1.0", "author": { "name": "The Linux Foundation" }, diff --git a/.claude/skills/lfx-doctor/SKILL.md b/.claude/skills/lfx-doctor/SKILL.md new file mode 100644 index 0000000..1d94ec9 --- /dev/null +++ b/.claude/skills/lfx-doctor/SKILL.md @@ -0,0 +1,21 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-doctor +description: > + Bootstrap wrapper for the LFX Skills doctor helper in this source repo. Use to + diagnose agents.md installs and legacy Claude symlink installs from inside the + lfx-skills clone. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +# LFX Doctor Bootstrap + +This is a repository-local bootstrap skill. Read `skills/lfx-doctor/SKILL.md` +from the LFX Skills repo root and follow that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-doctor/SKILL.md +``` diff --git a/.claude/skills/lfx-install/SKILL.md b/.claude/skills/lfx-install/SKILL.md new file mode 100644 index 0000000..a870d00 --- /dev/null +++ b/.claude/skills/lfx-install/SKILL.md @@ -0,0 +1,21 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-install +description: > + Bootstrap wrapper for the LFX Skills install helper in this source repo. Use + when the user has just cloned lfx-skills and wants to set up agents.md-compatible + tools, or needs the Claude Code plugin install path explained. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +# LFX Install Bootstrap + +This is a repository-local bootstrap skill. Read `skills/lfx-install/SKILL.md` +from the LFX Skills repo root and follow that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-install/SKILL.md +``` diff --git a/.claude/skills/lfx-new-skill/SKILL.md b/.claude/skills/lfx-new-skill/SKILL.md new file mode 100644 index 0000000..638f209 --- /dev/null +++ b/.claude/skills/lfx-new-skill/SKILL.md @@ -0,0 +1,20 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-new-skill +description: > + Bootstrap wrapper for the LFX Skills authoring helper in this source repo. Use + when a contributor wants to create a new lfx skill or asks how to author one. +allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion +--- + +# LFX New Skill Bootstrap + +This is a repository-local bootstrap skill. Read `skills/lfx-new-skill/SKILL.md` +from the LFX Skills repo root and follow that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-new-skill/SKILL.md +``` diff --git a/.claude/skills/lfx-skills-helper/SKILL.md b/.claude/skills/lfx-skills-helper/SKILL.md new file mode 100644 index 0000000..c9c01d0 --- /dev/null +++ b/.claude/skills/lfx-skills-helper/SKILL.md @@ -0,0 +1,22 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-skills-helper +description: > + Bootstrap wrapper for the LFX Skills management helper in this source repo. + Use to list, update, uninstall, inspect, or manage agents.md installs and + legacy Claude symlink cleanup. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +# LFX Skills Helper Bootstrap + +This is a repository-local bootstrap skill. Read +`skills/lfx-skills-helper/SKILL.md` from the LFX Skills repo root and follow +that canonical skill exactly. + +Do not use this wrapper as the source of truth. The canonical implementation is: + +```text +skills/lfx-skills-helper/SKILL.md +``` diff --git a/.gitignore b/.gitignore index 5b8c163..636ed45 100644 --- a/.gitignore +++ b/.gitignore @@ -17,13 +17,36 @@ Thumbs.db # Claude AI assistant — local state (settings.local.json, projects/, etc.) -# stays out of the repo. Claude Code distribution now goes through the -# .claude-plugin manifest; this repo does not commit .claude/skills symlinks. +# stays out of the repo. The four committed bootstrap skills below make this +# source repo usable in Claude Code without installing the published plugin. .claude/* +!.claude/ +!.claude/skills/ +.claude/skills/* +!.claude/skills/lfx-install/ +!.claude/skills/lfx-install/** +!.claude/skills/lfx-doctor/ +!.claude/skills/lfx-doctor/** +!.claude/skills/lfx-skills-helper/ +!.claude/skills/lfx-skills-helper/** +!.claude/skills/lfx-new-skill/ +!.claude/skills/lfx-new-skill/** -# agents.md-compatible tools can still use the CLI installer; no bootstrap -# symlinks are committed in this repo for now. +# agents.md-compatible tools can use the four committed bootstrap skills in +# this repo without running the installer. Other generated per-repo skill +# files or symlinks stay local. .agents/* +!.agents/ +!.agents/skills/ +.agents/skills/* +!.agents/skills/lfx-install/ +!.agents/skills/lfx-install/** +!.agents/skills/lfx-doctor/ +!.agents/skills/lfx-doctor/** +!.agents/skills/lfx-skills-helper/ +!.agents/skills/lfx-skills-helper/** +!.agents/skills/lfx-new-skill/ +!.agents/skills/lfx-new-skill/** # Legacy install manifest. Older clones of lfx-skills wrote this; the CLI # stores its manifest at ~/.lfx-skills/config.json, so this file is no diff --git a/AGENTS.md b/AGENTS.md index ecd9efb..af9de79 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -33,8 +33,10 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - `cli/lfx-skills` — multi-subcommand bash CLI. - `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). - `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. -- `.claude-plugin/plugin.json` — Claude Code plugin manifest. It explicitly lists Claude-facing runtime skills and intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. -- `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin, points its source at the released `linuxfoundation/lfx-skills` tag, and carries the published plugin version. +- `.agents/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so agents.md-compatible tools can use the repo helper skills before installation. These are real files, not symlinks; canonical bodies live under `skills/`. +- `.claude-plugin/plugin.json` — Claude Code plugin manifest. Its `skills` array is an allowlist: only listed skill paths are exposed through the Claude plugin. New user-facing Claude skills must be added here. It intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. +- `.claude/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so Claude Code can use the repo helper skills in this source repo without installing the published plugin. These are not part of the distributed plugin. +- `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin and points its source at `"./"`. - `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. - `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. - `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. @@ -50,13 +52,12 @@ The skill bodies in this repo use Claude Code's tool vocabulary by default (Bash - The `name:` frontmatter field equals the directory basename. - DCO-signed and cryptographically signed manual commits are required (`git commit -s -S`). -## Releases +## Versioning -- Releases use GitHub Releases with `vMAJOR.MINOR.PATCH`, like `lfx-mcp`. - One skill change or a batch of skill changes can ship in the same release. -- Create releases through GitHub Releases or `gh release create`; GitHub creates the tag. -- Before release, update `.claude-plugin/marketplace.json` so `plugins[].source.ref` equals the release tag and `plugins[].version` equals the release without the leading `v`. -- `.claude-plugin/plugin.json` must not contain `version`; Anthropic resolves `plugin.json` version before marketplace version, so keep the explicit version in the marketplace only. +- Claude Code plugin changes must bump `.claude-plugin/plugin.json` `version`; otherwise Claude Code will keep using the cached plugin version. +- New user-facing skills that should be available through the Claude Code plugin must also be added to `.claude-plugin/plugin.json` `skills`. +- The marketplace follows the LFX Skills default branch and uses `"./"` as the local plugin source. Version bump guidelines: @@ -66,26 +67,11 @@ Version bump guidelines: | New skills, substantial skill behavior updates, new supported platform behavior | **minor** | | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | -Release command shape: +Bump the plugin version with the skill changes: ```bash -LATEST=$(git tag --sort=-v:refname | head -1) -echo "Latest tag: $LATEST" -NEXT=v0.1.0 - -gh release create "$NEXT" \ - --generate-notes \ - --latest -``` - -Before creating the GitHub Release, update and commit the plugin version plus marketplace tag reference: - -```bash -# Edit .claude-plugin/marketplace.json: -# - plugins[].source.ref = "$NEXT" -# - plugins[].version = "${NEXT#v}" -git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: release lfx-skills plugin $NEXT" +git add .claude-plugin/plugin.json skills/<changed-skill> +git commit -s -S -m "feat: update lfx skills plugin" ``` -After the release, Claude users update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. +After the change is on `main`, Claude users update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. diff --git a/CLAUDE.md b/CLAUDE.md index 857a45b..2311fa5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -33,8 +33,10 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - `cli/lfx-skills` — multi-subcommand bash CLI. - `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). - `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. -- `.claude-plugin/plugin.json` — Claude Code plugin manifest. It explicitly lists Claude-facing runtime skills and intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. -- `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin, points its source at the released `linuxfoundation/lfx-skills` tag, and carries the published plugin version. +- `.agents/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so agents.md-compatible tools can use the repo helper skills before installation. These are real files, not symlinks; canonical bodies live under `skills/`. +- `.claude-plugin/plugin.json` — Claude Code plugin manifest. Its `skills` array is an allowlist: only listed skill paths are exposed through the Claude plugin. New user-facing Claude skills must be added here. It intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. +- `.claude/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so Claude Code can use the repo helper skills in this source repo without installing the published plugin. These are not part of the distributed plugin. +- `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin and points its source at `"./"`. - `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. - `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. - `~/.lfx-skills/dev-root` — single-line text file the 3 dev-root-aware skills `cat` to resolve `LFX_DEV_ROOT` without depending on shell env. @@ -46,13 +48,12 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - The `name:` frontmatter field equals the directory basename. - DCO-signed and cryptographically signed manual commits are required (`git commit -s -S`). -## Releases +## Versioning -- Releases use GitHub Releases with `vMAJOR.MINOR.PATCH`, like `lfx-mcp`. - One skill change or a batch of skill changes can ship in the same release. -- Create releases through GitHub Releases or `gh release create`; GitHub creates the tag. -- Before release, update `.claude-plugin/marketplace.json` so `plugins[].source.ref` equals the release tag and `plugins[].version` equals the release without the leading `v`. -- `.claude-plugin/plugin.json` must not contain `version`; Anthropic resolves `plugin.json` version before marketplace version, so keep the explicit version in the marketplace only. +- Claude Code plugin changes must bump `.claude-plugin/plugin.json` `version`; otherwise Claude Code will keep using the cached plugin version. +- New user-facing skills that should be available through the Claude Code plugin must also be added to `.claude-plugin/plugin.json` `skills`. +- The marketplace follows the LFX Skills default branch and uses `"./"` as the local plugin source. Version bump guidelines: @@ -62,26 +63,11 @@ Version bump guidelines: | New skills, substantial skill behavior updates, new supported platform behavior | **minor** | | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | -Release command shape: +Bump the plugin version with the skill changes: ```bash -LATEST=$(git tag --sort=-v:refname | head -1) -echo "Latest tag: $LATEST" -NEXT=v0.1.0 - -gh release create "$NEXT" \ - --generate-notes \ - --latest -``` - -Before creating the GitHub Release, update and commit the plugin version plus marketplace tag reference: - -```bash -# Edit .claude-plugin/marketplace.json: -# - plugins[].source.ref = "$NEXT" -# - plugins[].version = "${NEXT#v}" -git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: release lfx-skills plugin $NEXT" +git add .claude-plugin/plugin.json skills/<changed-skill> +git commit -s -S -m "feat: update lfx skills plugin" ``` -After the release, Claude users update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. +After the change is on `main`, Claude users update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`. agents.md users update with `lfx-skills update --pull` and `lfx-skills doctor`. diff --git a/README.md b/README.md index 143ab8d..bdd49bb 100644 --- a/README.md +++ b/README.md @@ -18,7 +18,7 @@ Install the Claude Code plugin from the LFX marketplace: After installation, start with the `lfx` skill. In Claude Code that command is namespaced as `/lfx-skills:lfx`; describe what you want in plain language and it will route you to the right workflow. -The marketplace metadata lives in `.claude-plugin/marketplace.json` in LFX Skills. The marketplace points the plugin source at the released `linuxfoundation/lfx-skills` tag and carries the published plugin version. +The marketplace metadata lives in `.claude-plugin/marketplace.json` in LFX Skills. It lists the local plugin source as `"./"`; the Claude plugin version is tracked in `.claude-plugin/plugin.json`. The Claude Code plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. @@ -37,20 +37,22 @@ git clone https://github.com/linuxfoundation/lfx-skills.git cd lfx-skills ``` -Start your coding agent in the cloned repo and ask it to set up LFX Skills. The repo includes four helper skills under `skills/` for this flow: +Start your coding agent in the cloned repo and ask it to set up LFX Skills. This repo ships four bootstrap helper skills out of the box through committed `.agents/skills/` and `.claude/skills/` wrappers, so agents can use them inside this repo without running the installer first: - `lfx-install` — guided first-time setup - `lfx-doctor` — install health checks and repair guidance - `lfx-skills-helper` — list, update, uninstall, and inspect the setup - `lfx-new-skill` — scaffold a new skill in this repo +The canonical source for those helpers still lives under `skills/`; the `.agents/skills/` and `.claude/skills/` files are repo-local bootstrap wrappers. + If you prefer to run the installer manually: ```bash ./install.sh ``` -The CLI installs skill symlinks into agents.md skill directories and records the install in `~/.lfx-skills/config.json`. +The CLI installs skill symlinks into agents.md skill directories outside this repo and records the install in `~/.lfx-skills/config.json`. agents.md installs include the 15 runtime skills plus `/lfx-doctor` and `/lfx-skills-helper`. `/lfx-install` and `/lfx-new-skill` stay clone-only. Then restart your AI coding assistant, open any LFX repo, and type `/lfx` to get started. @@ -67,18 +69,22 @@ The `/lfx` skill auto-detects your repo, branch, and context, then routes you to New to LFX development? Type `/lfx` and say **"show me an example"** for a walkthrough. +## Authoring Skills + +New skills should be authored from the cloned LFX Skills repo. Start your coding agent in this repo and use `/lfx-new-skill`; it guides the authoring flow, creates the `skills/lfx-<name>/SKILL.md` structure, applies the repo conventions, and explains how to test the skill locally. + +Use `/lfx-new-skill` whether you are starting from an idea or from a fully written skill. If you already have the skill body, give it to the agent and it should preserve the content while normalizing the repo-specific details such as frontmatter, license header, path, and validation. + +The authoring helper keeps Claude Code plugin testing separate from agents.md testing. For Claude Code, it points you at plugin validation and local `--plugin-dir` testing. For agents.md-compatible tools, it points you at the CLI update flow. It also offers to help prepare a signed commit with `git commit -s -S`. + +New user-facing skills that should be available through the Claude Code plugin must be added to the `skills` allowlist in `.claude-plugin/plugin.json`. Creating `skills/lfx-<name>/SKILL.md` is not enough for Claude plugin users; the plugin exposes only the skill paths listed in that manifest. + ## Prerequisites - An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. See [docs/overview.md](docs/overview.md) for details. - Access to LFX repositories (for the skills to operate on) - **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. The CLI records the chosen path in `~/.lfx-skills/dev-root` so skills can discover local repos without shell rc edits. Skills that use this will prompt the user to either set it or use default. -## CLI Safety - -`./install.sh` is a thin wrapper around `cli/lfx-skills install`. It does not edit your shell rc or rewrite your `PATH`. - -The only PATH-related write it attempts is creating an `lfx-skills` symlink in an existing writable directory already on your `PATH` (`~/.local/bin`, `~/bin`, `/opt/homebrew/bin`, or `/usr/local/bin`). It refuses to overwrite existing non-symlinks or symlinks it does not own. If no safe directory is available, it prints an alias fallback instead. - ## Verify Restart your AI coding assistant (or open a new session) in any LFX repo and type `/lfx`. @@ -103,11 +109,16 @@ Restart your AI coding assistant (or open a new session) in any LFX repo and typ /lfx-skills-helper ← manage what's installed where (install/uninstall/update/list) ``` -## Releases and Updates +## Versioning and Updates -Releases follow the same tag-first GitHub Release pattern used by LFX service repos such as `lfx-mcp`. +Skill changes can be shipped one at a time or batched together. For Claude Code users to receive plugin changes, update the `version` field in `.claude-plugin/plugin.json` before merging or pushing the change to `main`. -You can ship one skill change or a batch of skill changes in the same release. Merge the skill changes first, bump the plugin version and marketplace tag reference, then create a GitHub Release using `vMAJOR.MINOR.PATCH`; GitHub creates the tag. +The marketplace follows the LFX Skills default branch. Because `.claude-plugin/plugin.json` defines `version`, Claude Code uses that value for cache and update detection. If skill content changes but the plugin version stays the same, Claude Code will keep using the already cached plugin version. + +For new Claude-facing skills, update both parts of `.claude-plugin/plugin.json`: + +- Add the new skill path to the `skills` allowlist, for example `"./skills/lfx-example/"`. +- Bump `version` using the patch/minor/major guidance below. ### Version bump guidelines @@ -117,47 +128,30 @@ You can ship one skill change or a batch of skill changes in the same release. M | New skills, substantial skill behavior updates, new supported platform behavior | **minor** | | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** (only when explicitly instructed) | -Before creating the release, update the marketplace entry in `.claude-plugin/marketplace.json`: +Update `.claude-plugin/plugin.json`: ```json { - "source": { - "source": "github", - "repo": "linuxfoundation/lfx-skills", - "ref": "v0.1.0" - }, "version": "0.1.0" } ``` -Commit the version bump with DCO and cryptographic signing before creating the GitHub Release: +Commit the version bump with the skill changes, using DCO and cryptographic signing: ```bash -git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: release lfx-skills plugin v0.1.0" +git add .claude-plugin/plugin.json skills/<changed-skill> +git commit -s -S -m "feat: update lfx skills plugin" ``` -Do not set `version` in `.claude-plugin/plugin.json`. Anthropic's version-resolution order checks `plugin.json` first, then marketplace `version`, then the git commit SHA. Keeping the explicit version only in the marketplace avoids duplicate version sources while still pinning the plugin source to the release tag. - -Create the GitHub Release after the version bump commit is merged: - -```bash -LATEST=$(git tag --sort=-v:refname | head -1) -echo "Latest tag: $LATEST" -NEXT=v0.1.0 - -gh release create "$NEXT" \ - --generate-notes \ - --latest -``` - -Claude Code users then update from Claude: +After the change is on `main`, Claude Code users update from Claude: ```text /plugin marketplace update lfx-skills /plugin update lfx-skills@lfx-skills ``` +They can also enable auto-update. + For agents.md-compatible installs, update from the terminal or ask your coding agent to update LFX Skills: ```bash @@ -579,6 +573,16 @@ An **interactive setup guide** that walks through environment configuration step ├── lfx-skills-helper/ # CLI management front-end ├── lfx-install/ # CLI install guide for agents.md tools └── lfx-new-skill/ # Contributor scaffolder +├── .agents/skills/ # Repo-local bootstrap wrappers for agents.md tools +│ ├── lfx-doctor/ +│ ├── lfx-skills-helper/ +│ ├── lfx-install/ +│ └── lfx-new-skill/ +├── .claude/skills/ # Repo-local bootstrap wrappers for Claude Code +│ ├── lfx-doctor/ +│ ├── lfx-skills-helper/ +│ ├── lfx-install/ +│ └── lfx-new-skill/ ``` ## License diff --git a/docs/overview.md b/docs/overview.md index a1cab22..6087f2e 100644 --- a/docs/overview.md +++ b/docs/overview.md @@ -35,9 +35,11 @@ The marketplace lives in LFX Skills: linuxfoundation/lfx-skills ``` -The marketplace publishes the `lfx-skills` Claude Code plugin. Its source points at the released LFX Skills tag, so Claude Code users install a tagged release rather than an arbitrary branch commit. +The marketplace publishes the `lfx-skills` Claude Code plugin. Its source is `"./"`, so the plugin is loaded from the LFX Skills repo root when the marketplace is cloned. -LFX Skills also owns the plugin manifest. The marketplace carries both the published plugin version and the release tag reference. +LFX Skills also owns the plugin manifest. The Claude plugin version is tracked in `.claude-plugin/plugin.json`. + +The `skills` array in `.claude-plugin/plugin.json` is the Claude plugin allowlist. New user-facing skills are not available through the plugin just because they exist under `skills/`; they must be added to that allowlist and the plugin version must be bumped. ## Legacy Claude Symlink Cleanup @@ -77,13 +79,15 @@ cd lfx-skills Then start your coding agent in the cloned LFX Skills directory and ask it to set up LFX Skills. -LFX Skills includes four helper skills for this flow: +LFX Skills includes four helper skills for this flow. They are available out of the box inside the cloned repo through committed `.agents/skills/` and `.claude/skills/` bootstrap wrappers, so a coding agent can use them before anything is installed: - `lfx-install` — guided first-time setup - `lfx-doctor` — install health checks and repair guidance - `lfx-skills-helper` — list, update, uninstall, and inspect the setup - `lfx-new-skill` — scaffold a new LFX skill +The canonical helper skill bodies live under `skills/`; the repo-local `.agents/skills/` and `.claude/skills/` files are lightweight wrappers for bootstrapping work in this repository. + Manual fallback: ```bash @@ -131,13 +135,7 @@ If `lfx-skills` is not on `PATH`, run it from the local LFX Skills clone: ## Releases -Skill releases use GitHub Release tags in `linuxfoundation/lfx-skills`. - -Use SemVer tags: - -```text -vMAJOR.MINOR.PATCH -``` +Skill versions use SemVer in `.claude-plugin/plugin.json`. Version bump guide: @@ -147,35 +145,23 @@ Version bump guide: | New skills or substantial skill behavior updates | **minor** | | Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** | -Before creating the release, update the marketplace entry in `.claude-plugin/marketplace.json`. +For Claude Code users to receive plugin changes, update `.claude-plugin/plugin.json` before merging or pushing the change to `main`. Because the plugin manifest defines `version`, Claude Code uses that value for cache and update detection. If skill content changes but the plugin version stays the same, Claude Code will keep using the already cached plugin version. -Do not set `version` in `.claude-plugin/plugin.json`. Anthropic's version-resolution order checks `plugin.json` first, then marketplace `version`, then the git commit SHA. Keeping the explicit version only in the marketplace avoids duplicate version sources while still pinning the plugin source to the release tag. +For new Claude-facing skills, update both the `skills` allowlist and `version` in `.claude-plugin/plugin.json`. This follows Anthropic's Claude Code marketplace guidance: - [Create and distribute a plugin marketplace](https://code.claude.com/docs/en/plugin-marketplaces) - [Plugins reference](https://code.claude.com/docs/en/plugins-reference) -Commit the version bump with DCO and cryptographic signing: +Commit the version bump with the skill changes, using DCO and cryptographic signing: ```bash -git add .claude-plugin/marketplace.json -git commit -s -S -m "chore: release lfx-skills plugin v0.1.0" -``` - -After the version bump commit is merged, create the tag release with the same `gh release create` flow used by LFX MCP: - -```bash -LATEST=$(git tag --sort=-v:refname | head -1) -echo "Latest tag: $LATEST" -NEXT=v0.1.0 - -gh release create "$NEXT" \ - --generate-notes \ - --latest +git add .claude-plugin/plugin.json skills/<changed-skill> +git commit -s -S -m "feat: update lfx skills plugin" ``` -The `gh release create` command creates the release tag. The tag is the canonical LFX Skills version. +After the change is on `main`, Claude Code users update the marketplace and plugin from Claude Code. ## Removing agents.md Installs diff --git a/skills/lfx-doctor/SKILL.md b/skills/lfx-doctor/SKILL.md index a4052d7..3f7a6cc 100644 --- a/skills/lfx-doctor/SKILL.md +++ b/skills/lfx-doctor/SKILL.md @@ -3,12 +3,15 @@ # SPDX-License-Identifier: MIT name: lfx-doctor description: > - Diagnose problems with the LFX Skills installation: broken symlinks, missing - dev root, frontmatter errors, routing gaps, MCP setup. Use whenever a skill + Diagnose problems with the agents.md LFX Skills installation and legacy + Claude symlink installs: broken symlinks, missing dev root, frontmatter + errors, routing gaps, MCP setup. Use whenever an agents.md-installed skill isn't loading, when /lfx commands aren't appearing in autocomplete, when - newly installed skills don't show up, or when the user asks "what's wrong - with my install", "is my setup OK", or "check my lfx skills". Wraps - `lfx-skills doctor --json` with a conversational fix flow. + newly installed agents.md skills don't show up, or when the user asks + "what's wrong with my install", "is my setup OK", or "check my lfx skills". + For Claude Code plugin installs, explain the plugin marketplace update path + instead of running the CLI doctor. Wraps `lfx-skills doctor --json` with a + conversational fix flow. allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- @@ -16,7 +19,9 @@ allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion # LFX Skills Doctor -You diagnose problems with a user's LFX Skills install and walk them through fixing the ones that need a human-in-the-loop. The bash CLI handles mechanical repairs; you handle everything that needs judgment (content gaps, scaffolding, file edits). +You diagnose problems with a user's agents.md LFX Skills install and legacy Claude symlink installs, then walk them through fixing the ones that need a human-in-the-loop. The bash CLI handles mechanical repairs; you handle everything that needs judgment (content gaps, scaffolding, file edits). + +Claude Code plugin installs are separate. Claude Code uses `/plugin marketplace add linuxfoundation/lfx-skills` and `/plugin install lfx-skills@lfx-skills`; it does not use the CLI installer or agents.md symlinks. If the user is asking about the Claude Code plugin itself, do not run `lfx-skills doctor`. Tell them to update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`, and to run `claude plugin validate .` from the LFX Skills clone when validating local plugin metadata. Use the CLI doctor only for agents.md installs and legacy Claude symlink cleanup. ## Step 1: Locate the CLI @@ -27,7 +32,7 @@ The `lfx-skills` CLI lives in the user's lfx-skills clone at `cli/lfx-skills`. T 3. **Current dir:** if the user is inside the lfx-skills clone (a `cli/lfx-skills` exists relative to `pwd`), use `./cli/lfx-skills`. 4. **Last resort:** ask the user: "Where is your lfx-skills clone? (e.g., `~/lf/lfx-skills`)". -If none of the above works, the install was never run: tell the user to clone the repo and run `./install.sh` (or use `/lfx-install` if they're inside the clone). Stop here. +If none of the above works, the agents.md install was never run: tell the user to clone the repo and run `./install.sh` (or use `/lfx-install` if they're inside the clone). If they only need Claude Code, point them to the plugin marketplace commands instead. Stop here. ## Step 2: Run diagnostics @@ -120,6 +125,7 @@ End with: ## What this skill does NOT do +- Diagnose Claude Code plugin cache or marketplace state through the CLI. Use Claude Code plugin commands for that path. - Install new skills or change install scope: that's `/lfx-install`. - List, manage, or scaffold skills: `/lfx-skills-helper` and `/lfx-new-skill`. - Modify `lfx/SKILL.md`'s routing table without asking the user first. diff --git a/skills/lfx-new-skill/SKILL.md b/skills/lfx-new-skill/SKILL.md index d722b85..5ce0ad5 100644 --- a/skills/lfx-new-skill/SKILL.md +++ b/skills/lfx-new-skill/SKILL.md @@ -90,7 +90,8 @@ Ask which runtime they want to test: Claude Code plugin, agents.md, or both. For Claude Code plugin testing: -- If the skill should ship in the Claude plugin, add it to `.claude-plugin/plugin.json`. +- If the skill should ship in the Claude plugin, add its path to the `skills` allowlist in `.claude-plugin/plugin.json`. Creating `skills/lfx-<name>/SKILL.md` is not enough; Claude plugin users only get skills listed in that manifest. +- If the new skill is user-facing and should be available to Claude plugin users, treat the plugin allowlist entry as required, not optional. - Give the user this validation command to run from their normal terminal: ```bash @@ -129,7 +130,7 @@ If they say yes: - Do not add co-author trailers. - Do not push unless explicitly asked. -For releases, explain that after the PR merges they can ask their coding agent to help choose the next SemVer version, update `.claude-plugin/marketplace.json`, run validation, and draft the `gh release create` command. The user should review before committing or creating the release. +For plugin versioning, explain that Claude Code picks up plugin changes when `.claude-plugin/plugin.json` gets a new SemVer `version` and the change reaches `main`. Offer to help choose the next patch/minor/major version and include the plugin version bump in the signed commit. If the skill is Claude-facing, include both the `skills` allowlist entry and the version bump in the commit. ## Boundaries diff --git a/skills/lfx-new-skill/references/conventions-quickref.md b/skills/lfx-new-skill/references/conventions-quickref.md index f2b2223..a121671 100644 --- a/skills/lfx-new-skill/references/conventions-quickref.md +++ b/skills/lfx-new-skill/references/conventions-quickref.md @@ -84,7 +84,7 @@ This checks only the new skill's frontmatter and license header. ## Claude Code Local Test -If the skill should ship in the Claude plugin, add it to `.claude-plugin/plugin.json`. +If the skill should ship in the Claude plugin, add its path to the `skills` allowlist in `.claude-plugin/plugin.json`. Creating `skills/lfx-<name>/SKILL.md` is not enough; Claude plugin users only get skills listed in that manifest. Give the user: @@ -131,4 +131,4 @@ After validation, ask if the user wants help committing. If yes: - Do not add co-author trailers. - Do not push unless explicitly asked. -For releases, the user can ask an agent to prepare the release bump: choose SemVer, update `.claude-plugin/marketplace.json`, run validation, and draft `gh release create`. +For Claude Code plugin updates, bump the SemVer `version` in `.claude-plugin/plugin.json` with the skill changes. Claude Code will keep using the cached plugin if the version is unchanged. For new Claude-facing skills, commit both the `skills` allowlist entry and the version bump. diff --git a/skills/lfx-skills-helper/references/intents.md b/skills/lfx-skills-helper/references/intents.md index 22f2bb7..3609de1 100644 --- a/skills/lfx-skills-helper/references/intents.md +++ b/skills/lfx-skills-helper/references/intents.md @@ -46,7 +46,8 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs | User says | Run | Notes | |-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| -| "Update lfx skills" | `lfx-skills update --pull` | Suggest `/lfx-doctor` after | +| "Update lfx skills" | Ask whether they mean Claude Code plugin or agents.md CLI install. For agents.md, run `lfx-skills update --pull`. | Suggest `/lfx-doctor` after agents.md updates | +| "Update the Claude plugin" / "Update Claude skills" | Explain: `/plugin marketplace update lfx-skills`, then `/plugin update lfx-skills@lfx-skills` | Claude Code plugin updates are not handled by the CLI | | "Re-apply my install" | `lfx-skills update` | No `--pull`; just refresh symlinks against the manifest | | "Remove old Claude symlinks" | Confirm, then `lfx-skills uninstall --yes --legacy-claude-only` | Removes only lfx-skills-owned legacy Claude symlinks | | "Remove lfx-skills completely" | Confirm, then `lfx-skills uninstall --yes --all` | Removes agents.md symlinks, legacy Claude symlinks, CLI symlink, config | From a36366e06dc1171186f29802db5f7f30bf91c75f Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Thu, 7 May 2026 12:07:19 +0200 Subject: [PATCH 18/20] docs: clarify README action steps Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- README.md | 61 +++++++++++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 20 deletions(-) diff --git a/README.md b/README.md index bdd49bb..2ef2167 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,13 @@ The marketplace metadata lives in `.claude-plugin/marketplace.json` in LFX Skill The Claude Code plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. -If you previously installed LFX Skills into Claude Code with symlinks, clone this repo, start your coding agent here, and ask it to uninstall the legacy Claude setup. Manual fallback: +If you previously installed LFX Skills into Claude Code with symlinks: + +- Clone this repo. +- Start your coding agent in the clone. +- Ask it: `Uninstall the legacy Claude setup for LFX Skills.` + +Manual fallback: ```bash ./cli/lfx-skills uninstall --legacy-claude-only @@ -55,7 +61,11 @@ If you prefer to run the installer manually: The CLI installs skill symlinks into agents.md skill directories outside this repo and records the install in `~/.lfx-skills/config.json`. agents.md installs include the 15 runtime skills plus `/lfx-doctor` and `/lfx-skills-helper`. `/lfx-install` and `/lfx-new-skill` stay clone-only. -Then restart your AI coding assistant, open any LFX repo, and type `/lfx` to get started. +After setup: + +- Restart your AI coding assistant. +- Open any LFX repo. +- Type `/lfx` to get started. ## How It Works @@ -152,31 +162,42 @@ After the change is on `main`, Claude Code users update from Claude: They can also enable auto-update. -For agents.md-compatible installs, update from the terminal or ask your coding agent to update LFX Skills: +For agents.md-compatible installs: -```bash -lfx-skills update --pull -lfx-skills doctor -``` +- Ask your coding agent to update LFX Skills, or run: -If `lfx-skills` is not on `PATH`, run the CLI from the clone: + ```bash + lfx-skills update --pull + lfx-skills doctor + ``` -```bash -/path/to/lfx-skills/cli/lfx-skills update --pull -/path/to/lfx-skills/cli/lfx-skills doctor -``` +- If `lfx-skills` is not on `PATH`, run the CLI from the clone: -To remove old Claude symlink installs from before the plugin pivot, use the CLI's legacy cleanup mode: + ```bash + /path/to/lfx-skills/cli/lfx-skills update --pull + /path/to/lfx-skills/cli/lfx-skills doctor + ``` -```bash -lfx-skills uninstall --legacy-claude-only -``` +To remove old Claude symlink installs from before the plugin pivot: -To remove the whole agents.md installation, CLI symlink, config, and any legacy Claude symlinks owned by this clone: +- Use the legacy cleanup mode. +- It removes only LFX Skills-owned legacy Claude symlinks. +- It does not remove the Claude Code plugin. -```bash -lfx-skills uninstall --all -``` + ```bash + lfx-skills uninstall --legacy-claude-only + ``` + +To remove the whole agents.md installation: + +- Removes agents.md skill symlinks. +- Removes the `lfx-skills` CLI symlink. +- Removes `~/.lfx-skills` config for this install. +- Also removes any legacy Claude symlinks owned by this clone. + + ```bash + lfx-skills uninstall --all + ``` ## Architecture From f0c1085f40f46ea8c555bee4b36a92874904d87b Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Thu, 7 May 2026 12:49:43 +0200 Subject: [PATCH 19/20] docs: route new skill requests from lfx Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- skills/lfx/SKILL.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/skills/lfx/SKILL.md b/skills/lfx/SKILL.md index af64dfe..376d087 100644 --- a/skills/lfx/SKILL.md +++ b/skills/lfx/SKILL.md @@ -82,6 +82,7 @@ Listen to what the user says and classify their intent. **Do not ask technical q | "Fix Intercom", "Audit Intercom", "Add Intercom integration", "JWT setup for Intercom", "Intercom CSP" | Intercom integration in an LFX Angular app | `/lfx-intercom` | | "Add a Snowflake connector", "Scaffold a CDP source", "New crowd.dev data source", "snowflake-connector platform" | Scaffold a new CDP Snowflake connector | `/lfx-cdp-snowflake-connectors` | | "I need Snowflake access", "Add me to Snowflake", "Need a service account", "Request Snowflake permissions" | Request Snowflake access via Terraform PR | `/lfx-snowflake-access` | +| "Can we add a new skill?", "How do I create a skill?", "We need another skill", "This workflow should be a skill" | Skill authoring guidance | See "Need Another Skill?" below | | "Show me an example", "How do I use this?", "Help" | Guidance | Show quickstart examples | ## Step 3: Translate and Route @@ -253,6 +254,18 @@ Once the delegated skill completes, check back with the user: - If they validated → "Everything looks good! Want me to help create the pull request?" - If they addressed PR feedback → "Review comments are addressed and pushed! Run /lfx-pr-catchup to monitor for any follow-up." +## Need Another Skill? + +If the user asks how to add a new skill, says this workflow needs its own skill, or wonders whether another skill should exist, explain that LFX Skills has a dedicated authoring workflow in the source repository. + +Give concise guidance: + +```text +To add a new LFX skill, clone https://github.com/linuxfoundation/lfx-skills, start your coding agent in that repo, and ask it to help create the skill. The repo includes /lfx-new-skill, which can scaffold from scratch or adapt an existing SKILL.md, apply the repo conventions, validate formatting, and explain how to publish the skill through agents.md and the Claude Code plugin. +``` + +Do not try to author the new skill from a product repo. The canonical authoring flow lives in `linuxfoundation/lfx-skills`. + ## Scope Boundaries **This skill DOES:** From a56341eceebcb2c868bebe3892b9df172a300656 Mon Sep 17 00:00:00 2001 From: Josep Garcia-Reyero Sais <josepreyero@gmail.com> Date: Thu, 7 May 2026 13:48:54 +0200 Subject: [PATCH 20/20] feat: simplify plugin and agents skill split Signed-off-by: Josep Garcia-Reyero Sais <josepreyero@gmail.com> --- .agents/skills/lfx-doctor/SKILL.md | 21 -- .agents/skills/lfx-install/SKILL.md | 178 ++++++++++++++- .agents/skills/lfx-new-skill/SKILL.md | 132 +++++++++++- .../references/conventions-quickref.md | 4 +- .../skills/lfx-skills-doctor}/SKILL.md | 4 +- .../references/fix-recipes.md | 16 +- .agents/skills/lfx-skills-helper/SKILL.md | 100 ++++++++- .../lfx-skills-helper/references/intents.md | 12 +- .claude-plugin/marketplace.json | 4 +- .claude-plugin/plugin.json | 18 +- .claude/skills/lfx-doctor/SKILL.md | 21 -- .claude/skills/lfx-install/SKILL.md | 178 ++++++++++++++- .claude/skills/lfx-new-skill/SKILL.md | 132 +++++++++++- .../references/conventions-quickref.md | 132 ++++++++++++ .claude/skills/lfx-skills-doctor/SKILL.md | 136 ++++++++++++ .../references/fix-recipes.md | 203 ++++++++++++++++++ .claude/skills/lfx-skills-helper/SKILL.md | 100 ++++++++- .../lfx-skills-helper/references/intents.md | 73 +++++++ .gitignore | 8 +- AGENTS.md | 18 +- CLAUDE.md | 18 +- README.md | 68 ++---- cli/lfx-skills | 8 +- docs/overview.md | 180 ---------------- lib/doctor.sh | 8 +- lib/symlinks.sh | 63 ++++-- skills/lfx-install/SKILL.md | 183 ---------------- skills/lfx-new-skill/SKILL.md | 140 ------------ skills/lfx-skills-helper/SKILL.md | 100 --------- 29 files changed, 1431 insertions(+), 827 deletions(-) delete mode 100644 .agents/skills/lfx-doctor/SKILL.md rename {skills => .agents/skills}/lfx-new-skill/references/conventions-quickref.md (87%) rename {skills/lfx-doctor => .agents/skills/lfx-skills-doctor}/SKILL.md (97%) rename {skills/lfx-doctor => .agents/skills/lfx-skills-doctor}/references/fix-recipes.md (90%) rename {skills => .agents/skills}/lfx-skills-helper/references/intents.md (91%) delete mode 100644 .claude/skills/lfx-doctor/SKILL.md create mode 100644 .claude/skills/lfx-new-skill/references/conventions-quickref.md create mode 100644 .claude/skills/lfx-skills-doctor/SKILL.md create mode 100644 .claude/skills/lfx-skills-doctor/references/fix-recipes.md create mode 100644 .claude/skills/lfx-skills-helper/references/intents.md delete mode 100644 docs/overview.md delete mode 100644 skills/lfx-install/SKILL.md delete mode 100644 skills/lfx-new-skill/SKILL.md delete mode 100644 skills/lfx-skills-helper/SKILL.md diff --git a/.agents/skills/lfx-doctor/SKILL.md b/.agents/skills/lfx-doctor/SKILL.md deleted file mode 100644 index 1d94ec9..0000000 --- a/.agents/skills/lfx-doctor/SKILL.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -name: lfx-doctor -description: > - Bootstrap wrapper for the LFX Skills doctor helper in this source repo. Use to - diagnose agents.md installs and legacy Claude symlink installs from inside the - lfx-skills clone. -allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion ---- - -# LFX Doctor Bootstrap - -This is a repository-local bootstrap skill. Read `skills/lfx-doctor/SKILL.md` -from the LFX Skills repo root and follow that canonical skill exactly. - -Do not use this wrapper as the source of truth. The canonical implementation is: - -```text -skills/lfx-doctor/SKILL.md -``` diff --git a/.agents/skills/lfx-install/SKILL.md b/.agents/skills/lfx-install/SKILL.md index a870d00..45baf78 100644 --- a/.agents/skills/lfx-install/SKILL.md +++ b/.agents/skills/lfx-install/SKILL.md @@ -3,19 +3,181 @@ # SPDX-License-Identifier: MIT name: lfx-install description: > - Bootstrap wrapper for the LFX Skills install helper in this source repo. Use - when the user has just cloned lfx-skills and wants to set up agents.md-compatible - tools, or needs the Claude Code plugin install path explained. + Install or set up LFX Skills for agents.md-compatible tools via the + lfx-skills CLI, and point Claude Code-only users to the Claude plugin path. + Walks through the choices in plain language: where their LFX repos live, + scope, agents config dirs, then runs the installer and verifies. Use + whenever the user says "I just cloned this — what now?", "set up lfx skills", + "install lfx skills", "I'm new to lfx-skills", or "first-time setup". allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- -# LFX Install Bootstrap +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> -This is a repository-local bootstrap skill. Read `skills/lfx-install/SKILL.md` -from the LFX Skills repo root and follow that canonical skill exactly. +# LFX Skills Install -Do not use this wrapper as the source of truth. The canonical implementation is: +You guide the user through their first-time install. The bash CLI (`cli/lfx-skills install`) can do this non-interactively if every flag is supplied; this skill is the conversational layer that figures out what those flags should be by asking the user, in plain language, one question at a time. + +## Step 1: Verify you're in the clone + +This skill only works inside the `lfx-skills` clone. Verify: + +```bash +[ -x ./cli/lfx-skills ] && echo OK || echo NOT_IN_CLONE +``` + +If `NOT_IN_CLONE`, tell the user: + +> "I only run inside the lfx-skills clone. `cd` to your clone of `linuxfoundation/lfx-skills` and ask again." + +Stop. + +## Step 2: Probe the system + +Run `./cli/lfx-skills` indirectly via its install command's PROBE step — but for the conversation, you also want the data yourself so you can ask informed questions. Do these one-shot probes: + +```bash +# agents.md-compatible CLIs available +for cli in codex gemini opencode; do + command -v "$cli" >/dev/null 2>&1 && echo "$cli" +done + +# Agents config dirs +ls -d "$HOME"/.agents* 2>/dev/null + +# Dev root candidates +for d in "$HOME/lf" "$HOME/lfx" "$HOME/code/lfx" "$HOME/work/lfx"; do + [ -d "$d" ] && echo "$d" +done +``` + +## Step 3: Q1 — Install route + +Use `AskUserQuestion`: + +> "Which setup do you need? (1) agents.md-compatible tool (Codex, Gemini CLI, OpenCode), (2) Claude Code only, (3) both." + +If you detected only one CLI installed, default to it but still confirm. + +If the user picks Claude Code only, explain that Claude installs this repo as a plugin and does not use the CLI symlink installer: ```text -skills/lfx-install/SKILL.md +/plugin marketplace add linuxfoundation/lfx-skills +/plugin install lfx-skills@lfx-skills +``` + +If they are testing from a local checkout, tell them to run Claude Code with the local plugin directory or add the local marketplace per the Claude Code plugin docs. Stop after explaining the plugin path; do not run `./cli/lfx-skills install` for Claude-only installs. + +If the user picks both, use the plugin path for Claude Code and continue with the CLI flow below for agents.md-compatible tools. The CLI itself remains agents.md-only. + +## Step 4: Q2 — Scope + +> "Install scope? (1) **Global** — available in every session of your agents.md-compatible tool. (2) **Per-repo** — only in specific repos (their `.agents/skills/`). (3) **Both** — global plus pin into specific repos." + +This question goes second so subsequent questions can adapt. (Per-repo only? Skip the global config picker. Global only? Skip the repo picker.) + +## Step 5: Q3 — LFX dev root + +Show the candidates you probed with their repo counts: + +``` +Where do you keep your LFX repo clones? + 1. ~/lf (12 lf* repos) + 2. ~/code/lfx (3 lf* repos) + 3. Custom path… +``` + +Use `AskUserQuestion`. If the user already has `LFX_DEV_ROOT` set in the shell, mention it and ask whether to keep it. + +If the chosen path doesn't exist, ask whether to create it (`mkdir -p`). If it has zero `lf*` git repos, warn but proceed: the install will still work; the dev-root-empty doctor warning will trigger until they clone some. + +## Step 6: Q4 — Agents config dirs (only if scope includes Global) + +If you saw multiple `~/.agents*` config dirs, ask which to install into: + +> "I see these agents config dirs: …. Install into all of them, or just one? (defaults to `~/.agents`)" + +Most users have one; this only matters for power users with multiple profiles. + +If scope is Per-repo only, skip this step entirely. + +## Step 7: Q5 — Repos (only if scope includes Per-repo) + +List `lf*` git repos under the chosen dev root: + +> "Which repos? Pick numbers (e.g., `1, 3, 5`), `all`, or `none`." + +If scope is Global only, skip this step entirely. + +## Step 8: Show the plan + +Before running anything, summarise: + +``` +Plan: + Scope: global + per-repo + LFX_DEV_ROOT: ~/lf + Agents dir: ~/.agents + Repos (4): lfx-v2-ui, lfx-v2-meeting-service, lfx-v2-committee-service, lfx-v2-query-service + Skills: runtime suite + lfx-skills-doctor + lfx-skills-helper + +Will create approximately N symlinks. Proceed? +``` + +`AskUserQuestion`. If no, stop. + +## Step 9: Run the installer + +Compose the non-interactive flags from the user's answers: + +```bash +./cli/lfx-skills install --yes \ + --scope=<scope> \ + --lfx-dev-root=<path> \ + --agents-config=<dir> \ + --repos=<repo1,repo2,...> ``` + +Stream the output to the user. + +## Step 10: Verify + +Run a quick verification: + +```bash +./cli/lfx-skills doctor +``` + +If errors, walk the user through the auto-fix: + +> "One or more checks failed. Want me to run `/lfx-skills-doctor` to investigate?" + +## Step 11: Confirm the CLI is on PATH + +The installer creates a symlink at a writable PATH dir (`~/.local/bin/lfx-skills`, `~/bin/lfx-skills`, or `/usr/local/bin/lfx-skills`) so the user can type `lfx-skills` from anywhere — no shell rc edit. Read the install output to see which path was used and tell the user: + +> "`lfx-skills` is now at `<reported path>` and ready to use from any terminal." + +If the installer reported it couldn't find a writable PATH dir, share the alias snippet it printed: + +> "I couldn't find a writable PATH dir to drop the CLI into. Add this alias to your shell rc to use `lfx-skills` from anywhere: +> ```bash +> alias lfx-skills='<clone>/cli/lfx-skills' +> ``` +> Or extend PATH to include `~/.local/bin`, `~/bin`, or `/usr/local/bin`." + +## Step 12: Suggest next steps + +> "All set. Next: +> +> 1. Restart your AI coding assistant (or open a new session). +> 2. `cd` to any LFX repo and type `/lfx` — that's your plain-language entry point. +> 3. Run `/lfx-skills-doctor` anytime to recheck the install. +> 4. Run `/lfx-skills-helper` to manage what's installed where." + +## What this skill does NOT do + +- **Edit your shell rc** — never. Always print the snippet for the user to paste. +- **Install only some skills** — v1 always installs everything (the full set per chosen target). Filtering is a v2 idea. +- **Run outside the clone** — bail in Step 1. +- **Re-run silently** — confirm at the plan step before doing anything stateful. diff --git a/.agents/skills/lfx-new-skill/SKILL.md b/.agents/skills/lfx-new-skill/SKILL.md index 638f209..ea6e2da 100644 --- a/.agents/skills/lfx-new-skill/SKILL.md +++ b/.agents/skills/lfx-new-skill/SKILL.md @@ -3,18 +3,136 @@ # SPDX-License-Identifier: MIT name: lfx-new-skill description: > - Bootstrap wrapper for the LFX Skills authoring helper in this source repo. Use - when a contributor wants to create a new lfx skill or asks how to author one. + Scaffold a new LFX Skills suite skill under skills/. Use when someone wants + to add a new lfx skill, provides a complete SKILL.md to import, has an idea + for a skill and wants help drafting it, or asks how to create a skill. allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- -# LFX New Skill Bootstrap +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> -This is a repository-local bootstrap skill. Read `skills/lfx-new-skill/SKILL.md` -from the LFX Skills repo root and follow that canonical skill exactly. +# LFX New Skill — Scaffolder -Do not use this wrapper as the source of truth. The canonical implementation is: +You help contributors add LFX Skills suite skills under `skills/lfx-<name>/`. Read [`references/conventions-quickref.md`](references/conventions-quickref.md) before writing files. + +Use two modes: + +- **Complete skill provided:** if the user pastes or points to a complete `SKILL.md`, use it as the source of truth. Preserve the body. Only normalize frontmatter, license header, directory name, and repo-specific placeholders when needed. +- **Draft from idea:** if the user only has an idea, help write the body. Ask targeted questions about trigger phrases, inputs, steps, tools, outputs, and boundaries. Draft concrete step-by-step instructions from the answers. + +## Step 1: Verify Location + +Run: + +```bash +[ -x ./cli/lfx-skills ] && [ -d ./skills/lfx ] && echo OK || echo NOT_IN_CLONE +``` + +If `NOT_IN_CLONE`, tell the user to `cd` to the `lfx-skills` clone and stop. + +## Step 2: Gather Inputs + +Ask whether the user has a complete `SKILL.md` or wants help drafting one. + +For a complete skill: + +- Read the supplied file/content. +- Extract `name`, `description`, and `allowed-tools` when present. +- Ask only for missing required fields. + +For a draft: + +- Ask for the skill name. It must match `^lfx-[a-z0-9-]+$` and not already exist under `skills/`. +- Ask for a frontmatter description with 3-5 trigger phrases. +- Ask which tools it needs. Default: `Bash, Read, Glob, Grep, AskUserQuestion`. +- Ask what the skill should do, what inputs it needs, what it may inspect or modify, what output it should produce, and what it must not do. + +Ask whether it needs `references/`. + +## Step 3: Write Files + +Create: ```text -skills/lfx-new-skill/SKILL.md +skills/lfx-<name>/SKILL.md +``` + +If requested, also create: + +```text +skills/lfx-<name>/references/.gitkeep +``` + +Ensure: + +- frontmatter starts on line 1 +- license comments are lines 2-3 inside the frontmatter +- `name:` equals the directory basename +- `description:` uses a YAML folded scalar +- `allowed-tools:` is present +- MCP-dependent skills include a `## Prerequisites` section + +If the skill is user-facing, ask whether to add routing guidance to `skills/lfx/SKILL.md`. Internal-only skills can remain unrouted. + +## Step 4: Validate + +Run only the new skill formatting check: + +```bash +./cli/lfx-skills doctor --skill-formatting-only --skill=lfx-<name> ``` + +Fix `frontmatter-*` and `license-missing` issues, then rerun until clean. Do not run the full doctor for this scaffolding check; full doctor includes agents.md install/setup checks. + +## Step 5: Explain Local Testing + +Ask which runtime they want to test: Claude Code plugin, agents.md, or both. + +For Claude Code plugin testing: + +- Give the user this validation command to run from their normal terminal: + + ```bash + cd "<absolute-path-to-lfx-skills>" + claude plugin validate . + ``` + +- Ask which target LFX repo they want to test in. Resolve a repo name under `~/.lfx-skills/dev-root` or use the absolute path they provide. +- Give the user a ready-to-run command: + + ```bash + LFX_SKILLS_CLONE="<absolute-path-to-lfx-skills>" + cd "<resolved-target-repo-path>" + claude --plugin-dir "$LFX_SKILLS_CLONE" + ``` + +- Tell them to run `/lfx-skills:lfx-<name>` in that Claude Code session. + +For agents.md testing: + +- Run `./cli/lfx-skills update`. +- Tell the user to restart their agents.md-compatible coding agent and run `/lfx-<name>`. +- If LFX Skills is not installed for agents.md, point them to `/lfx-install` or `./install.sh`. + +Keep the two paths separate: the CLI is for agents.md installs; Claude Code uses the plugin path. + +## Step 6: Offer Commit And Release Help + +After validation, ask whether the user wants help committing the scaffolded skill. + +If they say yes: + +- Review `git diff` and `git status`. +- Commit only the intended files. +- Use `git commit -s -S`. +- Do not add co-author trailers. +- Do not push unless explicitly asked. + +For plugin versioning, explain that Claude Code picks up plugin changes when `.claude-plugin/plugin.json` gets a new SemVer `version` and the change reaches `main`. Offer to help choose the next patch/minor/major version and include the plugin version bump in the signed commit when plugin-visible behavior changed. + +## Boundaries + +- Do not install or repair the user's LFX Skills setup; route that to `/lfx-install` or `/lfx-skills-doctor`. +- Do not use the CLI to install Claude Code skills. +- Do not run Claude Code plugin commands for agents.md testing. +- Do not invent unclear behavior. Ask when scope, inputs, outputs, or safety boundaries are unclear. diff --git a/skills/lfx-new-skill/references/conventions-quickref.md b/.agents/skills/lfx-new-skill/references/conventions-quickref.md similarity index 87% rename from skills/lfx-new-skill/references/conventions-quickref.md rename to .agents/skills/lfx-new-skill/references/conventions-quickref.md index a121671..d3f3b34 100644 --- a/skills/lfx-new-skill/references/conventions-quickref.md +++ b/.agents/skills/lfx-new-skill/references/conventions-quickref.md @@ -84,8 +84,6 @@ This checks only the new skill's frontmatter and license header. ## Claude Code Local Test -If the skill should ship in the Claude plugin, add its path to the `skills` allowlist in `.claude-plugin/plugin.json`. Creating `skills/lfx-<name>/SKILL.md` is not enough; Claude plugin users only get skills listed in that manifest. - Give the user: ```bash @@ -131,4 +129,4 @@ After validation, ask if the user wants help committing. If yes: - Do not add co-author trailers. - Do not push unless explicitly asked. -For Claude Code plugin updates, bump the SemVer `version` in `.claude-plugin/plugin.json` with the skill changes. Claude Code will keep using the cached plugin if the version is unchanged. For new Claude-facing skills, commit both the `skills` allowlist entry and the version bump. +For Claude Code plugin updates, bump the SemVer `version` in `.claude-plugin/plugin.json` with the skill changes when plugin-visible behavior changed. Claude Code will keep using the cached plugin if the version is unchanged. diff --git a/skills/lfx-doctor/SKILL.md b/.agents/skills/lfx-skills-doctor/SKILL.md similarity index 97% rename from skills/lfx-doctor/SKILL.md rename to .agents/skills/lfx-skills-doctor/SKILL.md index 3f7a6cc..de7327b 100644 --- a/skills/lfx-doctor/SKILL.md +++ b/.agents/skills/lfx-skills-doctor/SKILL.md @@ -1,7 +1,7 @@ --- # Copyright The Linux Foundation and each contributor to LFX. # SPDX-License-Identifier: MIT -name: lfx-doctor +name: lfx-skills-doctor description: > Diagnose problems with the agents.md LFX Skills installation and legacy Claude symlink installs: broken symlinks, missing dev root, frontmatter @@ -121,7 +121,7 @@ Show the new counts. Celebrate if everything's green; otherwise, note what's sti End with: -> "Run `/lfx-doctor` anytime to recheck. For installation changes, `/lfx-install` or `/lfx-skills-helper` is the right entry point." +> "Run `/lfx-skills-doctor` anytime to recheck. For installation changes, `/lfx-install` or `/lfx-skills-helper` is the right entry point." ## What this skill does NOT do diff --git a/skills/lfx-doctor/references/fix-recipes.md b/.agents/skills/lfx-skills-doctor/references/fix-recipes.md similarity index 90% rename from skills/lfx-doctor/references/fix-recipes.md rename to .agents/skills/lfx-skills-doctor/references/fix-recipes.md index 0dce472..33397f1 100644 --- a/skills/lfx-doctor/references/fix-recipes.md +++ b/.agents/skills/lfx-skills-doctor/references/fix-recipes.md @@ -3,7 +3,7 @@ # Fix Recipes -Per-issue narrative for `/lfx-doctor`. The CLI's `--fix` flag handles the mechanical cases; this file covers everything else, plus extra context the JSON output doesn't include. +Per-issue narrative for `/lfx-skills-doctor`. The CLI's `--fix` flag handles the mechanical cases; this file covers everything else, plus extra context the JSON output doesn't include. Each entry follows the same shape: @@ -58,7 +58,7 @@ Each entry follows the same shape: **What:** A symlink target exists, but the directory has no `SKILL.md`. **Why it matters:** the skill won't load (the loader requires `SKILL.md`). Usually means someone created an empty `lfx-foo/` directory by accident, or pulled an in-progress branch. **Fix:** either delete the empty directory, or scaffold a real skill via `/lfx-new-skill`. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can hand off to `/lfx-new-skill`. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can hand off to `/lfx-new-skill`. --- @@ -137,7 +137,7 @@ git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git **What:** A `SKILL.md` doesn't start with `---` on line 1. **Why it matters:** the skill loader will refuse to load it. The loader requires frontmatter as the very first thing in the file (no blank lines, no comments above). **Fix:** insert a frontmatter block at the top with the skill `name`, `description`, and `allowed-tools`. Use `/lfx-new-skill` as a template, or copy the shape from a sibling skill. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can guide the rewrite. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can guide the rewrite. --- @@ -145,7 +145,7 @@ git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git **What:** Frontmatter present but the `name:` field is missing or empty. **Fix:** add `name: <skill-directory-basename>` to the frontmatter. Loader will fail without it. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch it via Edit. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch it via Edit. --- @@ -154,7 +154,7 @@ git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git **What:** `name:` in the SKILL.md doesn't match the directory basename. **Why it matters:** loaders use the directory name to register the slash command, but read the frontmatter for description and tools. A mismatch is confusing and may cause routing issues. **Fix:** make `name:` equal `basename "$skill_dir"`. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch via Edit. --- @@ -163,7 +163,7 @@ git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git **What:** No `description:` field, or it's empty. **Why it matters:** the loader uses `description` to decide when to surface the skill. Missing description means the model has no context for *when* to invoke it. **Fix:** write a one-paragraph description that includes 3–5 trigger phrases users might say. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can draft one from the SKILL.md body. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can draft one from the SKILL.md body. --- @@ -181,7 +181,7 @@ name: ... ``` (The `#` comments are valid YAML comments. They satisfy the license check without breaking frontmatter parsing.) -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch via Edit. --- @@ -200,4 +200,4 @@ name: ... **Why it matters:** users typing `/lfx` won't find the skill via the plain-language router. They can still invoke it directly with `/lfx-foo`. **Fix:** add an entry to `lfx/SKILL.md`'s routing table for the skill, including 1–2 example trigger phrases. **Caveat:** internal-only skills (like `lfx-backend-builder` and `lfx-ui-builder`, which are only invoked by `/lfx-coordinator`) can legitimately stay out of the routing table. Use judgment. -**Auto-fixable?** no by CLI; the `/lfx-doctor` skill can patch via Edit. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch via Edit. diff --git a/.agents/skills/lfx-skills-helper/SKILL.md b/.agents/skills/lfx-skills-helper/SKILL.md index c9c01d0..711f5f0 100644 --- a/.agents/skills/lfx-skills-helper/SKILL.md +++ b/.agents/skills/lfx-skills-helper/SKILL.md @@ -3,20 +3,98 @@ # SPDX-License-Identifier: MIT name: lfx-skills-helper description: > - Bootstrap wrapper for the LFX Skills management helper in this source repo. - Use to list, update, uninstall, inspect, or manage agents.md installs and - legacy Claude symlink cleanup. + Manage the agents.md LFX Skills installation via the lfx-skills CLI: list + what's installed, install or uninstall in this repo or globally, update from + upstream, view or change config, look up what a specific skill does, and + remove legacy Claude symlink installs. Use for "add lfx skills to this repo", + "what's installed", "update lfx skills", "show my lfx setup", "uninstall", + "remove old Claude symlinks", "what does /lfx-foo do". For Claude plugin + installs, explain the plugin marketplace path. For "which skill should I use + for X" or other plain-language routing questions, hand off to /lfx. For health + checks or repair, hand off to /lfx-skills-doctor. allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- -# LFX Skills Helper Bootstrap +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> -This is a repository-local bootstrap skill. Read -`skills/lfx-skills-helper/SKILL.md` from the LFX Skills repo root and follow -that canonical skill exactly. +# LFX Skills Helper -Do not use this wrapper as the source of truth. The canonical implementation is: +You are the conversational front-end for the agents.md-only `lfx-skills` CLI: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. -```text -skills/lfx-skills-helper/SKILL.md -``` +Claude Code is separate: it installs LFX Skills as a plugin with `/plugin marketplace add linuxfoundation/lfx-skills` and `/plugin install lfx-skills@lfx-skills`. Do not use the CLI to install Claude Code skills. The CLI can only remove old Claude symlink installs via `lfx-skills uninstall --legacy-claude-only` or as part of `lfx-skills uninstall --all`. + +## Step 1: Locate the CLI + +Same as `/lfx-skills-doctor`. Try in order: + +1. `command -v lfx-skills` (on PATH). +2. `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` then append `/cli/lfx-skills`. +3. `./cli/lfx-skills` if you're inside the lfx-skills clone. +4. Ask the user. + +If none works: the install was never run. Tell the user to clone `linuxfoundation/lfx-skills` and run `./install.sh` (or invoke `/lfx-install` if they're already in the clone). Stop. + +## Step 2: Classify the request + +Decide which surface the request belongs to: + +| User asks about… | Surface | Action | +|--------------------------------------------|------------------|-------------------------------------------| +| Installing, updating, removing, config | This skill | Map to a CLI subcommand (Step 3) | +| What's installed / available / where | This skill | Map to a CLI subcommand (Step 3) | +| What a specific skill does | This skill | `lfx-skills info <name>` | +| **Which skill** to use for a task | Hand off to `/lfx` | Stop here; let the router pick. | +| **Diagnose** a problem / "why isn't X working" | Hand off to `/lfx-skills-doctor` | Stop here. | +| **Scaffold a new skill** | Hand off to `/lfx-new-skill` | Stop here (clone-only). | + +Read `references/intents.md` once for the management-intent → CLI mapping. If the user's phrasing isn't in the table and doesn't fit your job either, say so and suggest the right entry point. + +## Step 3: Run the CLI + +Execute the chosen subcommand. Capture stdout. Use `--json` flags where available (currently `doctor --json`) when you need structured data. + +For commands that change state (`install`, `uninstall`, `update`, `config set`), **always confirm with the user first** via `AskUserQuestion`, showing exactly what you're about to run. The CLI's `--yes` flag is appropriate only after the user has confirmed in chat. + +## Step 4: Format the output + +The CLI output is structured but utilitarian. Reformat it for conversation: + +- **`lfx-skills list`** outputs `scope<TAB>skill<TAB>link`. Render as a friendly grouped list: + + ``` + Globally installed (agents.md): + /lfx + /lfx-coordinator + ... + + Per-repo (lfx-v2-meeting-service): + /lfx-coordinator + /lfx-pr-resolve + ... + ``` + +- **`lfx-skills info <skill>`** outputs frontmatter + install locations. Render the description in prose, list trigger phrases, summarise where it's installed. + +- **`lfx-skills config`** outputs raw JSON. Pretty-print it as a small table: dev root, canonical clone, total symlinks, and CLI symlink. + +- **`lfx-skills repos`** outputs one path per line. Group as a numbered list with sizes/last-modified if helpful. + +## Step 5: Hand-off rules + +If during the conversation the request shifts to something off your turf, hand off cleanly: + +- **Diagnostic questions** ("is my install OK?", "why isn't /lfx-foo working?", "fix my broken symlinks"): hand off to `/lfx-skills-doctor`. +- **Routing / discovery** ("which skill should I use for backend work?", "I want to add a feature, where do I start?"): hand off to `/lfx`. +- **Creating a new skill**: hand off to `/lfx-new-skill` (only available inside the lfx-skills clone). + +## What this skill does NOT do + +- **Pick which skill the user should use**: that's `/lfx`'s job. This skill manages the install; it doesn't recommend skills. +- **Diagnose problems**: hand off to `/lfx-skills-doctor`. +- **Scaffold new skills**: hand off to `/lfx-new-skill`. +- **Install/uninstall without confirmation**: always show the user the exact command first. +- **Invent categorisations**: skills aren't tagged backend/frontend/etc. anywhere in their metadata; don't pretend they are. + +## Reference files + +- [`references/intents.md`](references/intents.md) — management-intent → CLI command mapping. diff --git a/skills/lfx-skills-helper/references/intents.md b/.agents/skills/lfx-skills-helper/references/intents.md similarity index 91% rename from skills/lfx-skills-helper/references/intents.md rename to .agents/skills/lfx-skills-helper/references/intents.md index 3609de1..bd9bf68 100644 --- a/skills/lfx-skills-helper/references/intents.md +++ b/.agents/skills/lfx-skills-helper/references/intents.md @@ -5,7 +5,7 @@ Reference for `/lfx-skills-helper`. Maps natural-language **management** intents to the corresponding `lfx-skills` CLI invocation. -This file is for agents.md skill *management*: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. It is not a recommendation engine. Routing questions ("which skill should I use for X?") belong to `/lfx`. Diagnostic questions belong to `/lfx-doctor`. Authoring belongs to `/lfx-new-skill`. +This file is for agents.md skill *management*: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. It is not a recommendation engine. Routing questions ("which skill should I use for X?") belong to `/lfx`. Diagnostic questions belong to `/lfx-skills-doctor`. Authoring belongs to `/lfx-new-skill`. When the user's phrasing isn't an exact match, infer the closest intent and confirm the chosen command before running anything stateful. @@ -35,7 +35,7 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs | User says | Run | |-------------------------------------------------|--------------------------------------------------------------------------------------| -| "Add lfx skills to this repo" | Confirm, then `lfx-skills install --yes --scope=repo --repos="$(pwd)"`. Suggest `/lfx-doctor` after. | +| "Add lfx skills to this repo" | Confirm, then `lfx-skills install --yes --scope=repo --repos="$(pwd)"`. Suggest `/lfx-skills-doctor` after. | | "Remove lfx skills from this repo" | Confirm, then `lfx-skills uninstall --yes --scope=repo --repos="$(pwd)"` | | "Install lfx skills globally for Claude" | Explain the Claude Code plugin path: `/plugin marketplace add linuxfoundation/lfx-skills`, then `/plugin install lfx-skills@lfx-skills` | | "Add agents.md support" | Confirm, then `lfx-skills install --yes --scope=global` | @@ -46,7 +46,7 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs | User says | Run | Notes | |-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| -| "Update lfx skills" | Ask whether they mean Claude Code plugin or agents.md CLI install. For agents.md, run `lfx-skills update --pull`. | Suggest `/lfx-doctor` after agents.md updates | +| "Update lfx skills" | Ask whether they mean Claude Code plugin or agents.md CLI install. For agents.md, run `lfx-skills update --pull`. | Suggest `/lfx-skills-doctor` after agents.md updates | | "Update the Claude plugin" / "Update Claude skills" | Explain: `/plugin marketplace update lfx-skills`, then `/plugin update lfx-skills@lfx-skills` | Claude Code plugin updates are not handled by the CLI | | "Re-apply my install" | `lfx-skills update` | No `--pull`; just refresh symlinks against the manifest | | "Remove old Claude symlinks" | Confirm, then `lfx-skills uninstall --yes --legacy-claude-only` | Removes only lfx-skills-owned legacy Claude symlinks | @@ -59,9 +59,9 @@ Always confirm via `AskUserQuestion` before running. Show the exact command firs | User says | Hand off to | |-------------------------------------------------|--------------------------------------------------------------------------------------| | "Which skill should I use for X?" / task descriptions | `/lfx` | -| "Run a health check" / "is my install OK?" | `/lfx-doctor` | -| "Why isn't /lfx-foo working?" | `/lfx-doctor` | -| "Fix my broken symlinks" | `/lfx-doctor` | +| "Run a health check" / "is my install OK?" | `/lfx-skills-doctor` | +| "Why isn't /lfx-foo working?" | `/lfx-skills-doctor` | +| "Fix my broken symlinks" | `/lfx-skills-doctor` | | "How do I create a new lfx skill?" | `/lfx-new-skill` (only inside the lfx-skills clone) | | "Scaffold a new skill called lfx-foo" | `/lfx-new-skill` | diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 507b6f5..4675ed8 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -3,7 +3,9 @@ "owner": { "name": "The Linux Foundation" }, - "description": "Claude Code marketplace for LFX Skills.", + "metadata": { + "description": "Claude Code marketplace for LFX Skills." + }, "plugins": [ { "name": "lfx-skills", diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index efe3897..625a4ac 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -8,21 +8,5 @@ "homepage": "https://github.com/linuxfoundation/lfx-skills", "repository": "https://github.com/linuxfoundation/lfx-skills", "license": "MIT", - "skills": [ - "./skills/lfx/", - "./skills/lfx-backend-builder/", - "./skills/lfx-cdp-snowflake-connectors/", - "./skills/lfx-coordinator/", - "./skills/lfx-git-setup/", - "./skills/lfx-intercom/", - "./skills/lfx-pr-catchup/", - "./skills/lfx-pr-resolve/", - "./skills/lfx-preflight/", - "./skills/lfx-product-architect/", - "./skills/lfx-research/", - "./skills/lfx-setup/", - "./skills/lfx-snowflake-access/", - "./skills/lfx-test-journey/", - "./skills/lfx-ui-builder/" - ] + "skills": ["./skills/"] } diff --git a/.claude/skills/lfx-doctor/SKILL.md b/.claude/skills/lfx-doctor/SKILL.md deleted file mode 100644 index 1d94ec9..0000000 --- a/.claude/skills/lfx-doctor/SKILL.md +++ /dev/null @@ -1,21 +0,0 @@ ---- -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -name: lfx-doctor -description: > - Bootstrap wrapper for the LFX Skills doctor helper in this source repo. Use to - diagnose agents.md installs and legacy Claude symlink installs from inside the - lfx-skills clone. -allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion ---- - -# LFX Doctor Bootstrap - -This is a repository-local bootstrap skill. Read `skills/lfx-doctor/SKILL.md` -from the LFX Skills repo root and follow that canonical skill exactly. - -Do not use this wrapper as the source of truth. The canonical implementation is: - -```text -skills/lfx-doctor/SKILL.md -``` diff --git a/.claude/skills/lfx-install/SKILL.md b/.claude/skills/lfx-install/SKILL.md index a870d00..45baf78 100644 --- a/.claude/skills/lfx-install/SKILL.md +++ b/.claude/skills/lfx-install/SKILL.md @@ -3,19 +3,181 @@ # SPDX-License-Identifier: MIT name: lfx-install description: > - Bootstrap wrapper for the LFX Skills install helper in this source repo. Use - when the user has just cloned lfx-skills and wants to set up agents.md-compatible - tools, or needs the Claude Code plugin install path explained. + Install or set up LFX Skills for agents.md-compatible tools via the + lfx-skills CLI, and point Claude Code-only users to the Claude plugin path. + Walks through the choices in plain language: where their LFX repos live, + scope, agents config dirs, then runs the installer and verifies. Use + whenever the user says "I just cloned this — what now?", "set up lfx skills", + "install lfx skills", "I'm new to lfx-skills", or "first-time setup". allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- -# LFX Install Bootstrap +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> -This is a repository-local bootstrap skill. Read `skills/lfx-install/SKILL.md` -from the LFX Skills repo root and follow that canonical skill exactly. +# LFX Skills Install -Do not use this wrapper as the source of truth. The canonical implementation is: +You guide the user through their first-time install. The bash CLI (`cli/lfx-skills install`) can do this non-interactively if every flag is supplied; this skill is the conversational layer that figures out what those flags should be by asking the user, in plain language, one question at a time. + +## Step 1: Verify you're in the clone + +This skill only works inside the `lfx-skills` clone. Verify: + +```bash +[ -x ./cli/lfx-skills ] && echo OK || echo NOT_IN_CLONE +``` + +If `NOT_IN_CLONE`, tell the user: + +> "I only run inside the lfx-skills clone. `cd` to your clone of `linuxfoundation/lfx-skills` and ask again." + +Stop. + +## Step 2: Probe the system + +Run `./cli/lfx-skills` indirectly via its install command's PROBE step — but for the conversation, you also want the data yourself so you can ask informed questions. Do these one-shot probes: + +```bash +# agents.md-compatible CLIs available +for cli in codex gemini opencode; do + command -v "$cli" >/dev/null 2>&1 && echo "$cli" +done + +# Agents config dirs +ls -d "$HOME"/.agents* 2>/dev/null + +# Dev root candidates +for d in "$HOME/lf" "$HOME/lfx" "$HOME/code/lfx" "$HOME/work/lfx"; do + [ -d "$d" ] && echo "$d" +done +``` + +## Step 3: Q1 — Install route + +Use `AskUserQuestion`: + +> "Which setup do you need? (1) agents.md-compatible tool (Codex, Gemini CLI, OpenCode), (2) Claude Code only, (3) both." + +If you detected only one CLI installed, default to it but still confirm. + +If the user picks Claude Code only, explain that Claude installs this repo as a plugin and does not use the CLI symlink installer: ```text -skills/lfx-install/SKILL.md +/plugin marketplace add linuxfoundation/lfx-skills +/plugin install lfx-skills@lfx-skills +``` + +If they are testing from a local checkout, tell them to run Claude Code with the local plugin directory or add the local marketplace per the Claude Code plugin docs. Stop after explaining the plugin path; do not run `./cli/lfx-skills install` for Claude-only installs. + +If the user picks both, use the plugin path for Claude Code and continue with the CLI flow below for agents.md-compatible tools. The CLI itself remains agents.md-only. + +## Step 4: Q2 — Scope + +> "Install scope? (1) **Global** — available in every session of your agents.md-compatible tool. (2) **Per-repo** — only in specific repos (their `.agents/skills/`). (3) **Both** — global plus pin into specific repos." + +This question goes second so subsequent questions can adapt. (Per-repo only? Skip the global config picker. Global only? Skip the repo picker.) + +## Step 5: Q3 — LFX dev root + +Show the candidates you probed with their repo counts: + +``` +Where do you keep your LFX repo clones? + 1. ~/lf (12 lf* repos) + 2. ~/code/lfx (3 lf* repos) + 3. Custom path… +``` + +Use `AskUserQuestion`. If the user already has `LFX_DEV_ROOT` set in the shell, mention it and ask whether to keep it. + +If the chosen path doesn't exist, ask whether to create it (`mkdir -p`). If it has zero `lf*` git repos, warn but proceed: the install will still work; the dev-root-empty doctor warning will trigger until they clone some. + +## Step 6: Q4 — Agents config dirs (only if scope includes Global) + +If you saw multiple `~/.agents*` config dirs, ask which to install into: + +> "I see these agents config dirs: …. Install into all of them, or just one? (defaults to `~/.agents`)" + +Most users have one; this only matters for power users with multiple profiles. + +If scope is Per-repo only, skip this step entirely. + +## Step 7: Q5 — Repos (only if scope includes Per-repo) + +List `lf*` git repos under the chosen dev root: + +> "Which repos? Pick numbers (e.g., `1, 3, 5`), `all`, or `none`." + +If scope is Global only, skip this step entirely. + +## Step 8: Show the plan + +Before running anything, summarise: + +``` +Plan: + Scope: global + per-repo + LFX_DEV_ROOT: ~/lf + Agents dir: ~/.agents + Repos (4): lfx-v2-ui, lfx-v2-meeting-service, lfx-v2-committee-service, lfx-v2-query-service + Skills: runtime suite + lfx-skills-doctor + lfx-skills-helper + +Will create approximately N symlinks. Proceed? +``` + +`AskUserQuestion`. If no, stop. + +## Step 9: Run the installer + +Compose the non-interactive flags from the user's answers: + +```bash +./cli/lfx-skills install --yes \ + --scope=<scope> \ + --lfx-dev-root=<path> \ + --agents-config=<dir> \ + --repos=<repo1,repo2,...> ``` + +Stream the output to the user. + +## Step 10: Verify + +Run a quick verification: + +```bash +./cli/lfx-skills doctor +``` + +If errors, walk the user through the auto-fix: + +> "One or more checks failed. Want me to run `/lfx-skills-doctor` to investigate?" + +## Step 11: Confirm the CLI is on PATH + +The installer creates a symlink at a writable PATH dir (`~/.local/bin/lfx-skills`, `~/bin/lfx-skills`, or `/usr/local/bin/lfx-skills`) so the user can type `lfx-skills` from anywhere — no shell rc edit. Read the install output to see which path was used and tell the user: + +> "`lfx-skills` is now at `<reported path>` and ready to use from any terminal." + +If the installer reported it couldn't find a writable PATH dir, share the alias snippet it printed: + +> "I couldn't find a writable PATH dir to drop the CLI into. Add this alias to your shell rc to use `lfx-skills` from anywhere: +> ```bash +> alias lfx-skills='<clone>/cli/lfx-skills' +> ``` +> Or extend PATH to include `~/.local/bin`, `~/bin`, or `/usr/local/bin`." + +## Step 12: Suggest next steps + +> "All set. Next: +> +> 1. Restart your AI coding assistant (or open a new session). +> 2. `cd` to any LFX repo and type `/lfx` — that's your plain-language entry point. +> 3. Run `/lfx-skills-doctor` anytime to recheck the install. +> 4. Run `/lfx-skills-helper` to manage what's installed where." + +## What this skill does NOT do + +- **Edit your shell rc** — never. Always print the snippet for the user to paste. +- **Install only some skills** — v1 always installs everything (the full set per chosen target). Filtering is a v2 idea. +- **Run outside the clone** — bail in Step 1. +- **Re-run silently** — confirm at the plan step before doing anything stateful. diff --git a/.claude/skills/lfx-new-skill/SKILL.md b/.claude/skills/lfx-new-skill/SKILL.md index 638f209..ea6e2da 100644 --- a/.claude/skills/lfx-new-skill/SKILL.md +++ b/.claude/skills/lfx-new-skill/SKILL.md @@ -3,18 +3,136 @@ # SPDX-License-Identifier: MIT name: lfx-new-skill description: > - Bootstrap wrapper for the LFX Skills authoring helper in this source repo. Use - when a contributor wants to create a new lfx skill or asks how to author one. + Scaffold a new LFX Skills suite skill under skills/. Use when someone wants + to add a new lfx skill, provides a complete SKILL.md to import, has an idea + for a skill and wants help drafting it, or asks how to create a skill. allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion --- -# LFX New Skill Bootstrap +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> -This is a repository-local bootstrap skill. Read `skills/lfx-new-skill/SKILL.md` -from the LFX Skills repo root and follow that canonical skill exactly. +# LFX New Skill — Scaffolder -Do not use this wrapper as the source of truth. The canonical implementation is: +You help contributors add LFX Skills suite skills under `skills/lfx-<name>/`. Read [`references/conventions-quickref.md`](references/conventions-quickref.md) before writing files. + +Use two modes: + +- **Complete skill provided:** if the user pastes or points to a complete `SKILL.md`, use it as the source of truth. Preserve the body. Only normalize frontmatter, license header, directory name, and repo-specific placeholders when needed. +- **Draft from idea:** if the user only has an idea, help write the body. Ask targeted questions about trigger phrases, inputs, steps, tools, outputs, and boundaries. Draft concrete step-by-step instructions from the answers. + +## Step 1: Verify Location + +Run: + +```bash +[ -x ./cli/lfx-skills ] && [ -d ./skills/lfx ] && echo OK || echo NOT_IN_CLONE +``` + +If `NOT_IN_CLONE`, tell the user to `cd` to the `lfx-skills` clone and stop. + +## Step 2: Gather Inputs + +Ask whether the user has a complete `SKILL.md` or wants help drafting one. + +For a complete skill: + +- Read the supplied file/content. +- Extract `name`, `description`, and `allowed-tools` when present. +- Ask only for missing required fields. + +For a draft: + +- Ask for the skill name. It must match `^lfx-[a-z0-9-]+$` and not already exist under `skills/`. +- Ask for a frontmatter description with 3-5 trigger phrases. +- Ask which tools it needs. Default: `Bash, Read, Glob, Grep, AskUserQuestion`. +- Ask what the skill should do, what inputs it needs, what it may inspect or modify, what output it should produce, and what it must not do. + +Ask whether it needs `references/`. + +## Step 3: Write Files + +Create: ```text -skills/lfx-new-skill/SKILL.md +skills/lfx-<name>/SKILL.md +``` + +If requested, also create: + +```text +skills/lfx-<name>/references/.gitkeep +``` + +Ensure: + +- frontmatter starts on line 1 +- license comments are lines 2-3 inside the frontmatter +- `name:` equals the directory basename +- `description:` uses a YAML folded scalar +- `allowed-tools:` is present +- MCP-dependent skills include a `## Prerequisites` section + +If the skill is user-facing, ask whether to add routing guidance to `skills/lfx/SKILL.md`. Internal-only skills can remain unrouted. + +## Step 4: Validate + +Run only the new skill formatting check: + +```bash +./cli/lfx-skills doctor --skill-formatting-only --skill=lfx-<name> ``` + +Fix `frontmatter-*` and `license-missing` issues, then rerun until clean. Do not run the full doctor for this scaffolding check; full doctor includes agents.md install/setup checks. + +## Step 5: Explain Local Testing + +Ask which runtime they want to test: Claude Code plugin, agents.md, or both. + +For Claude Code plugin testing: + +- Give the user this validation command to run from their normal terminal: + + ```bash + cd "<absolute-path-to-lfx-skills>" + claude plugin validate . + ``` + +- Ask which target LFX repo they want to test in. Resolve a repo name under `~/.lfx-skills/dev-root` or use the absolute path they provide. +- Give the user a ready-to-run command: + + ```bash + LFX_SKILLS_CLONE="<absolute-path-to-lfx-skills>" + cd "<resolved-target-repo-path>" + claude --plugin-dir "$LFX_SKILLS_CLONE" + ``` + +- Tell them to run `/lfx-skills:lfx-<name>` in that Claude Code session. + +For agents.md testing: + +- Run `./cli/lfx-skills update`. +- Tell the user to restart their agents.md-compatible coding agent and run `/lfx-<name>`. +- If LFX Skills is not installed for agents.md, point them to `/lfx-install` or `./install.sh`. + +Keep the two paths separate: the CLI is for agents.md installs; Claude Code uses the plugin path. + +## Step 6: Offer Commit And Release Help + +After validation, ask whether the user wants help committing the scaffolded skill. + +If they say yes: + +- Review `git diff` and `git status`. +- Commit only the intended files. +- Use `git commit -s -S`. +- Do not add co-author trailers. +- Do not push unless explicitly asked. + +For plugin versioning, explain that Claude Code picks up plugin changes when `.claude-plugin/plugin.json` gets a new SemVer `version` and the change reaches `main`. Offer to help choose the next patch/minor/major version and include the plugin version bump in the signed commit when plugin-visible behavior changed. + +## Boundaries + +- Do not install or repair the user's LFX Skills setup; route that to `/lfx-install` or `/lfx-skills-doctor`. +- Do not use the CLI to install Claude Code skills. +- Do not run Claude Code plugin commands for agents.md testing. +- Do not invent unclear behavior. Ask when scope, inputs, outputs, or safety boundaries are unclear. diff --git a/.claude/skills/lfx-new-skill/references/conventions-quickref.md b/.claude/skills/lfx-new-skill/references/conventions-quickref.md new file mode 100644 index 0000000..d3f3b34 --- /dev/null +++ b/.claude/skills/lfx-new-skill/references/conventions-quickref.md @@ -0,0 +1,132 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# New Skill Quick Reference + +## Frontmatter + +```yaml +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-<name> +description: > + One paragraph with 3-5 trigger phrases users might say. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- +``` + +- `---` must be line 1. +- License comments must be lines 2-3 inside frontmatter. +- `name:` must match the directory basename. +- `description:` should use `>`. + +## Input Modes + +- **Complete skill:** preserve the supplied body; normalize only frontmatter, license, path, and repo-specific placeholders. +- **Idea:** ask enough questions to draft concrete runtime instructions. Cover triggers, inputs, steps, allowed tools, output, and explicit non-goals. + +## Tool Defaults + +| Use case | Tools | +|---|---| +| Read-only research | `Bash, Read, Glob, Grep, AskUserQuestion` | +| Reads external URLs | `Bash, Read, Glob, Grep, AskUserQuestion, WebFetch` | +| Edits files | `Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion` | +| Delegates to skills | `Bash, Read, Glob, Grep, AskUserQuestion, Skill` | +| MCP-dependent | Add the specific `mcp__*` tools | + +MCP-dependent skills should include `## Prerequisites`. + +## Body Shape + +Use this as the default body structure when drafting: + +```markdown +# <Title> + +<Who this helps and what it does.> + +## Step 1: <Action> + +<Concrete instructions. Ask for user input when needed.> + +## Step 2: <Action> + +<Concrete instructions.> + +## What this skill does NOT do + +- <Boundary> + +## Reference files + +- (none yet) +``` + +## References + +Use `references/` for long tables, checklists, examples, JSON/YAML snippets, or content the model should load only when needed. + +## Routing + +User-facing skills should usually be mentioned in `skills/lfx/SKILL.md` so `/lfx` can route to them. Internal-only skills can stay unrouted. + +## Validation + +Run: + +```bash +./cli/lfx-skills doctor --skill-formatting-only --skill=lfx-<name> +``` + +This checks only the new skill's frontmatter and license header. + +## Claude Code Local Test + +Give the user: + +```bash +cd "<absolute-path-to-lfx-skills>" +claude plugin validate . +``` + +Then ask which LFX repo they want to test in, resolve it under `~/.lfx-skills/dev-root` if they gave a repo name, and give: + +```bash +LFX_SKILLS_CLONE="<absolute-path-to-lfx-skills>" +cd "<resolved-target-repo-path>" +claude --plugin-dir "$LFX_SKILLS_CLONE" +``` + +They should test: + +```text +/lfx-skills:lfx-<name> +``` + +## agents.md Local Test + +Run: + +```bash +./cli/lfx-skills update +``` + +Then tell the user to restart their agents.md-compatible coding agent and run: + +```text +/lfx-<name> +``` + +## Commit And Release + +After validation, ask if the user wants help committing. If yes: + +- Review `git diff` and `git status`. +- Commit only intended files. +- Use `git commit -s -S`. +- Do not add co-author trailers. +- Do not push unless explicitly asked. + +For Claude Code plugin updates, bump the SemVer `version` in `.claude-plugin/plugin.json` with the skill changes when plugin-visible behavior changed. Claude Code will keep using the cached plugin if the version is unchanged. diff --git a/.claude/skills/lfx-skills-doctor/SKILL.md b/.claude/skills/lfx-skills-doctor/SKILL.md new file mode 100644 index 0000000..de7327b --- /dev/null +++ b/.claude/skills/lfx-skills-doctor/SKILL.md @@ -0,0 +1,136 @@ +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: lfx-skills-doctor +description: > + Diagnose problems with the agents.md LFX Skills installation and legacy + Claude symlink installs: broken symlinks, missing dev root, frontmatter + errors, routing gaps, MCP setup. Use whenever an agents.md-installed skill + isn't loading, when /lfx commands aren't appearing in autocomplete, when + newly installed agents.md skills don't show up, or when the user asks + "what's wrong with my install", "is my setup OK", or "check my lfx skills". + For Claude Code plugin installs, explain the plugin marketplace update path + instead of running the CLI doctor. Wraps `lfx-skills doctor --json` with a + conversational fix flow. +allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion +--- + +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> + +# LFX Skills Doctor + +You diagnose problems with a user's agents.md LFX Skills install and legacy Claude symlink installs, then walk them through fixing the ones that need a human-in-the-loop. The bash CLI handles mechanical repairs; you handle everything that needs judgment (content gaps, scaffolding, file edits). + +Claude Code plugin installs are separate. Claude Code uses `/plugin marketplace add linuxfoundation/lfx-skills` and `/plugin install lfx-skills@lfx-skills`; it does not use the CLI installer or agents.md symlinks. If the user is asking about the Claude Code plugin itself, do not run `lfx-skills doctor`. Tell them to update with `/plugin marketplace update lfx-skills` and `/plugin update lfx-skills@lfx-skills`, and to run `claude plugin validate .` from the LFX Skills clone when validating local plugin metadata. Use the CLI doctor only for agents.md installs and legacy Claude symlink cleanup. + +## Step 1: Locate the CLI + +The `lfx-skills` CLI lives in the user's lfx-skills clone at `cli/lfx-skills`. Try, in order: + +1. **On PATH:** `command -v lfx-skills` — if found, use it directly. +2. **From the manifest:** `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` — if the file exists, append `/cli/lfx-skills`. +3. **Current dir:** if the user is inside the lfx-skills clone (a `cli/lfx-skills` exists relative to `pwd`), use `./cli/lfx-skills`. +4. **Last resort:** ask the user: "Where is your lfx-skills clone? (e.g., `~/lf/lfx-skills`)". + +If none of the above works, the agents.md install was never run: tell the user to clone the repo and run `./install.sh` (or use `/lfx-install` if they're inside the clone). If they only need Claude Code, point them to the plugin marketplace commands instead. Stop here. + +## Step 2: Run diagnostics + +```bash +"$LFX_SKILLS_CLI" doctor --json +``` + +This emits a JSON array of records. Parse it. Each record has: +`{severity, id, category, title, detail, fixable, payload}`. + +Group by severity (`pass` / `warn` / `fail`). + +## Step 3: Render the report + +Use this layout. Keep it scannable. + +``` +LFX Skills Health Check +═══════════════════════════════════════════ + +✓ <N> checks passed. + +✗ <K> errors: + + 1. <title> + Why this matters: <plain-language consequence> + Fix: <action> + [auto-fixable] or [needs you to: <action>] + +⚠ <M> warnings: + + 1. <title> + Why: <consequence> + Fix: <action> +``` + +Don't dump every passing check. Summarise (`✓ 36 checks passed`) and let the user ask for the full list if they want it. + +For each error and warning, give plain-language context: not just the title from the JSON, but *why it matters* and *what to do about it*. Use `references/fix-recipes.md` (in this skill directory) to look up the per-issue narrative when the JSON record alone isn't enough. + +## Step 4: Offer fixes + +If there are fixable errors or warnings, ask: + +> "I can auto-fix N issue(s). Want me to walk through them?" + +Use `AskUserQuestion`. Wait for the answer. + +If yes: + +For each `fixable: true` record, ask per-issue (`AskUserQuestion`): "Fix `<id>`? — `<title>`". On yes, invoke: + +```bash +echo y | "$LFX_SKILLS_CLI" doctor --fix +``` + +(Or, more granularly, mark which issues to fix and answer the CLI's prompts. The CLI's `--fix` flow walks every fixable issue and asks per-issue too — you can let it drive, or you can pre-filter to the issues the user picked.) + +## Step 5: Handle the not-fixable cases + +For records with `fixable: false` that the user wants addressed, apply judgment. Common cases: + +| Issue ID | What you can offer | +|------------------------------|---------------------------------------------------------------------------------| +| `frontmatter-no-name` / `frontmatter-name-mismatch` | Read the SKILL.md, identify the line, offer to fix it via Edit. | +| `frontmatter-no-description` | Read the SKILL.md body, draft a one-paragraph description from it, offer to insert. | +| `license-missing` | Insert the YAML license-header lines (see `references/fix-recipes.md` template). | +| `routing-uncovered` | Read `lfx/SKILL.md`, find the routing table, offer to add an entry for the missing skill. | +| `routing-dangling` | Either remove the dangling entry from `lfx/SKILL.md` or hand off to `/lfx-new-skill` to scaffold the missing skill. Ask the user. | +| `symlink-no-skillmd` | Hand off to `/lfx-new-skill` to scaffold the missing SKILL.md. | +| `clone-dirty` | Informational only. Mention that the user has uncommitted changes; don't act. | + +Always ask before editing files. Never edit `lfx/SKILL.md` (or anything else) silently. + +## Step 6: Re-verify + +After applying fixes, re-run: + +```bash +"$LFX_SKILLS_CLI" doctor --json +``` + +Show the new counts. Celebrate if everything's green; otherwise, note what's still outstanding and why. + +## Step 7: Close + +End with: + +> "Run `/lfx-skills-doctor` anytime to recheck. For installation changes, `/lfx-install` or `/lfx-skills-helper` is the right entry point." + +## What this skill does NOT do + +- Diagnose Claude Code plugin cache or marketplace state through the CLI. Use Claude Code plugin commands for that path. +- Install new skills or change install scope: that's `/lfx-install`. +- List, manage, or scaffold skills: `/lfx-skills-helper` and `/lfx-new-skill`. +- Modify `lfx/SKILL.md`'s routing table without asking the user first. +- Run destructive operations (force-removing real files, etc.) — only mechanical repairs the CLI considers safe. + +## Reference files + +- [`references/fix-recipes.md`](references/fix-recipes.md) — per-issue narrative, fix templates, and copy-pasteable snippets. diff --git a/.claude/skills/lfx-skills-doctor/references/fix-recipes.md b/.claude/skills/lfx-skills-doctor/references/fix-recipes.md new file mode 100644 index 0000000..33397f1 --- /dev/null +++ b/.claude/skills/lfx-skills-doctor/references/fix-recipes.md @@ -0,0 +1,203 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# Fix Recipes + +Per-issue narrative for `/lfx-skills-doctor`. The CLI's `--fix` flag handles the mechanical cases; this file covers everything else, plus extra context the JSON output doesn't include. + +Each entry follows the same shape: + +- **What:** plain-language description of what's wrong +- **Why it matters:** consequence for the user +- **Fix this session:** quick one-shot +- **Fix permanently:** lasting change +- **Auto-fixable?** yes (CLI), yes (skill), or no + +--- + +## issue-id: no-config + +**What:** No `~/.lfx-skills/config.json`. +**Why it matters:** the CLI doesn't know what's installed where. Doctor can only check things relative to the manifest, so most other checks will be skipped. +**Fix:** run `lfx-skills install`. Or, inside the lfx-skills clone, run `/lfx-install` for a guided walkthrough. +**Auto-fixable?** no (requires user choices about scope / dev root). + +--- + +## issue-id: no-symlinks + +**What:** Manifest exists but is empty (no symlinks recorded). +**Why it matters:** no LFX skills are actually installed. +**Fix:** run `lfx-skills install` to add them. +**Auto-fixable?** no. + +--- + +## issue-id: symlink-missing + +**What:** A symlink the manifest expects is gone from disk. +**Why it matters:** the corresponding `/lfx-...` command won't be available in your AI tool. +**Fix this session:** `lfx-skills doctor --fix` (CLI recreates from the manifest). +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: symlink-broken + +**What:** A symlink exists but points to a path that's no longer a directory. +**Why it matters:** same as above — the skill won't load. +**Common cause:** the lfx-skills clone moved or was deleted. +**Fix this session:** `lfx-skills doctor --fix`. +**Fix permanently:** if you moved the clone, run `lfx-skills install` again from the new location to re-record `canonical_clone`. +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: symlink-no-skillmd + +**What:** A symlink target exists, but the directory has no `SKILL.md`. +**Why it matters:** the skill won't load (the loader requires `SKILL.md`). Usually means someone created an empty `lfx-foo/` directory by accident, or pulled an in-progress branch. +**Fix:** either delete the empty directory, or scaffold a real skill via `/lfx-new-skill`. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can hand off to `/lfx-new-skill`. + +--- + +## issue-id: clone-not-recorded / clone-mismatch + +**What:** The CLI doesn't know which clone is canonical, or the recorded path doesn't match where the script ran from. +**Why it matters:** `lfx-skills update` and other commands rely on `canonical_clone`. Symlinks may also be pointing to a different clone than the one you think you're working in. +**Fix:** re-run `lfx-skills install` from the clone you want to be canonical. +**Auto-fixable?** no. + +--- + +## issue-id: clone-dirty + +**What:** Your `lfx-skills` clone has uncommitted changes. +**Why it matters:** purely informational. If you ran `lfx-skills update --pull`, that would fail with this state. Otherwise no impact. +**Fix:** commit, stash, or discard, depending on intent. +**Auto-fixable?** no. + +--- + +## issue-id: dev-root-not-recorded + +**What:** No `lfx_dev_root` in the manifest. +**Why it matters:** skills like `/lfx-test-journey` and `/lfx-coordinator` can't auto-discover your local LFX repos. +**Fix:** `lfx-skills config set lfx_dev_root=/path/to/your/lfx-clones`. Or re-run `lfx-skills install`. +**Auto-fixable?** no (requires user input). + +--- + +## issue-id: dev-root-missing + +**What:** The recorded `lfx_dev_root` path doesn't exist on disk. +**Common cause:** moved your clones to a new location. +**Fix:** `lfx-skills config set lfx_dev_root=/new/path`. The CLI will rewrite `~/.lfx-skills/dev-root` automatically. +**Auto-fixable?** no. + +--- + +## issue-id: dev-root-empty + +**What:** `LFX_DEV_ROOT` exists but contains no `lf*` git repos. +**Why it matters:** the skills will run, but they won't find any local repos to work on (they'll fall back to GitHub API calls when possible). +**Fix:** clone the LFX repos you work on into that directory. Examples: + +```bash +cd "$LFX_DEV_ROOT" +git clone https://github.com/linuxfoundation/lfx-v2-ui.git +git clone https://github.com/linuxfoundation/lfx-v2-meeting-service.git +``` + +**Auto-fixable?** no. + +--- + +## issue-id: dev-root-file-missing + +**What:** `~/.lfx-skills/dev-root` is gone. +**Why it matters:** the 3 skills that need a local LFX path (`/lfx-coordinator`, `/lfx-research`, `/lfx-test-journey`) read this file via `cat` to resolve the dev root without depending on env vars. Without it, they fall back to `~/lf`, which may not match your setup. +**Fix:** `lfx-skills doctor --fix` regenerates it from the manifest. Or `lfx-skills config set lfx_dev_root=/your/path` if you also need to change the path. +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: dev-root-file-mismatch + +**What:** The contents of `~/.lfx-skills/dev-root` differ from `lfx_dev_root` in `config.json`. +**Common cause:** the file was hand-edited, or an old `lfx-skills` version wrote one source but not the other. +**Fix:** `lfx-skills doctor --fix` rewrites the file from `config.json` (the source of truth). +**Auto-fixable?** yes (CLI). + +--- + +## issue-id: frontmatter-missing + +**What:** A `SKILL.md` doesn't start with `---` on line 1. +**Why it matters:** the skill loader will refuse to load it. The loader requires frontmatter as the very first thing in the file (no blank lines, no comments above). +**Fix:** insert a frontmatter block at the top with the skill `name`, `description`, and `allowed-tools`. Use `/lfx-new-skill` as a template, or copy the shape from a sibling skill. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can guide the rewrite. + +--- + +## issue-id: frontmatter-no-name + +**What:** Frontmatter present but the `name:` field is missing or empty. +**Fix:** add `name: <skill-directory-basename>` to the frontmatter. Loader will fail without it. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch it via Edit. + +--- + +## issue-id: frontmatter-name-mismatch + +**What:** `name:` in the SKILL.md doesn't match the directory basename. +**Why it matters:** loaders use the directory name to register the slash command, but read the frontmatter for description and tools. A mismatch is confusing and may cause routing issues. +**Fix:** make `name:` equal `basename "$skill_dir"`. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch via Edit. + +--- + +## issue-id: frontmatter-no-description + +**What:** No `description:` field, or it's empty. +**Why it matters:** the loader uses `description` to decide when to surface the skill. Missing description means the model has no context for *when* to invoke it. +**Fix:** write a one-paragraph description that includes 3–5 trigger phrases users might say. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can draft one from the SKILL.md body. + +--- + +## issue-id: license-missing + +**What:** A `SKILL.md` doesn't have the LFX copyright header in its first 4 lines. +**Why it matters:** CI's `license-header-check` job will fail. +**Fix:** add these lines as lines 2–3, immediately after the opening `---`: + +```yaml +--- +# Copyright The Linux Foundation and each contributor to LFX. +# SPDX-License-Identifier: MIT +name: ... +``` + +(The `#` comments are valid YAML comments. They satisfy the license check without breaking frontmatter parsing.) +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch via Edit. + +--- + +## issue-id: routing-dangling + +**What:** `lfx/SKILL.md` mentions `/lfx-foo` but no `lfx-foo/` directory exists. +**Why it matters:** the user asks `/lfx` to route to `/lfx-foo` and gets a confused error. +**Fix:** either remove the dangling reference from `lfx/SKILL.md`, or create the missing skill via `/lfx-new-skill`. +**Auto-fixable?** no — needs your call on which. + +--- + +## issue-id: routing-uncovered + +**What:** A skill exists in the clone but `lfx/SKILL.md` doesn't route to it. +**Why it matters:** users typing `/lfx` won't find the skill via the plain-language router. They can still invoke it directly with `/lfx-foo`. +**Fix:** add an entry to `lfx/SKILL.md`'s routing table for the skill, including 1–2 example trigger phrases. +**Caveat:** internal-only skills (like `lfx-backend-builder` and `lfx-ui-builder`, which are only invoked by `/lfx-coordinator`) can legitimately stay out of the routing table. Use judgment. +**Auto-fixable?** no by CLI; the `/lfx-skills-doctor` skill can patch via Edit. diff --git a/.claude/skills/lfx-skills-helper/SKILL.md b/.claude/skills/lfx-skills-helper/SKILL.md index c9c01d0..711f5f0 100644 --- a/.claude/skills/lfx-skills-helper/SKILL.md +++ b/.claude/skills/lfx-skills-helper/SKILL.md @@ -3,20 +3,98 @@ # SPDX-License-Identifier: MIT name: lfx-skills-helper description: > - Bootstrap wrapper for the LFX Skills management helper in this source repo. - Use to list, update, uninstall, inspect, or manage agents.md installs and - legacy Claude symlink cleanup. + Manage the agents.md LFX Skills installation via the lfx-skills CLI: list + what's installed, install or uninstall in this repo or globally, update from + upstream, view or change config, look up what a specific skill does, and + remove legacy Claude symlink installs. Use for "add lfx skills to this repo", + "what's installed", "update lfx skills", "show my lfx setup", "uninstall", + "remove old Claude symlinks", "what does /lfx-foo do". For Claude plugin + installs, explain the plugin marketplace path. For "which skill should I use + for X" or other plain-language routing questions, hand off to /lfx. For health + checks or repair, hand off to /lfx-skills-doctor. allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion --- -# LFX Skills Helper Bootstrap +<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> -This is a repository-local bootstrap skill. Read -`skills/lfx-skills-helper/SKILL.md` from the LFX Skills repo root and follow -that canonical skill exactly. +# LFX Skills Helper -Do not use this wrapper as the source of truth. The canonical implementation is: +You are the conversational front-end for the agents.md-only `lfx-skills` CLI: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. -```text -skills/lfx-skills-helper/SKILL.md -``` +Claude Code is separate: it installs LFX Skills as a plugin with `/plugin marketplace add linuxfoundation/lfx-skills` and `/plugin install lfx-skills@lfx-skills`. Do not use the CLI to install Claude Code skills. The CLI can only remove old Claude symlink installs via `lfx-skills uninstall --legacy-claude-only` or as part of `lfx-skills uninstall --all`. + +## Step 1: Locate the CLI + +Same as `/lfx-skills-doctor`. Try in order: + +1. `command -v lfx-skills` (on PATH). +2. `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` then append `/cli/lfx-skills`. +3. `./cli/lfx-skills` if you're inside the lfx-skills clone. +4. Ask the user. + +If none works: the install was never run. Tell the user to clone `linuxfoundation/lfx-skills` and run `./install.sh` (or invoke `/lfx-install` if they're already in the clone). Stop. + +## Step 2: Classify the request + +Decide which surface the request belongs to: + +| User asks about… | Surface | Action | +|--------------------------------------------|------------------|-------------------------------------------| +| Installing, updating, removing, config | This skill | Map to a CLI subcommand (Step 3) | +| What's installed / available / where | This skill | Map to a CLI subcommand (Step 3) | +| What a specific skill does | This skill | `lfx-skills info <name>` | +| **Which skill** to use for a task | Hand off to `/lfx` | Stop here; let the router pick. | +| **Diagnose** a problem / "why isn't X working" | Hand off to `/lfx-skills-doctor` | Stop here. | +| **Scaffold a new skill** | Hand off to `/lfx-new-skill` | Stop here (clone-only). | + +Read `references/intents.md` once for the management-intent → CLI mapping. If the user's phrasing isn't in the table and doesn't fit your job either, say so and suggest the right entry point. + +## Step 3: Run the CLI + +Execute the chosen subcommand. Capture stdout. Use `--json` flags where available (currently `doctor --json`) when you need structured data. + +For commands that change state (`install`, `uninstall`, `update`, `config set`), **always confirm with the user first** via `AskUserQuestion`, showing exactly what you're about to run. The CLI's `--yes` flag is appropriate only after the user has confirmed in chat. + +## Step 4: Format the output + +The CLI output is structured but utilitarian. Reformat it for conversation: + +- **`lfx-skills list`** outputs `scope<TAB>skill<TAB>link`. Render as a friendly grouped list: + + ``` + Globally installed (agents.md): + /lfx + /lfx-coordinator + ... + + Per-repo (lfx-v2-meeting-service): + /lfx-coordinator + /lfx-pr-resolve + ... + ``` + +- **`lfx-skills info <skill>`** outputs frontmatter + install locations. Render the description in prose, list trigger phrases, summarise where it's installed. + +- **`lfx-skills config`** outputs raw JSON. Pretty-print it as a small table: dev root, canonical clone, total symlinks, and CLI symlink. + +- **`lfx-skills repos`** outputs one path per line. Group as a numbered list with sizes/last-modified if helpful. + +## Step 5: Hand-off rules + +If during the conversation the request shifts to something off your turf, hand off cleanly: + +- **Diagnostic questions** ("is my install OK?", "why isn't /lfx-foo working?", "fix my broken symlinks"): hand off to `/lfx-skills-doctor`. +- **Routing / discovery** ("which skill should I use for backend work?", "I want to add a feature, where do I start?"): hand off to `/lfx`. +- **Creating a new skill**: hand off to `/lfx-new-skill` (only available inside the lfx-skills clone). + +## What this skill does NOT do + +- **Pick which skill the user should use**: that's `/lfx`'s job. This skill manages the install; it doesn't recommend skills. +- **Diagnose problems**: hand off to `/lfx-skills-doctor`. +- **Scaffold new skills**: hand off to `/lfx-new-skill`. +- **Install/uninstall without confirmation**: always show the user the exact command first. +- **Invent categorisations**: skills aren't tagged backend/frontend/etc. anywhere in their metadata; don't pretend they are. + +## Reference files + +- [`references/intents.md`](references/intents.md) — management-intent → CLI command mapping. diff --git a/.claude/skills/lfx-skills-helper/references/intents.md b/.claude/skills/lfx-skills-helper/references/intents.md new file mode 100644 index 0000000..bd9bf68 --- /dev/null +++ b/.claude/skills/lfx-skills-helper/references/intents.md @@ -0,0 +1,73 @@ +<!-- Copyright The Linux Foundation and each contributor to LFX. --> +<!-- SPDX-License-Identifier: MIT --> + +# Intent → CLI mapping + +Reference for `/lfx-skills-helper`. Maps natural-language **management** intents to the corresponding `lfx-skills` CLI invocation. + +This file is for agents.md skill *management*: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. It is not a recommendation engine. Routing questions ("which skill should I use for X?") belong to `/lfx`. Diagnostic questions belong to `/lfx-skills-doctor`. Authoring belongs to `/lfx-new-skill`. + +When the user's phrasing isn't an exact match, infer the closest intent and confirm the chosen command before running anything stateful. + +## Listing + +| User says | Run | Notes | +|-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| +| "What lfx skills do I have here?" | `lfx-skills list --scope=repo --repo="$(pwd)"` | Per-repo install only | +| "What lfx skills are installed globally?" | `lfx-skills list --scope=global` | Across every recorded global config dir | +| "What lfx skills do I have anywhere?" | `lfx-skills list` | Both scopes combined | +| "What lfx skills are available in the clone?" | `lfx-skills list --available` | All installable skills, regardless of install state | + +## Inspecting one skill + +| User says | Run | Notes | +|-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| +| "What does /lfx-coordinator do?" | `lfx-skills info lfx-coordinator` | Strip the leading slash; pass the bare name | +| "Where is /lfx-coordinator installed?" | `lfx-skills info lfx-coordinator` | The output includes install locations | +| "Show me my full setup" | `lfx-skills config` | Pretty-print the JSON for the user | +| "Where is my LFX dev root?" | `lfx-skills config get lfx_dev_root` | | +| "Which clone of lfx-skills am I using?" | `lfx-skills config get canonical_clone` | | +| "What repos are in my LFX dev root?" | `lfx-skills repos` | | + +## Installing / changing scope + +Always confirm via `AskUserQuestion` before running. Show the exact command first. + +| User says | Run | +|-------------------------------------------------|--------------------------------------------------------------------------------------| +| "Add lfx skills to this repo" | Confirm, then `lfx-skills install --yes --scope=repo --repos="$(pwd)"`. Suggest `/lfx-skills-doctor` after. | +| "Remove lfx skills from this repo" | Confirm, then `lfx-skills uninstall --yes --scope=repo --repos="$(pwd)"` | +| "Install lfx skills globally for Claude" | Explain the Claude Code plugin path: `/plugin marketplace add linuxfoundation/lfx-skills`, then `/plugin install lfx-skills@lfx-skills` | +| "Add agents.md support" | Confirm, then `lfx-skills install --yes --scope=global` | +| "Install everything everywhere" | Confirm. Don't assume `--repos=`; ask the user which repos. | +| "Uninstall lfx skills" | Confirm, then `lfx-skills uninstall --yes --all` | + +## Maintenance + +| User says | Run | Notes | +|-------------------------------------------------|-----------------------------------------------------------|-----------------------------------------------------------------------------| +| "Update lfx skills" | Ask whether they mean Claude Code plugin or agents.md CLI install. For agents.md, run `lfx-skills update --pull`. | Suggest `/lfx-skills-doctor` after agents.md updates | +| "Update the Claude plugin" / "Update Claude skills" | Explain: `/plugin marketplace update lfx-skills`, then `/plugin update lfx-skills@lfx-skills` | Claude Code plugin updates are not handled by the CLI | +| "Re-apply my install" | `lfx-skills update` | No `--pull`; just refresh symlinks against the manifest | +| "Remove old Claude symlinks" | Confirm, then `lfx-skills uninstall --yes --legacy-claude-only` | Removes only lfx-skills-owned legacy Claude symlinks | +| "Remove lfx-skills completely" | Confirm, then `lfx-skills uninstall --yes --all` | Removes agents.md symlinks, legacy Claude symlinks, CLI symlink, config | +| "Update my LFX dev root" | Confirm new path, `lfx-skills config set lfx_dev_root=NEW_PATH` | Rewrites `~/.lfx-skills/dev-root` automatically | +| "Switch my Claude config dir" | Explain that Claude Code plugin scope is managed by Claude Code settings, not this CLI. | + +## Hand-offs (not your job) + +| User says | Hand off to | +|-------------------------------------------------|--------------------------------------------------------------------------------------| +| "Which skill should I use for X?" / task descriptions | `/lfx` | +| "Run a health check" / "is my install OK?" | `/lfx-skills-doctor` | +| "Why isn't /lfx-foo working?" | `/lfx-skills-doctor` | +| "Fix my broken symlinks" | `/lfx-skills-doctor` | +| "How do I create a new lfx skill?" | `/lfx-new-skill` (only inside the lfx-skills clone) | +| "Scaffold a new skill called lfx-foo" | `/lfx-new-skill` | + +## Disambiguation rules + +- The leading slash in user phrasing (e.g., `/lfx-coordinator`) is conversational; strip it when passing to the CLI. +- If the user says "skill X" without specifying scope, assume per-repo when they're inside a repo, global otherwise. Confirm before acting. +- If the user gives a custom command-line flag combo you don't recognise, defer to the CLI: `lfx-skills help <subcommand>`. +- If the user describes a *task* rather than asking about install state, that's a routing question — hand off to `/lfx` even mid-conversation. diff --git a/.gitignore b/.gitignore index 636ed45..f33ee5d 100644 --- a/.gitignore +++ b/.gitignore @@ -25,8 +25,8 @@ Thumbs.db .claude/skills/* !.claude/skills/lfx-install/ !.claude/skills/lfx-install/** -!.claude/skills/lfx-doctor/ -!.claude/skills/lfx-doctor/** +!.claude/skills/lfx-skills-doctor/ +!.claude/skills/lfx-skills-doctor/** !.claude/skills/lfx-skills-helper/ !.claude/skills/lfx-skills-helper/** !.claude/skills/lfx-new-skill/ @@ -41,8 +41,8 @@ Thumbs.db .agents/skills/* !.agents/skills/lfx-install/ !.agents/skills/lfx-install/** -!.agents/skills/lfx-doctor/ -!.agents/skills/lfx-doctor/** +!.agents/skills/lfx-skills-doctor/ +!.agents/skills/lfx-skills-doctor/** !.agents/skills/lfx-skills-helper/ !.agents/skills/lfx-skills-helper/** !.agents/skills/lfx-new-skill/ diff --git a/AGENTS.md b/AGENTS.md index af9de79..551192f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,10 +3,12 @@ # LFX Skills repo -You are inside the LFX Skills source repository. agents.md-compatible tools use the `cli/lfx-skills` CLI to install skills globally or per repo. Claude Code uses the `.claude-plugin/plugin.json` plugin manifest and does not use the CLI installer. The four helper skills live under `skills/` for clone/agent workflows and are intentionally not in the Claude plugin. +You are inside the LFX Skills source repository. agents.md-compatible tools use the `cli/lfx-skills` CLI to install skills globally or per repo. Claude Code uses the `.claude-plugin/plugin.json` plugin manifest and does not use the CLI installer. + +The four repo helper skills live under `.agents/skills/` and `.claude/skills/` so agents can work on and contribute to this repository locally. They are not part of the published LFX Skills suite. - **`/lfx-install`** — guides users through agents.md setup after clone. Walks them through scope, config dirs, repos, and dev root, then runs the CLI installer. -- **`/lfx-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. +- **`/lfx-skills-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. - **`/lfx-skills-helper`** — manages the install: lists what's installed, installs/uninstalls in this repo, updates from upstream, shows config. Skill management only — *not* a router. - **`/lfx-new-skill`** — scaffolds a new skill in this repo. Use when the contributor wants to add a new lfx skill or asks "how do I create a new skill". @@ -23,7 +25,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co ## When to use which - "How do I install this?" / "I just cloned, what now?" → `/lfx-install` -- "Skills aren't loading" / "is my setup OK?" → `/lfx-doctor` +- "Skills aren't loading" / "is my setup OK?" → `/lfx-skills-doctor` - "What's installed?" / "add to this repo" / "what does X do?" → `/lfx-skills-helper` - "Create a new skill called …" → `/lfx-new-skill` - Anything else (modifying an existing skill, writing docs, reviewing changes): proceed normally — use the existing user-facing skills (`/lfx`, `/lfx-coordinator`, `/lfx-preflight`, etc.) as you would in any LFX repo. @@ -32,10 +34,10 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - `cli/lfx-skills` — multi-subcommand bash CLI. - `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). -- `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. -- `.agents/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so agents.md-compatible tools can use the repo helper skills before installation. These are real files, not symlinks; canonical bodies live under `skills/`. -- `.claude-plugin/plugin.json` — Claude Code plugin manifest. Its `skills` array is an allowlist: only listed skill paths are exposed through the Claude plugin. New user-facing Claude skills must be added here. It intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. -- `.claude/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so Claude Code can use the repo helper skills in this source repo without installing the published plugin. These are not part of the distributed plugin. +- `skills/lfx*/` — runtime LFX Skills suite. Each directory is one skill, with `SKILL.md` and optional `references/`. Everything here is exposed by the Claude plugin and installed by the agents.md CLI. +- `.agents/skills/{lfx-install,lfx-skills-doctor,lfx-skills-helper,lfx-new-skill}/` — committed repo-local helpers for agents.md-compatible tools working inside this source repo. The CLI installs only `lfx-skills-doctor` and `lfx-skills-helper` from here outside the repo. +- `.claude-plugin/plugin.json` — Claude Code plugin manifest. It exposes `./skills/` as the runtime suite and intentionally does not include repo/install/meta helpers. +- `.claude/skills/{lfx-install,lfx-skills-doctor,lfx-skills-helper,lfx-new-skill}/` — committed repo-local helpers for Claude Code working inside this source repo. These are not part of the distributed plugin. - `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin and points its source at `"./"`. - `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. - `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. @@ -56,7 +58,7 @@ The skill bodies in this repo use Claude Code's tool vocabulary by default (Bash - One skill change or a batch of skill changes can ship in the same release. - Claude Code plugin changes must bump `.claude-plugin/plugin.json` `version`; otherwise Claude Code will keep using the cached plugin version. -- New user-facing skills that should be available through the Claude Code plugin must also be added to `.claude-plugin/plugin.json` `skills`. +- New LFX Skills suite skills go under `skills/`; repo/install/meta helpers for this source repo go under `.agents/skills/` and `.claude/skills/`. - The marketplace follows the LFX Skills default branch and uses `"./"` as the local plugin source. Version bump guidelines: diff --git a/CLAUDE.md b/CLAUDE.md index 2311fa5..9bb1ae4 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -3,10 +3,12 @@ # LFX Skills repo -You are inside the LFX Skills source repository. Claude Code users install the runtime skills through the `.claude-plugin/plugin.json` plugin manifest; agents.md-compatible tools use the `cli/lfx-skills` CLI. Claude Code does not use the CLI installer. The four helper skills live under `skills/` for clone/agent workflows and are intentionally not in the Claude plugin. +You are inside the LFX Skills source repository. Claude Code users install the runtime skills through the `.claude-plugin/plugin.json` plugin manifest; agents.md-compatible tools use the `cli/lfx-skills` CLI. Claude Code does not use the CLI installer. + +The four repo helper skills live under `.agents/skills/` and `.claude/skills/` so agents can work on and contribute to this repository locally. They are not part of the published LFX Skills suite. - **`/lfx-install`** — guides users through agents.md setup after clone. Walks them through scope, config dirs, repos, and dev root, then runs the CLI installer. -- **`/lfx-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. +- **`/lfx-skills-doctor`** — diagnoses problems with an existing install. Use when the user reports skills not loading, missing autocomplete entries, or unexpected behavior. - **`/lfx-skills-helper`** — manages the install: lists what's installed, installs/uninstalls in this repo, updates from upstream, shows config. Skill management only — *not* a router. - **`/lfx-new-skill`** — scaffolds a new skill in this repo. Use when the contributor wants to add a new lfx skill or asks "how do I create a new skill". @@ -23,7 +25,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co ## When to use which - "How do I install this?" / "I just cloned, what now?" → `/lfx-install` -- "Skills aren't loading" / "is my setup OK?" → `/lfx-doctor` +- "Skills aren't loading" / "is my setup OK?" → `/lfx-skills-doctor` - "What's installed?" / "add to this repo" / "what does X do?" → `/lfx-skills-helper` - "Create a new skill called …" → `/lfx-new-skill` - Anything else (modifying an existing skill, writing docs, reviewing changes): proceed normally — use the existing user-facing skills (`/lfx`, `/lfx-coordinator`, `/lfx-preflight`, etc.) as you would in any LFX repo. @@ -32,10 +34,10 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - `cli/lfx-skills` — multi-subcommand bash CLI. - `lib/*.sh` — sourced by the CLI (probe, config, symlinks, doctor, ui, targets). -- `skills/lfx*/` — each directory is one skill, with `SKILL.md` and optional `references/`. -- `.agents/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so agents.md-compatible tools can use the repo helper skills before installation. These are real files, not symlinks; canonical bodies live under `skills/`. -- `.claude-plugin/plugin.json` — Claude Code plugin manifest. Its `skills` array is an allowlist: only listed skill paths are exposed through the Claude plugin. New user-facing Claude skills must be added here. It intentionally excludes `lfx-install`, `lfx-doctor`, `lfx-skills-helper`, and `lfx-new-skill`. -- `.claude/skills/{lfx-install,lfx-doctor,lfx-skills-helper,lfx-new-skill}/` — committed bootstrap wrappers so Claude Code can use the repo helper skills in this source repo without installing the published plugin. These are not part of the distributed plugin. +- `skills/lfx*/` — runtime LFX Skills suite. Each directory is one skill, with `SKILL.md` and optional `references/`. Everything here is exposed by the Claude plugin and installed by the agents.md CLI. +- `.agents/skills/{lfx-install,lfx-skills-doctor,lfx-skills-helper,lfx-new-skill}/` — committed repo-local helpers for agents.md-compatible tools working inside this source repo. The CLI installs only `lfx-skills-doctor` and `lfx-skills-helper` from here outside the repo. +- `.claude-plugin/plugin.json` — Claude Code plugin manifest. It exposes `./skills/` as the runtime suite and intentionally does not include repo/install/meta helpers. +- `.claude/skills/{lfx-install,lfx-skills-doctor,lfx-skills-helper,lfx-new-skill}/` — committed repo-local helpers for Claude Code working inside this source repo. These are not part of the distributed plugin. - `.claude-plugin/marketplace.json` — Claude Code marketplace manifest. It lists the `lfx-skills` plugin and points its source at `"./"`. - `install.sh` — thin shim that execs `cli/lfx-skills install "$@"`. - `~/.lfx-skills/config.json` — agents.md install manifest written by the CLI (not in this repo). New installs do not record a platform. @@ -52,7 +54,7 @@ The CLI itself is at `cli/lfx-skills`. Run `cli/lfx-skills help` for the full co - One skill change or a batch of skill changes can ship in the same release. - Claude Code plugin changes must bump `.claude-plugin/plugin.json` `version`; otherwise Claude Code will keep using the cached plugin version. -- New user-facing skills that should be available through the Claude Code plugin must also be added to `.claude-plugin/plugin.json` `skills`. +- New LFX Skills suite skills go under `skills/`; repo/install/meta helpers for this source repo go under `.agents/skills/` and `.claude/skills/`. - The marketplace follows the LFX Skills default branch and uses `"./"` as the local plugin source. Version bump guidelines: diff --git a/README.md b/README.md index 2ef2167..cedbdd8 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ After installation, start with the `lfx` skill. In Claude Code that command is n The marketplace metadata lives in `.claude-plugin/marketplace.json` in LFX Skills. It lists the local plugin source as `"./"`; the Claude plugin version is tracked in `.claude-plugin/plugin.json`. -The Claude Code plugin is skills-only: it exposes the runtime skills listed in `.claude-plugin/plugin.json` and does not install or expose the CLI. +The Claude Code plugin is skills-only: it exposes the runtime LFX Skills suite from `skills/` and does not install or expose the CLI. If you previously installed LFX Skills into Claude Code with symlinks: @@ -43,14 +43,14 @@ git clone https://github.com/linuxfoundation/lfx-skills.git cd lfx-skills ``` -Start your coding agent in the cloned repo and ask it to set up LFX Skills. This repo ships four bootstrap helper skills out of the box through committed `.agents/skills/` and `.claude/skills/` wrappers, so agents can use them inside this repo without running the installer first: +Start your coding agent in the cloned repo and ask it to set up LFX Skills. This repo ships four local helper skills out of the box through committed `.agents/skills/` and `.claude/skills/` files, so agents can work on and contribute to this repository without running the installer first: - `lfx-install` — guided first-time setup -- `lfx-doctor` — install health checks and repair guidance +- `lfx-skills-doctor` — install health checks and repair guidance - `lfx-skills-helper` — list, update, uninstall, and inspect the setup - `lfx-new-skill` — scaffold a new skill in this repo -The canonical source for those helpers still lives under `skills/`; the `.agents/skills/` and `.claude/skills/` files are repo-local bootstrap wrappers. +These helpers are local to this source repo. They are not part of the published LFX Skills suite. If you prefer to run the installer manually: @@ -59,7 +59,7 @@ If you prefer to run the installer manually: ``` The CLI installs skill symlinks into agents.md skill directories outside this repo and records the install in `~/.lfx-skills/config.json`. -agents.md installs include the 15 runtime skills plus `/lfx-doctor` and `/lfx-skills-helper`. `/lfx-install` and `/lfx-new-skill` stay clone-only. +agents.md installs include the runtime suite from `skills/` plus `/lfx-skills-doctor` and `/lfx-skills-helper` from `.agents/skills/`. `/lfx-install` and `/lfx-new-skill` stay local to this clone. After setup: @@ -87,11 +87,11 @@ Use `/lfx-new-skill` whether you are starting from an idea or from a fully writt The authoring helper keeps Claude Code plugin testing separate from agents.md testing. For Claude Code, it points you at plugin validation and local `--plugin-dir` testing. For agents.md-compatible tools, it points you at the CLI update flow. It also offers to help prepare a signed commit with `git commit -s -S`. -New user-facing skills that should be available through the Claude Code plugin must be added to the `skills` allowlist in `.claude-plugin/plugin.json`. Creating `skills/lfx-<name>/SKILL.md` is not enough for Claude plugin users; the plugin exposes only the skill paths listed in that manifest. +All new LFX Skills suite skills go under `skills/`. Note that Repo/install/meta helpers that are not part of the LFX Skills suite, i.e. used only for working on this repository go under `.agents/skills/` and `.claude/skills/`. ## Prerequisites -- An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. See [docs/overview.md](docs/overview.md) for details. +- An AI coding assistant that supports skill-based workflows. Claude Code uses the plugin path; Codex, Gemini CLI, OpenCode, and similar tools use the CLI installer. - Access to LFX repositories (for the skills to operate on) - **Optional: `LFX_DEV_ROOT`** — environment variable pointing to the directory where you keep your LFX repo clones. Defaults to `~/lf/`. The CLI records the chosen path in `~/.lfx-skills/dev-root` so skills can discover local repos without shell rc edits. Skills that use this will prompt the user to either set it or use default. @@ -115,7 +115,12 @@ Restart your AI coding assistant (or open a new session) in any LFX repo and typ /lfx-intercom /lfx-snowflake-access /lfx-cdp-snowflake-connectors -/lfx-doctor ← health check + auto-fix for the install +``` + +agents.md CLI installs also include: + +``` +/lfx-skills-doctor ← health check + auto-fix for the install /lfx-skills-helper ← manage what's installed where (install/uninstall/update/list) ``` @@ -125,10 +130,7 @@ Skill changes can be shipped one at a time or batched together. For Claude Code The marketplace follows the LFX Skills default branch. Because `.claude-plugin/plugin.json` defines `version`, Claude Code uses that value for cache and update detection. If skill content changes but the plugin version stays the same, Claude Code will keep using the already cached plugin version. -For new Claude-facing skills, update both parts of `.claude-plugin/plugin.json`: - -- Add the new skill path to the `skills` allowlist, for example `"./skills/lfx-example/"`. -- Bump `version` using the patch/minor/major guidance below. +For Claude Code users to receive suite changes, bump `version` using the patch/minor/major guidance below. ### Version bump guidelines @@ -171,34 +173,6 @@ For agents.md-compatible installs: lfx-skills doctor ``` -- If `lfx-skills` is not on `PATH`, run the CLI from the clone: - - ```bash - /path/to/lfx-skills/cli/lfx-skills update --pull - /path/to/lfx-skills/cli/lfx-skills doctor - ``` - -To remove old Claude symlink installs from before the plugin pivot: - -- Use the legacy cleanup mode. -- It removes only LFX Skills-owned legacy Claude symlinks. -- It does not remove the Claude Code plugin. - - ```bash - lfx-skills uninstall --legacy-claude-only - ``` - -To remove the whole agents.md installation: - -- Removes agents.md skill symlinks. -- Removes the `lfx-skills` CLI symlink. -- Removes `~/.lfx-skills` config for this install. -- Also removes any legacy Claude symlinks owned by this clone. - - ```bash - lfx-skills uninstall --all - ``` - ## Architecture The skills form a layered system where each skill has a clear responsibility and mode of operation: @@ -589,18 +563,14 @@ An **interactive setup guide** that walks through environment configuration step ├── lfx-git-setup/ # DCO sign-off and GPG-signed commit setup ├── lfx-intercom/ # Intercom integration — add or fix ├── lfx-snowflake-access/ # Request Snowflake access via Terraform PR - ├── lfx-cdp-snowflake-connectors/# Snowflake connector scaffolding for CDP - ├── lfx-doctor/ # Install health checks and repair guidance - ├── lfx-skills-helper/ # CLI management front-end - ├── lfx-install/ # CLI install guide for agents.md tools - └── lfx-new-skill/ # Contributor scaffolder -├── .agents/skills/ # Repo-local bootstrap wrappers for agents.md tools -│ ├── lfx-doctor/ + └── lfx-cdp-snowflake-connectors/# Snowflake connector scaffolding for CDP +├── .agents/skills/ # Repo-local helpers for agents.md tools +│ ├── lfx-skills-doctor/ │ ├── lfx-skills-helper/ │ ├── lfx-install/ │ └── lfx-new-skill/ -├── .claude/skills/ # Repo-local bootstrap wrappers for Claude Code -│ ├── lfx-doctor/ +├── .claude/skills/ # Repo-local helpers for Claude Code +│ ├── lfx-skills-doctor/ │ ├── lfx-skills-helper/ │ ├── lfx-install/ │ └── lfx-new-skill/ diff --git a/cli/lfx-skills b/cli/lfx-skills index bec3a02..e455fad 100755 --- a/cli/lfx-skills +++ b/cli/lfx-skills @@ -331,13 +331,15 @@ cmd_info() { require_jq local skill="${1:-}" [ -z "$skill" ] && ui_die "Usage: lfx-skills info SKILL" - local skill_md="$CLONE_ROOT/skills/$skill/SKILL.md" + local skill_source skill_md + skill_source="$(symlinks_skill_source "$CLONE_ROOT" "$skill" 2>/dev/null || true)" + skill_md="$skill_source/SKILL.md" if [ ! -f "$skill_md" ]; then - ui_error "No SKILL.md for \`$skill\` in $CLONE_ROOT/skills" + ui_error "No SKILL.md for \`$skill\` in $CLONE_ROOT/skills or $CLONE_ROOT/.agents/skills" exit 1 fi ui_section "$skill" - printf 'Source: %s\n' "$CLONE_ROOT/skills/$skill" + printf 'Source: %s\n' "$skill_source" printf '\n' ui_bold "Frontmatter:" awk 'NR==1 && /^---$/ {start=1; next} start && /^---$/ {exit} start {print " " $0}' "$skill_md" diff --git a/docs/overview.md b/docs/overview.md deleted file mode 100644 index 6087f2e..0000000 --- a/docs/overview.md +++ /dev/null @@ -1,180 +0,0 @@ -<!-- Copyright The Linux Foundation and each contributor to LFX. --> -<!-- SPDX-License-Identifier: MIT --> - -# LFX Skills Distribution - -LFX Skills has two supported installation paths: - -- **Claude Code:** use the Claude Code plugin. -- **agents.md-compatible tools:** use the LFX Skills clone plus the `lfx-skills` CLI installer and agent first usage. - -Start with the `lfx` skill in either setup. It is the plain-language entry point and routes users to the right workflow. - -## Claude Code Plugin - -Claude Code users should install LFX Skills from the LFX plugin marketplace: - -```text -/plugin marketplace add linuxfoundation/lfx-skills -/plugin install lfx-skills@lfx-skills -``` - -After installation, start with: - -```text -/lfx-skills:lfx -``` - -The Claude Code plugin is only for Claude Code. It does not install the CLI and does not use the agents.md installer. - -## LFX Plugin Marketplace - -The marketplace lives in LFX Skills: - -```text -linuxfoundation/lfx-skills -``` - -The marketplace publishes the `lfx-skills` Claude Code plugin. Its source is `"./"`, so the plugin is loaded from the LFX Skills repo root when the marketplace is cloned. - -LFX Skills also owns the plugin manifest. The Claude plugin version is tracked in `.claude-plugin/plugin.json`. - -The `skills` array in `.claude-plugin/plugin.json` is the Claude plugin allowlist. New user-facing skills are not available through the plugin just because they exist under `skills/`; they must be added to that allowlist and the plugin version must be bumped. - -## Legacy Claude Symlink Cleanup - -Before the plugin split, some local setups may have installed LFX Skills into Claude Code with symlinks. Those installs should be removed before using the plugin. - -Recommended flow: - -```bash -git clone https://github.com/linuxfoundation/lfx-skills.git -cd lfx-skills -``` - -Start your coding agent in the cloned LFX Skills directory and ask: - -```text -Uninstall the legacy Claude setup for LFX Skills. -``` - -Manual fallback: - -```bash -./cli/lfx-skills uninstall --legacy-claude-only -``` - -The cleanup only removes legacy Claude links owned by the local LFX Skills clone. It does not remove unrelated Claude skills or unrelated files. - -## agents.md-Compatible Tools - -Codex, Gemini CLI, OpenCode, and similar tools use the agents.md path. - -Clone LFX Skills: - -```bash -git clone https://github.com/linuxfoundation/lfx-skills.git -cd lfx-skills -``` - -Then start your coding agent in the cloned LFX Skills directory and ask it to set up LFX Skills. - -LFX Skills includes four helper skills for this flow. They are available out of the box inside the cloned repo through committed `.agents/skills/` and `.claude/skills/` bootstrap wrappers, so a coding agent can use them before anything is installed: - -- `lfx-install` — guided first-time setup -- `lfx-doctor` — install health checks and repair guidance -- `lfx-skills-helper` — list, update, uninstall, and inspect the setup -- `lfx-new-skill` — scaffold a new LFX skill - -The canonical helper skill bodies live under `skills/`; the repo-local `.agents/skills/` and `.claude/skills/` files are lightweight wrappers for bootstrapping work in this repository. - -Manual fallback: - -```bash -./install.sh -``` - -After installation, restart your coding agent, open any LFX repo, and start with: - -```text -/lfx -``` - -The CLI installer is only for agents.md-compatible tools. It installs the skills into agents.md skill locations, keeps track of what it owns, and avoids editing shell startup files. - -## Updating - -Claude Code users update from Claude: - -```text -/plugin marketplace update lfx-skills -/plugin update lfx-skills@lfx-skills -``` - -Auto-updating can also be enabled in Claude Code. - -agents.md-compatible users can ask their coding agent: - -```text -Update LFX Skills and run the doctor. -``` - -Manual fallback: - -```bash -lfx-skills update --pull -lfx-skills doctor -``` - -If `lfx-skills` is not on `PATH`, run it from the local LFX Skills clone: - -```bash -/path/to/lfx-skills/cli/lfx-skills update --pull -/path/to/lfx-skills/cli/lfx-skills doctor -``` - -## Releases - -Skill versions use SemVer in `.claude-plugin/plugin.json`. - -Version bump guide: - -| Change type | Version component | -| ----------------------------------------------------------------------------------------------- | ----------------- | -| Docs, prompt wording, installer fixes, small bug fixes | **patch** | -| New skills or substantial skill behavior updates | **minor** | -| Breaking command names, plugin name changes, removing or renaming skills, install layout breaks | **major** | - -For Claude Code users to receive plugin changes, update `.claude-plugin/plugin.json` before merging or pushing the change to `main`. Because the plugin manifest defines `version`, Claude Code uses that value for cache and update detection. If skill content changes but the plugin version stays the same, Claude Code will keep using the already cached plugin version. - -For new Claude-facing skills, update both the `skills` allowlist and `version` in `.claude-plugin/plugin.json`. - -This follows Anthropic's Claude Code marketplace guidance: - -- [Create and distribute a plugin marketplace](https://code.claude.com/docs/en/plugin-marketplaces) -- [Plugins reference](https://code.claude.com/docs/en/plugins-reference) - -Commit the version bump with the skill changes, using DCO and cryptographic signing: - -```bash -git add .claude-plugin/plugin.json skills/<changed-skill> -git commit -s -S -m "feat: update lfx skills plugin" -``` - -After the change is on `main`, Claude Code users update the marketplace and plugin from Claude Code. - -## Removing agents.md Installs - -To remove the complete agents.md installation, CLI symlink, config, and any legacy Claude symlinks owned by the local LFX Skills clone: - -```bash -lfx-skills uninstall --all -``` - -## Other Tools - -For tools that do not support Claude plugins or agents.md skill directories directly: - -1. Clone LFX Skills. -2. Point the tool at the `SKILL.md` files under `skills/`. -3. Use [tool-mapping.md](tool-mapping.md) to translate tool names to the closest available tools on that platform. diff --git a/lib/doctor.sh b/lib/doctor.sh index f9a3fc2..fa3e7b8 100644 --- a/lib/doctor.sh +++ b/lib/doctor.sh @@ -52,7 +52,7 @@ check_symlinks() { if [ ! -f "$skill_md_target" ]; then # Not fixable by the CLI: the symlink itself is correct; the source skill # has no SKILL.md, which is a content gap, not an install issue. The Phase - # 4 /lfx-doctor skill can still act on this (e.g. offer to scaffold one). + # /lfx-skills-doctor can still act on this (e.g. offer to scaffold one). _emit warn symlink-no-skillmd symlinks \ "Symlink target missing SKILL.md: $(basename "$link")" \ "$skill_md_target does not exist" no @@ -212,12 +212,12 @@ check_routing() { done <<EOF $routed EOF - # Inverse: every USER-FACING skill mentioned in /lfx? + # Inverse: every runtime/user-facing skill mentioned in /lfx? # Skip /lfx itself and skills that intentionally don't belong in the user-task # router: internal builders only invoked by /lfx-coordinator, and management # surfaces (doctor / skills-helper) which the user reaches by name, not via # plain-language routing. - local routing_exempt="lfx lfx-backend-builder lfx-ui-builder lfx-doctor lfx-skills-helper" + local routing_exempt="lfx lfx-backend-builder lfx-ui-builder" local skill skipped while IFS= read -r skill; do [ -z "$skill" ] && continue @@ -231,7 +231,7 @@ EOF "/lfx routing table does not mention /$skill" \ "Add an entry for /$skill in $lfx_md" no fi - done < <(symlinks_eligible_skills "$clone") + done < <(symlinks_runtime_skills "$clone") } # ─── Category 6: License headers ───────────────────────────────────────── diff --git a/lib/symlinks.sh b/lib/symlinks.sh index 8e9c412..56805bb 100644 --- a/lib/symlinks.sh +++ b/lib/symlinks.sh @@ -4,33 +4,58 @@ # Symlink create/remove logic for cli/lfx-skills. Sourced; do not execute directly. # 3-way create logic lifted from install.sh and battle-tested. -# Skills that should NEVER be installed to user-level or per-repo targets by -# the CLI. These are authoring/install helpers for this clone, not runtime LFX -# workflow skills. -SYMLINKS_CLONE_ONLY="lfx-install lfx-new-skill" +# agents.md helper skills that should be installed alongside the runtime suite. +# These live under .agents/skills because they are install/manage helpers for +# this repo and are not part of the published lfx-skills runtime suite. +SYMLINKS_AGENTS_META_INSTALL="lfx-skills-doctor lfx-skills-helper" -# symlinks_eligible_skills CLONE → echo each installable skill directory name (one per line). -# A skill is eligible if: -# - basename matches lfx* (catches lfx + lfx-* variants) -# - directory contains SKILL.md -# - basename is NOT in SYMLINKS_CLONE_ONLY -symlinks_eligible_skills() { +# symlinks_runtime_skills CLONE → echo each runtime skill directory name. +# Runtime/user-facing skills all live under skills/. Anything placed there is +# part of the LFX Skills suite and is installed for agents.md and exposed by +# the Claude Code plugin. +symlinks_runtime_skills() { local clone="$1" local skills_dir="$clone/skills" - local skill_path skill_name excluded + local skill_path skill_name for skill_path in "$skills_dir"/lfx*/; do [ -d "$skill_path" ] || continue [ -f "${skill_path}SKILL.md" ] || continue skill_name="$(basename "${skill_path%/}")" - excluded=0 - for ex in $SYMLINKS_CLONE_ONLY; do - if [ "$skill_name" = "$ex" ]; then excluded=1; break; fi - done - [ "$excluded" -eq 1 ] && continue printf '%s\n' "$skill_name" done } +# symlinks_agents_meta_skills CLONE → echo installable agents.md helper skills. +# Only doctor/helper are installed outside this repo. lfx-install and +# lfx-new-skill stay repo-local contributor helpers. +symlinks_agents_meta_skills() { + local clone="$1" + local skill + for skill in $SYMLINKS_AGENTS_META_INSTALL; do + [ -f "$clone/.agents/skills/$skill/SKILL.md" ] || continue + printf '%s\n' "$skill" + done +} + +# symlinks_eligible_skills CLONE → echo each agents.md-installable skill name. +symlinks_eligible_skills() { + local clone="$1" + symlinks_runtime_skills "$clone" + symlinks_agents_meta_skills "$clone" +} + +# symlinks_skill_source CLONE SKILL → echo the source directory for SKILL. +symlinks_skill_source() { + local clone="$1" skill="$2" + if [ -f "$clone/skills/$skill/SKILL.md" ]; then + printf '%s/skills/%s\n' "$clone" "$skill" + elif [ -f "$clone/.agents/skills/$skill/SKILL.md" ]; then + printf '%s/.agents/skills/%s\n' "$clone" "$skill" + else + return 1 + fi +} + # symlinks_create_one SOURCE TARGET [PREVIOUS_SOURCE] → create or update one symlink. # Echoes one of: installed | updated | skipped | failed # Stderr gets a one-line human-readable note. @@ -91,7 +116,11 @@ symlinks_install_all() { while IFS= read -r skill; do [ -z "$skill" ] && continue n_total=$((n_total + 1)) - source="$clone/skills/$skill" + source="$(symlinks_skill_source "$clone" "$skill")" || { + ui_warn " skipped $skill (source skill missing)" + n_skipped=$((n_skipped + 1)) + continue + } target="$target_dir/$skill" local previous_source="" if config_exists; then diff --git a/skills/lfx-install/SKILL.md b/skills/lfx-install/SKILL.md deleted file mode 100644 index b15b05c..0000000 --- a/skills/lfx-install/SKILL.md +++ /dev/null @@ -1,183 +0,0 @@ ---- -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -name: lfx-install -description: > - Install or set up LFX Skills for agents.md-compatible tools via the - lfx-skills CLI, and point Claude Code-only users to the Claude plugin path. - Walks through the choices in plain language: where their LFX repos live, - scope, agents config dirs, then runs the installer and verifies. Use - whenever the user says "I just cloned this — what now?", "set up lfx skills", - "install lfx skills", "I'm new to lfx-skills", or "first-time setup". -allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion ---- - -<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> - -# LFX Skills Install - -You guide the user through their first-time install. The bash CLI (`cli/lfx-skills install`) can do this non-interactively if every flag is supplied; this skill is the conversational layer that figures out what those flags should be by asking the user, in plain language, one question at a time. - -## Step 1: Verify you're in the clone - -This skill only works inside the `lfx-skills` clone. Verify: - -```bash -[ -x ./cli/lfx-skills ] && echo OK || echo NOT_IN_CLONE -``` - -If `NOT_IN_CLONE`, tell the user: - -> "I only run inside the lfx-skills clone. `cd` to your clone of `linuxfoundation/lfx-skills` and ask again." - -Stop. - -## Step 2: Probe the system - -Run `./cli/lfx-skills` indirectly via its install command's PROBE step — but for the conversation, you also want the data yourself so you can ask informed questions. Do these one-shot probes: - -```bash -# agents.md-compatible CLIs available -for cli in codex gemini opencode; do - command -v "$cli" >/dev/null 2>&1 && echo "$cli" -done - -# Agents config dirs -ls -d "$HOME"/.agents* 2>/dev/null - -# Dev root candidates -for d in "$HOME/lf" "$HOME/lfx" "$HOME/code/lfx" "$HOME/work/lfx"; do - [ -d "$d" ] && echo "$d" -done -``` - -## Step 3: Q1 — Install route - -Use `AskUserQuestion`: - -> "Which setup do you need? (1) agents.md-compatible tool (Codex, Gemini CLI, OpenCode), (2) Claude Code only, (3) both." - -If you detected only one CLI installed, default to it but still confirm. - -If the user picks Claude Code only, explain that Claude installs this repo as a plugin and does not use the CLI symlink installer: - -```text -/plugin marketplace add linuxfoundation/lfx-skills -/plugin install lfx-skills@lfx-skills -``` - -If they are testing from a local checkout, tell them to run Claude Code with the local plugin directory or add the local marketplace per the Claude Code plugin docs. Stop after explaining the plugin path; do not run `./cli/lfx-skills install` for Claude-only installs. - -If the user picks both, use the plugin path for Claude Code and continue with the CLI flow below for agents.md-compatible tools. The CLI itself remains agents.md-only. - -## Step 4: Q2 — Scope - -> "Install scope? (1) **Global** — available in every session of your agents.md-compatible tool. (2) **Per-repo** — only in specific repos (their `.agents/skills/`). (3) **Both** — global plus pin into specific repos." - -This question goes second so subsequent questions can adapt. (Per-repo only? Skip the global config picker. Global only? Skip the repo picker.) - -## Step 5: Q3 — LFX dev root - -Show the candidates you probed with their repo counts: - -``` -Where do you keep your LFX repo clones? - 1. ~/lf (12 lf* repos) - 2. ~/code/lfx (3 lf* repos) - 3. Custom path… -``` - -Use `AskUserQuestion`. If the user already has `LFX_DEV_ROOT` set in the shell, mention it and ask whether to keep it. - -If the chosen path doesn't exist, ask whether to create it (`mkdir -p`). If it has zero `lf*` git repos, warn but proceed: the install will still work; the dev-root-empty doctor warning will trigger until they clone some. - -## Step 6: Q4 — Agents config dirs (only if scope includes Global) - -If you saw multiple `~/.agents*` config dirs, ask which to install into: - -> "I see these agents config dirs: …. Install into all of them, or just one? (defaults to `~/.agents`)" - -Most users have one; this only matters for power users with multiple profiles. - -If scope is Per-repo only, skip this step entirely. - -## Step 7: Q5 — Repos (only if scope includes Per-repo) - -List `lf*` git repos under the chosen dev root: - -> "Which repos? Pick numbers (e.g., `1, 3, 5`), `all`, or `none`." - -If scope is Global only, skip this step entirely. - -## Step 8: Show the plan - -Before running anything, summarise: - -``` -Plan: - Scope: global + per-repo - LFX_DEV_ROOT: ~/lf - Agents dir: ~/.agents - Repos (4): lfx-v2-ui, lfx-v2-meeting-service, lfx-v2-committee-service, lfx-v2-query-service - Skills: 15 user-facing + lfx-doctor + lfx-skills-helper - -Will create approximately N symlinks. Proceed? -``` - -`AskUserQuestion`. If no, stop. - -## Step 9: Run the installer - -Compose the non-interactive flags from the user's answers: - -```bash -./cli/lfx-skills install --yes \ - --scope=<scope> \ - --lfx-dev-root=<path> \ - --agents-config=<dir> \ - --repos=<repo1,repo2,...> -``` - -Stream the output to the user. - -## Step 10: Verify - -Run a quick verification: - -```bash -./cli/lfx-skills doctor -``` - -If errors, walk the user through the auto-fix: - -> "One or more checks failed. Want me to run `/lfx-doctor` to investigate?" - -## Step 11: Confirm the CLI is on PATH - -The installer creates a symlink at a writable PATH dir (`~/.local/bin/lfx-skills`, `~/bin/lfx-skills`, or `/usr/local/bin/lfx-skills`) so the user can type `lfx-skills` from anywhere — no shell rc edit. Read the install output to see which path was used and tell the user: - -> "`lfx-skills` is now at `<reported path>` and ready to use from any terminal." - -If the installer reported it couldn't find a writable PATH dir, share the alias snippet it printed: - -> "I couldn't find a writable PATH dir to drop the CLI into. Add this alias to your shell rc to use `lfx-skills` from anywhere: -> ```bash -> alias lfx-skills='<clone>/cli/lfx-skills' -> ``` -> Or extend PATH to include `~/.local/bin`, `~/bin`, or `/usr/local/bin`." - -## Step 12: Suggest next steps - -> "All set. Next: -> -> 1. Restart your AI coding assistant (or open a new session). -> 2. `cd` to any LFX repo and type `/lfx` — that's your plain-language entry point. -> 3. Run `/lfx-doctor` anytime to recheck the install. -> 4. Run `/lfx-skills-helper` to manage what's installed where." - -## What this skill does NOT do - -- **Edit your shell rc** — never. Always print the snippet for the user to paste. -- **Install only some skills** — v1 always installs everything (the full set per chosen target). Filtering is a v2 idea. -- **Run outside the clone** — bail in Step 1. -- **Re-run silently** — confirm at the plan step before doing anything stateful. diff --git a/skills/lfx-new-skill/SKILL.md b/skills/lfx-new-skill/SKILL.md deleted file mode 100644 index 5ce0ad5..0000000 --- a/skills/lfx-new-skill/SKILL.md +++ /dev/null @@ -1,140 +0,0 @@ ---- -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -name: lfx-new-skill -description: > - Scaffold a new skill in the lfx-skills repo under skills/. Use when someone - wants to add a new lfx skill, provides a complete SKILL.md to import, has an - idea for a skill and wants help drafting it, or asks how to create a skill. -allowed-tools: Bash, Read, Write, Edit, Glob, Grep, AskUserQuestion ---- - -<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> - -# LFX New Skill — Scaffolder - -You help contributors add a skill under `skills/lfx-<name>/`. Read [`references/conventions-quickref.md`](references/conventions-quickref.md) before writing files. - -Use two modes: - -- **Complete skill provided:** if the user pastes or points to a complete `SKILL.md`, use it as the source of truth. Preserve the body. Only normalize frontmatter, license header, directory name, and repo-specific placeholders when needed. -- **Draft from idea:** if the user only has an idea, help write the body. Ask targeted questions about trigger phrases, inputs, steps, tools, outputs, and boundaries. Draft concrete step-by-step instructions from the answers. - -## Step 1: Verify Location - -Run: - -```bash -[ -x ./cli/lfx-skills ] && [ -d ./skills/lfx ] && echo OK || echo NOT_IN_CLONE -``` - -If `NOT_IN_CLONE`, tell the user to `cd` to the `lfx-skills` clone and stop. - -## Step 2: Gather Inputs - -Ask whether the user has a complete `SKILL.md` or wants help drafting one. - -For a complete skill: - -- Read the supplied file/content. -- Extract `name`, `description`, and `allowed-tools` when present. -- Ask only for missing required fields. - -For a draft: - -- Ask for the skill name. It must match `^lfx-[a-z0-9-]+$` and not already exist under `skills/`. -- Ask for a frontmatter description with 3-5 trigger phrases. -- Ask which tools it needs. Default: `Bash, Read, Glob, Grep, AskUserQuestion`. -- Ask what the skill should do, what inputs it needs, what it may inspect or modify, what output it should produce, and what it must not do. - -Ask whether it needs `references/`. - -## Step 3: Write Files - -Create: - -```text -skills/lfx-<name>/SKILL.md -``` - -If requested, also create: - -```text -skills/lfx-<name>/references/.gitkeep -``` - -Ensure: - -- frontmatter starts on line 1 -- license comments are lines 2-3 inside the frontmatter -- `name:` equals the directory basename -- `description:` uses a YAML folded scalar -- `allowed-tools:` is present -- MCP-dependent skills include a `## Prerequisites` section - -If the skill is user-facing, ask whether to add routing guidance to `skills/lfx/SKILL.md`. Internal-only skills can remain unrouted. - -## Step 4: Validate - -Run only the new skill formatting check: - -```bash -./cli/lfx-skills doctor --skill-formatting-only --skill=lfx-<name> -``` - -Fix `frontmatter-*` and `license-missing` issues, then rerun until clean. Do not run the full doctor for this scaffolding check; full doctor includes agents.md install/setup checks. - -## Step 5: Explain Local Testing - -Ask which runtime they want to test: Claude Code plugin, agents.md, or both. - -For Claude Code plugin testing: - -- If the skill should ship in the Claude plugin, add its path to the `skills` allowlist in `.claude-plugin/plugin.json`. Creating `skills/lfx-<name>/SKILL.md` is not enough; Claude plugin users only get skills listed in that manifest. -- If the new skill is user-facing and should be available to Claude plugin users, treat the plugin allowlist entry as required, not optional. -- Give the user this validation command to run from their normal terminal: - - ```bash - cd "<absolute-path-to-lfx-skills>" - claude plugin validate . - ``` - -- Ask which target LFX repo they want to test in. Resolve a repo name under `~/.lfx-skills/dev-root` or use the absolute path they provide. -- Give the user a ready-to-run command: - - ```bash - LFX_SKILLS_CLONE="<absolute-path-to-lfx-skills>" - cd "<resolved-target-repo-path>" - claude --plugin-dir "$LFX_SKILLS_CLONE" - ``` - -- Tell them to run `/lfx-skills:lfx-<name>` in that Claude Code session. - -For agents.md testing: - -- Run `./cli/lfx-skills update`. -- Tell the user to restart their agents.md-compatible coding agent and run `/lfx-<name>`. -- If LFX Skills is not installed for agents.md, point them to `/lfx-install` or `./install.sh`. - -Keep the two paths separate: the CLI is for agents.md installs; Claude Code uses the plugin path. - -## Step 6: Offer Commit And Release Help - -After validation, ask whether the user wants help committing the scaffolded skill. - -If they say yes: - -- Review `git diff` and `git status`. -- Commit only the intended files. -- Use `git commit -s -S`. -- Do not add co-author trailers. -- Do not push unless explicitly asked. - -For plugin versioning, explain that Claude Code picks up plugin changes when `.claude-plugin/plugin.json` gets a new SemVer `version` and the change reaches `main`. Offer to help choose the next patch/minor/major version and include the plugin version bump in the signed commit. If the skill is Claude-facing, include both the `skills` allowlist entry and the version bump in the commit. - -## Boundaries - -- Do not install or repair the user's LFX Skills setup; route that to `/lfx-install` or `/lfx-doctor`. -- Do not use the CLI to install Claude Code skills. -- Do not run Claude Code plugin commands for agents.md testing. -- Do not invent unclear behavior. Ask when scope, inputs, outputs, or safety boundaries are unclear. diff --git a/skills/lfx-skills-helper/SKILL.md b/skills/lfx-skills-helper/SKILL.md deleted file mode 100644 index 8f5a5e5..0000000 --- a/skills/lfx-skills-helper/SKILL.md +++ /dev/null @@ -1,100 +0,0 @@ ---- -# Copyright The Linux Foundation and each contributor to LFX. -# SPDX-License-Identifier: MIT -name: lfx-skills-helper -description: > - Manage the agents.md LFX Skills installation via the lfx-skills CLI: list - what's installed, install or uninstall in this repo or globally, update from - upstream, view or change config, look up what a specific skill does, and - remove legacy Claude symlink installs. Use for "add lfx skills to this repo", - "what's installed", "update lfx skills", "show my lfx setup", "uninstall", - "remove old Claude symlinks", "what does /lfx-foo do". For Claude plugin - installs, explain the plugin marketplace path. For "which skill should I use - for X" or other plain-language routing questions, hand off to /lfx. For health - checks or repair, hand off to /lfx-doctor. -allowed-tools: Bash, Read, Glob, Grep, AskUserQuestion ---- - -<!-- Tool names in this file use Claude Code vocabulary. See docs/tool-mapping.md for other platforms. --> - -# LFX Skills Helper - -You are the conversational front-end for the agents.md-only `lfx-skills` CLI: install, uninstall, update, list, info, config, and legacy Claude symlink cleanup. You are NOT a router. If the user asks "which skill should I use for X" or describes a task ("I need to add a feature", "review my PR"), hand off to `/lfx` — that's the plain-language router. Your job is skill *management*, not skill *discovery*. - -Claude Code is separate: it installs LFX Skills as a plugin with `/plugin marketplace add linuxfoundation/lfx-skills` and `/plugin install lfx-skills@lfx-skills`. Do not use the CLI to install Claude Code skills. The CLI can only remove old Claude symlink installs via `lfx-skills uninstall --legacy-claude-only` or as part of `lfx-skills uninstall --all`. - -## Step 1: Locate the CLI - -Same as `/lfx-doctor`. Try in order: - -1. `command -v lfx-skills` (on PATH). -2. `jq -r .canonical_clone ~/.lfx-skills/config.json 2>/dev/null` then append `/cli/lfx-skills`. -3. `./cli/lfx-skills` if you're inside the lfx-skills clone. -4. Ask the user. - -If none works: the install was never run. Tell the user to clone `linuxfoundation/lfx-skills` and run `./install.sh` (or invoke `/lfx-install` if they're already in the clone). Stop. - -## Step 2: Classify the request - -Decide which surface the request belongs to: - -| User asks about… | Surface | Action | -|--------------------------------------------|------------------|-------------------------------------------| -| Installing, updating, removing, config | This skill | Map to a CLI subcommand (Step 3) | -| What's installed / available / where | This skill | Map to a CLI subcommand (Step 3) | -| What a specific skill does | This skill | `lfx-skills info <name>` | -| **Which skill** to use for a task | Hand off to `/lfx` | Stop here; let the router pick. | -| **Diagnose** a problem / "why isn't X working" | Hand off to `/lfx-doctor` | Stop here. | -| **Scaffold a new skill** | Hand off to `/lfx-new-skill` | Stop here (clone-only). | - -Read `references/intents.md` once for the management-intent → CLI mapping. If the user's phrasing isn't in the table and doesn't fit your job either, say so and suggest the right entry point. - -## Step 3: Run the CLI - -Execute the chosen subcommand. Capture stdout. Use `--json` flags where available (currently `doctor --json`) when you need structured data. - -For commands that change state (`install`, `uninstall`, `update`, `config set`), **always confirm with the user first** via `AskUserQuestion`, showing exactly what you're about to run. The CLI's `--yes` flag is appropriate only after the user has confirmed in chat. - -## Step 4: Format the output - -The CLI output is structured but utilitarian. Reformat it for conversation: - -- **`lfx-skills list`** outputs `scope<TAB>skill<TAB>link`. Render as a friendly grouped list: - - ``` - Globally installed (agents.md): - /lfx - /lfx-coordinator - ... - - Per-repo (lfx-v2-meeting-service): - /lfx-coordinator - /lfx-pr-resolve - ... - ``` - -- **`lfx-skills info <skill>`** outputs frontmatter + install locations. Render the description in prose, list trigger phrases, summarise where it's installed. - -- **`lfx-skills config`** outputs raw JSON. Pretty-print it as a small table: dev root, canonical clone, total symlinks, and CLI symlink. - -- **`lfx-skills repos`** outputs one path per line. Group as a numbered list with sizes/last-modified if helpful. - -## Step 5: Hand-off rules - -If during the conversation the request shifts to something off your turf, hand off cleanly: - -- **Diagnostic questions** ("is my install OK?", "why isn't /lfx-foo working?", "fix my broken symlinks"): hand off to `/lfx-doctor`. -- **Routing / discovery** ("which skill should I use for backend work?", "I want to add a feature, where do I start?"): hand off to `/lfx`. -- **Creating a new skill**: hand off to `/lfx-new-skill` (only available inside the lfx-skills clone). - -## What this skill does NOT do - -- **Pick which skill the user should use**: that's `/lfx`'s job. This skill manages the install; it doesn't recommend skills. -- **Diagnose problems**: hand off to `/lfx-doctor`. -- **Scaffold new skills**: hand off to `/lfx-new-skill`. -- **Install/uninstall without confirmation**: always show the user the exact command first. -- **Invent categorisations**: skills aren't tagged backend/frontend/etc. anywhere in their metadata; don't pretend they are. - -## Reference files - -- [`references/intents.md`](references/intents.md) — management-intent → CLI command mapping.