From f232070239d8b07422b38300365e7e9fac427c4d Mon Sep 17 00:00:00 2001 From: baokhang83 Date: Wed, 15 Jul 2026 06:14:52 +0200 Subject: [PATCH] Add Codex marketplace plugin --- .agents/plugins/marketplace.json | 20 +++ .github/workflows/ci.yml | 13 +- CHANGELOG.md | 9 ++ CONTRIBUTING.md | 10 +- MANIFESTO.md | 41 +++--- README.md | 121 ++++-------------- bin/fluencyloop | 2 +- bin/fluencyloop.cmd | 2 +- claude-skills/feature/SKILL.md | 5 +- claude-skills/plan/SKILL.md | 5 +- install.ps1 | 88 ------------- install.sh | 76 ----------- plugins/fluencyloop/.codex-plugin/plugin.json | 38 ++++++ VERSION => plugins/fluencyloop/VERSION | 0 .../fluencyloop/fluencyloop | 50 +------- .../fluencyloop/fluencyloop.cmd | 0 .../fluencyloop/fluencyloop.ps1 | 32 +---- .../fluencyloop/scripts}/bash/add-decision.sh | 0 .../scripts}/bash/assemble-pr-view.sh | 0 .../fluencyloop/scripts}/bash/calibration.sh | 0 .../fluencyloop/scripts}/bash/check.sh | 0 .../fluencyloop/scripts}/bash/common.sh | 0 .../fluencyloop/scripts}/bash/init.sh | 0 .../fluencyloop/scripts}/bash/migrate.sh | 0 .../fluencyloop/scripts}/bash/new-feature.sh | 0 .../fluencyloop/scripts}/bash/new-plan.sh | 0 .../fluencyloop/scripts}/bash/new-session.sh | 0 .../scripts}/bash/slice-context.sh | 0 .../powershell/PSScriptAnalyzerSettings.psd1 | 0 .../scripts}/powershell/add-decision.ps1 | 0 .../scripts}/powershell/assemble-pr-view.ps1 | 0 .../scripts}/powershell/calibration.ps1 | 0 .../fluencyloop/scripts}/powershell/check.ps1 | 0 .../scripts}/powershell/common.ps1 | 0 .../fluencyloop/scripts}/powershell/init.ps1 | 0 .../scripts}/powershell/migrate.ps1 | 0 .../scripts}/powershell/new-feature.ps1 | 0 .../scripts}/powershell/new-plan.ps1 | 0 .../scripts}/powershell/new-session.ps1 | 0 .../scripts}/powershell/slice-context.ps1 | 0 .../skills}/fluencyloop-backfill/SKILL.md | 7 + .../skills}/fluencyloop-feature/SKILL.md | 12 +- .../skills}/fluencyloop-plan/SKILL.md | 12 +- .../skills}/fluencyloop-review/SKILL.md | 7 + .../fluencyloop/skills}/fluencyloop/SKILL.md | 9 +- .../fluencyloop/templates}/constitution.md | 0 .../fluencyloop/templates}/design.md | 0 .../fluencyloop/templates}/plan.md | 0 .../fluencyloop/templates}/session.md | 0 tests/cli.bats | 9 +- tests/plugin.bats | 62 ++++++--- tests/powershell/_helper.ps1 | 3 +- tests/powershell/cli.Tests.ps1 | 13 +- tests/test_helper.bash | 5 +- 54 files changed, 233 insertions(+), 418 deletions(-) create mode 100644 .agents/plugins/marketplace.json delete mode 100644 install.ps1 delete mode 100755 install.sh create mode 100644 plugins/fluencyloop/.codex-plugin/plugin.json rename VERSION => plugins/fluencyloop/VERSION (100%) rename fluencyloop => plugins/fluencyloop/fluencyloop (58%) rename fluencyloop.cmd => plugins/fluencyloop/fluencyloop.cmd (100%) rename fluencyloop.ps1 => plugins/fluencyloop/fluencyloop.ps1 (63%) rename {scripts => plugins/fluencyloop/scripts}/bash/add-decision.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/assemble-pr-view.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/calibration.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/check.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/common.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/init.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/migrate.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/new-feature.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/new-plan.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/new-session.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/bash/slice-context.sh (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/PSScriptAnalyzerSettings.psd1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/add-decision.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/assemble-pr-view.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/calibration.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/check.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/common.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/init.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/migrate.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/new-feature.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/new-plan.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/new-session.ps1 (100%) rename {scripts => plugins/fluencyloop/scripts}/powershell/slice-context.ps1 (100%) rename {skills => plugins/fluencyloop/skills}/fluencyloop-backfill/SKILL.md (96%) rename {skills => plugins/fluencyloop/skills}/fluencyloop-feature/SKILL.md (97%) rename {skills => plugins/fluencyloop/skills}/fluencyloop-plan/SKILL.md (94%) rename {skills => plugins/fluencyloop/skills}/fluencyloop-review/SKILL.md (93%) rename {skills => plugins/fluencyloop/skills}/fluencyloop/SKILL.md (91%) rename {templates => plugins/fluencyloop/templates}/constitution.md (100%) rename {templates => plugins/fluencyloop/templates}/design.md (100%) rename {templates => plugins/fluencyloop/templates}/plan.md (100%) rename {templates => plugins/fluencyloop/templates}/session.md (100%) diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json new file mode 100644 index 0000000..4ad6713 --- /dev/null +++ b/.agents/plugins/marketplace.json @@ -0,0 +1,20 @@ +{ + "name": "fluencyloop", + "interface": { + "displayName": "FluencyLoop" + }, + "plugins": [ + { + "name": "fluencyloop", + "source": { + "source": "local", + "path": "./plugins/fluencyloop" + }, + "policy": { + "installation": "AVAILABLE", + "authentication": "ON_INSTALL" + }, + "category": "Productivity" + } + ] +} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cfd3d15..8bc7920 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -14,13 +14,13 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - name: ShellCheck (scripts, CLI, installer, plugin launcher) + - name: ShellCheck (bundled runtime and Claude launcher) # shellcheck is preinstalled on the ubuntu runner. -P SCRIPTDIR lets it follow the # `# shellcheck source=common.sh` directives so sourced helpers are analysed too. - run: shellcheck -x -P SCRIPTDIR scripts/bash/*.sh fluencyloop install.sh bin/fluencyloop + run: shellcheck -x -P SCRIPTDIR plugins/fluencyloop/scripts/bash/*.sh plugins/fluencyloop/fluencyloop bin/fluencyloop - name: Ban eval (shell=True-equivalent footgun) run: | - if grep -rnE '(^|[^[:alnum:]_])eval[[:space:]]' scripts/bash fluencyloop install.sh bin/fluencyloop; then + if grep -rnE '(^|[^[:alnum:]_])eval[[:space:]]' plugins/fluencyloop/scripts/bash plugins/fluencyloop/fluencyloop bin/fluencyloop; then echo "::error::eval is banned in FluencyLoop shell — it runs arbitrary strings as code." exit 1 fi @@ -86,11 +86,10 @@ jobs: Install-Module PSScriptAnalyzer -Force -Scope CurrentUser - name: PSScriptAnalyzer run: | - $set = 'scripts/powershell/PSScriptAnalyzerSettings.psd1' + $set = 'plugins/fluencyloop/scripts/powershell/PSScriptAnalyzerSettings.psd1' $r = @() - $r += Invoke-ScriptAnalyzer -Path scripts/powershell -Recurse -Settings $set -Severity Warning, Error - $r += Invoke-ScriptAnalyzer -Path fluencyloop.ps1 -Settings $set -Severity Warning, Error - $r += Invoke-ScriptAnalyzer -Path install.ps1 -Settings $set -Severity Warning, Error + $r += Invoke-ScriptAnalyzer -Path plugins/fluencyloop/scripts/powershell -Recurse -Settings $set -Severity Warning, Error + $r += Invoke-ScriptAnalyzer -Path plugins/fluencyloop/fluencyloop.ps1 -Settings $set -Severity Warning, Error if ($r) { $r | Format-Table -AutoSize | Out-String | Write-Host; throw "PSScriptAnalyzer found $($r.Count) issue(s)" } Write-Host 'PSScriptAnalyzer: clean' - name: Pester diff --git a/CHANGELOG.md b/CHANGELOG.md index f0bd446..649a37a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,15 @@ All notable changes to FluencyLoop are documented here. +## Unreleased + +### Changed + +- FluencyLoop is now distributed as both a Claude Code marketplace plugin and a Codex marketplace + plugin. The canonical runtime lives in `plugins/fluencyloop/`. +- Retired `install.sh`, `install.ps1`, and `fluencyloop self upgrade`; agent plugin managers now + own installation and updates. + ## 0.2.0 ### Added diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6d03e50..c9cba79 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,10 +7,10 @@ actively dogfooded, so expect rough edges and fast-moving changes. The scripts switch branches and write files in your repo, so they're tested. CI runs [`shellcheck`](https://www.shellcheck.net/) + a [`bats`](https://github.com/bats-core/bats-core) -suite on every push and PR; run them locally with `shellcheck -x -P SCRIPTDIR scripts/bash/*.sh` -and `bats tests`. +suite on every push and PR; run them locally with +`shellcheck -x -P SCRIPTDIR plugins/fluencyloop/scripts/bash/*.sh` and `bats tests`. -> **Distribution roadmap:** today it's clone + `install.sh`. Packaging the skills as a Claude -> Code **plugin/marketplace** entry (one-click install for others) and publishing the CLI -> (homebrew/npm) are the next distribution steps — not required to use. +> **Distribution:** FluencyLoop ships through its Claude Code and Codex marketplace plugins. +> The canonical runtime lives in `plugins/fluencyloop/`; do not add a machine-wide installer or +> copy skills into a user's agent directory. diff --git a/MANIFESTO.md b/MANIFESTO.md index 863fcea..31e996a 100644 --- a/MANIFESTO.md +++ b/MANIFESTO.md @@ -126,24 +126,21 @@ file skeletons that a script can create, and does not repeatedly ask a question private preferences. The journal is more reliable because its structure is deterministic; the model spends its tokens on the reasoning a script cannot supply. -## Command surface - -```bash -fluencyloop init # scaffold FluencyLoop in this repository -fluencyloop plan "split the monolith" # optional initiative map -fluencyloop feature "add rate limiting" # branch + design + active feature state -fluencyloop session "wire Redis store" # open a build session -fluencyloop review # assemble the reviewer-facing view -fluencyloop backfill # reconstruct skipped work after merge -fluencyloop check # inspect state and un-journaled drift -fluencyloop calibration init # initialise private teaching calibration -fluencyloop calibration show # inspect the profile -fluencyloop version # print the installed release -fluencyloop self upgrade # refresh an installed copy from its source checkout -``` +## Agent command surface + +FluencyLoop is activated through its agent plugins, not a global installer. Start an explicit +stage command in the agent you use: + +| Goal | Claude Code | Codex | +|---|---|---| +| Plan a large initiative | `/fluencyloop:plan` | `$fluencyloop-plan` | +| Build a feature | `/fluencyloop:feature` | `$fluencyloop-feature` | +| Assemble its PR view | `/fluencyloop:review` | `$fluencyloop-review` | +| Backfill skipped work | `/fluencyloop:backfill` | `$fluencyloop-backfill` | -The CLI is useful on its own. Agent skills supply the interactive loop and are installed through -the relevant agent's distribution mechanism. +The skills invoke the bundled deterministic CLI for `init`, feature/session scaffolding, review +assembly, and private calibration. The CLI is deliberately not a separate machine-wide install: +the agent's plugin manager owns installation and updates. ## What FluencyLoop refuses to be @@ -173,11 +170,11 @@ The design has real costs. ## Status: 0.2.0 -0.2.0 is a working cross-platform loop, not a manifesto-only concept: the Bash and PowerShell -CLIs scaffold and inspect state, construct feature and session records, assemble review context, -maintain private calibration, and support self-upgrade. The next measure of success is not a more -elaborate process; it is whether real contributors stay more fluent with a cost they are willing to -pay. +0.2.0 is a working cross-agent loop, not a manifesto-only concept: its Claude Code and Codex +plugins ship the same deterministic runtime to scaffold and inspect state, construct feature and +session records, assemble review context, and maintain private calibration. The next measure of +success is not a more elaborate process; it is whether real contributors stay more fluent with a +cost they are willing to pay. ## Standing principles diff --git a/README.md b/README.md index c495466..7dd65c2 100644 --- a/README.md +++ b/README.md @@ -41,9 +41,9 @@ principles from real decisions. Nothing gates a merge — work that skips the lo **after** merge by `backfill`. **Requires:** a coding agent ([Claude Code](https://claude.com/claude-code) or -[Codex](https://developers.openai.com/codex/)) plus `git`, and a -shell to run the CLI — `bash` (macOS/Linux/Git Bash/WSL) or **PowerShell 7** (Windows). The -`fluencyloop` CLI runs standalone; the interactive skills need the agent. +[Codex](https://developers.openai.com/codex/)), `git`, and `bash` (macOS/Linux/Git Bash/WSL). +The deterministic CLI is bundled inside the agent plugins; there is no separate machine-wide +installer or project skill vendoring step. ## Teaches to your level @@ -74,91 +74,35 @@ The plugin includes the interactive skills and a bundled `fluencyloop` command f Bash tool. Its skills are intentionally namespaced, for example `/fluencyloop:feature`, so they cannot collide with another plugin's skills. -### Codex or a standalone terminal CLI +### Codex -Clone and install the CLI. Use `--agent codex` to install the current Codex skills as well; omit -that flag (or pass `--no-skills`) when you only want the terminal command. +Install FluencyLoop from the same repository marketplace: ```bash -git clone https://github.com/baokhang83/fluencyloop && cd fluencyloop -./install.sh --agent codex +codex plugin marketplace add baokhang83/fluencyloop +codex plugin add fluencyloop@fluencyloop ``` -This copies the tool into `~/.fluencyloop/lib` and puts `fluencyloop` on your PATH -(`~/.local/bin` — make sure that's on your `$PATH`). - -### Initialise a project - -Inside a repository you want to use FluencyLoop on: - -```bash -fluencyloop init -``` - -This scaffolds that repo's `.fluencyloop/` state (scripts, templates, a constitution stub) and -adds the calibration `.gitignore` guard. - -### On Windows - -Two ways to run it: - -**Native PowerShell CLI** ([PowerShell 7](https://aka.ms/powershell)). Use this when you want the -CLI outside Claude Code. From a clone: - -```powershell -./install.ps1 -NoSkills -``` - -This copies the tool into `%USERPROFILE%\.fluencyloop\lib` and adds it to your user PATH. Then -`fluencyloop ` works from PowerShell **and** cmd (via a `.cmd` shim), with the same verbs -and `--json` output as the bash CLI — `fluencyloop version` and `fluencyloop self upgrade` -included. Use `./install.ps1 -Agent codex` for Codex; install Claude Code through the marketplace -above. - -**Git Bash / WSL.** The bash tool also runs unchanged in **Git Bash** (bundled with -[Git for Windows](https://git-scm.com/download/win)) or **WSL** — use `./install.sh` there. - -Both shells are verified on a Windows CI runner: the bash suite via Git Bash, and the PowerShell -port via `PSScriptAnalyzer` + a `Pester` suite that mirrors the bash tests. +The plugin makes the `$fluencyloop-*` skills available. Its bundled CLI stays private to the +plugin and is run by those skills, so it never needs to be copied onto your PATH. ## Quickstart -From inside the repository you want to work on: - -```bash -# Scaffold the project once. -fluencyloop init +Inside the repository you want to work on, invoke the workflow stage in your installed agent. +The plan and feature stages initialise `.fluencyloop/` automatically when needed. -# Choose one path. For a large initiative, make its architecture and roadmap first. -fluencyloop plan "revamp the checkout flow" - -# For a normal-sized change, start a feature: this creates its branch, design stub, and session journal. -fluencyloop feature "add rate limiting to the API" -``` - -Use **one** of the last two commands: `plan` is optional and is for work too large for one -feature; it creates an architecture + roadmap under `docs/fluencyloop/plans/`. Build each roadmap -item as a feature. `feature` creates the `feature/add-rate-limiting` branch and drops a design doc -and a session journal under `docs/fluencyloop/`. As you build, your agent teaches the *why* of each -real decision at the slice boundary and records it in the journal. When you're ready to open a PR: - -```bash -fluencyloop review -``` - -…assembles the reviewer-facing PR view straight from those journals — no manual linking, -because a feature *is* its branch. Shipped something without the loop? `fluencyloop backfill` -reconstructs the journal after merge. - -### Useful commands +| Goal | Claude Code | Codex | +|------|-------------|-------| +| Plan a large initiative — architecture + roadmap | `/fluencyloop:plan revamp the checkout flow` | `$fluencyloop-plan revamp the checkout flow` | +| Build a normal-sized feature — design → build + teach | `/fluencyloop:feature add rate limiting to the API` | `$fluencyloop-feature add rate limiting to the API` | +| Assemble the feature's PR view | `/fluencyloop:review` | `$fluencyloop-review` | +| Document merged work that skipped the loop | `/fluencyloop:backfill` | `$fluencyloop-backfill` | -```bash -fluencyloop check # inspect the active feature and un-journaled drift -fluencyloop version # print the installed release -fluencyloop self upgrade # refresh an installed copy from its source checkout -fluencyloop calibration show # inspect your private teaching profile -fluencyloop calibration edit # adjust it yourself, if you want to -``` +Use **plan** only for work too large for one feature branch. It creates an architecture + roadmap +under `docs/fluencyloop/plans/`; build each roadmap item as a feature. A feature creates its +branch, design, and session journal under `docs/fluencyloop/`, teaches the *why* of each real +decision at the slice boundary, and records it. Review assembles the reviewer-facing view from +those journals, because a feature *is* its branch. ### Calibration @@ -177,18 +121,6 @@ slice diff rather than whole files, asks only what the calibration profile does records rationale at the moment it is still grounded in the change. See [the efficiency principle](MANIFESTO.md#efficiency-is-a-product-principle). -## Use it - -| Step | Claude Code | Codex | -|------|-------------|-------| -| *(optionally)* Plan a big chunk — architecture + roadmap | `/fluencyloop:plan` | `$fluencyloop-plan` | -| Build a feature — design → build + teach *(per feature)* | `/fluencyloop:feature` | `$fluencyloop-feature` | -| Review — the PR view assembles itself *(per feature)* | `/fluencyloop:review` | `$fluencyloop-review` | -| Backfill — document work that skipped the loop *(post-merge)* | `/fluencyloop:backfill` | `$fluencyloop-backfill` | - -You can also describe the task naturally, but invoking the stage skill explicitly makes the -workflow unmistakable. - The **skills** carry the interactive, calibrated behaviour (teaching at slice boundaries, one-question-at-a-time constitution authoring). The **scripts** carry the deterministic plumbing (branches, files, PR-view assembly) so the journal is reliable rather than @@ -197,16 +129,11 @@ left to the model. ## Layout ``` -install.sh / install.ps1 machine install (bash / PowerShell): CLI on PATH; `--agent codex` adds Codex skills .claude-plugin/ Claude Code plugin manifest + self-hosted marketplace catalog +.agents/plugins/ Codex marketplace catalog claude-skills/ Claude-only aliases: `plan`, `feature`, `review`, `backfill` bin/ the plugin's bundled `fluencyloop` launchers -fluencyloop{,.ps1,.cmd} CLI dispatcher — verbs: init / plan / feature / session / decision / review / check / slice-context / calibration / version / self upgrade -VERSION the current version (0.2.0); `fluencyloop version` prints it -scripts/bash/ deterministic plumbing — bash (the reference implementation) -scripts/powershell/ the same plumbing, ported to PowerShell (Windows-native) -templates/ .fluencyloop state templates (constitution, design, session) -skills/ the interactive skills (activated for Claude Code or Codex) +plugins/fluencyloop/ Codex plugin and canonical runtime: CLI, skills, scripts, templates tests/ bats suite (bash) + tests/powershell Pester suite (mirror) MANIFESTO.md the why ``` diff --git a/bin/fluencyloop b/bin/fluencyloop index c266b71..08527f5 100755 --- a/bin/fluencyloop +++ b/bin/fluencyloop @@ -3,4 +3,4 @@ set -euo pipefail ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" -exec "$ROOT/fluencyloop" "$@" +exec "$ROOT/plugins/fluencyloop/fluencyloop" "$@" diff --git a/bin/fluencyloop.cmd b/bin/fluencyloop.cmd index 62d92e1..f473837 100644 --- a/bin/fluencyloop.cmd +++ b/bin/fluencyloop.cmd @@ -1,2 +1,2 @@ @echo off -pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\fluencyloop.ps1" %* +pwsh -NoProfile -ExecutionPolicy Bypass -File "%~dp0..\plugins\fluencyloop\fluencyloop.ps1" %* diff --git a/claude-skills/feature/SKILL.md b/claude-skills/feature/SKILL.md index 61ea400..8950131 100644 --- a/claude-skills/feature/SKILL.md +++ b/claude-skills/feature/SKILL.md @@ -20,8 +20,9 @@ permission to bury a real question in prose. ## 0. Preconditions -Confirm `.fluencyloop/` exists (`fluencyloop check` reports it). If it does not, tell -the user to run `fluencyloop init` first, and stop. +Confirm `.fluencyloop/` exists (`fluencyloop check --json` reports it). If it is absent, run +`fluencyloop init` yourself, say that the repository is now initialised, and continue. Only stop +if `init` itself fails (for example, because the directory is not a Git repository). **Read the loop state.** If `.fluencyloop/state.json` exists, read it *first* — it is the loop's single source of truth for the active feature (`feature` slug, `branch`, `stage`, `last_session`, diff --git a/claude-skills/plan/SKILL.md b/claude-skills/plan/SKILL.md index 7fd4d1a..2a66638 100644 --- a/claude-skills/plan/SKILL.md +++ b/claude-skills/plan/SKILL.md @@ -20,8 +20,9 @@ create issues, write a settled choice, or advance the workflow until the develop ## 0. Preconditions -Confirm `.fluencyloop/` exists (`fluencyloop check` reports it). If not, tell the user -to run `fluencyloop init` first, and stop. +Confirm `.fluencyloop/` exists (`fluencyloop check --json` reports it). If it is absent, run +`fluencyloop init` yourself, say that the repository is now initialised, and continue. Only stop +if `init` itself fails (for example, because the directory is not a Git repository). **Read the constitution up front** — `docs/fluencyloop/constitution.md`, and **if it's a pointer** (a `Source of truth:` line naming another file, e.g. `.specify/memory/constitution.md`), read diff --git a/install.ps1 b/install.ps1 deleted file mode 100644 index 05fac4d..0000000 --- a/install.ps1 +++ /dev/null @@ -1,88 +0,0 @@ -#!/usr/bin/env pwsh -# install.ps1 — install FluencyLoop onto a Windows machine (the PowerShell twin of install.sh). -# Copies the tool into %USERPROFILE%\.fluencyloop\lib, puts the `fluencyloop` CLI on PATH, installs -# the interactive skills user-wide for the selected coding agent, and records VERSION/SOURCE for self upgrade. -# -# Usage: ./install.ps1 [-Agent claude|codex|both] [-NoSkills] - -Set-StrictMode -Version Latest -$ErrorActionPreference = 'Stop' - -# $IsWindows is a read-only automatic var in PowerShell 7 but undefined in Windows PowerShell 5.x, -# so derive a portable flag (don't assign to the automatic var). -$onWindows = if (Test-Path variable:IsWindows) { $IsWindows } else { $env:OS -eq 'Windows_NT' } - -$SRC = $PSScriptRoot -$installSkills = $true -$skillsAgent = 'claude' -for ($i = 0; $i -lt $args.Count; $i++) { - if ($args[$i] -eq '-NoSkills' -or $args[$i] -eq '--no-skills') { $installSkills = $false } - elseif ($args[$i] -eq '-Agent' -or $args[$i] -eq '--agent') { - $i++; if ($i -ge $args.Count) { throw '-Agent needs claude, codex, or both' }; $skillsAgent = [string]$args[$i] - } -} -if ($skillsAgent -notin @('claude', 'codex', 'both')) { throw "-Agent must be claude, codex, or both (got '$skillsAgent')" } - -$base = if ($env:FLUENCYLOOP_HOME) { $env:FLUENCYLOOP_HOME } else { Join-Path $HOME '.fluencyloop' } -$LIB = Join-Path $base 'lib' - -function WriteTextLf([string]$path, [string]$text) { - $dir = Split-Path -Parent $path - if ($dir -and -not (Test-Path -LiteralPath $dir)) { New-Item -ItemType Directory -Force -Path $dir | Out-Null } - [System.IO.File]::WriteAllText($path, $text, (New-Object System.Text.UTF8Encoding($false))) -} - -# 1. Copy the tool into the lib dir (idempotent: refresh in place). -New-Item -ItemType Directory -Force -Path $LIB | Out-Null -foreach ($d in 'scripts', 'templates', 'skills') { - $t = Join-Path $LIB $d - if (Test-Path -LiteralPath $t) { Remove-Item -Recurse -Force $t } -} -Copy-Item -Recurse -Force -Path (Join-Path $SRC 'scripts') -Destination $LIB -Copy-Item -Recurse -Force -Path (Join-Path $SRC 'templates') -Destination $LIB -Copy-Item -Recurse -Force -Path (Join-Path $SRC 'skills') -Destination $LIB -Copy-Item -Force -Path (Join-Path $SRC 'fluencyloop.ps1') -Destination $LIB -Copy-Item -Force -Path (Join-Path $SRC 'fluencyloop.cmd') -Destination $LIB -Copy-Item -Force -Path (Join-Path $SRC 'VERSION') -Destination $LIB -WriteTextLf (Join-Path $LIB 'SOURCE') "$SRC`n" # where `fluencyloop self upgrade` pulls from - -# 2. Put the CLI on the PATH (Windows: add lib to the user PATH; the .cmd shim resolves the verb). -$pathAdded = $false -if ($onWindows) { - $userPath = [Environment]::GetEnvironmentVariable('Path', 'User'); if (-not $userPath) { $userPath = '' } - if (($userPath -split ';') -notcontains $LIB) { - [Environment]::SetEnvironmentVariable('Path', ($LIB + ';' + $userPath), 'User') - $pathAdded = $true - } -} - -# 3. Install skills for the selected agent. Claude remains the default for backwards compatibility. -$claudeSkillsDest = Join-Path $HOME '.claude/skills' -$codexHome = if ($env:CODEX_HOME) { $env:CODEX_HOME } else { Join-Path $HOME '.codex' } -$codexSkillsDest = Join-Path $codexHome 'skills' -if ($installSkills) { - $skillsDests = switch ($skillsAgent) { - 'claude' { @($claudeSkillsDest) } - 'codex' { @($codexSkillsDest) } - 'both' { @($claudeSkillsDest, $codexSkillsDest) } - } - foreach ($skillsDest in $skillsDests) { - New-Item -ItemType Directory -Force -Path $skillsDest | Out-Null - Copy-Item -Recurse -Force -Path (Join-Path $SRC 'skills/*') -Destination $skillsDest - } -} - -$version = (Get-Content -LiteralPath (Join-Path $SRC 'VERSION') -Raw).Trim() -Write-Output "FluencyLoop $version installed." -Write-Output " lib: $LIB" -Write-Output " cli: $(Join-Path $LIB 'fluencyloop.cmd') (run as: fluencyloop)" -if ($installSkills) { - Write-Output " skills: $skillsAgent ($($skillsDests -join ', '))" -} -Write-Output '' -if ($onWindows) { - if ($pathAdded) { Write-Output "Added $LIB to your user PATH - open a new terminal for fluencyloop to resolve." } -} else { - Write-Output "Non-Windows shell: add $LIB to your PATH to run fluencyloop (or use the bash install.sh)." -} -Write-Output "Next: cd into a project and run 'fluencyloop init'." diff --git a/install.sh b/install.sh deleted file mode 100755 index 0561d78..0000000 --- a/install.sh +++ /dev/null @@ -1,76 +0,0 @@ -#!/usr/bin/env bash -# install.sh — install FluencyLoop onto this machine (run once, from a clone of this repo). -# -# It does three things: -# 1. copies the tool into ~/.fluencyloop/lib (scripts, templates, skills, CLI) -# 2. symlinks the `fluencyloop` CLI onto your PATH (~/.local/bin by default) -# 3. installs the interactive skills user-wide for the selected coding agent -# -# After this, `fluencyloop init` works in any repo, and your coding agent sees the skills -# everywhere. Per-project state still lives in each repo's .fluencyloop/ (via `fluencyloop init`). -# -# Usage: ./install.sh [--bin-dir ] [--agent ] [--no-skills] - -set -euo pipefail - -SRC="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -LIB="${FLUENCYLOOP_HOME:-$HOME/.fluencyloop}/lib" -BIN_DIR="$HOME/.local/bin" -INSTALL_SKILLS=true -SKILLS_AGENT="claude" - -while [ "$#" -gt 0 ]; do - case "$1" in - --bin-dir) shift; BIN_DIR="${1:?--bin-dir needs a value}" ;; - --agent) shift; SKILLS_AGENT="${1:?--agent needs claude, codex, or both}" ;; - --no-skills) INSTALL_SKILLS=false ;; - -h|--help) sed -n '2,16p' "${BASH_SOURCE[0]}" | sed 's/^# \{0,1\}//'; exit 0 ;; - *) echo "Unknown option: $1" >&2; exit 1 ;; - esac - shift -done - -case "$SKILLS_AGENT" in claude|codex|both) ;; *) - echo "--agent must be claude, codex, or both (got '$SKILLS_AGENT')" >&2; exit 1 ;; -esac - -# 1. Copy the tool into the lib dir (idempotent: refresh in place). -mkdir -p "$LIB" -rm -rf "$LIB/scripts" "$LIB/templates" "$LIB/skills" -cp -R "$SRC/scripts" "$SRC/templates" "$SRC/skills" "$LIB/" -cp "$SRC/fluencyloop" "$LIB/fluencyloop" -cp "$SRC/VERSION" "$LIB/VERSION" -# Record where this install came from, so `fluencyloop self upgrade` knows what to pull. -printf '%s\n' "$SRC" > "$LIB/SOURCE" -chmod +x "$LIB/fluencyloop" "$LIB/scripts/bash/"*.sh - -# 2. Put the CLI on the PATH. -mkdir -p "$BIN_DIR" -ln -sf "$LIB/fluencyloop" "$BIN_DIR/fluencyloop" - -# 3. Install skills for the selected coding agent. Claude remains the default for backwards -# compatibility; `--agent both` is explicit for people who use both tools. -CLAUDE_SKILLS_DEST="$HOME/.claude/skills" -CODEX_SKILLS_DEST="${CODEX_HOME:-$HOME/.codex}/skills" -if $INSTALL_SKILLS; then - install_skills() { mkdir -p "$1"; cp -R "$SRC/skills/." "$1/"; } - case "$SKILLS_AGENT" in - claude) SKILLS_DESTS="$CLAUDE_SKILLS_DEST"; install_skills "$CLAUDE_SKILLS_DEST" ;; - codex) SKILLS_DESTS="$CODEX_SKILLS_DEST"; install_skills "$CODEX_SKILLS_DEST" ;; - both) SKILLS_DESTS="$CLAUDE_SKILLS_DEST, $CODEX_SKILLS_DEST"; install_skills "$CLAUDE_SKILLS_DEST"; install_skills "$CODEX_SKILLS_DEST" ;; - esac -fi - -VERSION="$(cat "$SRC/VERSION" 2>/dev/null || echo unknown)" -echo "FluencyLoop $VERSION installed." -echo " lib: $LIB" -echo " cli: $BIN_DIR/fluencyloop -> $LIB/fluencyloop" -if $INSTALL_SKILLS; then - echo " skills: $SKILLS_AGENT ($SKILLS_DESTS)" -fi -echo -if ! command -v fluencyloop >/dev/null 2>&1; then - echo "Add the CLI to your PATH (then restart your shell):" - echo " echo 'export PATH=\"$BIN_DIR:\$PATH\"' >> ~/.zshrc" -fi -echo "Next: cd into a project and run 'fluencyloop init'." diff --git a/plugins/fluencyloop/.codex-plugin/plugin.json b/plugins/fluencyloop/.codex-plugin/plugin.json new file mode 100644 index 0000000..0afb00d --- /dev/null +++ b/plugins/fluencyloop/.codex-plugin/plugin.json @@ -0,0 +1,38 @@ +{ + "name": "fluencyloop", + "version": "0.2.0", + "description": "Stay fluent in the code your AI agent writes with a per-feature design, build, teaching, and review loop.", + "author": { + "name": "baokhang83", + "url": "https://github.com/baokhang83" + }, + "homepage": "https://github.com/baokhang83/fluencyloop#readme", + "repository": "https://github.com/baokhang83/fluencyloop", + "license": "Apache-2.0", + "keywords": [ + "developer-education", + "documentation", + "planning", + "code-review", + "workflow" + ], + "skills": "./skills/", + "interface": { + "displayName": "FluencyLoop", + "shortDescription": "Stay fluent in AI-assisted code changes.", + "longDescription": "Design features before building, teach the important decisions at each slice, journal the rationale, and assemble a reviewer-ready PR view.", + "developerName": "baokhang83", + "category": "Productivity", + "capabilities": [ + "Interactive", + "Read", + "Write" + ], + "websiteURL": "https://github.com/baokhang83/fluencyloop#readme", + "defaultPrompt": [ + "Start a FluencyLoop feature for this change", + "Plan this large initiative with FluencyLoop", + "Prepare this FluencyLoop feature for review" + ] + } +} diff --git a/VERSION b/plugins/fluencyloop/VERSION similarity index 100% rename from VERSION rename to plugins/fluencyloop/VERSION diff --git a/fluencyloop b/plugins/fluencyloop/fluencyloop similarity index 58% rename from fluencyloop rename to plugins/fluencyloop/fluencyloop index 3309b77..3d33ce2 100755 --- a/fluencyloop +++ b/plugins/fluencyloop/fluencyloop @@ -2,8 +2,8 @@ # fluencyloop — the FluencyLoop CLI dispatcher. # # Deterministic commands run directly; the interactive stages (constitution, feature, -# review, backfill) are driven by the skills your coding agent loads from .claude/skills or -# .codex/skills. +# review, backfill) are driven by the skills supplied by the installed Claude Code or Codex +# plugin. # This dispatcher scaffolds a project and exposes the plumbing so the skills — or you — can # call it. # @@ -19,13 +19,11 @@ # fluencyloop calibration your knowledge profile + its ledger # fluencyloop migrate [--dry-run] move docs from .fluencyloop/ to docs/fluencyloop/ # fluencyloop version print the installed version -# fluencyloop self upgrade pull the latest distribution and re-install # fluencyloop help set -euo pipefail -# Resolve this script's real location, following symlinks (so a symlinked `fluencyloop` on the -# PATH still finds its own scripts). Portable: macOS `readlink` has no -f. +# Resolve this script's real location, following symlinks. Portable: macOS `readlink` has no -f. resolve_self() { local src="${BASH_SOURCE[0]}" dir while [ -h "$src" ]; do @@ -37,17 +35,13 @@ resolve_self() { } SELF="$(resolve_self)" -# Version + upgrade-source markers live next to this script: at the repo root when running from a -# checkout, or in the install lib when installed (install.sh copies VERSION and writes SOURCE -# there). SOURCE records the distribution checkout `self upgrade` pulls from. +# Version lives beside the bundled CLI. VERSION_FILE="$SELF/VERSION" -SOURCE_FILE="$SELF/SOURCE" read_version() { [ -f "$1" ] && cat "$1" || echo "unknown"; } -# Find the scripts dir: the distribution checkout / install lib, or the current repo's -# vendored copy after `fluencyloop init`. +# Find the bundled scripts dir, or the current project's vendored copy after `fluencyloop init`. if [ -d "$SELF/scripts/bash" ]; then - BIN="$SELF/scripts/bash" # running from the fluencyloop checkout or install lib + BIN="$SELF/scripts/bash" # running from the bundled plugin runtime elif [ -d "$SELF/scripts" ] && [ -f "$SELF/scripts/common.sh" ]; then BIN="$SELF/scripts" # running from an installed .fluencyloop/ else @@ -73,38 +67,6 @@ case "$cmd" in calibration) exec bash "$BIN/calibration.sh" "$@" ;; migrate) exec bash "$BIN/migrate.sh" "$@" ;; version|--version|-v) read_version "$VERSION_FILE" ;; - self) - action="${1:-}"; shift || true - if [ "$action" != "upgrade" ]; then - echo "Unknown command: fluencyloop self ${action:-}" >&2; usage >&2; exit 1 - fi - # Locate the distribution checkout to pull from: the path install.sh recorded, or this - # script's own dir if we're running straight from a checkout. - if [ -f "$SOURCE_FILE" ]; then - SRC="$(cat "$SOURCE_FILE")" - elif [ -d "$SELF/.git" ]; then - SRC="$SELF" - else - SRC="" - fi - if [ -z "$SRC" ] || [ ! -d "$SRC/.git" ]; then - echo "Can't find a git checkout to upgrade from (source: ${SRC:-none})." >&2 - echo "Re-clone and re-install:" >&2 - echo " git clone https://github.com/baokhang83/fluencyloop && cd fluencyloop && ./install.sh" >&2 - exit 1 - fi - OLD="$(read_version "$VERSION_FILE")" - echo "Upgrading from $SRC ..." - git -C "$SRC" pull --ff-only - NEW="$(read_version "$SRC/VERSION")" - bash "$SRC/install.sh" "$@" - echo - if [ "$OLD" = "$NEW" ]; then - echo "FluencyLoop is up to date ($NEW)." - else - echo "FluencyLoop upgraded: $OLD -> $NEW" - fi - ;; help|-h|--help) usage ;; *) echo "Unknown command: $cmd" >&2; usage >&2; exit 1 ;; esac diff --git a/fluencyloop.cmd b/plugins/fluencyloop/fluencyloop.cmd similarity index 100% rename from fluencyloop.cmd rename to plugins/fluencyloop/fluencyloop.cmd diff --git a/fluencyloop.ps1 b/plugins/fluencyloop/fluencyloop.ps1 similarity index 63% rename from fluencyloop.ps1 rename to plugins/fluencyloop/fluencyloop.ps1 index afd5760..44818ae 100644 --- a/fluencyloop.ps1 +++ b/plugins/fluencyloop/fluencyloop.ps1 @@ -6,14 +6,13 @@ Set-StrictMode -Version Latest $ErrorActionPreference = 'Stop' -$SELF = $PSScriptRoot # this script's dir: the install lib, or the repo/checkout root +$SELF = $PSScriptRoot # this script's bundled plugin directory $VersionFile = Join-Path $SELF 'VERSION' -$SourceFile = Join-Path $SELF 'SOURCE' function ReadVersion([string]$p) { if (Test-Path -LiteralPath $p) { (Get-Content -LiteralPath $p -Raw).Trim() } else { 'unknown' } } -# Find the scripts dir: the checkout / install lib, the installed .fluencyloop/, or the current -# repo's vendored copy after `fluencyloop init`. +# Find the bundled scripts dir, the installed .fluencyloop/ copy, or the current repository's +# vendored copy after `fluencyloop init`. if (Test-Path -LiteralPath (Join-Path $SELF 'scripts/powershell')) { $BIN = Join-Path $SELF 'scripts/powershell' } elseif ((Test-Path -LiteralPath (Join-Path $SELF 'scripts/common.ps1'))) { @@ -27,7 +26,7 @@ $usage = @' fluencyloop — the FluencyLoop CLI dispatcher. Deterministic commands run directly; the interactive stages (constitution, feature, review, -backfill) are driven by the skills your coding agent loads from .claude/skills or .codex/skills. +backfill) are driven by the skills supplied by the installed Claude Code or Codex plugin. Usage: fluencyloop init scaffold .fluencyloop/ state + docs/fluencyloop/ @@ -41,7 +40,6 @@ Usage: fluencyloop calibration your knowledge profile + its ledger fluencyloop migrate [--dry-run] move docs from .fluencyloop/ to docs/fluencyloop/ fluencyloop version print the installed version - fluencyloop self upgrade pull the latest distribution and re-install fluencyloop help '@ @@ -67,28 +65,6 @@ switch -Regex ($cmd) { '^calibration$' { Run 'calibration.ps1' } '^migrate$' { Run 'migrate.ps1' } '^(version|--version|-v)$' { [Console]::Out.Write((ReadVersion $VersionFile) + "`n"); exit 0 } - '^self$' { - $action = if ($rest.Count -ge 1) { [string]$rest[0] } else { '' } - if ($action -ne 'upgrade') { [Console]::Error.WriteLine("Unknown command: fluencyloop self $action"); [Console]::Error.WriteLine($usage); exit 1 } - if (Test-Path -LiteralPath $SourceFile) { $src = (Get-Content -LiteralPath $SourceFile -Raw).Trim() } - elseif (Test-Path -LiteralPath (Join-Path $SELF '.git')) { $src = $SELF } - else { $src = '' } - if (-not $src -or -not (Test-Path -LiteralPath (Join-Path $src '.git'))) { - [Console]::Error.WriteLine("Can't find a git checkout to upgrade from (source: $(if ($src) { $src } else { 'none' })).") - [Console]::Error.WriteLine('Re-clone and re-install:') - [Console]::Error.WriteLine(' git clone https://github.com/baokhang83/fluencyloop; cd fluencyloop; ./install.ps1') - exit 1 - } - $old = ReadVersion $VersionFile - [Console]::Out.Write("Upgrading from $src ...`n") - & git -C $src pull --ff-only - $new = ReadVersion (Join-Path $src 'VERSION') - $after = @(); if ($rest.Count -gt 1) { $after = @($rest[1..($rest.Count - 1)]) } - & (Join-Path $src 'install.ps1') @after - if ($old -eq $new) { [Console]::Out.Write("`nFluencyLoop is up to date ($new).`n") } - else { [Console]::Out.Write("`nFluencyLoop upgraded: $old -> $new`n") } - exit 0 - } '^(help|-h|--help)$' { [Console]::Out.Write($usage + "`n"); exit 0 } default { [Console]::Error.WriteLine("Unknown command: $cmd"); [Console]::Error.WriteLine($usage); exit 1 } } diff --git a/scripts/bash/add-decision.sh b/plugins/fluencyloop/scripts/bash/add-decision.sh similarity index 100% rename from scripts/bash/add-decision.sh rename to plugins/fluencyloop/scripts/bash/add-decision.sh diff --git a/scripts/bash/assemble-pr-view.sh b/plugins/fluencyloop/scripts/bash/assemble-pr-view.sh similarity index 100% rename from scripts/bash/assemble-pr-view.sh rename to plugins/fluencyloop/scripts/bash/assemble-pr-view.sh diff --git a/scripts/bash/calibration.sh b/plugins/fluencyloop/scripts/bash/calibration.sh similarity index 100% rename from scripts/bash/calibration.sh rename to plugins/fluencyloop/scripts/bash/calibration.sh diff --git a/scripts/bash/check.sh b/plugins/fluencyloop/scripts/bash/check.sh similarity index 100% rename from scripts/bash/check.sh rename to plugins/fluencyloop/scripts/bash/check.sh diff --git a/scripts/bash/common.sh b/plugins/fluencyloop/scripts/bash/common.sh similarity index 100% rename from scripts/bash/common.sh rename to plugins/fluencyloop/scripts/bash/common.sh diff --git a/scripts/bash/init.sh b/plugins/fluencyloop/scripts/bash/init.sh similarity index 100% rename from scripts/bash/init.sh rename to plugins/fluencyloop/scripts/bash/init.sh diff --git a/scripts/bash/migrate.sh b/plugins/fluencyloop/scripts/bash/migrate.sh similarity index 100% rename from scripts/bash/migrate.sh rename to plugins/fluencyloop/scripts/bash/migrate.sh diff --git a/scripts/bash/new-feature.sh b/plugins/fluencyloop/scripts/bash/new-feature.sh similarity index 100% rename from scripts/bash/new-feature.sh rename to plugins/fluencyloop/scripts/bash/new-feature.sh diff --git a/scripts/bash/new-plan.sh b/plugins/fluencyloop/scripts/bash/new-plan.sh similarity index 100% rename from scripts/bash/new-plan.sh rename to plugins/fluencyloop/scripts/bash/new-plan.sh diff --git a/scripts/bash/new-session.sh b/plugins/fluencyloop/scripts/bash/new-session.sh similarity index 100% rename from scripts/bash/new-session.sh rename to plugins/fluencyloop/scripts/bash/new-session.sh diff --git a/scripts/bash/slice-context.sh b/plugins/fluencyloop/scripts/bash/slice-context.sh similarity index 100% rename from scripts/bash/slice-context.sh rename to plugins/fluencyloop/scripts/bash/slice-context.sh diff --git a/scripts/powershell/PSScriptAnalyzerSettings.psd1 b/plugins/fluencyloop/scripts/powershell/PSScriptAnalyzerSettings.psd1 similarity index 100% rename from scripts/powershell/PSScriptAnalyzerSettings.psd1 rename to plugins/fluencyloop/scripts/powershell/PSScriptAnalyzerSettings.psd1 diff --git a/scripts/powershell/add-decision.ps1 b/plugins/fluencyloop/scripts/powershell/add-decision.ps1 similarity index 100% rename from scripts/powershell/add-decision.ps1 rename to plugins/fluencyloop/scripts/powershell/add-decision.ps1 diff --git a/scripts/powershell/assemble-pr-view.ps1 b/plugins/fluencyloop/scripts/powershell/assemble-pr-view.ps1 similarity index 100% rename from scripts/powershell/assemble-pr-view.ps1 rename to plugins/fluencyloop/scripts/powershell/assemble-pr-view.ps1 diff --git a/scripts/powershell/calibration.ps1 b/plugins/fluencyloop/scripts/powershell/calibration.ps1 similarity index 100% rename from scripts/powershell/calibration.ps1 rename to plugins/fluencyloop/scripts/powershell/calibration.ps1 diff --git a/scripts/powershell/check.ps1 b/plugins/fluencyloop/scripts/powershell/check.ps1 similarity index 100% rename from scripts/powershell/check.ps1 rename to plugins/fluencyloop/scripts/powershell/check.ps1 diff --git a/scripts/powershell/common.ps1 b/plugins/fluencyloop/scripts/powershell/common.ps1 similarity index 100% rename from scripts/powershell/common.ps1 rename to plugins/fluencyloop/scripts/powershell/common.ps1 diff --git a/scripts/powershell/init.ps1 b/plugins/fluencyloop/scripts/powershell/init.ps1 similarity index 100% rename from scripts/powershell/init.ps1 rename to plugins/fluencyloop/scripts/powershell/init.ps1 diff --git a/scripts/powershell/migrate.ps1 b/plugins/fluencyloop/scripts/powershell/migrate.ps1 similarity index 100% rename from scripts/powershell/migrate.ps1 rename to plugins/fluencyloop/scripts/powershell/migrate.ps1 diff --git a/scripts/powershell/new-feature.ps1 b/plugins/fluencyloop/scripts/powershell/new-feature.ps1 similarity index 100% rename from scripts/powershell/new-feature.ps1 rename to plugins/fluencyloop/scripts/powershell/new-feature.ps1 diff --git a/scripts/powershell/new-plan.ps1 b/plugins/fluencyloop/scripts/powershell/new-plan.ps1 similarity index 100% rename from scripts/powershell/new-plan.ps1 rename to plugins/fluencyloop/scripts/powershell/new-plan.ps1 diff --git a/scripts/powershell/new-session.ps1 b/plugins/fluencyloop/scripts/powershell/new-session.ps1 similarity index 100% rename from scripts/powershell/new-session.ps1 rename to plugins/fluencyloop/scripts/powershell/new-session.ps1 diff --git a/scripts/powershell/slice-context.ps1 b/plugins/fluencyloop/scripts/powershell/slice-context.ps1 similarity index 100% rename from scripts/powershell/slice-context.ps1 rename to plugins/fluencyloop/scripts/powershell/slice-context.ps1 diff --git a/skills/fluencyloop-backfill/SKILL.md b/plugins/fluencyloop/skills/fluencyloop-backfill/SKILL.md similarity index 96% rename from skills/fluencyloop-backfill/SKILL.md rename to plugins/fluencyloop/skills/fluencyloop-backfill/SKILL.md index 6947494..2e8f3b4 100644 --- a/skills/fluencyloop-backfill/SKILL.md +++ b/plugins/fluencyloop/skills/fluencyloop-backfill/SKILL.md @@ -18,6 +18,13 @@ components the work touched — the fluency the missing real-time loop never gav don't just draft and ask "ok?"; you *show them the shapes, rendered,* map each decision onto them, and confirm decision-by-decision. +## Bundled CLI (Codex) + +Before invoking a deterministic command, set `FLUENCYLOOP_SKILL_DIR` to the absolute path of +this loaded `skills/fluencyloop-backfill` directory. The bundled command is then +`"$FLUENCYLOOP_SKILL_DIR/../../fluencyloop"`. Every `fluencyloop …` command below means that +bundled command; do not require a globally installed CLI. + ## Question delivery — preserve the pause For a real answer, choice, or confirmation, use **`AskUserQuestion` in Claude Code**. Codex has diff --git a/skills/fluencyloop-feature/SKILL.md b/plugins/fluencyloop/skills/fluencyloop-feature/SKILL.md similarity index 97% rename from skills/fluencyloop-feature/SKILL.md rename to plugins/fluencyloop/skills/fluencyloop-feature/SKILL.md index 14b635b..ba590d1 100644 --- a/skills/fluencyloop-feature/SKILL.md +++ b/plugins/fluencyloop/skills/fluencyloop-feature/SKILL.md @@ -10,6 +10,13 @@ the design diagrams and the session journals. You will: (1) declare the feature, its design, (3) build it in slices — teaching and journaling one or two real decisions at each slice boundary. Never gate; never lecture. Keep the developer the author. +## Bundled CLI (Codex) + +Before invoking a deterministic command, set `FLUENCYLOOP_SKILL_DIR` to the absolute path of +this loaded `skills/fluencyloop-feature` directory. The bundled command is then +`"$FLUENCYLOOP_SKILL_DIR/../../fluencyloop"`. Every `fluencyloop …` command below means that +bundled command; do not require a globally installed CLI. + ## Question delivery — preserve the pause When this workflow needs a real answer, choice, confirmation, or knowledge probe, use @@ -20,8 +27,9 @@ permission to bury a real question in prose. ## 0. Preconditions -Confirm `.fluencyloop/` exists (`fluencyloop check` reports it). If it does not, tell -the user to run `fluencyloop init` first, and stop. +Confirm `.fluencyloop/` exists (`fluencyloop check --json` reports it). If it is absent, run +`fluencyloop init` yourself, say that the repository is now initialised, and continue. Only stop +if `init` itself fails (for example, because the directory is not a Git repository). **Read the loop state.** If `.fluencyloop/state.json` exists, read it *first* — it is the loop's single source of truth for the active feature (`feature` slug, `branch`, `stage`, `last_session`, diff --git a/skills/fluencyloop-plan/SKILL.md b/plugins/fluencyloop/skills/fluencyloop-plan/SKILL.md similarity index 94% rename from skills/fluencyloop-plan/SKILL.md rename to plugins/fluencyloop/skills/fluencyloop-plan/SKILL.md index 8d8c589..60104eb 100644 --- a/skills/fluencyloop-plan/SKILL.md +++ b/plugins/fluencyloop/skills/fluencyloop-plan/SKILL.md @@ -12,6 +12,13 @@ with a critical path, (5) offer to open GitHub tickets under a milestone, (6) ha off to `fluencyloop-feature`. The plan is a **map you build against, not a spec to ratify** — do not over-invest. Keep the developer the architect. +## Bundled CLI (Codex) + +Before invoking a deterministic command, set `FLUENCYLOOP_SKILL_DIR` to the absolute path of +this loaded `skills/fluencyloop-plan` directory. The bundled command is then +`"$FLUENCYLOOP_SKILL_DIR/../../fluencyloop"`. Every `fluencyloop …` command below means that +bundled command; do not require a globally installed CLI. + ## Question delivery — preserve the pause For a real answer, choice, or confirmation, use **`AskUserQuestion` in Claude Code**. Codex has @@ -20,8 +27,9 @@ create issues, write a settled choice, or advance the workflow until the develop ## 0. Preconditions -Confirm `.fluencyloop/` exists (`fluencyloop check` reports it). If not, tell the user -to run `fluencyloop init` first, and stop. +Confirm `.fluencyloop/` exists (`fluencyloop check --json` reports it). If it is absent, run +`fluencyloop init` yourself, say that the repository is now initialised, and continue. Only stop +if `init` itself fails (for example, because the directory is not a Git repository). **Read the constitution up front** — `docs/fluencyloop/constitution.md`, and **if it's a pointer** (a `Source of truth:` line naming another file, e.g. `.specify/memory/constitution.md`), read diff --git a/skills/fluencyloop-review/SKILL.md b/plugins/fluencyloop/skills/fluencyloop-review/SKILL.md similarity index 93% rename from skills/fluencyloop-review/SKILL.md rename to plugins/fluencyloop/skills/fluencyloop-review/SKILL.md index a35afa0..4e631c5 100644 --- a/skills/fluencyloop-review/SKILL.md +++ b/plugins/fluencyloop/skills/fluencyloop-review/SKILL.md @@ -8,6 +8,13 @@ description: 'FluencyLoop Stage 4. Assemble the reviewer-facing PR view from a f A **feature is a branch**, so the review view assembles itself: no manual linking. You turn the feature's sessions into a summary a reviewer can read to get fluent fast. +## Bundled CLI (Codex) + +Before invoking a deterministic command, set `FLUENCYLOOP_SKILL_DIR` to the absolute path of +this loaded `skills/fluencyloop-review` directory. The bundled command is then +`"$FLUENCYLOOP_SKILL_DIR/../../fluencyloop"`. Every `fluencyloop …` command below means that +bundled command; do not require a globally installed CLI. + ## Question delivery — preserve the pause For a real answer, choice, or confirmation, use **`AskUserQuestion` in Claude Code**. Codex has diff --git a/skills/fluencyloop/SKILL.md b/plugins/fluencyloop/skills/fluencyloop/SKILL.md similarity index 91% rename from skills/fluencyloop/SKILL.md rename to plugins/fluencyloop/skills/fluencyloop/SKILL.md index 9a44d53..092bd6f 100644 --- a/skills/fluencyloop/SKILL.md +++ b/plugins/fluencyloop/skills/fluencyloop/SKILL.md @@ -7,6 +7,13 @@ description: 'FluencyLoop — stay fluent in code as AI writes it. Router/overvi *The code and your fluency in it are produced together, or not at all.* +## Bundled CLI (Codex) + +Before invoking a deterministic command, set `FLUENCYLOOP_SKILL_DIR` to the absolute path of +this loaded `skills/fluencyloop` directory. The bundled command is then +`"$FLUENCYLOOP_SKILL_DIR/../../fluencyloop"`. Every `fluencyloop …` command in this skill means +that bundled command; it is **not** a command the developer must install on their PATH. + ## Question delivery FluencyLoop's stage skills use **`AskUserQuestion` in Claude Code** for genuine prompts. Codex @@ -43,7 +50,7 @@ repeatable stances from real decisions. Same law as the journal and the calibrat | Document work that shipped without the loop | **fluencyloop-backfill** | If the user just says "fluency" or "set up FluencyLoop" and `.fluencyloop/` does not exist yet, -run `fluencyloop init` (it scaffolds the state + an **empty** constitution stub), then go to +run the bundled `fluencyloop init` (it scaffolds the state + an **empty** constitution stub), then go to **fluencyloop-feature** (or **fluencyloop-plan** for a big chunk) — the constitution fills itself in from there. diff --git a/templates/constitution.md b/plugins/fluencyloop/templates/constitution.md similarity index 100% rename from templates/constitution.md rename to plugins/fluencyloop/templates/constitution.md diff --git a/templates/design.md b/plugins/fluencyloop/templates/design.md similarity index 100% rename from templates/design.md rename to plugins/fluencyloop/templates/design.md diff --git a/templates/plan.md b/plugins/fluencyloop/templates/plan.md similarity index 100% rename from templates/plan.md rename to plugins/fluencyloop/templates/plan.md diff --git a/templates/session.md b/plugins/fluencyloop/templates/session.md similarity index 100% rename from templates/session.md rename to plugins/fluencyloop/templates/session.md diff --git a/tests/cli.bats b/tests/cli.bats index 7b31e4b..a7c2b0b 100644 --- a/tests/cli.bats +++ b/tests/cli.bats @@ -1,5 +1,5 @@ #!/usr/bin/env bats -# fluencyloop — the CLI dispatcher: version, help, and safe handling of bad input. +# fluencyloop — the plugin-bundled CLI dispatcher: version, help, and safe handling of bad input. load test_helper @@ -14,7 +14,7 @@ load test_helper [ "$status" -eq 0 ] [[ "$output" == *"feature"* ]] [[ "$output" == *"check"* ]] - [[ "$output" == *"self upgrade"* ]] + [[ "$output" != *"self upgrade"* ]] } @test "an unknown command exits non-zero and prints usage" { @@ -23,11 +23,6 @@ load test_helper [[ "$output" == *"Unknown command"* ]] } -@test "self with a bad subcommand exits non-zero" { - run bash "$DIST/fluencyloop" self bogus - [ "$status" -ne 0 ] -} - @test "check --json is wired through the dispatcher inside a repo" { setup_initialized_repo run bash "$DIST/fluencyloop" check --json diff --git a/tests/plugin.bats b/tests/plugin.bats index 85e77df..50e2228 100644 --- a/tests/plugin.bats +++ b/tests/plugin.bats @@ -1,29 +1,38 @@ #!/usr/bin/env bats -# Claude Code plugin package: metadata stays aligned with the CLI release and the bundled -# launcher can find the distribution scripts after marketplace installation. +# Agent plugin packages: metadata stays aligned with the bundled runtime, and neither agent +# relies on the retired machine-wide installer. load test_helper -@test "Claude plugin manifest and marketplace describe the distributable package" { - run python3 - "$DIST" <<'PY' +@test "Claude Code and Codex marketplace packages describe the same runtime" { + run python3 - "$REPO_ROOT" "$DIST" <<'PY' import json import pathlib import sys root = pathlib.Path(sys.argv[1]) -version = (root / "VERSION").read_text().strip() -plugin = json.loads((root / ".claude-plugin" / "plugin.json").read_text()) -marketplace = json.loads((root / ".claude-plugin" / "marketplace.json").read_text()) - -assert plugin["name"] == "fluencyloop" -assert plugin["version"] == version -assert plugin["license"] == "Apache-2.0" -assert marketplace["name"] == "fluencyloop" -assert len(marketplace["plugins"]) == 1 -entry = marketplace["plugins"][0] -assert entry["name"] == "fluencyloop" -assert entry["source"] == "." -assert entry["version"] == version +dist = pathlib.Path(sys.argv[2]) +version = (dist / "VERSION").read_text().strip() +claude_plugin = json.loads((root / ".claude-plugin" / "plugin.json").read_text()) +claude_marketplace = json.loads((root / ".claude-plugin" / "marketplace.json").read_text()) +codex_plugin = json.loads((dist / ".codex-plugin" / "plugin.json").read_text()) +codex_marketplace = json.loads((root / ".agents" / "plugins" / "marketplace.json").read_text()) + +assert claude_plugin["name"] == codex_plugin["name"] == "fluencyloop" +assert claude_plugin["version"] == codex_plugin["version"] == version +assert claude_plugin["license"] == codex_plugin["license"] == "Apache-2.0" +assert claude_marketplace["name"] == codex_marketplace["name"] == "fluencyloop" + +claude_entry, = claude_marketplace["plugins"] +assert claude_entry["source"] == "." +assert claude_entry["version"] == version +codex_entry, = codex_marketplace["plugins"] +assert codex_entry["name"] == "fluencyloop" +assert codex_entry["source"] == {"source": "local", "path": "./plugins/fluencyloop"} +assert codex_entry["policy"] == {"installation": "AVAILABLE", "authentication": "ON_INSTALL"} +assert codex_entry["category"] == "Productivity" +assert codex_plugin["skills"] == "./skills/" + for alias, source in { "plan": "fluencyloop-plan", "feature": "fluencyloop-feature", @@ -31,20 +40,31 @@ for alias, source in { "backfill": "fluencyloop-backfill", }.items(): alias_text = (root / "claude-skills" / alias / "SKILL.md").read_text() - source_text = (root / "skills" / source / "SKILL.md").read_text() - assert alias_text.replace(f"name: {alias}", f"name: {source}", 1) == source_text, alias -assert marketplace["plugins"][0]["skills"] == [ + source_text = (dist / "skills" / source / "SKILL.md").read_text() + assert f"name: {alias}" in alias_text + assert f"name: {source}" in source_text + assert "## Bundled CLI (Codex)" in source_text +assert claude_entry["skills"] == [ "./claude-skills/plan", "./claude-skills/feature", "./claude-skills/review", "./claude-skills/backfill", ] +assert not (root / "install.sh").exists() +assert not (root / "install.ps1").exists() +assert not (root / "skills").exists() PY [ "$status" -eq 0 ] } @test "Claude plugin launcher runs the bundled CLI" { - run bash "$DIST/bin/fluencyloop" version + run bash "$REPO_ROOT/bin/fluencyloop" version + [ "$status" -eq 0 ] + [ "$output" = "$(cat "$DIST/VERSION")" ] +} + +@test "Codex plugin bundles the CLI beside its skills" { + run bash "$DIST/fluencyloop" version [ "$status" -eq 0 ] [ "$output" = "$(cat "$DIST/VERSION")" ] } diff --git a/tests/powershell/_helper.ps1 b/tests/powershell/_helper.ps1 index 752b5a4..958eac4 100644 --- a/tests/powershell/_helper.ps1 +++ b/tests/powershell/_helper.ps1 @@ -5,7 +5,8 @@ # Scripts are invoked as `pwsh -File` subprocesses (like bats runs bash): they write to OS stdout # via [Console]::Out.Write, which the call operator would not otherwise capture. -$script:Bin = (Resolve-Path "$PSScriptRoot/../../scripts/powershell").Path +$script:Dist = (Resolve-Path "$PSScriptRoot/../../plugins/fluencyloop").Path +$script:Bin = (Resolve-Path "$script:Dist/scripts/powershell").Path $script:PwshExe = (Get-Process -Id $PID).Path if (-not $script:PwshExe) { $script:PwshExe = 'pwsh' } diff --git a/tests/powershell/cli.Tests.ps1 b/tests/powershell/cli.Tests.ps1 index efa13b4..1a37118 100644 --- a/tests/powershell/cli.Tests.ps1 +++ b/tests/powershell/cli.Tests.ps1 @@ -3,8 +3,8 @@ Describe 'fluencyloop.ps1 (dispatcher)' { BeforeAll { . "$PSScriptRoot/_helper.ps1" - $script:Cli = (Resolve-Path "$PSScriptRoot/../../fluencyloop.ps1").Path - $script:Version = (Get-Content -Raw (Resolve-Path "$PSScriptRoot/../../VERSION").Path).Trim() + $script:Cli = (Resolve-Path "$PSScriptRoot/../../plugins/fluencyloop/fluencyloop.ps1").Path + $script:Version = (Get-Content -Raw (Resolve-Path "$PSScriptRoot/../../plugins/fluencyloop/VERSION").Path).Trim() } AfterEach { Set-Location -LiteralPath $PSScriptRoot @@ -16,11 +16,11 @@ Describe 'fluencyloop.ps1 (dispatcher)' { $out.Trim() | Should -Be $script:Version } - It 'help lists the core commands' { + It 'help lists core commands without legacy self upgrade' { $out = (& $script:PwshExe -NoProfile -File $script:Cli 'help' 2>&1 | ForEach-Object { $_.ToString() }) -join "`n" $out | Should -Match 'feature' $out | Should -Match 'check' - $out | Should -Match 'self upgrade' + $out | Should -Not -Match 'self upgrade' } It 'an unknown command exits non-zero and prints usage' { @@ -30,11 +30,6 @@ Describe 'fluencyloop.ps1 (dispatcher)' { $out | Should -Match 'Unknown command' } - It 'self with a bad subcommand exits non-zero' { - & $script:PwshExe -NoProfile -File $script:Cli 'self' 'bogus' *> $null - $LASTEXITCODE | Should -Not -Be 0 - } - It 'check --json is wired through the dispatcher inside a repo' { $script:repo = Initialize-TestRepo $j = (& $script:PwshExe -NoProfile -File $script:Cli 'check' '--json') | ConvertFrom-Json diff --git a/tests/test_helper.bash b/tests/test_helper.bash index 5c29172..458bc89 100644 --- a/tests/test_helper.bash +++ b/tests/test_helper.bash @@ -2,8 +2,9 @@ # Each test runs in a throwaway git repo with an isolated FLUENCYLOOP_HOME, so nothing touches # the developer's real repo, branches, or calibration profile. -# The distribution under test = the repo this tests/ dir lives in. -DIST="$(cd "$BATS_TEST_DIRNAME/.." && pwd)" +# The distribution under test = the Codex plugin package in this repo. +REPO_ROOT="$(cd "$BATS_TEST_DIRNAME/.." && pwd)" +DIST="$REPO_ROOT/plugins/fluencyloop" BIN="$DIST/scripts/bash" # Make a fresh git repo on `main` with one commit. Does NOT run init (some tests want a