feat: rename the project from skilly to panoplie
Existing installs keep working: ~/.skilly, skilly.json, skilly.lock, the catalog .skilly/index.json and the skilly AGENTS.md markers are read and migrated to the new names (see packages/core/src/legacy.ts).
b0cc64e parent: 6fcf0c1 modified
.gitignore +3 -3 | @@ -11,6 +11,6 @@ dist/ | ||
| 11 | 11 | docs/ |
| 12 | 12 | design/ |
| 13 | 13 | |
| 14 | -# Local skilly config | |
| 15 | -skilly.json | |
| 16 | -skilly.lock | |
| 14 | +# Local panoplie config | |
| 15 | +panoplie.json | |
| 16 | +panoplie.lock | |
| @@ -11,6 +11,6 @@ dist/ | |||
| 11 | docs/ | 11 | docs/ |
| 12 | design/ | 12 | design/ |
| 13 | 13 | ||
| 14 | -# Local skilly config | 14 | +# Local panoplie config |
| 15 | -skilly.json | 15 | +panoplie.json |
| 16 | -skilly.lock | 16 | +panoplie.lock |
modified
.rickub/workflows/deploy-site.yml +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -# Builds the showcase site (site/) and uploads it to Bunny CDN, served as https://skilly.eventuallycoding.com. | |
| 1 | +# Builds the showcase site (site/) and uploads it to Bunny CDN, served as https://panoplie.eventuallycoding.com. | |
| 2 | 2 | # Only runs when the site, the screenshots or the favicon it uses, or this file change. |
| 3 | 3 | # |
| 4 | 4 | # The build has no dependencies (plain node), so there is no install step and ubuntu-latest is enough. |
| @@ -1,4 +1,4 @@ | |||
| 1 | -# Builds the showcase site (site/) and uploads it to Bunny CDN, served as https://skilly.eventuallycoding.com. | 1 | +# Builds the showcase site (site/) and uploads it to Bunny CDN, served as https://panoplie.eventuallycoding.com. |
| 2 | # Only runs when the site, the screenshots or the favicon it uses, or this file change. | 2 | # Only runs when the site, the screenshots or the favicon it uses, or this file change. |
| 3 | # | 3 | # |
| 4 | # The build has no dependencies (plain node), so there is no install step and ubuntu-latest is enough. | 4 | # The build has no dependencies (plain node), so there is no install step and ubuntu-latest is enough. |
modified
README.md +35 -35 | @@ -1,8 +1,8 @@ | ||
| 1 | 1 | <p align="center"> |
| 2 | - <img src="packages/ui/public/favicon.svg" alt="skilly" width="96" height="96"> | |
| 2 | + <img src="packages/ui/public/favicon.svg" alt="panoplie" width="96" height="96"> | |
| 3 | 3 | </p> |
| 4 | 4 | |
| 5 | -<h1 align="center">skilly</h1> | |
| 5 | +<h1 align="center">panoplie</h1> | |
| 6 | 6 | |
| 7 | 7 | <p align="center"> |
| 8 | 8 | One place to manage the skills, rules, MCP servers, hooks and subagents of your coding agents,<br> |
| @@ -10,12 +10,12 @@ | ||
| 10 | 10 | </p> |
| 11 | 11 | |
| 12 | 12 | <p align="center"> |
| 13 | - <img src="media/screenshots/project.png" alt="A project in skilly: presets, items and their sync state" width="900"> | |
| 13 | + <img src="media/screenshots/project.png" alt="A project in panoplie: presets, items and their sync state" width="900"> | |
| 14 | 14 | </p> |
| 15 | 15 | |
| 16 | 16 | --- |
| 17 | 17 | |
| 18 | -## Why skilly | |
| 18 | +## Why panoplie | |
| 19 | 19 | |
| 20 | 20 | Coding agents are configured through a growing pile of files: `SKILL.md` folders, rules, `.mcp.json`, `settings.json`, `AGENTS.md`, `config.toml`… Once you use them on more than a couple of projects, three problems show up: |
| 21 | 21 | |
| @@ -23,7 +23,7 @@ Coding agents are configured through a growing pile of files: `SKILL.md` folders | ||
| 23 | 23 | - **Global pollution.** Putting everything in `~/.claude` or `~/.codex` makes every item load in every session of every project. It costs context and triggers skills where they make no sense. |
| 24 | 24 | - **No overview.** Nothing tells you what exists, where it is used, and which copy is the right one. |
| 25 | 25 | |
| 26 | -skilly fixes this with a **catalog** (a git repository holding your items) and a local UI that **writes the right files into each project**, for each agent, and keeps them in sync. | |
| 26 | +panoplie fixes this with a **catalog** (a git repository holding your items) and a local UI that **writes the right files into each project**, for each agent, and keeps them in sync. | |
| 27 | 27 | |
| 28 | 28 | ## How it works |
| 29 | 29 | |
| @@ -38,12 +38,12 @@ skilly fixes this with a **catalog** (a git repository holding your items) and a | ||
| 38 | 38 | acme-shop/ billing-api/ blog/ |
| 39 | 39 | .claude/skills/… .agents/skills/… .claude/skills/… |
| 40 | 40 | .agents/skills/… AGENTS.md (block) .claude/rules/… |
| 41 | - .mcp.json .codex/config.toml skilly.json | |
| 42 | - skilly.json skilly.json | |
| 41 | + .mcp.json .codex/config.toml panoplie.json | |
| 42 | + panoplie.json panoplie.json | |
| 43 | 43 | ``` |
| 44 | 44 | |
| 45 | 45 | - **The catalog is the source of truth.** It is a plain git repository, hosted wherever you like (or nowhere). Sync it across your machines or share it with your team. |
| 46 | -- **Enabling an item writes real files into the project**, where each agent expects them. The project then works without skilly, for you and for anyone who clones it. You commit those files with the project. | |
| 46 | +- **Enabling an item writes real files into the project**, where each agent expects them. The project then works without panoplie, for you and for anyone who clones it. You commit those files with the project. | |
| 47 | 47 | - **Nothing is written until you apply.** Every change goes through a review, and a file edited by hand is never overwritten without asking. |
| 48 | 48 | - **Global stays minimal.** Keep in `~` only what you really use everywhere, and move the rest to the projects that need it. |
| 49 | 49 | |
| @@ -54,7 +54,7 @@ skilly fixes this with a **catalog** (a git repository holding your items) and a | ||
| 54 | 54 | | **Item** | A skill, rule, MCP server, hook or subagent. | |
| 55 | 55 | | **Catalog** | Your library of items and presets, stored in a git repository. | |
| 56 | 56 | | **Preset** | A named bundle of items, for example *nuxt-saas* = Nuxt and Tailwind skills, TypeScript rules and the Nuxt docs MCP server. A preset can include other presets. | |
| 57 | -| **Project** | A code folder you added to skilly. | | |
| 57 | +| **Project** | A code folder you added to panoplie. | | |
| 58 | 58 | | **Global** | Your user-level agent configuration (`~/.claude`, `~/.agents`, `~/.codex`). It loads in every project. | |
| 59 | 59 | | **Automatic / on demand** | Skill mode. *Automatic*: the agent sees the skill and uses it when it thinks it is relevant. *On demand*: the skill is installed but hidden from the agent until you call it yourself (`/name` in Claude Code, `$name` in Codex). Ideal for large families of niche skills you want at hand without paying for them in context. | |
| 60 | 60 | |
| @@ -66,7 +66,7 @@ Pick presets, add or exclude single items, switch skills between automatic and o | ||
| 66 | 66 | |
| 67 | 67 | ### Review before anything is written |
| 68 | 68 | |
| 69 | -skilly computes a plan: files to add, update or remove, for each agent. You can undo any line before applying. When a file already exists or was edited by hand, you choose: **keep the project version**, **take the catalog version**, or **push the change back to the catalog**. You can compare both versions first. | |
| 69 | +panoplie computes a plan: files to add, update or remove, for each agent. You can undo any line before applying. When a file already exists or was edited by hand, you choose: **keep the project version**, **take the catalog version**, or **push the change back to the catalog**. You can compare both versions first. | |
| 70 | 70 | |
| 71 | 71 | <p align="center"> |
| 72 | 72 | <img src="media/screenshots/review.png" alt="Review changes before applying them, with conflict resolution" width="900"> |
| @@ -74,7 +74,7 @@ skilly computes a plan: files to add, update or remove, for each agent. You can | ||
| 74 | 74 | |
| 75 | 75 | ### Clean up Global |
| 76 | 76 | |
| 77 | -skilly lists everything loaded from your home folder and spots the items that are only used in some projects. **Move to projects** copies them to the catalog, enables them where they are needed, then removes them from Global. Nothing is removed from your home folder without an explicit action. | |
| 77 | +panoplie lists everything loaded from your home folder and spots the items that are only used in some projects. **Move to projects** copies them to the catalog, enables them where they are needed, then removes them from Global. Nothing is removed from your home folder without an explicit action. | |
| 78 | 78 | |
| 79 | 79 | <p align="center"> |
| 80 | 80 | <img src="media/screenshots/global.png" alt="Global items loaded in every project, with tidy-up suggestions" width="900"> |
| @@ -118,74 +118,74 @@ Commit, pull and push from the UI, against any remote: GitHub, GitLab, Codeberg, | ||
| 118 | 118 | | Subagents | `.claude/agents/<name>.md` | `.codex/agents/<name>.toml` | |
| 119 | 119 | | Hide a Global skill or MCP server | Yes | Not supported by Codex yet | |
| 120 | 120 | |
| 121 | -skilly also writes two files at the root of each project, meant to be committed: | |
| 121 | +panoplie also writes two files at the root of each project, meant to be committed: | |
| 122 | 122 | |
| 123 | -- **`skilly.json`**: what the project uses (target agents, presets, added and excluded items, on-demand skills, hidden Global items). | |
| 124 | -- **`skilly.lock`**: what skilly installed, with content hashes, so it knows which files it owns and can detect manual edits. | |
| 123 | +- **`panoplie.json`**: what the project uses (target agents, presets, added and excluded items, on-demand skills, hidden Global items). | |
| 124 | +- **`panoplie.lock`**: what panoplie installed, with content hashes, so it knows which files it owns and can detect manual edits. | |
| 125 | 125 | |
| 126 | -Shared files (`.mcp.json`, `.claude/settings.json`, `.codex/config.toml`, `AGENTS.md`) are edited surgically: skilly only touches the keys or the block it owns and leaves the rest as is. | |
| 126 | +Shared files (`.mcp.json`, `.claude/settings.json`, `.codex/config.toml`, `AGENTS.md`) are edited surgically: panoplie only touches the keys or the block it owns and leaves the rest as is. | |
| 127 | 127 | |
| 128 | -skilly also warns you about agent-specific pitfalls: a Codex project that is not trusted (Codex then ignores its `.codex/` folder), an `AGENTS.md` close to the 32 KiB Codex limit, or rules that Claude Code would load twice. | |
| 128 | +panoplie also warns you about agent-specific pitfalls: a Codex project that is not trusted (Codex then ignores its `.codex/` folder), an `AGENTS.md` close to the 32 KiB Codex limit, or rules that Claude Code would load twice. | |
| 129 | 129 | |
| 130 | 130 | `CLAUDE.md` and `AGENTS.md` are shown read-only in each project, with their size. They are specific to each project and never go into the catalog. |
| 131 | 131 | |
| 132 | 132 | ## Install and run |
| 133 | 133 | |
| 134 | -> skilly is in early development and not published on npm yet. Until the first release, run it from source (see below). | |
| 134 | +> panoplie is in early development and not published on npm yet. Until the first release, run it from source (see below). | |
| 135 | 135 | |
| 136 | 136 | Requirements: **Node.js 22+**, **git**, and preferably **Microsoft Edge or Google Chrome** for the app window. |
| 137 | 137 | |
| 138 | 138 | ```sh |
| 139 | -npx @skilly/cli | |
| 139 | +npx @panoplie/cli | |
| 140 | 140 | ``` |
| 141 | 141 | |
| 142 | -skilly starts a local server and opens the UI in an app window (Edge or Chrome in app mode, your default browser otherwise). Closing the window stops skilly. Running the command again while skilly is open brings the existing window back. | |
| 142 | +panoplie starts a local server and opens the UI in an app window (Edge or Chrome in app mode, your default browser otherwise). Closing the window stops panoplie. Running the command again while panoplie is open brings the existing window back. | |
| 143 | 143 | |
| 144 | 144 | | Option | Effect | |
| 145 | 145 | |---|---| |
| 146 | 146 | | `--no-open` | Do not open a window, only print the URL. | |
| 147 | 147 | | `--port <n>` | Use this port instead of the default one (47821, or a free port if taken). | |
| 148 | 148 | |
| 149 | -The first launch walks you through the setup: create a new catalog or clone an existing one from git, check the detected agents, then add your projects (skilly can scan your usual code folders for you). | |
| 149 | +The first launch walks you through the setup: create a new catalog or clone an existing one from git, check the detected agents, then add your projects (panoplie can scan your usual code folders for you). | |
| 150 | 150 | |
| 151 | 151 | ### From source |
| 152 | 152 | |
| 153 | 153 | ```sh |
| 154 | -git clone <repository-url> skilly | |
| 155 | -cd skilly | |
| 154 | +git clone <repository-url> panoplie | |
| 155 | +cd panoplie | |
| 156 | 156 | pnpm install |
| 157 | 157 | pnpm build # builds the UI |
| 158 | -pnpm start # starts skilly and opens the window | |
| 158 | +pnpm start # starts panoplie and opens the window | |
| 159 | 159 | ``` |
| 160 | 160 | |
| 161 | 161 | ## Where your data lives |
| 162 | 162 | |
| 163 | 163 | | What | Where | |
| 164 | 164 | |---|---| |
| 165 | -| Catalog | `~/.skilly/catalog` by default (a git repository, the location can be changed in Settings) | | |
| 166 | -| Machine settings (projects list, preferences, Global configuration) | `~/.skilly/` | | |
| 167 | -| Project configuration | `skilly.json` and `skilly.lock` in each project | | |
| 165 | +| Catalog | `~/.panoplie/catalog` by default (a git repository, the location can be changed in Settings) | | |
| 166 | +| Machine settings (projects list, preferences, Global configuration) | `~/.panoplie/` | | |
| 167 | +| Project configuration | `panoplie.json` and `panoplie.lock` in each project | | |
| 168 | 168 | |
| 169 | -Set `SKILLY_CONFIG_DIR` to use another folder than `~/.skilly`, and `CODEX_HOME` if your Codex home is not `~/.codex`. | |
| 169 | +Set `PANOPLIE_CONFIG_DIR` to use another folder than `~/.panoplie`, and `CODEX_HOME` if your Codex home is not `~/.codex`. | |
| 170 | 170 | |
| 171 | 171 | ## Security and privacy |
| 172 | 172 | |
| 173 | 173 | - **Local only.** The server listens on `127.0.0.1`, rejects other host names, and requires a random token generated at each launch, so other websites open in your browser cannot call it. |
| 174 | 174 | - **No secrets in the catalog.** MCP configurations store the *name* of environment variables, never their values. Credentials found in MCP arguments, URLs or environment when importing are redacted. |
| 175 | -- **Limited writes.** skilly writes only in the projects you added, in the catalog, and in your home folder when you explicitly apply a change to Global. | |
| 176 | -- **No telemetry.** skilly only goes online to fetch the marketplace sources you browse and to sync your catalog with the git remote you configure. | |
| 177 | -- skilly never executes the scripts contained in skills. | |
| 175 | +- **Limited writes.** panoplie writes only in the projects you added, in the catalog, and in your home folder when you explicitly apply a change to Global. | |
| 176 | +- **No telemetry.** panoplie only goes online to fetch the marketplace sources you browse and to sync your catalog with the git remote you configure. | |
| 177 | +- panoplie never executes the scripts contained in skills. | |
| 178 | 178 | |
| 179 | 179 | ## FAQ |
| 180 | 180 | |
| 181 | -**Do my teammates need skilly?** | |
| 182 | -No. The generated files are regular agent files committed in the project. skilly only makes them easier to manage, and it makes catalog sharing easier for those who use it. | |
| 181 | +**Do my teammates need panoplie?** | |
| 182 | +No. The generated files are regular agent files committed in the project. panoplie only makes them easier to manage, and it makes catalog sharing easier for those who use it. | |
| 183 | 183 | |
| 184 | 184 | **What if I edit a generated file by hand?** |
| 185 | -skilly detects it (thanks to `skilly.lock`) and asks what to do on the next apply: keep your version, take the catalog version, or push your change back to the catalog. | |
| 185 | +panoplie detects it (thanks to `panoplie.lock`) and asks what to do on the next apply: keep your version, take the catalog version, or push your change back to the catalog. | |
| 186 | 186 | |
| 187 | 187 | **Why are skills copied twice instead of symlinked?** |
| 188 | -Claude Code reads `.claude/skills` and Codex reads `.agents/skills`. Symlinks are fragile on Windows and do not survive a clone, so skilly writes plain copies and keeps them in sync. | |
| 188 | +Claude Code reads `.claude/skills` and Codex reads `.agents/skills`. Symlinks are fragile on Windows and do not survive a clone, so panoplie writes plain copies and keeps them in sync. | |
| 189 | 189 | |
| 190 | 190 | **Does it work with other agents (Cursor, Copilot, Gemini CLI…)?** |
| 191 | 191 | Not yet. Claude Code and Codex are supported today; more agents are planned. |
| @@ -199,7 +199,7 @@ The repository is a TypeScript monorepo managed with pnpm: | ||
| 199 | 199 | | `packages/core` | Model, inventory scanners, catalog, resolution, rendering per agent, sync plan and apply. No UI dependency. | |
| 200 | 200 | | `packages/server` | Local HTTP API over `core`. | |
| 201 | 201 | | `packages/ui` | Nuxt single-page app served by the server. | |
| 202 | -| `packages/cli` | The `skilly` command: starts the server and opens the window. | | |
| 202 | +| `packages/cli` | The `panoplie` command: starts the server and opens the window. | | |
| 203 | 203 | |
| 204 | 204 | ```sh |
| 205 | 205 | pnpm dev # API on :4300 and the UI with hot reload on :3000 |
| @@ -1,8 +1,8 @@ | |||
| 1 | <p align="center"> | 1 | <p align="center"> |
| 2 | - <img src="packages/ui/public/favicon.svg" alt="skilly" width="96" height="96"> | 2 | + <img src="packages/ui/public/favicon.svg" alt="panoplie" width="96" height="96"> |
| 3 | </p> | 3 | </p> |
| 4 | 4 | ||
| 5 | -<h1 align="center">skilly</h1> | 5 | +<h1 align="center">panoplie</h1> |
| 6 | 6 | ||
| 7 | <p align="center"> | 7 | <p align="center"> |
| 8 | One place to manage the skills, rules, MCP servers, hooks and subagents of your coding agents,<br> | 8 | One place to manage the skills, rules, MCP servers, hooks and subagents of your coding agents,<br> |
| @@ -10,12 +10,12 @@ | |||
| 10 | </p> | 10 | </p> |
| 11 | 11 | ||
| 12 | <p align="center"> | 12 | <p align="center"> |
| 13 | - <img src="media/screenshots/project.png" alt="A project in skilly: presets, items and their sync state" width="900"> | 13 | + <img src="media/screenshots/project.png" alt="A project in panoplie: presets, items and their sync state" width="900"> |
| 14 | </p> | 14 | </p> |
| 15 | 15 | ||
| 16 | --- | 16 | --- |
| 17 | 17 | ||
| 18 | -## Why skilly | 18 | +## Why panoplie |
| 19 | 19 | ||
| 20 | Coding agents are configured through a growing pile of files: `SKILL.md` folders, rules, `.mcp.json`, `settings.json`, `AGENTS.md`, `config.toml`… Once you use them on more than a couple of projects, three problems show up: | 20 | Coding agents are configured through a growing pile of files: `SKILL.md` folders, rules, `.mcp.json`, `settings.json`, `AGENTS.md`, `config.toml`… Once you use them on more than a couple of projects, three problems show up: |
| 21 | 21 | ||
| @@ -23,7 +23,7 @@ Coding agents are configured through a growing pile of files: `SKILL.md` folders | |||
| 23 | - **Global pollution.** Putting everything in `~/.claude` or `~/.codex` makes every item load in every session of every project. It costs context and triggers skills where they make no sense. | 23 | - **Global pollution.** Putting everything in `~/.claude` or `~/.codex` makes every item load in every session of every project. It costs context and triggers skills where they make no sense. |
| 24 | - **No overview.** Nothing tells you what exists, where it is used, and which copy is the right one. | 24 | - **No overview.** Nothing tells you what exists, where it is used, and which copy is the right one. |
| 25 | 25 | ||
| 26 | -skilly fixes this with a **catalog** (a git repository holding your items) and a local UI that **writes the right files into each project**, for each agent, and keeps them in sync. | 26 | +panoplie fixes this with a **catalog** (a git repository holding your items) and a local UI that **writes the right files into each project**, for each agent, and keeps them in sync. |
| 27 | 27 | ||
| 28 | ## How it works | 28 | ## How it works |
| 29 | 29 | ||
| @@ -38,12 +38,12 @@ skilly fixes this with a **catalog** (a git repository holding your items) and a | |||
| 38 | acme-shop/ billing-api/ blog/ | 38 | acme-shop/ billing-api/ blog/ |
| 39 | .claude/skills/… .agents/skills/… .claude/skills/… | 39 | .claude/skills/… .agents/skills/… .claude/skills/… |
| 40 | .agents/skills/… AGENTS.md (block) .claude/rules/… | 40 | .agents/skills/… AGENTS.md (block) .claude/rules/… |
| 41 | - .mcp.json .codex/config.toml skilly.json | 41 | + .mcp.json .codex/config.toml panoplie.json |
| 42 | - skilly.json skilly.json | 42 | + panoplie.json panoplie.json |
| 43 | ``` | 43 | ``` |
| 44 | 44 | ||
| 45 | - **The catalog is the source of truth.** It is a plain git repository, hosted wherever you like (or nowhere). Sync it across your machines or share it with your team. | 45 | - **The catalog is the source of truth.** It is a plain git repository, hosted wherever you like (or nowhere). Sync it across your machines or share it with your team. |
| 46 | -- **Enabling an item writes real files into the project**, where each agent expects them. The project then works without skilly, for you and for anyone who clones it. You commit those files with the project. | 46 | +- **Enabling an item writes real files into the project**, where each agent expects them. The project then works without panoplie, for you and for anyone who clones it. You commit those files with the project. |
| 47 | - **Nothing is written until you apply.** Every change goes through a review, and a file edited by hand is never overwritten without asking. | 47 | - **Nothing is written until you apply.** Every change goes through a review, and a file edited by hand is never overwritten without asking. |
| 48 | - **Global stays minimal.** Keep in `~` only what you really use everywhere, and move the rest to the projects that need it. | 48 | - **Global stays minimal.** Keep in `~` only what you really use everywhere, and move the rest to the projects that need it. |
| 49 | 49 | ||
| @@ -54,7 +54,7 @@ skilly fixes this with a **catalog** (a git repository holding your items) and a | |||
| 54 | | **Item** | A skill, rule, MCP server, hook or subagent. | | 54 | | **Item** | A skill, rule, MCP server, hook or subagent. | |
| 55 | | **Catalog** | Your library of items and presets, stored in a git repository. | | 55 | | **Catalog** | Your library of items and presets, stored in a git repository. | |
| 56 | | **Preset** | A named bundle of items, for example *nuxt-saas* = Nuxt and Tailwind skills, TypeScript rules and the Nuxt docs MCP server. A preset can include other presets. | | 56 | | **Preset** | A named bundle of items, for example *nuxt-saas* = Nuxt and Tailwind skills, TypeScript rules and the Nuxt docs MCP server. A preset can include other presets. | |
| 57 | -| **Project** | A code folder you added to skilly. | | 57 | +| **Project** | A code folder you added to panoplie. | |
| 58 | | **Global** | Your user-level agent configuration (`~/.claude`, `~/.agents`, `~/.codex`). It loads in every project. | | 58 | | **Global** | Your user-level agent configuration (`~/.claude`, `~/.agents`, `~/.codex`). It loads in every project. | |
| 59 | | **Automatic / on demand** | Skill mode. *Automatic*: the agent sees the skill and uses it when it thinks it is relevant. *On demand*: the skill is installed but hidden from the agent until you call it yourself (`/name` in Claude Code, `$name` in Codex). Ideal for large families of niche skills you want at hand without paying for them in context. | | 59 | | **Automatic / on demand** | Skill mode. *Automatic*: the agent sees the skill and uses it when it thinks it is relevant. *On demand*: the skill is installed but hidden from the agent until you call it yourself (`/name` in Claude Code, `$name` in Codex). Ideal for large families of niche skills you want at hand without paying for them in context. | |
| 60 | 60 | ||
| @@ -66,7 +66,7 @@ Pick presets, add or exclude single items, switch skills between automatic and o | |||
| 66 | 66 | ||
| 67 | ### Review before anything is written | 67 | ### Review before anything is written |
| 68 | 68 | ||
| 69 | -skilly computes a plan: files to add, update or remove, for each agent. You can undo any line before applying. When a file already exists or was edited by hand, you choose: **keep the project version**, **take the catalog version**, or **push the change back to the catalog**. You can compare both versions first. | 69 | +panoplie computes a plan: files to add, update or remove, for each agent. You can undo any line before applying. When a file already exists or was edited by hand, you choose: **keep the project version**, **take the catalog version**, or **push the change back to the catalog**. You can compare both versions first. |
| 70 | 70 | ||
| 71 | <p align="center"> | 71 | <p align="center"> |
| 72 | <img src="media/screenshots/review.png" alt="Review changes before applying them, with conflict resolution" width="900"> | 72 | <img src="media/screenshots/review.png" alt="Review changes before applying them, with conflict resolution" width="900"> |
| @@ -74,7 +74,7 @@ skilly computes a plan: files to add, update or remove, for each agent. You can | |||
| 74 | 74 | ||
| 75 | ### Clean up Global | 75 | ### Clean up Global |
| 76 | 76 | ||
| 77 | -skilly lists everything loaded from your home folder and spots the items that are only used in some projects. **Move to projects** copies them to the catalog, enables them where they are needed, then removes them from Global. Nothing is removed from your home folder without an explicit action. | 77 | +panoplie lists everything loaded from your home folder and spots the items that are only used in some projects. **Move to projects** copies them to the catalog, enables them where they are needed, then removes them from Global. Nothing is removed from your home folder without an explicit action. |
| 78 | 78 | ||
| 79 | <p align="center"> | 79 | <p align="center"> |
| 80 | <img src="media/screenshots/global.png" alt="Global items loaded in every project, with tidy-up suggestions" width="900"> | 80 | <img src="media/screenshots/global.png" alt="Global items loaded in every project, with tidy-up suggestions" width="900"> |
| @@ -118,74 +118,74 @@ Commit, pull and push from the UI, against any remote: GitHub, GitLab, Codeberg, | |||
| 118 | | Subagents | `.claude/agents/<name>.md` | `.codex/agents/<name>.toml` | | 118 | | Subagents | `.claude/agents/<name>.md` | `.codex/agents/<name>.toml` | |
| 119 | | Hide a Global skill or MCP server | Yes | Not supported by Codex yet | | 119 | | Hide a Global skill or MCP server | Yes | Not supported by Codex yet | |
| 120 | 120 | ||
| 121 | -skilly also writes two files at the root of each project, meant to be committed: | 121 | +panoplie also writes two files at the root of each project, meant to be committed: |
| 122 | 122 | ||
| 123 | -- **`skilly.json`**: what the project uses (target agents, presets, added and excluded items, on-demand skills, hidden Global items). | 123 | +- **`panoplie.json`**: what the project uses (target agents, presets, added and excluded items, on-demand skills, hidden Global items). |
| 124 | -- **`skilly.lock`**: what skilly installed, with content hashes, so it knows which files it owns and can detect manual edits. | 124 | +- **`panoplie.lock`**: what panoplie installed, with content hashes, so it knows which files it owns and can detect manual edits. |
| 125 | 125 | ||
| 126 | -Shared files (`.mcp.json`, `.claude/settings.json`, `.codex/config.toml`, `AGENTS.md`) are edited surgically: skilly only touches the keys or the block it owns and leaves the rest as is. | 126 | +Shared files (`.mcp.json`, `.claude/settings.json`, `.codex/config.toml`, `AGENTS.md`) are edited surgically: panoplie only touches the keys or the block it owns and leaves the rest as is. |
| 127 | 127 | ||
| 128 | -skilly also warns you about agent-specific pitfalls: a Codex project that is not trusted (Codex then ignores its `.codex/` folder), an `AGENTS.md` close to the 32 KiB Codex limit, or rules that Claude Code would load twice. | 128 | +panoplie also warns you about agent-specific pitfalls: a Codex project that is not trusted (Codex then ignores its `.codex/` folder), an `AGENTS.md` close to the 32 KiB Codex limit, or rules that Claude Code would load twice. |
| 129 | 129 | ||
| 130 | `CLAUDE.md` and `AGENTS.md` are shown read-only in each project, with their size. They are specific to each project and never go into the catalog. | 130 | `CLAUDE.md` and `AGENTS.md` are shown read-only in each project, with their size. They are specific to each project and never go into the catalog. |
| 131 | 131 | ||
| 132 | ## Install and run | 132 | ## Install and run |
| 133 | 133 | ||
| 134 | -> skilly is in early development and not published on npm yet. Until the first release, run it from source (see below). | 134 | +> panoplie is in early development and not published on npm yet. Until the first release, run it from source (see below). |
| 135 | 135 | ||
| 136 | Requirements: **Node.js 22+**, **git**, and preferably **Microsoft Edge or Google Chrome** for the app window. | 136 | Requirements: **Node.js 22+**, **git**, and preferably **Microsoft Edge or Google Chrome** for the app window. |
| 137 | 137 | ||
| 138 | ```sh | 138 | ```sh |
| 139 | -npx @skilly/cli | 139 | +npx @panoplie/cli |
| 140 | ``` | 140 | ``` |
| 141 | 141 | ||
| 142 | -skilly starts a local server and opens the UI in an app window (Edge or Chrome in app mode, your default browser otherwise). Closing the window stops skilly. Running the command again while skilly is open brings the existing window back. | 142 | +panoplie starts a local server and opens the UI in an app window (Edge or Chrome in app mode, your default browser otherwise). Closing the window stops panoplie. Running the command again while panoplie is open brings the existing window back. |
| 143 | 143 | ||
| 144 | | Option | Effect | | 144 | | Option | Effect | |
| 145 | |---|---| | 145 | |---|---| |
| 146 | | `--no-open` | Do not open a window, only print the URL. | | 146 | | `--no-open` | Do not open a window, only print the URL. | |
| 147 | | `--port <n>` | Use this port instead of the default one (47821, or a free port if taken). | | 147 | | `--port <n>` | Use this port instead of the default one (47821, or a free port if taken). | |
| 148 | 148 | ||
| 149 | -The first launch walks you through the setup: create a new catalog or clone an existing one from git, check the detected agents, then add your projects (skilly can scan your usual code folders for you). | 149 | +The first launch walks you through the setup: create a new catalog or clone an existing one from git, check the detected agents, then add your projects (panoplie can scan your usual code folders for you). |
| 150 | 150 | ||
| 151 | ### From source | 151 | ### From source |
| 152 | 152 | ||
| 153 | ```sh | 153 | ```sh |
| 154 | -git clone <repository-url> skilly | 154 | +git clone <repository-url> panoplie |
| 155 | -cd skilly | 155 | +cd panoplie |
| 156 | pnpm install | 156 | pnpm install |
| 157 | pnpm build # builds the UI | 157 | pnpm build # builds the UI |
| 158 | -pnpm start # starts skilly and opens the window | 158 | +pnpm start # starts panoplie and opens the window |
| 159 | ``` | 159 | ``` |
| 160 | 160 | ||
| 161 | ## Where your data lives | 161 | ## Where your data lives |
| 162 | 162 | ||
| 163 | | What | Where | | 163 | | What | Where | |
| 164 | |---|---| | 164 | |---|---| |
| 165 | -| Catalog | `~/.skilly/catalog` by default (a git repository, the location can be changed in Settings) | | 165 | +| Catalog | `~/.panoplie/catalog` by default (a git repository, the location can be changed in Settings) | |
| 166 | -| Machine settings (projects list, preferences, Global configuration) | `~/.skilly/` | | 166 | +| Machine settings (projects list, preferences, Global configuration) | `~/.panoplie/` | |
| 167 | -| Project configuration | `skilly.json` and `skilly.lock` in each project | | 167 | +| Project configuration | `panoplie.json` and `panoplie.lock` in each project | |
| 168 | 168 | ||
| 169 | -Set `SKILLY_CONFIG_DIR` to use another folder than `~/.skilly`, and `CODEX_HOME` if your Codex home is not `~/.codex`. | 169 | +Set `PANOPLIE_CONFIG_DIR` to use another folder than `~/.panoplie`, and `CODEX_HOME` if your Codex home is not `~/.codex`. |
| 170 | 170 | ||
| 171 | ## Security and privacy | 171 | ## Security and privacy |
| 172 | 172 | ||
| 173 | - **Local only.** The server listens on `127.0.0.1`, rejects other host names, and requires a random token generated at each launch, so other websites open in your browser cannot call it. | 173 | - **Local only.** The server listens on `127.0.0.1`, rejects other host names, and requires a random token generated at each launch, so other websites open in your browser cannot call it. |
| 174 | - **No secrets in the catalog.** MCP configurations store the *name* of environment variables, never their values. Credentials found in MCP arguments, URLs or environment when importing are redacted. | 174 | - **No secrets in the catalog.** MCP configurations store the *name* of environment variables, never their values. Credentials found in MCP arguments, URLs or environment when importing are redacted. |
| 175 | -- **Limited writes.** skilly writes only in the projects you added, in the catalog, and in your home folder when you explicitly apply a change to Global. | 175 | +- **Limited writes.** panoplie writes only in the projects you added, in the catalog, and in your home folder when you explicitly apply a change to Global. |
| 176 | -- **No telemetry.** skilly only goes online to fetch the marketplace sources you browse and to sync your catalog with the git remote you configure. | 176 | +- **No telemetry.** panoplie only goes online to fetch the marketplace sources you browse and to sync your catalog with the git remote you configure. |
| 177 | -- skilly never executes the scripts contained in skills. | 177 | +- panoplie never executes the scripts contained in skills. |
| 178 | 178 | ||
| 179 | ## FAQ | 179 | ## FAQ |
| 180 | 180 | ||
| 181 | -**Do my teammates need skilly?** | 181 | +**Do my teammates need panoplie?** |
| 182 | -No. The generated files are regular agent files committed in the project. skilly only makes them easier to manage, and it makes catalog sharing easier for those who use it. | 182 | +No. The generated files are regular agent files committed in the project. panoplie only makes them easier to manage, and it makes catalog sharing easier for those who use it. |
| 183 | 183 | ||
| 184 | **What if I edit a generated file by hand?** | 184 | **What if I edit a generated file by hand?** |
| 185 | -skilly detects it (thanks to `skilly.lock`) and asks what to do on the next apply: keep your version, take the catalog version, or push your change back to the catalog. | 185 | +panoplie detects it (thanks to `panoplie.lock`) and asks what to do on the next apply: keep your version, take the catalog version, or push your change back to the catalog. |
| 186 | 186 | ||
| 187 | **Why are skills copied twice instead of symlinked?** | 187 | **Why are skills copied twice instead of symlinked?** |
| 188 | -Claude Code reads `.claude/skills` and Codex reads `.agents/skills`. Symlinks are fragile on Windows and do not survive a clone, so skilly writes plain copies and keeps them in sync. | 188 | +Claude Code reads `.claude/skills` and Codex reads `.agents/skills`. Symlinks are fragile on Windows and do not survive a clone, so panoplie writes plain copies and keeps them in sync. |
| 189 | 189 | ||
| 190 | **Does it work with other agents (Cursor, Copilot, Gemini CLI…)?** | 190 | **Does it work with other agents (Cursor, Copilot, Gemini CLI…)?** |
| 191 | Not yet. Claude Code and Codex are supported today; more agents are planned. | 191 | Not yet. Claude Code and Codex are supported today; more agents are planned. |
| @@ -199,7 +199,7 @@ The repository is a TypeScript monorepo managed with pnpm: | |||
| 199 | | `packages/core` | Model, inventory scanners, catalog, resolution, rendering per agent, sync plan and apply. No UI dependency. | | 199 | | `packages/core` | Model, inventory scanners, catalog, resolution, rendering per agent, sync plan and apply. No UI dependency. | |
| 200 | | `packages/server` | Local HTTP API over `core`. | | 200 | | `packages/server` | Local HTTP API over `core`. | |
| 201 | | `packages/ui` | Nuxt single-page app served by the server. | | 201 | | `packages/ui` | Nuxt single-page app served by the server. | |
| 202 | -| `packages/cli` | The `skilly` command: starts the server and opens the window. | | 202 | +| `packages/cli` | The `panoplie` command: starts the server and opens the window. | |
| 203 | 203 | ||
| 204 | ```sh | 204 | ```sh |
| 205 | pnpm dev # API on :4300 and the UI with hot reload on :3000 | 205 | pnpm dev # API on :4300 and the UI with hot reload on :3000 |
modified
package.json +4 -4 | @@ -1,13 +1,13 @@ | ||
| 1 | 1 | { |
| 2 | - "name": "skilly-monorepo", | |
| 2 | + "name": "panoplie-monorepo", | |
| 3 | 3 | "private": true, |
| 4 | 4 | "license": "MIT", |
| 5 | 5 | "type": "module", |
| 6 | 6 | "packageManager": "pnpm@10.23.0", |
| 7 | 7 | "scripts": { |
| 8 | - "dev": "pnpm --parallel --filter @skilly/server --filter @skilly/ui dev", | |
| 9 | - "build": "pnpm --filter @skilly/ui build", | |
| 10 | - "start": "pnpm --filter skilly start", | |
| 8 | + "dev": "pnpm --parallel --filter @panoplie/server --filter @panoplie/ui dev", | |
| 9 | + "build": "pnpm --filter @panoplie/ui build", | |
| 10 | + "start": "pnpm --filter panoplie start", | |
| 11 | 11 | "test": "vitest run", |
| 12 | 12 | "typecheck": "tsc -p packages/core && tsc -p packages/server && tsc -p packages/cli", |
| 13 | 13 | "site:build": "node site/build.mjs" |
| @@ -1,13 +1,13 @@ | |||
| 1 | { | 1 | { |
| 2 | - "name": "skilly-monorepo", | 2 | + "name": "panoplie-monorepo", |
| 3 | "private": true, | 3 | "private": true, |
| 4 | "license": "MIT", | 4 | "license": "MIT", |
| 5 | "type": "module", | 5 | "type": "module", |
| 6 | "packageManager": "pnpm@10.23.0", | 6 | "packageManager": "pnpm@10.23.0", |
| 7 | "scripts": { | 7 | "scripts": { |
| 8 | - "dev": "pnpm --parallel --filter @skilly/server --filter @skilly/ui dev", | 8 | + "dev": "pnpm --parallel --filter @panoplie/server --filter @panoplie/ui dev", |
| 9 | - "build": "pnpm --filter @skilly/ui build", | 9 | + "build": "pnpm --filter @panoplie/ui build", |
| 10 | - "start": "pnpm --filter skilly start", | 10 | + "start": "pnpm --filter panoplie start", |
| 11 | "test": "vitest run", | 11 | "test": "vitest run", |
| 12 | "typecheck": "tsc -p packages/core && tsc -p packages/server && tsc -p packages/cli", | 12 | "typecheck": "tsc -p packages/core && tsc -p packages/server && tsc -p packages/cli", |
| 13 | "site:build": "node site/build.mjs" | 13 | "site:build": "node site/build.mjs" |
renamed
packages/cli/bin/panoplie.js +0 -0 | similarity index 100% | ||
| rename from packages/cli/bin/skilly.js | ||
| rename to packages/cli/bin/panoplie.js |
| similarity index 100% | |||
| rename from packages/cli/bin/skilly.js | |||
| rename to packages/cli/bin/panoplie.js |
modified
packages/cli/package.json +4 -4 | @@ -1,17 +1,17 @@ | ||
| 1 | 1 | { |
| 2 | - "name": "skilly", | |
| 2 | + "name": "panoplie", | |
| 3 | 3 | "version": "0.0.0", |
| 4 | 4 | "private": true, |
| 5 | 5 | "license": "MIT", |
| 6 | 6 | "type": "module", |
| 7 | 7 | "bin": { |
| 8 | - "skilly": "./bin/skilly.js" | |
| 8 | + "panoplie": "./bin/panoplie.js" | |
| 9 | 9 | }, |
| 10 | 10 | "scripts": { |
| 11 | 11 | "start": "tsx src/main.ts" |
| 12 | 12 | }, |
| 13 | 13 | "dependencies": { |
| 14 | - "@skilly/core": "workspace:*", | |
| 15 | - "@skilly/server": "workspace:*" | |
| 14 | + "@panoplie/core": "workspace:*", | |
| 15 | + "@panoplie/server": "workspace:*" | |
| 16 | 16 | } |
| 17 | 17 | } |
| @@ -1,17 +1,17 @@ | |||
| 1 | { | 1 | { |
| 2 | - "name": "skilly", | 2 | + "name": "panoplie", |
| 3 | "version": "0.0.0", | 3 | "version": "0.0.0", |
| 4 | "private": true, | 4 | "private": true, |
| 5 | "license": "MIT", | 5 | "license": "MIT", |
| 6 | "type": "module", | 6 | "type": "module", |
| 7 | "bin": { | 7 | "bin": { |
| 8 | - "skilly": "./bin/skilly.js" | 8 | + "panoplie": "./bin/panoplie.js" |
| 9 | }, | 9 | }, |
| 10 | "scripts": { | 10 | "scripts": { |
| 11 | "start": "tsx src/main.ts" | 11 | "start": "tsx src/main.ts" |
| 12 | }, | 12 | }, |
| 13 | "dependencies": { | 13 | "dependencies": { |
| 14 | - "@skilly/core": "workspace:*", | 14 | + "@panoplie/core": "workspace:*", |
| 15 | - "@skilly/server": "workspace:*" | 15 | + "@panoplie/server": "workspace:*" |
| 16 | } | 16 | } |
| 17 | } | 17 | } |
modified
packages/cli/src/main.ts +12 -7 | @@ -4,7 +4,8 @@ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' | ||
| 4 | 4 | import os from 'node:os' |
| 5 | 5 | import path from 'node:path' |
| 6 | 6 | import { fileURLToPath } from 'node:url' |
| 7 | -import { startServer } from '@skilly/server' | |
| 7 | +import { migrateLegacyConfigDir } from '@panoplie/core' | |
| 8 | +import { startServer } from '@panoplie/server' | |
| 8 | 9 | import { openWindow } from './window.ts' |
| 9 | 10 | |
| 10 | 11 | const IDLE_EXIT_MS = 20_000 |
| @@ -54,7 +55,7 @@ async function readInstance(file: string): Promise<InstanceInfo | undefined> { | ||
| 54 | 55 | async function isAlive(info: InstanceInfo): Promise<boolean> { |
| 55 | 56 | try { |
| 56 | 57 | const res = await fetch(`http://127.0.0.1:${info.port}/api/health`, { |
| 57 | - headers: { 'x-skilly-token': info.token }, | |
| 58 | + headers: { 'x-panoplie-token': info.token }, | |
| 58 | 59 | signal: AbortSignal.timeout(1500), |
| 59 | 60 | }) |
| 60 | 61 | return res.ok |
| @@ -90,11 +91,15 @@ async function startOnPreferredPort(options: Omit<ServerOptions, 'port'>, explic | ||
| 90 | 91 | async function main(): Promise<void> { |
| 91 | 92 | const args = parseArgs(process.argv.slice(2)) |
| 92 | 93 | if (args.help) { |
| 93 | - console.log('Usage: skilly [--no-open] [--port N]') | |
| 94 | + console.log('Usage: panoplie [--no-open] [--port N]') | |
| 94 | 95 | return |
| 95 | 96 | } |
| 96 | 97 | |
| 97 | - const configDir = process.env.SKILLY_CONFIG_DIR || path.join(os.homedir(), '.skilly') | |
| 98 | + const configDir = process.env.PANOPLIE_CONFIG_DIR || path.join(os.homedir(), '.panoplie') | |
| 99 | + // Installs made under the previous name (skilly) keep their catalog, projects and settings. | |
| 100 | + if (!process.env.PANOPLIE_CONFIG_DIR && (await migrateLegacyConfigDir(configDir))) { | |
| 101 | + console.log(`panoplie: moved your settings from ~/.skilly to ${configDir}`) | |
| 102 | + } | |
| 98 | 103 | const instanceFile = path.join(configDir, 'instance.json') |
| 99 | 104 | const profileDir = path.join(configDir, 'browser-profile') |
| 100 | 105 | await mkdir(configDir, { recursive: true }) |
| @@ -104,11 +109,11 @@ async function main(): Promise<void> { | ||
| 104 | 109 | if (existing && (await isAlive(existing))) { |
| 105 | 110 | const url = windowUrl(existing.port, existing.token) |
| 106 | 111 | if (args.open) await launch(url, profileDir) |
| 107 | - console.log(args.open ? `skilly is already running: ${url}` : url) | |
| 112 | + console.log(args.open ? `panoplie is already running: ${url}` : url) | |
| 108 | 113 | return |
| 109 | 114 | } |
| 110 | 115 | |
| 111 | - const staticDir = process.env.SKILLY_STATIC_DIR || fileURLToPath(new URL('../../ui/.output/public', import.meta.url)) | |
| 116 | + const staticDir = process.env.PANOPLIE_STATIC_DIR || fileURLToPath(new URL('../../ui/.output/public', import.meta.url)) | |
| 112 | 117 | if (!existsSync(path.join(staticDir, 'index.html')) && !existsSync(path.join(staticDir, '200.html'))) { |
| 113 | 118 | console.error(`The UI is not built (missing ${staticDir}).\nRun "pnpm build" from the repository root first.`) |
| 114 | 119 | process.exitCode = 1 |
| @@ -134,7 +139,7 @@ async function main(): Promise<void> { | ||
| 134 | 139 | process.on('SIGTERM', () => void cleanup()) |
| 135 | 140 | |
| 136 | 141 | const url = windowUrl(server.port, token) |
| 137 | - console.log(`skilly: ${url} (close the window to stop)`) | |
| 142 | + console.log(`panoplie: ${url} (close the window to stop)`) | |
| 138 | 143 | if (args.open) await launch(url, profileDir) |
| 139 | 144 | } |
| 140 | 145 | |
| @@ -4,7 +4,8 @@ import { mkdir, readFile, rm, writeFile } from 'node:fs/promises' | |||
| 4 | import os from 'node:os' | 4 | import os from 'node:os' |
| 5 | import path from 'node:path' | 5 | import path from 'node:path' |
| 6 | import { fileURLToPath } from 'node:url' | 6 | import { fileURLToPath } from 'node:url' |
| 7 | -import { startServer } from '@skilly/server' | 7 | +import { migrateLegacyConfigDir } from '@panoplie/core' |
| 8 | +import { startServer } from '@panoplie/server' | ||
| 8 | import { openWindow } from './window.ts' | 9 | import { openWindow } from './window.ts' |
| 9 | 10 | ||
| 10 | const IDLE_EXIT_MS = 20_000 | 11 | const IDLE_EXIT_MS = 20_000 |
| @@ -54,7 +55,7 @@ async function readInstance(file: string): Promise<InstanceInfo | undefined> { | |||
| 54 | async function isAlive(info: InstanceInfo): Promise<boolean> { | 55 | async function isAlive(info: InstanceInfo): Promise<boolean> { |
| 55 | try { | 56 | try { |
| 56 | const res = await fetch(`http://127.0.0.1:${info.port}/api/health`, { | 57 | const res = await fetch(`http://127.0.0.1:${info.port}/api/health`, { |
| 57 | - headers: { 'x-skilly-token': info.token }, | 58 | + headers: { 'x-panoplie-token': info.token }, |
| 58 | signal: AbortSignal.timeout(1500), | 59 | signal: AbortSignal.timeout(1500), |
| 59 | }) | 60 | }) |
| 60 | return res.ok | 61 | return res.ok |
| @@ -90,11 +91,15 @@ async function startOnPreferredPort(options: Omit<ServerOptions, 'port'>, explic | |||
| 90 | async function main(): Promise<void> { | 91 | async function main(): Promise<void> { |
| 91 | const args = parseArgs(process.argv.slice(2)) | 92 | const args = parseArgs(process.argv.slice(2)) |
| 92 | if (args.help) { | 93 | if (args.help) { |
| 93 | - console.log('Usage: skilly [--no-open] [--port N]') | 94 | + console.log('Usage: panoplie [--no-open] [--port N]') |
| 94 | return | 95 | return |
| 95 | } | 96 | } |
| 96 | 97 | ||
| 97 | - const configDir = process.env.SKILLY_CONFIG_DIR || path.join(os.homedir(), '.skilly') | 98 | + const configDir = process.env.PANOPLIE_CONFIG_DIR || path.join(os.homedir(), '.panoplie') |
| 99 | + // Installs made under the previous name (skilly) keep their catalog, projects and settings. | ||
| 100 | + if (!process.env.PANOPLIE_CONFIG_DIR && (await migrateLegacyConfigDir(configDir))) { | ||
| 101 | + console.log(`panoplie: moved your settings from ~/.skilly to ${configDir}`) | ||
| 102 | + } | ||
| 98 | const instanceFile = path.join(configDir, 'instance.json') | 103 | const instanceFile = path.join(configDir, 'instance.json') |
| 99 | const profileDir = path.join(configDir, 'browser-profile') | 104 | const profileDir = path.join(configDir, 'browser-profile') |
| 100 | await mkdir(configDir, { recursive: true }) | 105 | await mkdir(configDir, { recursive: true }) |
| @@ -104,11 +109,11 @@ async function main(): Promise<void> { | |||
| 104 | if (existing && (await isAlive(existing))) { | 109 | if (existing && (await isAlive(existing))) { |
| 105 | const url = windowUrl(existing.port, existing.token) | 110 | const url = windowUrl(existing.port, existing.token) |
| 106 | if (args.open) await launch(url, profileDir) | 111 | if (args.open) await launch(url, profileDir) |
| 107 | - console.log(args.open ? `skilly is already running: ${url}` : url) | 112 | + console.log(args.open ? `panoplie is already running: ${url}` : url) |
| 108 | return | 113 | return |
| 109 | } | 114 | } |
| 110 | 115 | ||
| 111 | - const staticDir = process.env.SKILLY_STATIC_DIR || fileURLToPath(new URL('../../ui/.output/public', import.meta.url)) | 116 | + const staticDir = process.env.PANOPLIE_STATIC_DIR || fileURLToPath(new URL('../../ui/.output/public', import.meta.url)) |
| 112 | if (!existsSync(path.join(staticDir, 'index.html')) && !existsSync(path.join(staticDir, '200.html'))) { | 117 | if (!existsSync(path.join(staticDir, 'index.html')) && !existsSync(path.join(staticDir, '200.html'))) { |
| 113 | console.error(`The UI is not built (missing ${staticDir}).\nRun "pnpm build" from the repository root first.`) | 118 | console.error(`The UI is not built (missing ${staticDir}).\nRun "pnpm build" from the repository root first.`) |
| 114 | process.exitCode = 1 | 119 | process.exitCode = 1 |
| @@ -134,7 +139,7 @@ async function main(): Promise<void> { | |||
| 134 | process.on('SIGTERM', () => void cleanup()) | 139 | process.on('SIGTERM', () => void cleanup()) |
| 135 | 140 | ||
| 136 | const url = windowUrl(server.port, token) | 141 | const url = windowUrl(server.port, token) |
| 137 | - console.log(`skilly: ${url} (close the window to stop)`) | 142 | + console.log(`panoplie: ${url} (close the window to stop)`) |
| 138 | if (args.open) await launch(url, profileDir) | 143 | if (args.open) await launch(url, profileDir) |
| 139 | } | 144 | } |
| 140 | 145 | ||
modified
packages/core/package.json +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | - "name": "@skilly/core", | |
| 2 | + "name": "@panoplie/core", | |
| 3 | 3 | "version": "0.0.0", |
| 4 | 4 | "private": true, |
| 5 | 5 | "license": "MIT", |
| @@ -1,5 +1,5 @@ | |||
| 1 | { | 1 | { |
| 2 | - "name": "@skilly/core", | 2 | + "name": "@panoplie/core", |
| 3 | "version": "0.0.0", | 3 | "version": "0.0.0", |
| 4 | "private": true, | 4 | "private": true, |
| 5 | "license": "MIT", | 5 | "license": "MIT", |
modified
packages/core/src/api.ts +6 -6 | @@ -1,8 +1,8 @@ | ||
| 1 | 1 | /** |
| 2 | - * HTTP API contract between @skilly/server and @skilly/ui. | |
| 2 | + * HTTP API contract between @panoplie/server and @panoplie/ui. | |
| 3 | 3 | * |
| 4 | 4 | * All routes live under `/api` and require the session token, sent as the |
| 5 | - * `x-skilly-token` header (the UI reads it once from the `?t=` query of the | |
| 5 | + * `x-panoplie-token` header (the UI reads it once from the `?t=` query of the | |
| 6 | 6 | * launch URL and keeps it in sessionStorage). |
| 7 | 7 | * |
| 8 | 8 | * GET /api/health -> { ok: true, version } |
| @@ -112,7 +112,7 @@ export interface FileContent { | ||
| 112 | 112 | * |
| 113 | 113 | * Scope configuration and sync (projects and Home) |
| 114 | 114 | * GET /api/scopes/:scope/state -> ScopeState |
| 115 | - * PUT /api/scopes/:scope/config ScopeConfig -> ScopeState (writes skilly.json / home.json; does not apply) | |
| 115 | + * PUT /api/scopes/:scope/config ScopeConfig -> ScopeState (writes panoplie.json / home.json; does not apply) | |
| 116 | 116 | * POST /api/scopes/:scope/apply ApplyBody -> ApplyResponse (re-plans server side, applies, returns fresh state) |
| 117 | 117 | * POST /api/home/remove-from-global RemoveFromGlobalBody -> RemoveFromGlobalResult |
| 118 | 118 | * |
| @@ -142,7 +142,7 @@ import type { | ||
| 142 | 142 | import type { ScopeInventory } from './model.ts' |
| 143 | 143 | |
| 144 | 144 | export interface Settings { |
| 145 | - /** Absolute path of the catalog folder. Default: `~/.skilly/catalog`. */ | |
| 145 | + /** Absolute path of the catalog folder. Default: `~/.panoplie/catalog`. */ | |
| 146 | 146 | catalogPath: string |
| 147 | 147 | /** Commit, pull and push the catalog automatically after changes (debounced). */ |
| 148 | 148 | autoSync: boolean |
| @@ -179,7 +179,7 @@ export interface ImportResult { | ||
| 179 | 179 | |
| 180 | 180 | export interface ScopeState { |
| 181 | 181 | scope: string |
| 182 | - /** Null when the scope has no skilly.json yet. */ | |
| 182 | + /** Null when the scope has no panoplie.json yet. */ | |
| 183 | 183 | config: ScopeConfig | null |
| 184 | 184 | inventory: ScopeInventory |
| 185 | 185 | /** Inventory item id -> status. */ |
| @@ -202,7 +202,7 @@ export interface ApplyResponse { | ||
| 202 | 202 | export interface RemoveFromGlobalBody { |
| 203 | 203 | /** Home inventory item ids (skill, rule or agent). */ |
| 204 | 204 | itemIds: string[] |
| 205 | - /** Project ids in which to enable the items (added to their skilly.json `add`, then applied). */ | |
| 205 | + /** Project ids in which to enable the items (added to their panoplie.json `add`, then applied). */ | |
| 206 | 206 | enableIn: string[] |
| 207 | 207 | } |
| 208 | 208 | |
| @@ -1,8 +1,8 @@ | |||
| 1 | /** | 1 | /** |
| 2 | - * HTTP API contract between @skilly/server and @skilly/ui. | 2 | + * HTTP API contract between @panoplie/server and @panoplie/ui. |
| 3 | * | 3 | * |
| 4 | * All routes live under `/api` and require the session token, sent as the | 4 | * All routes live under `/api` and require the session token, sent as the |
| 5 | - * `x-skilly-token` header (the UI reads it once from the `?t=` query of the | 5 | + * `x-panoplie-token` header (the UI reads it once from the `?t=` query of the |
| 6 | * launch URL and keeps it in sessionStorage). | 6 | * launch URL and keeps it in sessionStorage). |
| 7 | * | 7 | * |
| 8 | * GET /api/health -> { ok: true, version } | 8 | * GET /api/health -> { ok: true, version } |
| @@ -112,7 +112,7 @@ export interface FileContent { | |||
| 112 | * | 112 | * |
| 113 | * Scope configuration and sync (projects and Home) | 113 | * Scope configuration and sync (projects and Home) |
| 114 | * GET /api/scopes/:scope/state -> ScopeState | 114 | * GET /api/scopes/:scope/state -> ScopeState |
| 115 | - * PUT /api/scopes/:scope/config ScopeConfig -> ScopeState (writes skilly.json / home.json; does not apply) | 115 | + * PUT /api/scopes/:scope/config ScopeConfig -> ScopeState (writes panoplie.json / home.json; does not apply) |
| 116 | * POST /api/scopes/:scope/apply ApplyBody -> ApplyResponse (re-plans server side, applies, returns fresh state) | 116 | * POST /api/scopes/:scope/apply ApplyBody -> ApplyResponse (re-plans server side, applies, returns fresh state) |
| 117 | * POST /api/home/remove-from-global RemoveFromGlobalBody -> RemoveFromGlobalResult | 117 | * POST /api/home/remove-from-global RemoveFromGlobalBody -> RemoveFromGlobalResult |
| 118 | * | 118 | * |
| @@ -142,7 +142,7 @@ import type { | |||
| 142 | import type { ScopeInventory } from './model.ts' | 142 | import type { ScopeInventory } from './model.ts' |
| 143 | 143 | ||
| 144 | export interface Settings { | 144 | export interface Settings { |
| 145 | - /** Absolute path of the catalog folder. Default: `~/.skilly/catalog`. */ | 145 | + /** Absolute path of the catalog folder. Default: `~/.panoplie/catalog`. */ |
| 146 | catalogPath: string | 146 | catalogPath: string |
| 147 | /** Commit, pull and push the catalog automatically after changes (debounced). */ | 147 | /** Commit, pull and push the catalog automatically after changes (debounced). */ |
| 148 | autoSync: boolean | 148 | autoSync: boolean |
| @@ -179,7 +179,7 @@ export interface ImportResult { | |||
| 179 | 179 | ||
| 180 | export interface ScopeState { | 180 | export interface ScopeState { |
| 181 | scope: string | 181 | scope: string |
| 182 | - /** Null when the scope has no skilly.json yet. */ | 182 | + /** Null when the scope has no panoplie.json yet. */ |
| 183 | config: ScopeConfig | null | 183 | config: ScopeConfig | null |
| 184 | inventory: ScopeInventory | 184 | inventory: ScopeInventory |
| 185 | /** Inventory item id -> status. */ | 185 | /** Inventory item id -> status. */ |
| @@ -202,7 +202,7 @@ export interface ApplyResponse { | |||
| 202 | export interface RemoveFromGlobalBody { | 202 | export interface RemoveFromGlobalBody { |
| 203 | /** Home inventory item ids (skill, rule or agent). */ | 203 | /** Home inventory item ids (skill, rule or agent). */ |
| 204 | itemIds: string[] | 204 | itemIds: string[] |
| 205 | - /** Project ids in which to enable the items (added to their skilly.json `add`, then applied). */ | 205 | + /** Project ids in which to enable the items (added to their panoplie.json `add`, then applied). */ |
| 206 | enableIn: string[] | 206 | enableIn: string[] |
| 207 | } | 207 | } |
| 208 | 208 | ||
modified
packages/core/src/catalog-model.ts +10 -10 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /** |
| 2 | - * Data model for the catalog, presets, scope configuration (skilly.json), | |
| 2 | + * Data model for the catalog, presets, scope configuration (panoplie.json), | |
| 3 | 3 | * lock file and sync plans. Shared by core modules, server and UI. |
| 4 | 4 | * |
| 5 | 5 | * Catalog layout on disk (a plain folder, usually a git repository): |
| @@ -10,7 +10,7 @@ | ||
| 10 | 10 | * <catalog>/mcp/<name>.yaml McpTemplate |
| 11 | 11 | * <catalog>/hooks/<name>.yaml HookTemplate |
| 12 | 12 | * <catalog>/presets/<name>.yaml Preset |
| 13 | - * <catalog>/.skilly/index.json CatalogIndex (tags, origins) | |
| 13 | + * <catalog>/.panoplie/index.json CatalogIndex (tags, origins) | |
| 14 | 14 | * |
| 15 | 15 | * Names are kebab-case: /^[a-z0-9][a-z0-9._-]*$/ (presets too). |
| 16 | 16 | */ |
| @@ -30,7 +30,7 @@ export interface CatalogRef { | ||
| 30 | 30 | export type CatalogKey = string |
| 31 | 31 | |
| 32 | 32 | export interface ItemOrigin { |
| 33 | - /** `import`: copied from Home or a project; `marketplace`: imported from a marketplace source; `created`: written in skilly. */ | |
| 33 | + /** `import`: copied from Home or a project; `marketplace`: imported from a marketplace source; `created`: written in panoplie. */ | |
| 34 | 34 | kind: 'import' | 'marketplace' | 'created' |
| 35 | 35 | /** Source path (import) or marketplace source id (marketplace). */ |
| 36 | 36 | source?: string |
| @@ -134,7 +134,7 @@ export interface CatalogSnapshot { | ||
| 134 | 134 | errors: { path: string; message: string }[] |
| 135 | 135 | } |
| 136 | 136 | |
| 137 | -/** Contents of `skilly.json` in a project, or `~/.skilly/home.json` for Home. */ | |
| 137 | +/** Contents of `panoplie.json` in a project, or `~/.panoplie/home.json` for Home. */ | |
| 138 | 138 | export interface ScopeConfig { |
| 139 | 139 | version: 1 |
| 140 | 140 | targets: AgentId[] |
| @@ -153,10 +153,10 @@ export interface LockFileEntry { | ||
| 153 | 153 | sha256: string |
| 154 | 154 | } |
| 155 | 155 | |
| 156 | -/** A key skilly owns inside a shared config file (settings.json, .mcp.json, config.toml, hooks.json, AGENTS.md block). */ | |
| 156 | +/** A key panoplie owns inside a shared config file (settings.json, .mcp.json, config.toml, hooks.json, AGENTS.md block). */ | |
| 157 | 157 | export interface LockConfigEntry { |
| 158 | 158 | file: string |
| 159 | - /** Dotted key path, e.g. `mcpServers.github`, `skillOverrides.seo-audit`, `mcp_servers.github`, `hooks.PreToolUse[skilly:fmt]`, `agents-md-block:typescript-strict`. */ | |
| 159 | + /** Dotted key path, e.g. `mcpServers.github`, `skillOverrides.seo-audit`, `mcp_servers.github`, `hooks.PreToolUse[panoplie:fmt]`, `agents-md-block:typescript-strict`. */ | |
| 160 | 160 | key: string |
| 161 | 161 | sha256: string |
| 162 | 162 | } |
| @@ -174,7 +174,7 @@ export interface LockEntry { | ||
| 174 | 174 | via: string[] |
| 175 | 175 | } |
| 176 | 176 | |
| 177 | -/** `skilly.lock` in a project, `~/.skilly/home.lock` for Home. */ | |
| 177 | +/** `panoplie.lock` in a project, `~/.panoplie/home.lock` for Home. */ | |
| 178 | 178 | export interface LockFile { |
| 179 | 179 | version: 1 |
| 180 | 180 | /** Catalog git commit at the last sync, when the catalog is a repo. */ |
| @@ -188,7 +188,7 @@ export type PlanActionKind = 'add' | 'update' | 'remove' | 'unchanged' | 'confli | ||
| 188 | 188 | export type ConflictKind = |
| 189 | 189 | /** A managed file was edited in the scope since the last sync. */ |
| 190 | 190 | | 'modified-locally' |
| 191 | - /** The target path exists but skilly does not own it (not in the lock). */ | |
| 191 | + /** The target path exists but panoplie does not own it (not in the lock). */ | |
| 192 | 192 | | 'exists-unmanaged' |
| 193 | 193 | /** The item is in the lock but no longer in the catalog. */ |
| 194 | 194 | | 'missing-in-catalog' |
| @@ -237,7 +237,7 @@ export interface ApplyResult { | ||
| 237 | 237 | |
| 238 | 238 | /** Status of an inventory item relative to the catalog and the lock, computed by `annotateInventory`. */ |
| 239 | 239 | export interface ItemStatus { |
| 240 | - /** True when the item was written by skilly (present in the lock). */ | |
| 240 | + /** True when the item was written by panoplie (present in the lock). */ | |
| 241 | 241 | managed: boolean |
| 242 | 242 | catalogRef?: CatalogRef |
| 243 | 243 | state: |
| @@ -256,7 +256,7 @@ export interface SyncTarget { | ||
| 256 | 256 | root: string |
| 257 | 257 | homeDir: string |
| 258 | 258 | codexHome?: string |
| 259 | - /** Where scope config and lock are stored: project root for projects, `~/.skilly` for Home. */ | |
| 259 | + /** Where scope config and lock are stored: project root for projects, `~/.panoplie` for Home. */ | |
| 260 | 260 | configDir: string |
| 261 | 261 | } |
| 262 | 262 | |
| @@ -1,5 +1,5 @@ | |||
| 1 | /** | 1 | /** |
| 2 | - * Data model for the catalog, presets, scope configuration (skilly.json), | 2 | + * Data model for the catalog, presets, scope configuration (panoplie.json), |
| 3 | * lock file and sync plans. Shared by core modules, server and UI. | 3 | * lock file and sync plans. Shared by core modules, server and UI. |
| 4 | * | 4 | * |
| 5 | * Catalog layout on disk (a plain folder, usually a git repository): | 5 | * Catalog layout on disk (a plain folder, usually a git repository): |
| @@ -10,7 +10,7 @@ | |||
| 10 | * <catalog>/mcp/<name>.yaml McpTemplate | 10 | * <catalog>/mcp/<name>.yaml McpTemplate |
| 11 | * <catalog>/hooks/<name>.yaml HookTemplate | 11 | * <catalog>/hooks/<name>.yaml HookTemplate |
| 12 | * <catalog>/presets/<name>.yaml Preset | 12 | * <catalog>/presets/<name>.yaml Preset |
| 13 | - * <catalog>/.skilly/index.json CatalogIndex (tags, origins) | 13 | + * <catalog>/.panoplie/index.json CatalogIndex (tags, origins) |
| 14 | * | 14 | * |
| 15 | * Names are kebab-case: /^[a-z0-9][a-z0-9._-]*$/ (presets too). | 15 | * Names are kebab-case: /^[a-z0-9][a-z0-9._-]*$/ (presets too). |
| 16 | */ | 16 | */ |
| @@ -30,7 +30,7 @@ export interface CatalogRef { | |||
| 30 | export type CatalogKey = string | 30 | export type CatalogKey = string |
| 31 | 31 | ||
| 32 | export interface ItemOrigin { | 32 | export interface ItemOrigin { |
| 33 | - /** `import`: copied from Home or a project; `marketplace`: imported from a marketplace source; `created`: written in skilly. */ | 33 | + /** `import`: copied from Home or a project; `marketplace`: imported from a marketplace source; `created`: written in panoplie. */ |
| 34 | kind: 'import' | 'marketplace' | 'created' | 34 | kind: 'import' | 'marketplace' | 'created' |
| 35 | /** Source path (import) or marketplace source id (marketplace). */ | 35 | /** Source path (import) or marketplace source id (marketplace). */ |
| 36 | source?: string | 36 | source?: string |
| @@ -134,7 +134,7 @@ export interface CatalogSnapshot { | |||
| 134 | errors: { path: string; message: string }[] | 134 | errors: { path: string; message: string }[] |
| 135 | } | 135 | } |
| 136 | 136 | ||
| 137 | -/** Contents of `skilly.json` in a project, or `~/.skilly/home.json` for Home. */ | 137 | +/** Contents of `panoplie.json` in a project, or `~/.panoplie/home.json` for Home. */ |
| 138 | export interface ScopeConfig { | 138 | export interface ScopeConfig { |
| 139 | version: 1 | 139 | version: 1 |
| 140 | targets: AgentId[] | 140 | targets: AgentId[] |
| @@ -153,10 +153,10 @@ export interface LockFileEntry { | |||
| 153 | sha256: string | 153 | sha256: string |
| 154 | } | 154 | } |
| 155 | 155 | ||
| 156 | -/** A key skilly owns inside a shared config file (settings.json, .mcp.json, config.toml, hooks.json, AGENTS.md block). */ | 156 | +/** A key panoplie owns inside a shared config file (settings.json, .mcp.json, config.toml, hooks.json, AGENTS.md block). */ |
| 157 | export interface LockConfigEntry { | 157 | export interface LockConfigEntry { |
| 158 | file: string | 158 | file: string |
| 159 | - /** Dotted key path, e.g. `mcpServers.github`, `skillOverrides.seo-audit`, `mcp_servers.github`, `hooks.PreToolUse[skilly:fmt]`, `agents-md-block:typescript-strict`. */ | 159 | + /** Dotted key path, e.g. `mcpServers.github`, `skillOverrides.seo-audit`, `mcp_servers.github`, `hooks.PreToolUse[panoplie:fmt]`, `agents-md-block:typescript-strict`. */ |
| 160 | key: string | 160 | key: string |
| 161 | sha256: string | 161 | sha256: string |
| 162 | } | 162 | } |
| @@ -174,7 +174,7 @@ export interface LockEntry { | |||
| 174 | via: string[] | 174 | via: string[] |
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | -/** `skilly.lock` in a project, `~/.skilly/home.lock` for Home. */ | 177 | +/** `panoplie.lock` in a project, `~/.panoplie/home.lock` for Home. */ |
| 178 | export interface LockFile { | 178 | export interface LockFile { |
| 179 | version: 1 | 179 | version: 1 |
| 180 | /** Catalog git commit at the last sync, when the catalog is a repo. */ | 180 | /** Catalog git commit at the last sync, when the catalog is a repo. */ |
| @@ -188,7 +188,7 @@ export type PlanActionKind = 'add' | 'update' | 'remove' | 'unchanged' | 'confli | |||
| 188 | export type ConflictKind = | 188 | export type ConflictKind = |
| 189 | /** A managed file was edited in the scope since the last sync. */ | 189 | /** A managed file was edited in the scope since the last sync. */ |
| 190 | | 'modified-locally' | 190 | | 'modified-locally' |
| 191 | - /** The target path exists but skilly does not own it (not in the lock). */ | 191 | + /** The target path exists but panoplie does not own it (not in the lock). */ |
| 192 | | 'exists-unmanaged' | 192 | | 'exists-unmanaged' |
| 193 | /** The item is in the lock but no longer in the catalog. */ | 193 | /** The item is in the lock but no longer in the catalog. */ |
| 194 | | 'missing-in-catalog' | 194 | | 'missing-in-catalog' |
| @@ -237,7 +237,7 @@ export interface ApplyResult { | |||
| 237 | 237 | ||
| 238 | /** Status of an inventory item relative to the catalog and the lock, computed by `annotateInventory`. */ | 238 | /** Status of an inventory item relative to the catalog and the lock, computed by `annotateInventory`. */ |
| 239 | export interface ItemStatus { | 239 | export interface ItemStatus { |
| 240 | - /** True when the item was written by skilly (present in the lock). */ | 240 | + /** True when the item was written by panoplie (present in the lock). */ |
| 241 | managed: boolean | 241 | managed: boolean |
| 242 | catalogRef?: CatalogRef | 242 | catalogRef?: CatalogRef |
| 243 | state: | 243 | state: |
| @@ -256,7 +256,7 @@ export interface SyncTarget { | |||
| 256 | root: string | 256 | root: string |
| 257 | homeDir: string | 257 | homeDir: string |
| 258 | codexHome?: string | 258 | codexHome?: string |
| 259 | - /** Where scope config and lock are stored: project root for projects, `~/.skilly` for Home. */ | 259 | + /** Where scope config and lock are stored: project root for projects, `~/.panoplie` for Home. */ |
| 260 | configDir: string | 260 | configDir: string |
| 261 | } | 261 | } |
| 262 | 262 | ||
modified
packages/core/src/catalog/git.ts +19 -13 | @@ -4,6 +4,7 @@ import path from 'node:path' | ||
| 4 | 4 | import type { GitStatus } from '../catalog-model.ts' |
| 5 | 5 | import type { CatalogGit, OpenCatalogGit } from '../contracts.ts' |
| 6 | 6 | import { pathExists, stripBom } from '../util.ts' |
| 7 | +import { LEGACY_CATALOG_META_DIR } from '../legacy.ts' | |
| 7 | 8 | |
| 8 | 9 | interface GitResult { |
| 9 | 10 | code: number |
| @@ -22,7 +23,7 @@ class GitError extends Error { | ||
| 22 | 23 | |
| 23 | 24 | /** |
| 24 | 25 | * Line endings are never converted (Git for Windows ships `core.autocrlf=true`): the catalog files must keep |
| 25 | - * the exact bytes of the repository, otherwise item hashes (recorded in the committed `skilly.lock`) and | |
| 26 | + * the exact bytes of the repository, otherwise item hashes (recorded in the committed `panoplie.lock`) and | |
| 26 | 27 | * rendered files would differ from one machine to another. |
| 27 | 28 | */ |
| 28 | 29 | const NO_EOL_CONVERSION = ['-c', 'core.autocrlf=false', '-c', 'core.eol=lf'] |
| @@ -88,8 +89,8 @@ class GitCatalog implements CatalogGit { | ||
| 88 | 89 | if (i > 0 && line.slice(i + 1).trim()) keys.add(line.slice(0, i).toLowerCase()) |
| 89 | 90 | } |
| 90 | 91 | const args: string[] = [] |
| 91 | - if (!keys.has('user.name')) args.push('-c', 'user.name=skilly') | |
| 92 | - if (!keys.has('user.email')) args.push('-c', 'user.email=skilly@localhost') | |
| 92 | + if (!keys.has('user.name')) args.push('-c', 'user.name=panoplie') | |
| 93 | + if (!keys.has('user.email')) args.push('-c', 'user.email=panoplie@localhost') | |
| 93 | 94 | return args |
| 94 | 95 | } |
| 95 | 96 | |
| @@ -182,7 +183,7 @@ class GitCatalog implements CatalogGit { | ||
| 182 | 183 | this.identityArgs(), |
| 183 | 184 | ]) |
| 184 | 185 | if (!s.trim() && merge.code !== 0) return undefined |
| 185 | - await this.git([...identity, 'commit', '-m', message || 'skilly sync']) | |
| 186 | + await this.git([...identity, 'commit', '-m', message || 'panoplie sync']) | |
| 186 | 187 | return (await this.currentCommit()) ?? undefined |
| 187 | 188 | } |
| 188 | 189 | |
| @@ -221,15 +222,20 @@ class GitCatalog implements CatalogGit { | ||
| 221 | 222 | return this.status() |
| 222 | 223 | } |
| 223 | 224 | |
| 224 | - /** Removes `.skilly/index.json` when it holds no item metadata (the file written by `CatalogStore.init`). */ | |
| 225 | + /** | |
| 226 | + * Removes `.panoplie/index.json` when it holds no item metadata (the file written by `CatalogStore.init`). | |
| 227 | + * Same for the index written under the previous name (skilly). | |
| 228 | + */ | |
| 225 | 229 | private async dropEmptyIndex(): Promise<void> { |
| 226 | - const file = path.join(this.root, '.skilly', 'index.json') | |
| 227 | - try { | |
| 228 | - const index = JSON.parse(stripBom(await readFile(file, 'utf8'))) as { items?: unknown } | null | |
| 229 | - const items = index?.items | |
| 230 | - if (items && typeof items === 'object' && Object.keys(items).length === 0) await rm(file, { force: true }) | |
| 231 | - } catch { | |
| 232 | - // Missing or not ours: left as is (git reports a clear error if it is in the way). | |
| 230 | + for (const dir of ['.panoplie', LEGACY_CATALOG_META_DIR]) { | |
| 231 | + const file = path.join(this.root, dir, 'index.json') | |
| 232 | + try { | |
| 233 | + const index = JSON.parse(stripBom(await readFile(file, 'utf8'))) as { items?: unknown } | null | |
| 234 | + const items = index?.items | |
| 235 | + if (items && typeof items === 'object' && Object.keys(items).length === 0) await rm(file, { force: true }) | |
| 236 | + } catch { | |
| 237 | + // Missing or not ours: left as is (git reports a clear error if it is in the way). | |
| 238 | + } | |
| 233 | 239 | } |
| 234 | 240 | } |
| 235 | 241 | |
| @@ -245,7 +251,7 @@ class GitCatalog implements CatalogGit { | ||
| 245 | 251 | return this.status() |
| 246 | 252 | } |
| 247 | 253 | |
| 248 | - async sync(message = 'skilly sync'): Promise<GitStatus> { | |
| 254 | + async sync(message = 'panoplie sync'): Promise<GitStatus> { | |
| 249 | 255 | await this.requireRepo() |
| 250 | 256 | await this.commitAll(message) |
| 251 | 257 | if (!(await this.remoteName())) throw new Error('No git remote configured') |
| @@ -4,6 +4,7 @@ import path from 'node:path' | |||
| 4 | import type { GitStatus } from '../catalog-model.ts' | 4 | import type { GitStatus } from '../catalog-model.ts' |
| 5 | import type { CatalogGit, OpenCatalogGit } from '../contracts.ts' | 5 | import type { CatalogGit, OpenCatalogGit } from '../contracts.ts' |
| 6 | import { pathExists, stripBom } from '../util.ts' | 6 | import { pathExists, stripBom } from '../util.ts' |
| 7 | +import { LEGACY_CATALOG_META_DIR } from '../legacy.ts' | ||
| 7 | 8 | ||
| 8 | interface GitResult { | 9 | interface GitResult { |
| 9 | code: number | 10 | code: number |
| @@ -22,7 +23,7 @@ class GitError extends Error { | |||
| 22 | 23 | ||
| 23 | /** | 24 | /** |
| 24 | * Line endings are never converted (Git for Windows ships `core.autocrlf=true`): the catalog files must keep | 25 | * Line endings are never converted (Git for Windows ships `core.autocrlf=true`): the catalog files must keep |
| 25 | - * the exact bytes of the repository, otherwise item hashes (recorded in the committed `skilly.lock`) and | 26 | + * the exact bytes of the repository, otherwise item hashes (recorded in the committed `panoplie.lock`) and |
| 26 | * rendered files would differ from one machine to another. | 27 | * rendered files would differ from one machine to another. |
| 27 | */ | 28 | */ |
| 28 | const NO_EOL_CONVERSION = ['-c', 'core.autocrlf=false', '-c', 'core.eol=lf'] | 29 | const NO_EOL_CONVERSION = ['-c', 'core.autocrlf=false', '-c', 'core.eol=lf'] |
| @@ -88,8 +89,8 @@ class GitCatalog implements CatalogGit { | |||
| 88 | if (i > 0 && line.slice(i + 1).trim()) keys.add(line.slice(0, i).toLowerCase()) | 89 | if (i > 0 && line.slice(i + 1).trim()) keys.add(line.slice(0, i).toLowerCase()) |
| 89 | } | 90 | } |
| 90 | const args: string[] = [] | 91 | const args: string[] = [] |
| 91 | - if (!keys.has('user.name')) args.push('-c', 'user.name=skilly') | 92 | + if (!keys.has('user.name')) args.push('-c', 'user.name=panoplie') |
| 92 | - if (!keys.has('user.email')) args.push('-c', 'user.email=skilly@localhost') | 93 | + if (!keys.has('user.email')) args.push('-c', 'user.email=panoplie@localhost') |
| 93 | return args | 94 | return args |
| 94 | } | 95 | } |
| 95 | 96 | ||
| @@ -182,7 +183,7 @@ class GitCatalog implements CatalogGit { | |||
| 182 | this.identityArgs(), | 183 | this.identityArgs(), |
| 183 | ]) | 184 | ]) |
| 184 | if (!s.trim() && merge.code !== 0) return undefined | 185 | if (!s.trim() && merge.code !== 0) return undefined |
| 185 | - await this.git([...identity, 'commit', '-m', message || 'skilly sync']) | 186 | + await this.git([...identity, 'commit', '-m', message || 'panoplie sync']) |
| 186 | return (await this.currentCommit()) ?? undefined | 187 | return (await this.currentCommit()) ?? undefined |
| 187 | } | 188 | } |
| 188 | 189 | ||
| @@ -221,15 +222,20 @@ class GitCatalog implements CatalogGit { | |||
| 221 | return this.status() | 222 | return this.status() |
| 222 | } | 223 | } |
| 223 | 224 | ||
| 224 | - /** Removes `.skilly/index.json` when it holds no item metadata (the file written by `CatalogStore.init`). */ | 225 | + /** |
| 226 | + * Removes `.panoplie/index.json` when it holds no item metadata (the file written by `CatalogStore.init`). | ||
| 227 | + * Same for the index written under the previous name (skilly). | ||
| 228 | + */ | ||
| 225 | private async dropEmptyIndex(): Promise<void> { | 229 | private async dropEmptyIndex(): Promise<void> { |
| 226 | - const file = path.join(this.root, '.skilly', 'index.json') | 230 | + for (const dir of ['.panoplie', LEGACY_CATALOG_META_DIR]) { |
| 227 | - try { | 231 | + const file = path.join(this.root, dir, 'index.json') |
| 228 | - const index = JSON.parse(stripBom(await readFile(file, 'utf8'))) as { items?: unknown } | null | 232 | + try { |
| 229 | - const items = index?.items | 233 | + const index = JSON.parse(stripBom(await readFile(file, 'utf8'))) as { items?: unknown } | null |
| 230 | - if (items && typeof items === 'object' && Object.keys(items).length === 0) await rm(file, { force: true }) | 234 | + const items = index?.items |
| 231 | - } catch { | 235 | + if (items && typeof items === 'object' && Object.keys(items).length === 0) await rm(file, { force: true }) |
| 232 | - // Missing or not ours: left as is (git reports a clear error if it is in the way). | 236 | + } catch { |
| 237 | + // Missing or not ours: left as is (git reports a clear error if it is in the way). | ||
| 238 | + } | ||
| 233 | } | 239 | } |
| 234 | } | 240 | } |
| 235 | 241 | ||
| @@ -245,7 +251,7 @@ class GitCatalog implements CatalogGit { | |||
| 245 | return this.status() | 251 | return this.status() |
| 246 | } | 252 | } |
| 247 | 253 | ||
| 248 | - async sync(message = 'skilly sync'): Promise<GitStatus> { | 254 | + async sync(message = 'panoplie sync'): Promise<GitStatus> { |
| 249 | await this.requireRepo() | 255 | await this.requireRepo() |
| 250 | await this.commitAll(message) | 256 | await this.commitAll(message) |
| 251 | if (!(await this.remoteName())) throw new Error('No git remote configured') | 257 | if (!(await this.remoteName())) throw new Error('No git remote configured') |
modified
packages/core/src/catalog/index.ts +13 -5 | @@ -1,4 +1,4 @@ | ||
| 1 | -import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises' | |
| 1 | +import { mkdir, readdir, readFile, rm, rmdir, stat } from 'node:fs/promises' | |
| 2 | 2 | import path from 'node:path' |
| 3 | 3 | import { stringify } from 'yaml' |
| 4 | 4 | import { |
| @@ -17,6 +17,7 @@ import { | ||
| 17 | 17 | type SaveItemInput, |
| 18 | 18 | } from '../catalog-model.ts' |
| 19 | 19 | import type { CatalogStore, OpenCatalog } from '../contracts.ts' |
| 20 | +import { LEGACY_CATALOG_META_DIR } from '../legacy.ts' | |
| 20 | 21 | import type { HookDetails, InventoryItem, McpDetails } from '../model.ts' |
| 21 | 22 | import { hashPath, isDirectory, listDirs, listFilesRecursive, parseFrontmatter, pathExists, pathFingerprint, readText } from '../util.ts' |
| 22 | 23 | import { atomicWrite, copyDir, movePath, tmpSuffix } from './fsutil.ts' |
| @@ -91,19 +92,24 @@ class Catalog implements CatalogStore { | ||
| 91 | 92 | } |
| 92 | 93 | |
| 93 | 94 | private get indexPath(): string { |
| 94 | - return path.join(this.root, '.skilly', 'index.json') | |
| 95 | + return path.join(this.root, '.panoplie', 'index.json') | |
| 96 | + } | |
| 97 | + | |
| 98 | + /** Index written under the previous name (skilly): read while the new one is missing, removed on the next write. */ | |
| 99 | + private get legacyIndexPath(): string { | |
| 100 | + return path.join(this.root, LEGACY_CATALOG_META_DIR, 'index.json') | |
| 95 | 101 | } |
| 96 | 102 | |
| 97 | 103 | // ---- index ---- |
| 98 | 104 | |
| 99 | 105 | private async readIndex(): Promise<CatalogIndex> { |
| 100 | - const text = await readText(this.indexPath) | |
| 106 | + const text = (await readText(this.indexPath)) ?? (await readText(this.legacyIndexPath)) | |
| 101 | 107 | if (text === undefined) return { version: 1, items: {} } |
| 102 | 108 | let raw: unknown |
| 103 | 109 | try { |
| 104 | 110 | raw = JSON.parse(text.replace(/^/, '')) |
| 105 | 111 | } catch (e) { |
| 106 | - throw new Error(`Invalid .skilly/index.json: ${(e as Error).message}`) | |
| 112 | + throw new Error(`Invalid .panoplie/index.json: ${(e as Error).message}`) | |
| 107 | 113 | } |
| 108 | 114 | const items = (raw as Partial<CatalogIndex> | null)?.items |
| 109 | 115 | return { version: 1, items: items && typeof items === 'object' ? items : {} } |
| @@ -114,6 +120,8 @@ class Catalog implements CatalogStore { | ||
| 114 | 120 | const index = await this.readIndex() |
| 115 | 121 | fn(index) |
| 116 | 122 | await atomicWrite(this.indexPath, JSON.stringify(index, null, 2) + '\n') |
| 123 | + await rm(this.legacyIndexPath, { force: true }) | |
| 124 | + await rmdir(path.dirname(this.legacyIndexPath)).catch(() => {}) | |
| 117 | 125 | } |
| 118 | 126 | const next = this.indexLock.then(run, run) |
| 119 | 127 | this.indexLock = next.catch(() => {}) |
| @@ -130,7 +138,7 @@ class Catalog implements CatalogStore { | ||
| 130 | 138 | for (const t of CATALOG_ITEM_TYPES) await mkdir(this.typeDir(t), { recursive: true }) |
| 131 | 139 | await mkdir(path.join(this.root, 'presets'), { recursive: true }) |
| 132 | 140 | await mkdir(path.dirname(this.indexPath), { recursive: true }) |
| 133 | - if (!(await pathExists(this.indexPath))) { | |
| 141 | + if (!(await pathExists(this.indexPath)) && !(await pathExists(this.legacyIndexPath))) { | |
| 134 | 142 | await atomicWrite(this.indexPath, JSON.stringify({ version: 1, items: {} }, null, 2) + '\n') |
| 135 | 143 | } |
| 136 | 144 | } |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import { mkdir, readdir, readFile, rm, stat } from 'node:fs/promises' | 1 | +import { mkdir, readdir, readFile, rm, rmdir, stat } from 'node:fs/promises' |
| 2 | import path from 'node:path' | 2 | import path from 'node:path' |
| 3 | import { stringify } from 'yaml' | 3 | import { stringify } from 'yaml' |
| 4 | import { | 4 | import { |
| @@ -17,6 +17,7 @@ import { | |||
| 17 | type SaveItemInput, | 17 | type SaveItemInput, |
| 18 | } from '../catalog-model.ts' | 18 | } from '../catalog-model.ts' |
| 19 | import type { CatalogStore, OpenCatalog } from '../contracts.ts' | 19 | import type { CatalogStore, OpenCatalog } from '../contracts.ts' |
| 20 | +import { LEGACY_CATALOG_META_DIR } from '../legacy.ts' | ||
| 20 | import type { HookDetails, InventoryItem, McpDetails } from '../model.ts' | 21 | import type { HookDetails, InventoryItem, McpDetails } from '../model.ts' |
| 21 | import { hashPath, isDirectory, listDirs, listFilesRecursive, parseFrontmatter, pathExists, pathFingerprint, readText } from '../util.ts' | 22 | import { hashPath, isDirectory, listDirs, listFilesRecursive, parseFrontmatter, pathExists, pathFingerprint, readText } from '../util.ts' |
| 22 | import { atomicWrite, copyDir, movePath, tmpSuffix } from './fsutil.ts' | 23 | import { atomicWrite, copyDir, movePath, tmpSuffix } from './fsutil.ts' |
| @@ -91,19 +92,24 @@ class Catalog implements CatalogStore { | |||
| 91 | } | 92 | } |
| 92 | 93 | ||
| 93 | private get indexPath(): string { | 94 | private get indexPath(): string { |
| 94 | - return path.join(this.root, '.skilly', 'index.json') | 95 | + return path.join(this.root, '.panoplie', 'index.json') |
| 96 | + } | ||
| 97 | + | ||
| 98 | + /** Index written under the previous name (skilly): read while the new one is missing, removed on the next write. */ | ||
| 99 | + private get legacyIndexPath(): string { | ||
| 100 | + return path.join(this.root, LEGACY_CATALOG_META_DIR, 'index.json') | ||
| 95 | } | 101 | } |
| 96 | 102 | ||
| 97 | // ---- index ---- | 103 | // ---- index ---- |
| 98 | 104 | ||
| 99 | private async readIndex(): Promise<CatalogIndex> { | 105 | private async readIndex(): Promise<CatalogIndex> { |
| 100 | - const text = await readText(this.indexPath) | 106 | + const text = (await readText(this.indexPath)) ?? (await readText(this.legacyIndexPath)) |
| 101 | if (text === undefined) return { version: 1, items: {} } | 107 | if (text === undefined) return { version: 1, items: {} } |
| 102 | let raw: unknown | 108 | let raw: unknown |
| 103 | try { | 109 | try { |
| 104 | raw = JSON.parse(text.replace(/^/, '')) | 110 | raw = JSON.parse(text.replace(/^/, '')) |
| 105 | } catch (e) { | 111 | } catch (e) { |
| 106 | - throw new Error(`Invalid .skilly/index.json: ${(e as Error).message}`) | 112 | + throw new Error(`Invalid .panoplie/index.json: ${(e as Error).message}`) |
| 107 | } | 113 | } |
| 108 | const items = (raw as Partial<CatalogIndex> | null)?.items | 114 | const items = (raw as Partial<CatalogIndex> | null)?.items |
| 109 | return { version: 1, items: items && typeof items === 'object' ? items : {} } | 115 | return { version: 1, items: items && typeof items === 'object' ? items : {} } |
| @@ -114,6 +120,8 @@ class Catalog implements CatalogStore { | |||
| 114 | const index = await this.readIndex() | 120 | const index = await this.readIndex() |
| 115 | fn(index) | 121 | fn(index) |
| 116 | await atomicWrite(this.indexPath, JSON.stringify(index, null, 2) + '\n') | 122 | await atomicWrite(this.indexPath, JSON.stringify(index, null, 2) + '\n') |
| 123 | + await rm(this.legacyIndexPath, { force: true }) | ||
| 124 | + await rmdir(path.dirname(this.legacyIndexPath)).catch(() => {}) | ||
| 117 | } | 125 | } |
| 118 | const next = this.indexLock.then(run, run) | 126 | const next = this.indexLock.then(run, run) |
| 119 | this.indexLock = next.catch(() => {}) | 127 | this.indexLock = next.catch(() => {}) |
| @@ -130,7 +138,7 @@ class Catalog implements CatalogStore { | |||
| 130 | for (const t of CATALOG_ITEM_TYPES) await mkdir(this.typeDir(t), { recursive: true }) | 138 | for (const t of CATALOG_ITEM_TYPES) await mkdir(this.typeDir(t), { recursive: true }) |
| 131 | await mkdir(path.join(this.root, 'presets'), { recursive: true }) | 139 | await mkdir(path.join(this.root, 'presets'), { recursive: true }) |
| 132 | await mkdir(path.dirname(this.indexPath), { recursive: true }) | 140 | await mkdir(path.dirname(this.indexPath), { recursive: true }) |
| 133 | - if (!(await pathExists(this.indexPath))) { | 141 | + if (!(await pathExists(this.indexPath)) && !(await pathExists(this.legacyIndexPath))) { |
| 134 | await atomicWrite(this.indexPath, JSON.stringify({ version: 1, items: {} }, null, 2) + '\n') | 142 | await atomicWrite(this.indexPath, JSON.stringify({ version: 1, items: {} }, null, 2) + '\n') |
| 135 | } | 143 | } |
| 136 | } | 144 | } |
modified
packages/core/src/contracts.ts +2 -2 | @@ -28,7 +28,7 @@ import type { InventoryItem, ScopeInventory } from './model.ts' | ||
| 28 | 28 | export interface CatalogStore { |
| 29 | 29 | readonly root: string |
| 30 | 30 | exists(): Promise<boolean> |
| 31 | - /** Creates the folder layout and `.skilly/index.json` if missing (idempotent). Does not run git. */ | |
| 31 | + /** Creates the folder layout and `.panoplie/index.json` if missing (idempotent). Does not run git. */ | |
| 32 | 32 | init(): Promise<void> |
| 33 | 33 | snapshot(): Promise<CatalogSnapshot> |
| 34 | 34 | /** `known`: the item as listed by a fresh snapshot, reused instead of hashing the item again. */ |
| @@ -107,7 +107,7 @@ export interface SyncEngine { | ||
| 107 | 107 | |
| 108 | 108 | /** packages/core/src/marketplace — browse sources and fetch skills. */ |
| 109 | 109 | export interface Marketplace { |
| 110 | - /** Built-in + user sources (user sources stored by the server in ~/.skilly/config.json and passed in). */ | |
| 110 | + /** Built-in + user sources (user sources stored by the server in ~/.panoplie/config.json and passed in). */ | |
| 111 | 111 | sources(): MarketplaceSource[] |
| 112 | 112 | /** Clones or updates the source in the cache (`git clone --depth 1` / `git pull`). */ |
| 113 | 113 | refresh(sourceId: string): Promise<MarketplaceSource> |
| @@ -28,7 +28,7 @@ import type { InventoryItem, ScopeInventory } from './model.ts' | |||
| 28 | export interface CatalogStore { | 28 | export interface CatalogStore { |
| 29 | readonly root: string | 29 | readonly root: string |
| 30 | exists(): Promise<boolean> | 30 | exists(): Promise<boolean> |
| 31 | - /** Creates the folder layout and `.skilly/index.json` if missing (idempotent). Does not run git. */ | 31 | + /** Creates the folder layout and `.panoplie/index.json` if missing (idempotent). Does not run git. */ |
| 32 | init(): Promise<void> | 32 | init(): Promise<void> |
| 33 | snapshot(): Promise<CatalogSnapshot> | 33 | snapshot(): Promise<CatalogSnapshot> |
| 34 | /** `known`: the item as listed by a fresh snapshot, reused instead of hashing the item again. */ | 34 | /** `known`: the item as listed by a fresh snapshot, reused instead of hashing the item again. */ |
| @@ -107,7 +107,7 @@ export interface SyncEngine { | |||
| 107 | 107 | ||
| 108 | /** packages/core/src/marketplace — browse sources and fetch skills. */ | 108 | /** packages/core/src/marketplace — browse sources and fetch skills. */ |
| 109 | export interface Marketplace { | 109 | export interface Marketplace { |
| 110 | - /** Built-in + user sources (user sources stored by the server in ~/.skilly/config.json and passed in). */ | 110 | + /** Built-in + user sources (user sources stored by the server in ~/.panoplie/config.json and passed in). */ |
| 111 | sources(): MarketplaceSource[] | 111 | sources(): MarketplaceSource[] |
| 112 | /** Clones or updates the source in the cache (`git clone --depth 1` / `git pull`). */ | 112 | /** Clones or updates the source in the cache (`git clone --depth 1` / `git pull`). */ |
| 113 | refresh(sourceId: string): Promise<MarketplaceSource> | 113 | refresh(sourceId: string): Promise<MarketplaceSource> |
modified
packages/core/src/index.ts +1 -0 | @@ -8,3 +8,4 @@ export { openCatalogGit } from './catalog/git.ts' | ||
| 8 | 8 | export { syncEngine } from './sync/index.ts' |
| 9 | 9 | export { createMarketplace } from './marketplace/index.ts' |
| 10 | 10 | export { normalizePath, hashPath } from './util.ts' |
| 11 | +export { migrateLegacyConfigDir } from './legacy.ts' | |
| @@ -8,3 +8,4 @@ export { openCatalogGit } from './catalog/git.ts' | |||
| 8 | export { syncEngine } from './sync/index.ts' | 8 | export { syncEngine } from './sync/index.ts' |
| 9 | export { createMarketplace } from './marketplace/index.ts' | 9 | export { createMarketplace } from './marketplace/index.ts' |
| 10 | export { normalizePath, hashPath } from './util.ts' | 10 | export { normalizePath, hashPath } from './util.ts' |
| 11 | +export { migrateLegacyConfigDir } from './legacy.ts' | ||
added
packages/core/src/legacy.ts +57 -0 | new file mode 100644 | ||
| @@ -0,0 +1,57 @@ | ||
| 1 | +/** | |
| 2 | + * Migration from the previous name of the project, skilly. Existing installs keep working: | |
| 3 | + * | |
| 4 | + * ~/.skilly/ moved to ~/.panoplie/ on startup (migrateLegacyConfigDir) | |
| 5 | + * skilly.json / skilly.lock read when the new files are missing, renamed on the next write (sync/config.ts) | |
| 6 | + * <catalog>/.skilly/index.json read when the new index is missing, removed on the next write (catalog/index.ts) | |
| 7 | + * <!-- skilly:begin --> AGENTS.md markers still recognized, rewritten on the next change (sync/merge/agents-md.ts) | |
| 8 | + * | |
| 9 | + * Everything that reads a legacy name goes through the constants below, so this can be removed in one go later. | |
| 10 | + */ | |
| 11 | + | |
| 12 | +import { readFile, rename } from 'node:fs/promises' | |
| 13 | +import os from 'node:os' | |
| 14 | +import path from 'node:path' | |
| 15 | +import { pathExists } from './util.ts' | |
| 16 | +import { isRecord, writeFileAtomic } from './sync/helpers.ts' | |
| 17 | + | |
| 18 | +export const LEGACY_CONFIG_FILE = 'skilly.json' | |
| 19 | +export const LEGACY_LOCK_FILE = 'skilly.lock' | |
| 20 | +export const LEGACY_CATALOG_META_DIR = '.skilly' | |
| 21 | +export const LEGACY_AGENTS_MD_NAME = 'skilly' | |
| 22 | + | |
| 23 | +export function legacyConfigDir(home = os.homedir()): string { | |
| 24 | + return path.join(home, '.skilly') | |
| 25 | +} | |
| 26 | + | |
| 27 | +/** | |
| 28 | + * Moves the legacy machine folder to `configDir` when `configDir` does not exist yet. A catalog path set | |
| 29 | + * inside the legacy folder is moved along. Returns true when a migration happened. | |
| 30 | + */ | |
| 31 | +export async function migrateLegacyConfigDir(configDir: string, legacyDir = legacyConfigDir()): Promise<boolean> { | |
| 32 | + if ((await pathExists(configDir)) || !(await pathExists(legacyDir))) return false | |
| 33 | + try { | |
| 34 | + await rename(legacyDir, configDir) | |
| 35 | + } catch (e) { | |
| 36 | + const reason = e instanceof Error ? e.message : String(e) | |
| 37 | + throw new Error( | |
| 38 | + `Could not move ${legacyDir} to ${configDir} (${reason}). Close any running instance and try again, or move the folder by hand.`, | |
| 39 | + ) | |
| 40 | + } | |
| 41 | + await relocateCatalogPath(path.join(configDir, 'config.json'), legacyDir, configDir) | |
| 42 | + return true | |
| 43 | +} | |
| 44 | + | |
| 45 | +async function relocateCatalogPath(file: string, legacyDir: string, configDir: string): Promise<void> { | |
| 46 | + let raw: unknown | |
| 47 | + try { | |
| 48 | + raw = JSON.parse((await readFile(file, 'utf8')).replace(/^/, '')) | |
| 49 | + } catch { | |
| 50 | + return | |
| 51 | + } | |
| 52 | + if (!isRecord(raw) || !isRecord(raw.settings) || typeof raw.settings.catalogPath !== 'string') return | |
| 53 | + const rel = path.relative(legacyDir, raw.settings.catalogPath) | |
| 54 | + if (rel.startsWith('..') || path.isAbsolute(rel)) return | |
| 55 | + raw.settings.catalogPath = path.join(configDir, rel) | |
| 56 | + await writeFileAtomic(file, `${JSON.stringify(raw, null, 2)}\n`) | |
| 57 | +} | |
| new file mode 100644 | |||
| @@ -0,0 +1,57 @@ | |||
| 1 | +/** | ||
| 2 | + * Migration from the previous name of the project, skilly. Existing installs keep working: | ||
| 3 | + * | ||
| 4 | + * ~/.skilly/ moved to ~/.panoplie/ on startup (migrateLegacyConfigDir) | ||
| 5 | + * skilly.json / skilly.lock read when the new files are missing, renamed on the next write (sync/config.ts) | ||
| 6 | + * <catalog>/.skilly/index.json read when the new index is missing, removed on the next write (catalog/index.ts) | ||
| 7 | + * <!-- skilly:begin --> AGENTS.md markers still recognized, rewritten on the next change (sync/merge/agents-md.ts) | ||
| 8 | + * | ||
| 9 | + * Everything that reads a legacy name goes through the constants below, so this can be removed in one go later. | ||
| 10 | + */ | ||
| 11 | + | ||
| 12 | +import { readFile, rename } from 'node:fs/promises' | ||
| 13 | +import os from 'node:os' | ||
| 14 | +import path from 'node:path' | ||
| 15 | +import { pathExists } from './util.ts' | ||
| 16 | +import { isRecord, writeFileAtomic } from './sync/helpers.ts' | ||
| 17 | + | ||
| 18 | +export const LEGACY_CONFIG_FILE = 'skilly.json' | ||
| 19 | +export const LEGACY_LOCK_FILE = 'skilly.lock' | ||
| 20 | +export const LEGACY_CATALOG_META_DIR = '.skilly' | ||
| 21 | +export const LEGACY_AGENTS_MD_NAME = 'skilly' | ||
| 22 | + | ||
| 23 | +export function legacyConfigDir(home = os.homedir()): string { | ||
| 24 | + return path.join(home, '.skilly') | ||
| 25 | +} | ||
| 26 | + | ||
| 27 | +/** | ||
| 28 | + * Moves the legacy machine folder to `configDir` when `configDir` does not exist yet. A catalog path set | ||
| 29 | + * inside the legacy folder is moved along. Returns true when a migration happened. | ||
| 30 | + */ | ||
| 31 | +export async function migrateLegacyConfigDir(configDir: string, legacyDir = legacyConfigDir()): Promise<boolean> { | ||
| 32 | + if ((await pathExists(configDir)) || !(await pathExists(legacyDir))) return false | ||
| 33 | + try { | ||
| 34 | + await rename(legacyDir, configDir) | ||
| 35 | + } catch (e) { | ||
| 36 | + const reason = e instanceof Error ? e.message : String(e) | ||
| 37 | + throw new Error( | ||
| 38 | + `Could not move ${legacyDir} to ${configDir} (${reason}). Close any running instance and try again, or move the folder by hand.`, | ||
| 39 | + ) | ||
| 40 | + } | ||
| 41 | + await relocateCatalogPath(path.join(configDir, 'config.json'), legacyDir, configDir) | ||
| 42 | + return true | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +async function relocateCatalogPath(file: string, legacyDir: string, configDir: string): Promise<void> { | ||
| 46 | + let raw: unknown | ||
| 47 | + try { | ||
| 48 | + raw = JSON.parse((await readFile(file, 'utf8')).replace(/^/, '')) | ||
| 49 | + } catch { | ||
| 50 | + return | ||
| 51 | + } | ||
| 52 | + if (!isRecord(raw) || !isRecord(raw.settings) || typeof raw.settings.catalogPath !== 'string') return | ||
| 53 | + const rel = path.relative(legacyDir, raw.settings.catalogPath) | ||
| 54 | + if (rel.startsWith('..') || path.isAbsolute(rel)) return | ||
| 55 | + raw.settings.catalogPath = path.join(configDir, rel) | ||
| 56 | + await writeFileAtomic(file, `${JSON.stringify(raw, null, 2)}\n`) | ||
| 57 | +} | ||
modified
packages/core/src/sync/config-entries.ts +4 -4 | @@ -1,10 +1,10 @@ | ||
| 1 | 1 | /** |
| 2 | - * Keys skilly owns inside shared files, and how to read / write / remove them. | |
| 2 | + * Keys panoplie owns inside shared files, and how to read / write / remove them. | |
| 3 | 3 | * |
| 4 | 4 | * Lock key formats (`LockConfigEntry.key`): |
| 5 | 5 | * `mcpServers.<name>`, `skillOverrides.<name>` JSON object entry (first dot separates the container) |
| 6 | 6 | * `hooks.<Event>[<name>]`, `deniedMcpServers[<name>]` |
| 7 | - * JSON array item; found by the hash of what skilly wrote | |
| 7 | + * JSON array item; found by the hash of what panoplie wrote | |
| 8 | 8 | * (JSON has no comments to mark it) |
| 9 | 9 | * `mcp_servers.<name>` in a `.toml` file TOML table, edited surgically |
| 10 | 10 | * `agents-md-block:<name>` rule section of the AGENTS.md managed block |
| @@ -32,7 +32,7 @@ export type ConfigLocation = | ||
| 32 | 32 | | { kind: 'toml-table'; file: string; key: string; path: string[] } |
| 33 | 33 | | { kind: 'agents-md-section'; file: string; key: string; name: string } |
| 34 | 34 | |
| 35 | -/** A value skilly wants at a location. `value` is a string (section text) for AGENTS.md. */ | |
| 35 | +/** A value panoplie wants at a location. `value` is a string (section text) for AGENTS.md. */ | |
| 36 | 36 | export interface ConfigWrite { |
| 37 | 37 | loc: ConfigLocation |
| 38 | 38 | value: unknown |
| @@ -112,7 +112,7 @@ export function entryMatches(text: string | undefined, loc: ConfigLocation, sha: | ||
| 112 | 112 | } |
| 113 | 113 | |
| 114 | 114 | /** |
| 115 | - * Writes the value. For array items, `previousSha` identifies the item skilly wrote last time (replaced in place). | |
| 115 | + * Writes the value. For array items, `previousSha` identifies the item panoplie wrote last time (replaced in place). | |
| 116 | 116 | * Returns the new text, or undefined when the file should be deleted. |
| 117 | 117 | */ |
| 118 | 118 | export function setEntry(text: string | undefined, write: ConfigWrite, previousSha?: string): string | undefined { |
| @@ -1,10 +1,10 @@ | |||
| 1 | /** | 1 | /** |
| 2 | - * Keys skilly owns inside shared files, and how to read / write / remove them. | 2 | + * Keys panoplie owns inside shared files, and how to read / write / remove them. |
| 3 | * | 3 | * |
| 4 | * Lock key formats (`LockConfigEntry.key`): | 4 | * Lock key formats (`LockConfigEntry.key`): |
| 5 | * `mcpServers.<name>`, `skillOverrides.<name>` JSON object entry (first dot separates the container) | 5 | * `mcpServers.<name>`, `skillOverrides.<name>` JSON object entry (first dot separates the container) |
| 6 | * `hooks.<Event>[<name>]`, `deniedMcpServers[<name>]` | 6 | * `hooks.<Event>[<name>]`, `deniedMcpServers[<name>]` |
| 7 | - * JSON array item; found by the hash of what skilly wrote | 7 | + * JSON array item; found by the hash of what panoplie wrote |
| 8 | * (JSON has no comments to mark it) | 8 | * (JSON has no comments to mark it) |
| 9 | * `mcp_servers.<name>` in a `.toml` file TOML table, edited surgically | 9 | * `mcp_servers.<name>` in a `.toml` file TOML table, edited surgically |
| 10 | * `agents-md-block:<name>` rule section of the AGENTS.md managed block | 10 | * `agents-md-block:<name>` rule section of the AGENTS.md managed block |
| @@ -32,7 +32,7 @@ export type ConfigLocation = | |||
| 32 | | { kind: 'toml-table'; file: string; key: string; path: string[] } | 32 | | { kind: 'toml-table'; file: string; key: string; path: string[] } |
| 33 | | { kind: 'agents-md-section'; file: string; key: string; name: string } | 33 | | { kind: 'agents-md-section'; file: string; key: string; name: string } |
| 34 | 34 | ||
| 35 | -/** A value skilly wants at a location. `value` is a string (section text) for AGENTS.md. */ | 35 | +/** A value panoplie wants at a location. `value` is a string (section text) for AGENTS.md. */ |
| 36 | export interface ConfigWrite { | 36 | export interface ConfigWrite { |
| 37 | loc: ConfigLocation | 37 | loc: ConfigLocation |
| 38 | value: unknown | 38 | value: unknown |
| @@ -112,7 +112,7 @@ export function entryMatches(text: string | undefined, loc: ConfigLocation, sha: | |||
| 112 | } | 112 | } |
| 113 | 113 | ||
| 114 | /** | 114 | /** |
| 115 | - * Writes the value. For array items, `previousSha` identifies the item skilly wrote last time (replaced in place). | 115 | + * Writes the value. For array items, `previousSha` identifies the item panoplie wrote last time (replaced in place). |
| 116 | * Returns the new text, or undefined when the file should be deleted. | 116 | * Returns the new text, or undefined when the file should be deleted. |
| 117 | */ | 117 | */ |
| 118 | export function setEntry(text: string | undefined, write: ConfigWrite, previousSha?: string): string | undefined { | 118 | export function setEntry(text: string | undefined, write: ConfigWrite, previousSha?: string): string | undefined { |
modified
packages/core/src/sync/config.ts +34 -6 | @@ -1,17 +1,43 @@ | ||
| 1 | -/** Scope configuration (`skilly.json` / `home.json`) and lock (`skilly.lock` / `home.lock`) files. */ | |
| 1 | +/** Scope configuration (`panoplie.json` / `home.json`) and lock (`panoplie.lock` / `home.lock`) files. */ | |
| 2 | 2 | |
| 3 | +import { rename } from 'node:fs/promises' | |
| 3 | 4 | import path from 'node:path' |
| 4 | 5 | import { CATALOG_ITEM_TYPES, type CatalogItemType, type LockEntry, type LockFile, type ScopeConfig, type SyncTarget } from '../catalog-model.ts' |
| 5 | 6 | import { AGENTS, type AgentId } from '../model.ts' |
| 6 | -import { readJson } from '../util.ts' | |
| 7 | +import { LEGACY_CONFIG_FILE, LEGACY_LOCK_FILE } from '../legacy.ts' | |
| 8 | +import { pathExists, readJson } from '../util.ts' | |
| 7 | 9 | import { isRecord, writeFileAtomic } from './helpers.ts' |
| 8 | 10 | |
| 9 | 11 | export function configFile(target: SyncTarget): string { |
| 10 | - return path.join(target.configDir, target.kind === 'home' ? 'home.json' : 'skilly.json') | |
| 12 | + return path.join(target.configDir, target.kind === 'home' ? 'home.json' : 'panoplie.json') | |
| 11 | 13 | } |
| 12 | 14 | |
| 13 | 15 | export function lockFile(target: SyncTarget): string { |
| 14 | - return path.join(target.configDir, target.kind === 'home' ? 'home.lock' : 'skilly.lock') | |
| 16 | + return path.join(target.configDir, target.kind === 'home' ? 'home.lock' : 'panoplie.lock') | |
| 17 | +} | |
| 18 | + | |
| 19 | +/** Project files written under the previous name (skilly). Home files kept their names. */ | |
| 20 | +function legacyFiles(target: SyncTarget): [legacy: string, current: string][] { | |
| 21 | + if (target.kind === 'home') return [] | |
| 22 | + return [ | |
| 23 | + [path.join(target.configDir, LEGACY_CONFIG_FILE), configFile(target)], | |
| 24 | + [path.join(target.configDir, LEGACY_LOCK_FILE), lockFile(target)], | |
| 25 | + ] | |
| 26 | +} | |
| 27 | + | |
| 28 | +/** Reads `file`, or its legacy counterpart when only the legacy one exists. */ | |
| 29 | +async function readJsonWithLegacy(target: SyncTarget, file: string): Promise<unknown> { | |
| 30 | + const raw = await readJson(file) | |
| 31 | + if (raw !== undefined) return raw | |
| 32 | + const legacy = legacyFiles(target).find(([, current]) => current === file)?.[0] | |
| 33 | + return legacy === undefined ? undefined : readJson(legacy) | |
| 34 | +} | |
| 35 | + | |
| 36 | +/** Renames the legacy config and lock before a write, so both files move together. */ | |
| 37 | +async function migrateLegacyFiles(target: SyncTarget): Promise<void> { | |
| 38 | + for (const [legacy, current] of legacyFiles(target)) { | |
| 39 | + if ((await pathExists(legacy)) && !(await pathExists(current))) await rename(legacy, current) | |
| 40 | + } | |
| 15 | 41 | } |
| 16 | 42 | |
| 17 | 43 | export function defaultConfig(): ScopeConfig { |
| @@ -77,11 +103,12 @@ export function formatJsonFile(value: unknown): string { | ||
| 77 | 103 | } |
| 78 | 104 | |
| 79 | 105 | export async function readConfig(target: SyncTarget): Promise<ScopeConfig | undefined> { |
| 80 | - const raw = await readJson(configFile(target)) | |
| 106 | + const raw = await readJsonWithLegacy(target, configFile(target)) | |
| 81 | 107 | return raw === undefined ? undefined : normalizeConfig(raw) |
| 82 | 108 | } |
| 83 | 109 | |
| 84 | 110 | export async function writeConfig(target: SyncTarget, config: ScopeConfig): Promise<void> { |
| 111 | + await migrateLegacyFiles(target) | |
| 85 | 112 | await writeFileAtomic(configFile(target), formatJsonFile(orderedConfig(config))) |
| 86 | 113 | } |
| 87 | 114 | |
| @@ -118,7 +145,7 @@ export function sortLockEntries(entries: LockEntry[]): LockEntry[] { | ||
| 118 | 145 | } |
| 119 | 146 | |
| 120 | 147 | export async function readLock(target: SyncTarget): Promise<LockFile | undefined> { |
| 121 | - const raw = await readJson(lockFile(target)) | |
| 148 | + const raw = await readJsonWithLegacy(target, lockFile(target)) | |
| 122 | 149 | if (raw === undefined) return undefined |
| 123 | 150 | const r = isRecord(raw) ? raw : {} |
| 124 | 151 | const entries = (Array.isArray(r.entries) ? r.entries : []).map(normalizeEntry).filter((e) => e !== undefined) |
| @@ -147,5 +174,6 @@ export async function writeLock(target: SyncTarget, lock: LockFile): Promise<voi | ||
| 147 | 174 | via: e.via, |
| 148 | 175 | })), |
| 149 | 176 | } |
| 177 | + await migrateLegacyFiles(target) | |
| 150 | 178 | await writeFileAtomic(lockFile(target), formatJsonFile(ordered)) |
| 151 | 179 | } |
| @@ -1,17 +1,43 @@ | |||
| 1 | -/** Scope configuration (`skilly.json` / `home.json`) and lock (`skilly.lock` / `home.lock`) files. */ | 1 | +/** Scope configuration (`panoplie.json` / `home.json`) and lock (`panoplie.lock` / `home.lock`) files. */ |
| 2 | 2 | ||
| 3 | +import { rename } from 'node:fs/promises' | ||
| 3 | import path from 'node:path' | 4 | import path from 'node:path' |
| 4 | import { CATALOG_ITEM_TYPES, type CatalogItemType, type LockEntry, type LockFile, type ScopeConfig, type SyncTarget } from '../catalog-model.ts' | 5 | import { CATALOG_ITEM_TYPES, type CatalogItemType, type LockEntry, type LockFile, type ScopeConfig, type SyncTarget } from '../catalog-model.ts' |
| 5 | import { AGENTS, type AgentId } from '../model.ts' | 6 | import { AGENTS, type AgentId } from '../model.ts' |
| 6 | -import { readJson } from '../util.ts' | 7 | +import { LEGACY_CONFIG_FILE, LEGACY_LOCK_FILE } from '../legacy.ts' |
| 8 | +import { pathExists, readJson } from '../util.ts' | ||
| 7 | import { isRecord, writeFileAtomic } from './helpers.ts' | 9 | import { isRecord, writeFileAtomic } from './helpers.ts' |
| 8 | 10 | ||
| 9 | export function configFile(target: SyncTarget): string { | 11 | export function configFile(target: SyncTarget): string { |
| 10 | - return path.join(target.configDir, target.kind === 'home' ? 'home.json' : 'skilly.json') | 12 | + return path.join(target.configDir, target.kind === 'home' ? 'home.json' : 'panoplie.json') |
| 11 | } | 13 | } |
| 12 | 14 | ||
| 13 | export function lockFile(target: SyncTarget): string { | 15 | export function lockFile(target: SyncTarget): string { |
| 14 | - return path.join(target.configDir, target.kind === 'home' ? 'home.lock' : 'skilly.lock') | 16 | + return path.join(target.configDir, target.kind === 'home' ? 'home.lock' : 'panoplie.lock') |
| 17 | +} | ||
| 18 | + | ||
| 19 | +/** Project files written under the previous name (skilly). Home files kept their names. */ | ||
| 20 | +function legacyFiles(target: SyncTarget): [legacy: string, current: string][] { | ||
| 21 | + if (target.kind === 'home') return [] | ||
| 22 | + return [ | ||
| 23 | + [path.join(target.configDir, LEGACY_CONFIG_FILE), configFile(target)], | ||
| 24 | + [path.join(target.configDir, LEGACY_LOCK_FILE), lockFile(target)], | ||
| 25 | + ] | ||
| 26 | +} | ||
| 27 | + | ||
| 28 | +/** Reads `file`, or its legacy counterpart when only the legacy one exists. */ | ||
| 29 | +async function readJsonWithLegacy(target: SyncTarget, file: string): Promise<unknown> { | ||
| 30 | + const raw = await readJson(file) | ||
| 31 | + if (raw !== undefined) return raw | ||
| 32 | + const legacy = legacyFiles(target).find(([, current]) => current === file)?.[0] | ||
| 33 | + return legacy === undefined ? undefined : readJson(legacy) | ||
| 34 | +} | ||
| 35 | + | ||
| 36 | +/** Renames the legacy config and lock before a write, so both files move together. */ | ||
| 37 | +async function migrateLegacyFiles(target: SyncTarget): Promise<void> { | ||
| 38 | + for (const [legacy, current] of legacyFiles(target)) { | ||
| 39 | + if ((await pathExists(legacy)) && !(await pathExists(current))) await rename(legacy, current) | ||
| 40 | + } | ||
| 15 | } | 41 | } |
| 16 | 42 | ||
| 17 | export function defaultConfig(): ScopeConfig { | 43 | export function defaultConfig(): ScopeConfig { |
| @@ -77,11 +103,12 @@ export function formatJsonFile(value: unknown): string { | |||
| 77 | } | 103 | } |
| 78 | 104 | ||
| 79 | export async function readConfig(target: SyncTarget): Promise<ScopeConfig | undefined> { | 105 | export async function readConfig(target: SyncTarget): Promise<ScopeConfig | undefined> { |
| 80 | - const raw = await readJson(configFile(target)) | 106 | + const raw = await readJsonWithLegacy(target, configFile(target)) |
| 81 | return raw === undefined ? undefined : normalizeConfig(raw) | 107 | return raw === undefined ? undefined : normalizeConfig(raw) |
| 82 | } | 108 | } |
| 83 | 109 | ||
| 84 | export async function writeConfig(target: SyncTarget, config: ScopeConfig): Promise<void> { | 110 | export async function writeConfig(target: SyncTarget, config: ScopeConfig): Promise<void> { |
| 111 | + await migrateLegacyFiles(target) | ||
| 85 | await writeFileAtomic(configFile(target), formatJsonFile(orderedConfig(config))) | 112 | await writeFileAtomic(configFile(target), formatJsonFile(orderedConfig(config))) |
| 86 | } | 113 | } |
| 87 | 114 | ||
| @@ -118,7 +145,7 @@ export function sortLockEntries(entries: LockEntry[]): LockEntry[] { | |||
| 118 | } | 145 | } |
| 119 | 146 | ||
| 120 | export async function readLock(target: SyncTarget): Promise<LockFile | undefined> { | 147 | export async function readLock(target: SyncTarget): Promise<LockFile | undefined> { |
| 121 | - const raw = await readJson(lockFile(target)) | 148 | + const raw = await readJsonWithLegacy(target, lockFile(target)) |
| 122 | if (raw === undefined) return undefined | 149 | if (raw === undefined) return undefined |
| 123 | const r = isRecord(raw) ? raw : {} | 150 | const r = isRecord(raw) ? raw : {} |
| 124 | const entries = (Array.isArray(r.entries) ? r.entries : []).map(normalizeEntry).filter((e) => e !== undefined) | 151 | const entries = (Array.isArray(r.entries) ? r.entries : []).map(normalizeEntry).filter((e) => e !== undefined) |
| @@ -147,5 +174,6 @@ export async function writeLock(target: SyncTarget, lock: LockFile): Promise<voi | |||
| 147 | via: e.via, | 174 | via: e.via, |
| 148 | })), | 175 | })), |
| 149 | } | 176 | } |
| 177 | + await migrateLegacyFiles(target) | ||
| 150 | await writeFileAtomic(lockFile(target), formatJsonFile(ordered)) | 178 | await writeFileAtomic(lockFile(target), formatJsonFile(ordered)) |
| 151 | } | 179 | } |
modified
packages/core/src/sync/helpers.ts +1 -1 | @@ -88,7 +88,7 @@ export function hashFiles(files: { rel: string; content: Buffer }[]): string { | ||
| 88 | 88 | /** Writes through a temporary file and a rename, creating parent folders. */ |
| 89 | 89 | export async function writeFileAtomic(file: string, content: string | Buffer): Promise<void> { |
| 90 | 90 | await mkdir(path.dirname(file), { recursive: true }) |
| 91 | - const tmp = `${file}.skilly-${randomBytes(4).toString('hex')}.tmp` | |
| 91 | + const tmp = `${file}.panoplie-${randomBytes(4).toString('hex')}.tmp` | |
| 92 | 92 | try { |
| 93 | 93 | await writeFile(tmp, content) |
| 94 | 94 | await rename(tmp, file) |
| @@ -88,7 +88,7 @@ export function hashFiles(files: { rel: string; content: Buffer }[]): string { | |||
| 88 | /** Writes through a temporary file and a rename, creating parent folders. */ | 88 | /** Writes through a temporary file and a rename, creating parent folders. */ |
| 89 | export async function writeFileAtomic(file: string, content: string | Buffer): Promise<void> { | 89 | export async function writeFileAtomic(file: string, content: string | Buffer): Promise<void> { |
| 90 | await mkdir(path.dirname(file), { recursive: true }) | 90 | await mkdir(path.dirname(file), { recursive: true }) |
| 91 | - const tmp = `${file}.skilly-${randomBytes(4).toString('hex')}.tmp` | 91 | + const tmp = `${file}.panoplie-${randomBytes(4).toString('hex')}.tmp` |
| 92 | try { | 92 | try { |
| 93 | await writeFile(tmp, content) | 93 | await writeFile(tmp, content) |
| 94 | await rename(tmp, file) | 94 | await rename(tmp, file) |
modified
packages/core/src/sync/index.ts +2 -2 | @@ -1,12 +1,12 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * Sync engine: scope configuration, rendering per agent, plan / apply, drift annotation, Home cleanup. |
| 3 | 3 | * |
| 4 | - * config.ts skilly.json / home.json and skilly.lock / home.lock | |
| 4 | + * config.ts panoplie.json / home.json and panoplie.lock / home.lock | |
| 5 | 5 | * resolve.ts presets + add - exclude -> catalog refs |
| 6 | 6 | * layout.ts where each agent reads items (project and Home) |
| 7 | 7 | * render/ catalog item -> files and config entries, per agent; hidden global items |
| 8 | 8 | * merge/ surgical edits of shared files (JSON, TOML tables, AGENTS.md block) |
| 9 | - * config-entries.ts keys skilly owns in shared files (lock key formats) | |
| 9 | + * config-entries.ts keys panoplie owns in shared files (lock key formats) | |
| 10 | 10 | * plan.ts desired renders vs lock vs disk -> actions (pure) |
| 11 | 11 | * apply.ts executes actions and conflict resolutions, writes the lock |
| 12 | 12 | * push.ts push-to-catalog conversions |
| @@ -1,12 +1,12 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * Sync engine: scope configuration, rendering per agent, plan / apply, drift annotation, Home cleanup. | 2 | * Sync engine: scope configuration, rendering per agent, plan / apply, drift annotation, Home cleanup. |
| 3 | * | 3 | * |
| 4 | - * config.ts skilly.json / home.json and skilly.lock / home.lock | 4 | + * config.ts panoplie.json / home.json and panoplie.lock / home.lock |
| 5 | * resolve.ts presets + add - exclude -> catalog refs | 5 | * resolve.ts presets + add - exclude -> catalog refs |
| 6 | * layout.ts where each agent reads items (project and Home) | 6 | * layout.ts where each agent reads items (project and Home) |
| 7 | * render/ catalog item -> files and config entries, per agent; hidden global items | 7 | * render/ catalog item -> files and config entries, per agent; hidden global items |
| 8 | * merge/ surgical edits of shared files (JSON, TOML tables, AGENTS.md block) | 8 | * merge/ surgical edits of shared files (JSON, TOML tables, AGENTS.md block) |
| 9 | - * config-entries.ts keys skilly owns in shared files (lock key formats) | 9 | + * config-entries.ts keys panoplie owns in shared files (lock key formats) |
| 10 | * plan.ts desired renders vs lock vs disk -> actions (pure) | 10 | * plan.ts desired renders vs lock vs disk -> actions (pure) |
| 11 | * apply.ts executes actions and conflict resolutions, writes the lock | 11 | * apply.ts executes actions and conflict resolutions, writes the lock |
| 12 | * push.ts push-to-catalog conversions | 12 | * push.ts push-to-catalog conversions |
modified
packages/core/src/sync/layout.ts +1 -1 | @@ -45,7 +45,7 @@ function projectLayout(): Layout { | ||
| 45 | 45 | codexAgentFile: (n) => `.codex/agents/${n}.toml`, |
| 46 | 46 | codexHooks: '.codex/hooks.json', |
| 47 | 47 | codexConfig: '.codex/config.toml', |
| 48 | - // Empty folders mean nothing to git: in projects every empty folder skilly leaves is removed. | |
| 48 | + // Empty folders mean nothing to git: in projects every empty folder panoplie leaves is removed. | |
| 49 | 49 | keepDirs: [''], |
| 50 | 50 | } |
| 51 | 51 | } |
| @@ -45,7 +45,7 @@ function projectLayout(): Layout { | |||
| 45 | codexAgentFile: (n) => `.codex/agents/${n}.toml`, | 45 | codexAgentFile: (n) => `.codex/agents/${n}.toml`, |
| 46 | codexHooks: '.codex/hooks.json', | 46 | codexHooks: '.codex/hooks.json', |
| 47 | codexConfig: '.codex/config.toml', | 47 | codexConfig: '.codex/config.toml', |
| 48 | - // Empty folders mean nothing to git: in projects every empty folder skilly leaves is removed. | 48 | + // Empty folders mean nothing to git: in projects every empty folder panoplie leaves is removed. |
| 49 | keepDirs: [''], | 49 | keepDirs: [''], |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
modified
packages/core/src/sync/merge/agents-md.ts +22 -13 | @@ -1,23 +1,27 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * Managed block in AGENTS.md. Content outside the block is never touched. |
| 3 | 3 | * |
| 4 | - * <!-- skilly:begin (generated by skilly, do not edit) --> | |
| 5 | - * <!-- skilly:rule typescript-strict --> | |
| 4 | + * <!-- panoplie:begin (generated by panoplie, do not edit) --> | |
| 5 | + * <!-- panoplie:rule typescript-strict --> | |
| 6 | 6 | * ## typescript-strict (applies to **\/*.ts, **\/*.vue) |
| 7 | 7 | * |
| 8 | 8 | * body |
| 9 | - * <!-- skilly:end --> | |
| 9 | + * <!-- panoplie:end --> | |
| 10 | 10 | * |
| 11 | - * Each rule section is preceded by an invisible `skilly:rule` marker so that headings inside rule bodies | |
| 11 | + * Each rule section is preceded by an invisible `panoplie:rule` marker so that headings inside rule bodies | |
| 12 | 12 | * cannot be mistaken for section boundaries. Sections are sorted by rule name. |
| 13 | 13 | */ |
| 14 | 14 | |
| 15 | +import { LEGACY_AGENTS_MD_NAME } from '../../legacy.ts' | |
| 15 | 16 | import { detectEol } from '../helpers.ts' |
| 16 | 17 | |
| 17 | -export const BLOCK_BEGIN = '<!-- skilly:begin (generated by skilly, do not edit) -->' | |
| 18 | -export const BLOCK_END = '<!-- skilly:end -->' | |
| 19 | -const BEGIN_PREFIX = '<!-- skilly:begin' | |
| 20 | -const SECTION_MARKER = /^<!-- skilly:rule (\S+) -->\s*$/ | |
| 18 | +export const BLOCK_BEGIN = '<!-- panoplie:begin (generated by panoplie, do not edit) -->' | |
| 19 | +export const BLOCK_END = '<!-- panoplie:end -->' | |
| 20 | +const BEGIN_PREFIX = '<!-- panoplie:begin' | |
| 21 | +/** Blocks written under the previous name (skilly) are still read; they get the new markers on the next write. */ | |
| 22 | +const LEGACY_BEGIN_PREFIX = `<!-- ${LEGACY_AGENTS_MD_NAME}:begin` | |
| 23 | +const LEGACY_BLOCK_END = `<!-- ${LEGACY_AGENTS_MD_NAME}:end -->` | |
| 24 | +const SECTION_MARKER = /^<!-- (?:panoplie|skilly):rule (\S+) -->\s*$/ | |
| 21 | 25 | |
| 22 | 26 | interface ParsedAgentsMd { |
| 23 | 27 | /** Text before the begin marker line (or the whole file when there is no block). */ |
| @@ -41,14 +45,19 @@ export function normalizeSection(text: string): string { | ||
| 41 | 45 | |
| 42 | 46 | export function parseAgentsMd(text: string | undefined): ParsedAgentsMd { |
| 43 | 47 | const source = text ?? '' |
| 44 | - const begin = findLineStart(source, BEGIN_PREFIX) | |
| 48 | + let begin = findLineStart(source, BEGIN_PREFIX) | |
| 49 | + let endMarker = BLOCK_END | |
| 50 | + if (begin < 0) { | |
| 51 | + begin = findLineStart(source, LEGACY_BEGIN_PREFIX) | |
| 52 | + endMarker = LEGACY_BLOCK_END | |
| 53 | + } | |
| 45 | 54 | if (begin < 0) return { before: source, after: '', hasBlock: false, sections: new Map() } |
| 46 | 55 | const innerStart = lineEnd(source, begin) |
| 47 | - const end = source.indexOf(BLOCK_END, innerStart) | |
| 48 | - if (end < 0) throw new Error('AGENTS.md: the skilly block has no end marker; fix it by hand') | |
| 56 | + const end = source.indexOf(endMarker, innerStart) | |
| 57 | + if (end < 0) throw new Error('AGENTS.md: the panoplie block has no end marker; fix it by hand') | |
| 49 | 58 | return { |
| 50 | 59 | before: source.slice(0, begin), |
| 51 | - after: source.slice(end + BLOCK_END.length), | |
| 60 | + after: source.slice(end + endMarker.length), | |
| 52 | 61 | hasBlock: true, |
| 53 | 62 | sections: parseSections(source.slice(innerStart, end)), |
| 54 | 63 | } |
| @@ -88,7 +97,7 @@ function renderAgentsMd(p: ParsedAgentsMd, eol: string): string | undefined { | ||
| 88 | 97 | |
| 89 | 98 | function renderBlock(sections: Map<string, string>): string { |
| 90 | 99 | const names = [...sections.keys()].sort((a, b) => a.localeCompare(b)) |
| 91 | - const body = names.map((n) => `<!-- skilly:rule ${n} -->\n${sections.get(n)}`).join('\n\n') | |
| 100 | + const body = names.map((n) => `<!-- panoplie:rule ${n} -->\n${sections.get(n)}`).join('\n\n') | |
| 92 | 101 | return `${BLOCK_BEGIN}\n${body}\n${BLOCK_END}` |
| 93 | 102 | } |
| 94 | 103 | |
| @@ -1,23 +1,27 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * Managed block in AGENTS.md. Content outside the block is never touched. | 2 | * Managed block in AGENTS.md. Content outside the block is never touched. |
| 3 | * | 3 | * |
| 4 | - * <!-- skilly:begin (generated by skilly, do not edit) --> | 4 | + * <!-- panoplie:begin (generated by panoplie, do not edit) --> |
| 5 | - * <!-- skilly:rule typescript-strict --> | 5 | + * <!-- panoplie:rule typescript-strict --> |
| 6 | * ## typescript-strict (applies to **\/*.ts, **\/*.vue) | 6 | * ## typescript-strict (applies to **\/*.ts, **\/*.vue) |
| 7 | * | 7 | * |
| 8 | * body | 8 | * body |
| 9 | - * <!-- skilly:end --> | 9 | + * <!-- panoplie:end --> |
| 10 | * | 10 | * |
| 11 | - * Each rule section is preceded by an invisible `skilly:rule` marker so that headings inside rule bodies | 11 | + * Each rule section is preceded by an invisible `panoplie:rule` marker so that headings inside rule bodies |
| 12 | * cannot be mistaken for section boundaries. Sections are sorted by rule name. | 12 | * cannot be mistaken for section boundaries. Sections are sorted by rule name. |
| 13 | */ | 13 | */ |
| 14 | 14 | ||
| 15 | +import { LEGACY_AGENTS_MD_NAME } from '../../legacy.ts' | ||
| 15 | import { detectEol } from '../helpers.ts' | 16 | import { detectEol } from '../helpers.ts' |
| 16 | 17 | ||
| 17 | -export const BLOCK_BEGIN = '<!-- skilly:begin (generated by skilly, do not edit) -->' | 18 | +export const BLOCK_BEGIN = '<!-- panoplie:begin (generated by panoplie, do not edit) -->' |
| 18 | -export const BLOCK_END = '<!-- skilly:end -->' | 19 | +export const BLOCK_END = '<!-- panoplie:end -->' |
| 19 | -const BEGIN_PREFIX = '<!-- skilly:begin' | 20 | +const BEGIN_PREFIX = '<!-- panoplie:begin' |
| 20 | -const SECTION_MARKER = /^<!-- skilly:rule (\S+) -->\s*$/ | 21 | +/** Blocks written under the previous name (skilly) are still read; they get the new markers on the next write. */ |
| 22 | +const LEGACY_BEGIN_PREFIX = `<!-- ${LEGACY_AGENTS_MD_NAME}:begin` | ||
| 23 | +const LEGACY_BLOCK_END = `<!-- ${LEGACY_AGENTS_MD_NAME}:end -->` | ||
| 24 | +const SECTION_MARKER = /^<!-- (?:panoplie|skilly):rule (\S+) -->\s*$/ | ||
| 21 | 25 | ||
| 22 | interface ParsedAgentsMd { | 26 | interface ParsedAgentsMd { |
| 23 | /** Text before the begin marker line (or the whole file when there is no block). */ | 27 | /** Text before the begin marker line (or the whole file when there is no block). */ |
| @@ -41,14 +45,19 @@ export function normalizeSection(text: string): string { | |||
| 41 | 45 | ||
| 42 | export function parseAgentsMd(text: string | undefined): ParsedAgentsMd { | 46 | export function parseAgentsMd(text: string | undefined): ParsedAgentsMd { |
| 43 | const source = text ?? '' | 47 | const source = text ?? '' |
| 44 | - const begin = findLineStart(source, BEGIN_PREFIX) | 48 | + let begin = findLineStart(source, BEGIN_PREFIX) |
| 49 | + let endMarker = BLOCK_END | ||
| 50 | + if (begin < 0) { | ||
| 51 | + begin = findLineStart(source, LEGACY_BEGIN_PREFIX) | ||
| 52 | + endMarker = LEGACY_BLOCK_END | ||
| 53 | + } | ||
| 45 | if (begin < 0) return { before: source, after: '', hasBlock: false, sections: new Map() } | 54 | if (begin < 0) return { before: source, after: '', hasBlock: false, sections: new Map() } |
| 46 | const innerStart = lineEnd(source, begin) | 55 | const innerStart = lineEnd(source, begin) |
| 47 | - const end = source.indexOf(BLOCK_END, innerStart) | 56 | + const end = source.indexOf(endMarker, innerStart) |
| 48 | - if (end < 0) throw new Error('AGENTS.md: the skilly block has no end marker; fix it by hand') | 57 | + if (end < 0) throw new Error('AGENTS.md: the panoplie block has no end marker; fix it by hand') |
| 49 | return { | 58 | return { |
| 50 | before: source.slice(0, begin), | 59 | before: source.slice(0, begin), |
| 51 | - after: source.slice(end + BLOCK_END.length), | 60 | + after: source.slice(end + endMarker.length), |
| 52 | hasBlock: true, | 61 | hasBlock: true, |
| 53 | sections: parseSections(source.slice(innerStart, end)), | 62 | sections: parseSections(source.slice(innerStart, end)), |
| 54 | } | 63 | } |
| @@ -88,7 +97,7 @@ function renderAgentsMd(p: ParsedAgentsMd, eol: string): string | undefined { | |||
| 88 | 97 | ||
| 89 | function renderBlock(sections: Map<string, string>): string { | 98 | function renderBlock(sections: Map<string, string>): string { |
| 90 | const names = [...sections.keys()].sort((a, b) => a.localeCompare(b)) | 99 | const names = [...sections.keys()].sort((a, b) => a.localeCompare(b)) |
| 91 | - const body = names.map((n) => `<!-- skilly:rule ${n} -->\n${sections.get(n)}`).join('\n\n') | 100 | + const body = names.map((n) => `<!-- panoplie:rule ${n} -->\n${sections.get(n)}`).join('\n\n') |
| 92 | return `${BLOCK_BEGIN}\n${body}\n${BLOCK_END}` | 101 | return `${BLOCK_BEGIN}\n${body}\n${BLOCK_END}` |
| 93 | } | 102 | } |
| 94 | 103 | ||
modified
packages/core/src/sync/plan.ts +2 -2 | @@ -187,7 +187,7 @@ async function classifyDesired(d: DesiredItem, entry: LockEntry | undefined, dis | ||
| 187 | 187 | if (state === 'identical') { |
| 188 | 188 | return { action: makeAction('add', d, 'Already present with identical content; taken over', parts), desired: d, wanted: true } |
| 189 | 189 | } |
| 190 | - const action = makeAction('conflict', d, 'The target already exists and is not managed by skilly', parts, { | |
| 190 | + const action = makeAction('conflict', d, 'The target already exists and is not managed by panoplie', parts, { | |
| 191 | 191 | conflict: 'exists-unmanaged', |
| 192 | 192 | }) |
| 193 | 193 | return { action, desired: d, wanted: true } |
| @@ -230,7 +230,7 @@ export function ownedDir(entry: LockEntry): string | undefined { | ||
| 230 | 230 | return top ? path.posix.dirname(top) : undefined |
| 231 | 231 | } |
| 232 | 232 | |
| 233 | -/** True when every file and key recorded in the lock still holds what skilly wrote (and nothing was added to an owned folder). */ | |
| 233 | +/** True when every file and key recorded in the lock still holds what panoplie wrote (and nothing was added to an owned folder). */ | |
| 234 | 234 | export async function diskMatchesLock(entry: LockEntry, disk: DiskReader): Promise<boolean> { |
| 235 | 235 | for (const f of entry.files) { |
| 236 | 236 | if ((await disk.fileHash(f.path)) !== f.sha256) return false |
| @@ -187,7 +187,7 @@ async function classifyDesired(d: DesiredItem, entry: LockEntry | undefined, dis | |||
| 187 | if (state === 'identical') { | 187 | if (state === 'identical') { |
| 188 | return { action: makeAction('add', d, 'Already present with identical content; taken over', parts), desired: d, wanted: true } | 188 | return { action: makeAction('add', d, 'Already present with identical content; taken over', parts), desired: d, wanted: true } |
| 189 | } | 189 | } |
| 190 | - const action = makeAction('conflict', d, 'The target already exists and is not managed by skilly', parts, { | 190 | + const action = makeAction('conflict', d, 'The target already exists and is not managed by panoplie', parts, { |
| 191 | conflict: 'exists-unmanaged', | 191 | conflict: 'exists-unmanaged', |
| 192 | }) | 192 | }) |
| 193 | return { action, desired: d, wanted: true } | 193 | return { action, desired: d, wanted: true } |
| @@ -230,7 +230,7 @@ export function ownedDir(entry: LockEntry): string | undefined { | |||
| 230 | return top ? path.posix.dirname(top) : undefined | 230 | return top ? path.posix.dirname(top) : undefined |
| 231 | } | 231 | } |
| 232 | 232 | ||
| 233 | -/** True when every file and key recorded in the lock still holds what skilly wrote (and nothing was added to an owned folder). */ | 233 | +/** True when every file and key recorded in the lock still holds what panoplie wrote (and nothing was added to an owned folder). */ |
| 234 | export async function diskMatchesLock(entry: LockEntry, disk: DiskReader): Promise<boolean> { | 234 | export async function diskMatchesLock(entry: LockEntry, disk: DiskReader): Promise<boolean> { |
| 235 | for (const f of entry.files) { | 235 | for (const f of entry.files) { |
| 236 | if ((await disk.fileHash(f.path)) !== f.sha256) return false | 236 | if ((await disk.fileHash(f.path)) !== f.sha256) return false |
modified
packages/core/src/sync/push.ts +2 -2 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * push-to-catalog: converts the scope version of an item back to the catalog format and saves it. |
| 3 | 3 | * Supported: skills, rules, subagents, MCP servers. Hooks and hidden global items cannot be pushed |
| 4 | - * (a modified hook group cannot be located once it differs from what skilly wrote). | |
| 4 | + * (a modified hook group cannot be located once it differs from what panoplie wrote). | |
| 5 | 5 | */ |
| 6 | 6 | |
| 7 | 7 | import { readFile } from 'node:fs/promises' |
| @@ -65,7 +65,7 @@ async function readRequired(ctx: PushContext, rel: string): Promise<string> { | ||
| 65 | 65 | |
| 66 | 66 | async function skillVersion(ref: CatalogRef, agent: AgentId, ctx: PushContext): Promise<SaveItemInput> { |
| 67 | 67 | const dir = agent === 'claude' ? ctx.layout.claudeSkillDir(ref.name) : ctx.layout.codexSkillDir(ref.name) |
| 68 | - // The Codex on-demand policy file is generated by skilly: it is not part of the skill. | |
| 68 | + // The Codex on-demand policy file is generated by panoplie: it is not part of the skill. | |
| 69 | 69 | const skipGenerated = agent === 'codex' && matchesAny(ctx.config.onDemand.skills, ref.name) |
| 70 | 70 | const local = (await ctx.disk.listFiles(dir)).filter((rel) => !(skipGenerated && rel === OPENAI_YAML)) |
| 71 | 71 | if (!local.includes('SKILL.md')) throw new Error(`${dir}/SKILL.md does not exist`) |
| @@ -1,7 +1,7 @@ | |||
| 1 | /** | 1 | /** |
| 2 | * push-to-catalog: converts the scope version of an item back to the catalog format and saves it. | 2 | * push-to-catalog: converts the scope version of an item back to the catalog format and saves it. |
| 3 | * Supported: skills, rules, subagents, MCP servers. Hooks and hidden global items cannot be pushed | 3 | * Supported: skills, rules, subagents, MCP servers. Hooks and hidden global items cannot be pushed |
| 4 | - * (a modified hook group cannot be located once it differs from what skilly wrote). | 4 | + * (a modified hook group cannot be located once it differs from what panoplie wrote). |
| 5 | */ | 5 | */ |
| 6 | 6 | ||
| 7 | import { readFile } from 'node:fs/promises' | 7 | import { readFile } from 'node:fs/promises' |
| @@ -65,7 +65,7 @@ async function readRequired(ctx: PushContext, rel: string): Promise<string> { | |||
| 65 | 65 | ||
| 66 | async function skillVersion(ref: CatalogRef, agent: AgentId, ctx: PushContext): Promise<SaveItemInput> { | 66 | async function skillVersion(ref: CatalogRef, agent: AgentId, ctx: PushContext): Promise<SaveItemInput> { |
| 67 | const dir = agent === 'claude' ? ctx.layout.claudeSkillDir(ref.name) : ctx.layout.codexSkillDir(ref.name) | 67 | const dir = agent === 'claude' ? ctx.layout.claudeSkillDir(ref.name) : ctx.layout.codexSkillDir(ref.name) |
| 68 | - // The Codex on-demand policy file is generated by skilly: it is not part of the skill. | 68 | + // The Codex on-demand policy file is generated by panoplie: it is not part of the skill. |
| 69 | const skipGenerated = agent === 'codex' && matchesAny(ctx.config.onDemand.skills, ref.name) | 69 | const skipGenerated = agent === 'codex' && matchesAny(ctx.config.onDemand.skills, ref.name) |
| 70 | const local = (await ctx.disk.listFiles(dir)).filter((rel) => !(skipGenerated && rel === OPENAI_YAML)) | 70 | const local = (await ctx.disk.listFiles(dir)).filter((rel) => !(skipGenerated && rel === OPENAI_YAML)) |
| 71 | if (!local.includes('SKILL.md')) throw new Error(`${dir}/SKILL.md does not exist`) | 71 | if (!local.includes('SKILL.md')) throw new Error(`${dir}/SKILL.md does not exist`) |
modified
packages/core/src/sync/render/types.ts +1 -1 | @@ -10,7 +10,7 @@ export interface RenderedFile { | ||
| 10 | 10 | sha256: string |
| 11 | 11 | } |
| 12 | 12 | |
| 13 | -/** What skilly wants on disk for one item and one agent. */ | |
| 13 | +/** What panoplie wants on disk for one item and one agent. */ | |
| 14 | 14 | export interface RenderedItem { |
| 15 | 15 | type: CatalogItemType |
| 16 | 16 | name: string |
| @@ -10,7 +10,7 @@ export interface RenderedFile { | |||
| 10 | sha256: string | 10 | sha256: string |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | -/** What skilly wants on disk for one item and one agent. */ | 13 | +/** What panoplie wants on disk for one item and one agent. */ |
| 14 | export interface RenderedItem { | 14 | export interface RenderedItem { |
| 15 | type: CatalogItemType | 15 | type: CatalogItemType |
| 16 | name: string | 16 | name: string |
modified
packages/core/test/catalog-git.test.ts +1 -1 | @@ -25,7 +25,7 @@ async function setup(dir: string): Promise<void> { | ||
| 25 | 25 | } |
| 26 | 26 | |
| 27 | 27 | beforeAll(async () => { |
| 28 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-git-')) | |
| 28 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-git-')) | |
| 29 | 29 | remote = path.join(tmp, 'remote.git') |
| 30 | 30 | git(tmp, 'init', '--bare', '--initial-branch=main', remote) |
| 31 | 31 | a = path.join(tmp, 'a') |
| @@ -25,7 +25,7 @@ async function setup(dir: string): Promise<void> { | |||
| 25 | } | 25 | } |
| 26 | 26 | ||
| 27 | beforeAll(async () => { | 27 | beforeAll(async () => { |
| 28 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-git-')) | 28 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-git-')) |
| 29 | remote = path.join(tmp, 'remote.git') | 29 | remote = path.join(tmp, 'remote.git') |
| 30 | git(tmp, 'init', '--bare', '--initial-branch=main', remote) | 30 | git(tmp, 'init', '--bare', '--initial-branch=main', remote) |
| 31 | a = path.join(tmp, 'a') | 31 | a = path.join(tmp, 'a') |
modified
packages/core/test/catalog.test.ts +5 -5 | @@ -14,7 +14,7 @@ let cat: CatalogStore | ||
| 14 | 14 | |
| 15 | 15 | const roots: string[] = [] |
| 16 | 16 | beforeEach(async () => { |
| 17 | - tmp ??= await mkdtemp(path.join(os.tmpdir(), 'skilly-catalog-')) | |
| 17 | + tmp ??= await mkdtemp(path.join(os.tmpdir(), 'panoplie-catalog-')) | |
| 18 | 18 | roots.push(tmp) |
| 19 | 19 | root = path.join(tmp, `cat${n++}`) |
| 20 | 20 | cat = openCatalog(root) |
| @@ -46,8 +46,8 @@ describe('catalog store', () => { | ||
| 46 | 46 | await cat.init() |
| 47 | 47 | await cat.init() |
| 48 | 48 | expect(await cat.exists()).toBe(true) |
| 49 | - expect((await readdir(root)).sort()).toEqual(['.skilly', 'agents', 'hooks', 'mcp', 'presets', 'rules', 'skills']) | |
| 50 | - expect(JSON.parse(await readFile(path.join(root, '.skilly', 'index.json'), 'utf8'))).toEqual({ version: 1, items: {} }) | |
| 49 | + expect((await readdir(root)).sort()).toEqual(['.panoplie', 'agents', 'hooks', 'mcp', 'presets', 'rules', 'skills']) | |
| 50 | + expect(JSON.parse(await readFile(path.join(root, '.panoplie', 'index.json'), 'utf8'))).toEqual({ version: 1, items: {} }) | |
| 51 | 51 | }) |
| 52 | 52 | |
| 53 | 53 | it('validates names', async () => { |
| @@ -102,7 +102,7 @@ describe('catalog store', () => { | ||
| 102 | 102 | expect(renamed.tags).toEqual(['t']) |
| 103 | 103 | expect(renamed.origin?.source).toBe('/x') |
| 104 | 104 | expect(await cat.getItem({ type: 'skill', name: 'demo' })).toBeUndefined() |
| 105 | - const index = JSON.parse(await readFile(path.join(root, '.skilly', 'index.json'), 'utf8')) | |
| 105 | + const index = JSON.parse(await readFile(path.join(root, '.panoplie', 'index.json'), 'utf8')) | |
| 106 | 106 | expect(Object.keys(index.items)).toEqual(['skill/demo2']) |
| 107 | 107 | await cat.saveItem({ type: 'rule', name: 'r1', content: '---\ndescription: R\n---\nx' }) |
| 108 | 108 | await cat.saveItem({ type: 'rule', name: 'r2', content: 'y' }) |
| @@ -138,7 +138,7 @@ describe('catalog store', () => { | ||
| 138 | 138 | await put(path.join(root, 'skills', 'nofile', 'README.md'), 'x') |
| 139 | 139 | await put(path.join(root, 'presets', 'bad.yaml'), 'includes: nope\n') |
| 140 | 140 | await put(path.join(root, 'rules', 'ok.md'), '---\ndescription: fine\n---\n') |
| 141 | - await put(path.join(root, '.skilly', 'index.json'), '{not json') | |
| 141 | + await put(path.join(root, '.panoplie', 'index.json'), '{not json') | |
| 142 | 142 | const snap = await cat.snapshot() |
| 143 | 143 | expect(snap.items.map((i) => i.name)).toEqual(['ok']) |
| 144 | 144 | expect(snap.items[0]?.description).toBe('fine') |
| @@ -14,7 +14,7 @@ let cat: CatalogStore | |||
| 14 | 14 | ||
| 15 | const roots: string[] = [] | 15 | const roots: string[] = [] |
| 16 | beforeEach(async () => { | 16 | beforeEach(async () => { |
| 17 | - tmp ??= await mkdtemp(path.join(os.tmpdir(), 'skilly-catalog-')) | 17 | + tmp ??= await mkdtemp(path.join(os.tmpdir(), 'panoplie-catalog-')) |
| 18 | roots.push(tmp) | 18 | roots.push(tmp) |
| 19 | root = path.join(tmp, `cat${n++}`) | 19 | root = path.join(tmp, `cat${n++}`) |
| 20 | cat = openCatalog(root) | 20 | cat = openCatalog(root) |
| @@ -46,8 +46,8 @@ describe('catalog store', () => { | |||
| 46 | await cat.init() | 46 | await cat.init() |
| 47 | await cat.init() | 47 | await cat.init() |
| 48 | expect(await cat.exists()).toBe(true) | 48 | expect(await cat.exists()).toBe(true) |
| 49 | - expect((await readdir(root)).sort()).toEqual(['.skilly', 'agents', 'hooks', 'mcp', 'presets', 'rules', 'skills']) | 49 | + expect((await readdir(root)).sort()).toEqual(['.panoplie', 'agents', 'hooks', 'mcp', 'presets', 'rules', 'skills']) |
| 50 | - expect(JSON.parse(await readFile(path.join(root, '.skilly', 'index.json'), 'utf8'))).toEqual({ version: 1, items: {} }) | 50 | + expect(JSON.parse(await readFile(path.join(root, '.panoplie', 'index.json'), 'utf8'))).toEqual({ version: 1, items: {} }) |
| 51 | }) | 51 | }) |
| 52 | 52 | ||
| 53 | it('validates names', async () => { | 53 | it('validates names', async () => { |
| @@ -102,7 +102,7 @@ describe('catalog store', () => { | |||
| 102 | expect(renamed.tags).toEqual(['t']) | 102 | expect(renamed.tags).toEqual(['t']) |
| 103 | expect(renamed.origin?.source).toBe('/x') | 103 | expect(renamed.origin?.source).toBe('/x') |
| 104 | expect(await cat.getItem({ type: 'skill', name: 'demo' })).toBeUndefined() | 104 | expect(await cat.getItem({ type: 'skill', name: 'demo' })).toBeUndefined() |
| 105 | - const index = JSON.parse(await readFile(path.join(root, '.skilly', 'index.json'), 'utf8')) | 105 | + const index = JSON.parse(await readFile(path.join(root, '.panoplie', 'index.json'), 'utf8')) |
| 106 | expect(Object.keys(index.items)).toEqual(['skill/demo2']) | 106 | expect(Object.keys(index.items)).toEqual(['skill/demo2']) |
| 107 | await cat.saveItem({ type: 'rule', name: 'r1', content: '---\ndescription: R\n---\nx' }) | 107 | await cat.saveItem({ type: 'rule', name: 'r1', content: '---\ndescription: R\n---\nx' }) |
| 108 | await cat.saveItem({ type: 'rule', name: 'r2', content: 'y' }) | 108 | await cat.saveItem({ type: 'rule', name: 'r2', content: 'y' }) |
| @@ -138,7 +138,7 @@ describe('catalog store', () => { | |||
| 138 | await put(path.join(root, 'skills', 'nofile', 'README.md'), 'x') | 138 | await put(path.join(root, 'skills', 'nofile', 'README.md'), 'x') |
| 139 | await put(path.join(root, 'presets', 'bad.yaml'), 'includes: nope\n') | 139 | await put(path.join(root, 'presets', 'bad.yaml'), 'includes: nope\n') |
| 140 | await put(path.join(root, 'rules', 'ok.md'), '---\ndescription: fine\n---\n') | 140 | await put(path.join(root, 'rules', 'ok.md'), '---\ndescription: fine\n---\n') |
| 141 | - await put(path.join(root, '.skilly', 'index.json'), '{not json') | 141 | + await put(path.join(root, '.panoplie', 'index.json'), '{not json') |
| 142 | const snap = await cat.snapshot() | 142 | const snap = await cat.snapshot() |
| 143 | expect(snap.items.map((i) => i.name)).toEqual(['ok']) | 143 | expect(snap.items.map((i) => i.name)).toEqual(['ok']) |
| 144 | expect(snap.items[0]?.description).toBe('fine') | 144 | expect(snap.items[0]?.description).toBe('fine') |
modified
packages/core/test/claude.test.ts +1 -1 | @@ -28,7 +28,7 @@ function find(r: ScanResult, type: string, name: string): InventoryItem { | ||
| 28 | 28 | const SECRET = 'sk-ant-api03-AAAAAAAAAAAAAAAAAAAAAAAA' |
| 29 | 29 | |
| 30 | 30 | beforeAll(async () => { |
| 31 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-claude-')) | |
| 31 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-claude-')) | |
| 32 | 32 | home = path.join(tmp, 'home') |
| 33 | 33 | project = path.join(tmp, 'proj') |
| 34 | 34 | ctx = { homeDir: home } |
| @@ -28,7 +28,7 @@ function find(r: ScanResult, type: string, name: string): InventoryItem { | |||
| 28 | const SECRET = 'sk-ant-api03-AAAAAAAAAAAAAAAAAAAAAAAA' | 28 | const SECRET = 'sk-ant-api03-AAAAAAAAAAAAAAAAAAAAAAAA' |
| 29 | 29 | ||
| 30 | beforeAll(async () => { | 30 | beforeAll(async () => { |
| 31 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-claude-')) | 31 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-claude-')) |
| 32 | home = path.join(tmp, 'home') | 32 | home = path.join(tmp, 'home') |
| 33 | project = path.join(tmp, 'proj') | 33 | project = path.join(tmp, 'proj') |
| 34 | ctx = { homeDir: home } | 34 | ctx = { homeDir: home } |
modified
packages/core/test/codex.test.ts +3 -3 | @@ -30,7 +30,7 @@ function find(r: ScanResult, type: string, name: string): InventoryItem { | ||
| 30 | 30 | const TOKEN = 'abcDEF1234567890ghijKLMN0987654321xyz' |
| 31 | 31 | |
| 32 | 32 | beforeAll(async () => { |
| 33 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-codex-')) | |
| 33 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-codex-')) | |
| 34 | 34 | homeDir = path.join(tmp, 'home') |
| 35 | 35 | codexHome = path.join(tmp, 'codex-home') |
| 36 | 36 | project = path.join(tmp, 'proj') |
| @@ -226,7 +226,7 @@ describe('codex scanHome', () => { | ||
| 226 | 226 | }) |
| 227 | 227 | |
| 228 | 228 | it('is not detected on an empty home', async () => { |
| 229 | - const empty = await mkdtemp(path.join(os.tmpdir(), 'skilly-codex-empty-')) | |
| 229 | + const empty = await mkdtemp(path.join(os.tmpdir(), 'panoplie-codex-empty-')) | |
| 230 | 230 | try { |
| 231 | 231 | const res = await codexScanner.scanHome({ homeDir: empty }) |
| 232 | 232 | expect(res.detected).toBe(false) |
| @@ -298,7 +298,7 @@ describe('codex scanProject', () => { | ||
| 298 | 298 | }) |
| 299 | 299 | |
| 300 | 300 | it('is not detected on a folder without codex files', async () => { |
| 301 | - const empty = await mkdtemp(path.join(os.tmpdir(), 'skilly-codex-emptyproj-')) | |
| 301 | + const empty = await mkdtemp(path.join(os.tmpdir(), 'panoplie-codex-emptyproj-')) | |
| 302 | 302 | try { |
| 303 | 303 | const r = await codexScanner.scanProject(empty, ctx) |
| 304 | 304 | expect(r.detected).toBe(false) |
| @@ -30,7 +30,7 @@ function find(r: ScanResult, type: string, name: string): InventoryItem { | |||
| 30 | const TOKEN = 'abcDEF1234567890ghijKLMN0987654321xyz' | 30 | const TOKEN = 'abcDEF1234567890ghijKLMN0987654321xyz' |
| 31 | 31 | ||
| 32 | beforeAll(async () => { | 32 | beforeAll(async () => { |
| 33 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-codex-')) | 33 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-codex-')) |
| 34 | homeDir = path.join(tmp, 'home') | 34 | homeDir = path.join(tmp, 'home') |
| 35 | codexHome = path.join(tmp, 'codex-home') | 35 | codexHome = path.join(tmp, 'codex-home') |
| 36 | project = path.join(tmp, 'proj') | 36 | project = path.join(tmp, 'proj') |
| @@ -226,7 +226,7 @@ describe('codex scanHome', () => { | |||
| 226 | }) | 226 | }) |
| 227 | 227 | ||
| 228 | it('is not detected on an empty home', async () => { | 228 | it('is not detected on an empty home', async () => { |
| 229 | - const empty = await mkdtemp(path.join(os.tmpdir(), 'skilly-codex-empty-')) | 229 | + const empty = await mkdtemp(path.join(os.tmpdir(), 'panoplie-codex-empty-')) |
| 230 | try { | 230 | try { |
| 231 | const res = await codexScanner.scanHome({ homeDir: empty }) | 231 | const res = await codexScanner.scanHome({ homeDir: empty }) |
| 232 | expect(res.detected).toBe(false) | 232 | expect(res.detected).toBe(false) |
| @@ -298,7 +298,7 @@ describe('codex scanProject', () => { | |||
| 298 | }) | 298 | }) |
| 299 | 299 | ||
| 300 | it('is not detected on a folder without codex files', async () => { | 300 | it('is not detected on a folder without codex files', async () => { |
| 301 | - const empty = await mkdtemp(path.join(os.tmpdir(), 'skilly-codex-emptyproj-')) | 301 | + const empty = await mkdtemp(path.join(os.tmpdir(), 'panoplie-codex-emptyproj-')) |
| 302 | try { | 302 | try { |
| 303 | const r = await codexScanner.scanProject(empty, ctx) | 303 | const r = await codexScanner.scanProject(empty, ctx) |
| 304 | expect(r.detected).toBe(false) | 304 | expect(r.detected).toBe(false) |
added
packages/core/test/legacy.test.ts +100 -0 | new file mode 100644 | ||
| @@ -0,0 +1,100 @@ | ||
| 1 | +import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' | |
| 2 | +import os from 'node:os' | |
| 3 | +import path from 'node:path' | |
| 4 | +import { afterAll, beforeEach, describe, expect, it } from 'vitest' | |
| 5 | +import type { SyncTarget } from '../src/catalog-model.ts' | |
| 6 | +import { openCatalog } from '../src/catalog/index.ts' | |
| 7 | +import { migrateLegacyConfigDir } from '../src/legacy.ts' | |
| 8 | +import { readConfig, readLock, writeConfig, writeLock } from '../src/sync/config.ts' | |
| 9 | +import { getSection, setSection } from '../src/sync/merge/agents-md.ts' | |
| 10 | +import { pathExists } from '../src/util.ts' | |
| 11 | + | |
| 12 | +let tmp: string | |
| 13 | +let n = 0 | |
| 14 | +let dir: string | |
| 15 | + | |
| 16 | +beforeEach(async () => { | |
| 17 | + tmp ??= await mkdtemp(path.join(os.tmpdir(), 'panoplie-legacy-')) | |
| 18 | + dir = path.join(tmp, `case${n++}`) | |
| 19 | + await mkdir(dir, { recursive: true }) | |
| 20 | +}) | |
| 21 | +afterAll(async () => { | |
| 22 | + if (tmp) await rm(tmp, { recursive: true, force: true }) | |
| 23 | +}) | |
| 24 | + | |
| 25 | +async function put(file: string, content: string): Promise<void> { | |
| 26 | + await mkdir(path.dirname(file), { recursive: true }) | |
| 27 | + await writeFile(file, content) | |
| 28 | +} | |
| 29 | + | |
| 30 | +describe('migration from skilly', () => { | |
| 31 | + it('moves the legacy machine folder and a catalog path inside it', async () => { | |
| 32 | + const legacy = path.join(dir, '.skilly') | |
| 33 | + const current = path.join(dir, '.panoplie') | |
| 34 | + await put(path.join(legacy, 'catalog', 'skills', 'x', 'SKILL.md'), 'x') | |
| 35 | + await put(path.join(legacy, 'config.json'), JSON.stringify({ version: 1, settings: { catalogPath: path.join(legacy, 'catalog') } })) | |
| 36 | + | |
| 37 | + expect(await migrateLegacyConfigDir(current, legacy)).toBe(true) | |
| 38 | + expect(await pathExists(legacy)).toBe(false) | |
| 39 | + expect(await pathExists(path.join(current, 'catalog', 'skills', 'x', 'SKILL.md'))).toBe(true) | |
| 40 | + const config = JSON.parse(await readFile(path.join(current, 'config.json'), 'utf8')) | |
| 41 | + expect(config.settings.catalogPath).toBe(path.join(current, 'catalog')) | |
| 42 | + | |
| 43 | + // Nothing to do once migrated, or when the new folder already exists. | |
| 44 | + await mkdir(legacy) | |
| 45 | + expect(await migrateLegacyConfigDir(current, legacy)).toBe(false) | |
| 46 | + expect(await pathExists(legacy)).toBe(true) | |
| 47 | + }) | |
| 48 | + | |
| 49 | + it('keeps a catalog path outside the legacy folder', async () => { | |
| 50 | + const legacy = path.join(dir, '.skilly') | |
| 51 | + const elsewhere = path.join(dir, 'my-catalog') | |
| 52 | + await put(path.join(legacy, 'config.json'), JSON.stringify({ version: 1, settings: { catalogPath: elsewhere } })) | |
| 53 | + await migrateLegacyConfigDir(path.join(dir, '.panoplie'), legacy) | |
| 54 | + const config = JSON.parse(await readFile(path.join(dir, '.panoplie', 'config.json'), 'utf8')) | |
| 55 | + expect(config.settings.catalogPath).toBe(elsewhere) | |
| 56 | + }) | |
| 57 | + | |
| 58 | + it('reads skilly.json and skilly.lock, then renames both on the next write', async () => { | |
| 59 | + const target: SyncTarget = { kind: 'project', root: dir, homeDir: dir, configDir: dir } | |
| 60 | + await put(path.join(dir, 'skilly.json'), JSON.stringify({ presets: ['base'] })) | |
| 61 | + await put(path.join(dir, 'skilly.lock'), JSON.stringify({ version: 1, syncedAt: 'then', entries: [] })) | |
| 62 | + | |
| 63 | + expect((await readConfig(target))?.presets).toEqual(['base']) | |
| 64 | + expect((await readLock(target))?.syncedAt).toBe('then') | |
| 65 | + | |
| 66 | + const lock = (await readLock(target))! | |
| 67 | + await writeLock(target, { ...lock, syncedAt: 'now' }) | |
| 68 | + expect(await pathExists(path.join(dir, 'skilly.json'))).toBe(false) | |
| 69 | + expect(await pathExists(path.join(dir, 'skilly.lock'))).toBe(false) | |
| 70 | + expect((await readConfig(target))?.presets).toEqual(['base']) | |
| 71 | + expect((await readLock(target))?.syncedAt).toBe('now') | |
| 72 | + | |
| 73 | + await writeConfig(target, { ...(await readConfig(target))!, presets: ['other'] }) | |
| 74 | + expect((await readConfig(target))?.presets).toEqual(['other']) | |
| 75 | + }) | |
| 76 | + | |
| 77 | + it('reads the legacy catalog index and replaces it on the next write', async () => { | |
| 78 | + const root = path.join(dir, 'catalog') | |
| 79 | + await put(path.join(root, 'skills', 'demo', 'SKILL.md'), '---\nname: demo\ndescription: A demo\n---\nBody\n') | |
| 80 | + await put(path.join(root, '.skilly', 'index.json'), JSON.stringify({ version: 1, items: { 'skill/demo': { tags: ['old'] } } })) | |
| 81 | + const cat = openCatalog(root) | |
| 82 | + await cat.init() | |
| 83 | + expect(await pathExists(path.join(root, '.panoplie', 'index.json'))).toBe(false) | |
| 84 | + expect((await cat.getItem({ type: 'skill', name: 'demo' }))?.item.tags).toEqual(['old']) | |
| 85 | + | |
| 86 | + await cat.setTags({ type: 'skill', name: 'demo' }, ['old', 'new']) | |
| 87 | + expect(await pathExists(path.join(root, '.skilly'))).toBe(false) | |
| 88 | + const index = JSON.parse(await readFile(path.join(root, '.panoplie', 'index.json'), 'utf8')) | |
| 89 | + expect(index.items['skill/demo'].tags).toEqual(['old', 'new']) | |
| 90 | + }) | |
| 91 | + | |
| 92 | + it('reads a legacy AGENTS.md block and rewrites it with the new markers', () => { | |
| 93 | + const text = '# Mine\n\n<!-- skilly:begin (generated by skilly, do not edit) -->\n<!-- skilly:rule ts -->\n## ts\n\nStrict.\n<!-- skilly:end -->\n\nAfter\n' | |
| 94 | + expect(getSection(text, 'ts')).toBe('## ts\n\nStrict.') | |
| 95 | + const out = setSection(text, 'ts', '## ts\n\nStricter.') | |
| 96 | + expect(out).toBe( | |
| 97 | + '# Mine\n\n<!-- panoplie:begin (generated by panoplie, do not edit) -->\n<!-- panoplie:rule ts -->\n## ts\n\nStricter.\n<!-- panoplie:end -->\n\nAfter\n', | |
| 98 | + ) | |
| 99 | + }) | |
| 100 | +}) | |
| new file mode 100644 | |||
| @@ -0,0 +1,100 @@ | |||
| 1 | +import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises' | ||
| 2 | +import os from 'node:os' | ||
| 3 | +import path from 'node:path' | ||
| 4 | +import { afterAll, beforeEach, describe, expect, it } from 'vitest' | ||
| 5 | +import type { SyncTarget } from '../src/catalog-model.ts' | ||
| 6 | +import { openCatalog } from '../src/catalog/index.ts' | ||
| 7 | +import { migrateLegacyConfigDir } from '../src/legacy.ts' | ||
| 8 | +import { readConfig, readLock, writeConfig, writeLock } from '../src/sync/config.ts' | ||
| 9 | +import { getSection, setSection } from '../src/sync/merge/agents-md.ts' | ||
| 10 | +import { pathExists } from '../src/util.ts' | ||
| 11 | + | ||
| 12 | +let tmp: string | ||
| 13 | +let n = 0 | ||
| 14 | +let dir: string | ||
| 15 | + | ||
| 16 | +beforeEach(async () => { | ||
| 17 | + tmp ??= await mkdtemp(path.join(os.tmpdir(), 'panoplie-legacy-')) | ||
| 18 | + dir = path.join(tmp, `case${n++}`) | ||
| 19 | + await mkdir(dir, { recursive: true }) | ||
| 20 | +}) | ||
| 21 | +afterAll(async () => { | ||
| 22 | + if (tmp) await rm(tmp, { recursive: true, force: true }) | ||
| 23 | +}) | ||
| 24 | + | ||
| 25 | +async function put(file: string, content: string): Promise<void> { | ||
| 26 | + await mkdir(path.dirname(file), { recursive: true }) | ||
| 27 | + await writeFile(file, content) | ||
| 28 | +} | ||
| 29 | + | ||
| 30 | +describe('migration from skilly', () => { | ||
| 31 | + it('moves the legacy machine folder and a catalog path inside it', async () => { | ||
| 32 | + const legacy = path.join(dir, '.skilly') | ||
| 33 | + const current = path.join(dir, '.panoplie') | ||
| 34 | + await put(path.join(legacy, 'catalog', 'skills', 'x', 'SKILL.md'), 'x') | ||
| 35 | + await put(path.join(legacy, 'config.json'), JSON.stringify({ version: 1, settings: { catalogPath: path.join(legacy, 'catalog') } })) | ||
| 36 | + | ||
| 37 | + expect(await migrateLegacyConfigDir(current, legacy)).toBe(true) | ||
| 38 | + expect(await pathExists(legacy)).toBe(false) | ||
| 39 | + expect(await pathExists(path.join(current, 'catalog', 'skills', 'x', 'SKILL.md'))).toBe(true) | ||
| 40 | + const config = JSON.parse(await readFile(path.join(current, 'config.json'), 'utf8')) | ||
| 41 | + expect(config.settings.catalogPath).toBe(path.join(current, 'catalog')) | ||
| 42 | + | ||
| 43 | + // Nothing to do once migrated, or when the new folder already exists. | ||
| 44 | + await mkdir(legacy) | ||
| 45 | + expect(await migrateLegacyConfigDir(current, legacy)).toBe(false) | ||
| 46 | + expect(await pathExists(legacy)).toBe(true) | ||
| 47 | + }) | ||
| 48 | + | ||
| 49 | + it('keeps a catalog path outside the legacy folder', async () => { | ||
| 50 | + const legacy = path.join(dir, '.skilly') | ||
| 51 | + const elsewhere = path.join(dir, 'my-catalog') | ||
| 52 | + await put(path.join(legacy, 'config.json'), JSON.stringify({ version: 1, settings: { catalogPath: elsewhere } })) | ||
| 53 | + await migrateLegacyConfigDir(path.join(dir, '.panoplie'), legacy) | ||
| 54 | + const config = JSON.parse(await readFile(path.join(dir, '.panoplie', 'config.json'), 'utf8')) | ||
| 55 | + expect(config.settings.catalogPath).toBe(elsewhere) | ||
| 56 | + }) | ||
| 57 | + | ||
| 58 | + it('reads skilly.json and skilly.lock, then renames both on the next write', async () => { | ||
| 59 | + const target: SyncTarget = { kind: 'project', root: dir, homeDir: dir, configDir: dir } | ||
| 60 | + await put(path.join(dir, 'skilly.json'), JSON.stringify({ presets: ['base'] })) | ||
| 61 | + await put(path.join(dir, 'skilly.lock'), JSON.stringify({ version: 1, syncedAt: 'then', entries: [] })) | ||
| 62 | + | ||
| 63 | + expect((await readConfig(target))?.presets).toEqual(['base']) | ||
| 64 | + expect((await readLock(target))?.syncedAt).toBe('then') | ||
| 65 | + | ||
| 66 | + const lock = (await readLock(target))! | ||
| 67 | + await writeLock(target, { ...lock, syncedAt: 'now' }) | ||
| 68 | + expect(await pathExists(path.join(dir, 'skilly.json'))).toBe(false) | ||
| 69 | + expect(await pathExists(path.join(dir, 'skilly.lock'))).toBe(false) | ||
| 70 | + expect((await readConfig(target))?.presets).toEqual(['base']) | ||
| 71 | + expect((await readLock(target))?.syncedAt).toBe('now') | ||
| 72 | + | ||
| 73 | + await writeConfig(target, { ...(await readConfig(target))!, presets: ['other'] }) | ||
| 74 | + expect((await readConfig(target))?.presets).toEqual(['other']) | ||
| 75 | + }) | ||
| 76 | + | ||
| 77 | + it('reads the legacy catalog index and replaces it on the next write', async () => { | ||
| 78 | + const root = path.join(dir, 'catalog') | ||
| 79 | + await put(path.join(root, 'skills', 'demo', 'SKILL.md'), '---\nname: demo\ndescription: A demo\n---\nBody\n') | ||
| 80 | + await put(path.join(root, '.skilly', 'index.json'), JSON.stringify({ version: 1, items: { 'skill/demo': { tags: ['old'] } } })) | ||
| 81 | + const cat = openCatalog(root) | ||
| 82 | + await cat.init() | ||
| 83 | + expect(await pathExists(path.join(root, '.panoplie', 'index.json'))).toBe(false) | ||
| 84 | + expect((await cat.getItem({ type: 'skill', name: 'demo' }))?.item.tags).toEqual(['old']) | ||
| 85 | + | ||
| 86 | + await cat.setTags({ type: 'skill', name: 'demo' }, ['old', 'new']) | ||
| 87 | + expect(await pathExists(path.join(root, '.skilly'))).toBe(false) | ||
| 88 | + const index = JSON.parse(await readFile(path.join(root, '.panoplie', 'index.json'), 'utf8')) | ||
| 89 | + expect(index.items['skill/demo'].tags).toEqual(['old', 'new']) | ||
| 90 | + }) | ||
| 91 | + | ||
| 92 | + it('reads a legacy AGENTS.md block and rewrites it with the new markers', () => { | ||
| 93 | + const text = '# Mine\n\n<!-- skilly:begin (generated by skilly, do not edit) -->\n<!-- skilly:rule ts -->\n## ts\n\nStrict.\n<!-- skilly:end -->\n\nAfter\n' | ||
| 94 | + expect(getSection(text, 'ts')).toBe('## ts\n\nStrict.') | ||
| 95 | + const out = setSection(text, 'ts', '## ts\n\nStricter.') | ||
| 96 | + expect(out).toBe( | ||
| 97 | + '# Mine\n\n<!-- panoplie:begin (generated by panoplie, do not edit) -->\n<!-- panoplie:rule ts -->\n## ts\n\nStricter.\n<!-- panoplie:end -->\n\nAfter\n', | ||
| 98 | + ) | ||
| 99 | + }) | ||
| 100 | +}) | ||
modified
packages/core/test/marketplace.test.ts +2 -2 | @@ -45,7 +45,7 @@ let cacheDir: string | ||
| 45 | 45 | let sources: MarketplaceSource[] |
| 46 | 46 | |
| 47 | 47 | beforeAll(async () => { |
| 48 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-mkt-')) | |
| 48 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-mkt-')) | |
| 49 | 49 | cacheDir = path.join(tmp, 'cache') |
| 50 | 50 | plainRepo = await makeRepo('plain', { |
| 51 | 51 | 'skills/alpha/SKILL.md': skillMd('alpha', 'Alpha skill'), |
| @@ -183,7 +183,7 @@ describe('marketplace', () => { | ||
| 183 | 183 | await expect(m.skillDir('plain', 'skills')).rejects.toThrow(/No skill found/) |
| 184 | 184 | }) |
| 185 | 185 | |
| 186 | - it.skipIf(!process.env.SKILLY_NET_TESTS)('clones the real built-in sources', { timeout: 300_000 }, async () => { | |
| 186 | + it.skipIf(!process.env.PANOPLIE_NET_TESTS)('clones the real built-in sources', { timeout: 300_000 }, async () => { | |
| 187 | 187 | const netCache = path.join(tmp, 'net-cache') |
| 188 | 188 | const m = createMarketplace({ cacheDir: netCache, userSources: [] }) |
| 189 | 189 | for (const s of m.sources().filter((x) => x.builtIn)) { |
| @@ -45,7 +45,7 @@ let cacheDir: string | |||
| 45 | let sources: MarketplaceSource[] | 45 | let sources: MarketplaceSource[] |
| 46 | 46 | ||
| 47 | beforeAll(async () => { | 47 | beforeAll(async () => { |
| 48 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-mkt-')) | 48 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-mkt-')) |
| 49 | cacheDir = path.join(tmp, 'cache') | 49 | cacheDir = path.join(tmp, 'cache') |
| 50 | plainRepo = await makeRepo('plain', { | 50 | plainRepo = await makeRepo('plain', { |
| 51 | 'skills/alpha/SKILL.md': skillMd('alpha', 'Alpha skill'), | 51 | 'skills/alpha/SKILL.md': skillMd('alpha', 'Alpha skill'), |
| @@ -183,7 +183,7 @@ describe('marketplace', () => { | |||
| 183 | await expect(m.skillDir('plain', 'skills')).rejects.toThrow(/No skill found/) | 183 | await expect(m.skillDir('plain', 'skills')).rejects.toThrow(/No skill found/) |
| 184 | }) | 184 | }) |
| 185 | 185 | ||
| 186 | - it.skipIf(!process.env.SKILLY_NET_TESTS)('clones the real built-in sources', { timeout: 300_000 }, async () => { | 186 | + it.skipIf(!process.env.PANOPLIE_NET_TESTS)('clones the real built-in sources', { timeout: 300_000 }, async () => { |
| 187 | const netCache = path.join(tmp, 'net-cache') | 187 | const netCache = path.join(tmp, 'net-cache') |
| 188 | const m = createMarketplace({ cacheDir: netCache, userSources: [] }) | 188 | const m = createMarketplace({ cacheDir: netCache, userSources: [] }) |
| 189 | for (const s of m.sources().filter((x) => x.builtIn)) { | 189 | for (const s of m.sources().filter((x) => x.builtIn)) { |
modified
packages/core/test/sync-home.test.ts +5 -5 | @@ -19,7 +19,7 @@ async function put(file: string, content: string): Promise<void> { | ||
| 19 | 19 | } |
| 20 | 20 | |
| 21 | 21 | function homeTarget(home: string, codexHome = path.join(home, '.codex')): SyncTarget { |
| 22 | - return { kind: 'home', root: home, homeDir: home, codexHome, configDir: path.join(home, '.skilly') } | |
| 22 | + return { kind: 'home', root: home, homeDir: home, codexHome, configDir: path.join(home, '.panoplie') } | |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | const CONFIG: ScopeConfig = { |
| @@ -33,7 +33,7 @@ const CONFIG: ScopeConfig = { | ||
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | beforeAll(async () => { |
| 36 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-sync-home-')) | |
| 36 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-sync-home-')) | |
| 37 | 37 | catalog = new FakeCatalog(path.join(tmp, 'catalog')) |
| 38 | 38 | await catalog.put('skills/nuxt/SKILL.md', '---\nname: nuxt\ndescription: Nuxt\n---\nBody\n') |
| 39 | 39 | await catalog.put('rules/vue.md', '---\nname: vue\n---\nUse SFCs.\n') |
| @@ -57,7 +57,7 @@ describe('Home sync', () => { | ||
| 57 | 57 | await put(path.join(home, '.claude', 'settings.json'), '{\n "theme": "dark"\n}\n') |
| 58 | 58 | |
| 59 | 59 | await syncEngine.writeConfig(target, CONFIG) |
| 60 | - expect(await pathExists(path.join(home, '.skilly', 'home.json'))).toBe(true) | |
| 60 | + expect(await pathExists(path.join(home, '.panoplie', 'home.json'))).toBe(true) | |
| 61 | 61 | const config = (await syncEngine.readConfig(target))! |
| 62 | 62 | |
| 63 | 63 | const plan = await syncEngine.plan(target, config, catalog) |
| @@ -78,7 +78,7 @@ describe('Home sync', () => { | ||
| 78 | 78 | |
| 79 | 79 | const result = await syncEngine.apply(target, config, catalog, plan, {}) |
| 80 | 80 | expect(result.errors).toEqual([]) |
| 81 | - expect(await pathExists(path.join(home, '.skilly', 'home.lock'))).toBe(true) | |
| 81 | + expect(await pathExists(path.join(home, '.panoplie', 'home.lock'))).toBe(true) | |
| 82 | 82 | expect(await pathExists(path.join(home, '.claude.json'))).toBe(false) |
| 83 | 83 | expect(await pathExists(path.join(home, '.mcp.json'))).toBe(false) |
| 84 | 84 | |
| @@ -88,7 +88,7 @@ describe('Home sync', () => { | ||
| 88 | 88 | policy: { allow_implicit_invocation: false }, |
| 89 | 89 | }) |
| 90 | 90 | const agentsMd = await readFile(path.join(home, '.codex', 'AGENTS.md'), 'utf8') |
| 91 | - expect(agentsMd).toMatch(/^# Global\n\n<!-- skilly:begin/) | |
| 91 | + expect(agentsMd).toMatch(/^# Global\n\n<!-- panoplie:begin/) | |
| 92 | 92 | expect(await readFile(path.join(home, '.codex', 'agents', 'reviewer.toml'), 'utf8')).toContain('model = "gpt-5"') |
| 93 | 93 | |
| 94 | 94 | const inv = await scanHome({ homeDir: home, codexHome: path.join(home, '.codex') }) |
| @@ -19,7 +19,7 @@ async function put(file: string, content: string): Promise<void> { | |||
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | function homeTarget(home: string, codexHome = path.join(home, '.codex')): SyncTarget { | 21 | function homeTarget(home: string, codexHome = path.join(home, '.codex')): SyncTarget { |
| 22 | - return { kind: 'home', root: home, homeDir: home, codexHome, configDir: path.join(home, '.skilly') } | 22 | + return { kind: 'home', root: home, homeDir: home, codexHome, configDir: path.join(home, '.panoplie') } |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | const CONFIG: ScopeConfig = { | 25 | const CONFIG: ScopeConfig = { |
| @@ -33,7 +33,7 @@ const CONFIG: ScopeConfig = { | |||
| 33 | } | 33 | } |
| 34 | 34 | ||
| 35 | beforeAll(async () => { | 35 | beforeAll(async () => { |
| 36 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-sync-home-')) | 36 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-sync-home-')) |
| 37 | catalog = new FakeCatalog(path.join(tmp, 'catalog')) | 37 | catalog = new FakeCatalog(path.join(tmp, 'catalog')) |
| 38 | await catalog.put('skills/nuxt/SKILL.md', '---\nname: nuxt\ndescription: Nuxt\n---\nBody\n') | 38 | await catalog.put('skills/nuxt/SKILL.md', '---\nname: nuxt\ndescription: Nuxt\n---\nBody\n') |
| 39 | await catalog.put('rules/vue.md', '---\nname: vue\n---\nUse SFCs.\n') | 39 | await catalog.put('rules/vue.md', '---\nname: vue\n---\nUse SFCs.\n') |
| @@ -57,7 +57,7 @@ describe('Home sync', () => { | |||
| 57 | await put(path.join(home, '.claude', 'settings.json'), '{\n "theme": "dark"\n}\n') | 57 | await put(path.join(home, '.claude', 'settings.json'), '{\n "theme": "dark"\n}\n') |
| 58 | 58 | ||
| 59 | await syncEngine.writeConfig(target, CONFIG) | 59 | await syncEngine.writeConfig(target, CONFIG) |
| 60 | - expect(await pathExists(path.join(home, '.skilly', 'home.json'))).toBe(true) | 60 | + expect(await pathExists(path.join(home, '.panoplie', 'home.json'))).toBe(true) |
| 61 | const config = (await syncEngine.readConfig(target))! | 61 | const config = (await syncEngine.readConfig(target))! |
| 62 | 62 | ||
| 63 | const plan = await syncEngine.plan(target, config, catalog) | 63 | const plan = await syncEngine.plan(target, config, catalog) |
| @@ -78,7 +78,7 @@ describe('Home sync', () => { | |||
| 78 | 78 | ||
| 79 | const result = await syncEngine.apply(target, config, catalog, plan, {}) | 79 | const result = await syncEngine.apply(target, config, catalog, plan, {}) |
| 80 | expect(result.errors).toEqual([]) | 80 | expect(result.errors).toEqual([]) |
| 81 | - expect(await pathExists(path.join(home, '.skilly', 'home.lock'))).toBe(true) | 81 | + expect(await pathExists(path.join(home, '.panoplie', 'home.lock'))).toBe(true) |
| 82 | expect(await pathExists(path.join(home, '.claude.json'))).toBe(false) | 82 | expect(await pathExists(path.join(home, '.claude.json'))).toBe(false) |
| 83 | expect(await pathExists(path.join(home, '.mcp.json'))).toBe(false) | 83 | expect(await pathExists(path.join(home, '.mcp.json'))).toBe(false) |
| 84 | 84 | ||
| @@ -88,7 +88,7 @@ describe('Home sync', () => { | |||
| 88 | policy: { allow_implicit_invocation: false }, | 88 | policy: { allow_implicit_invocation: false }, |
| 89 | }) | 89 | }) |
| 90 | const agentsMd = await readFile(path.join(home, '.codex', 'AGENTS.md'), 'utf8') | 90 | const agentsMd = await readFile(path.join(home, '.codex', 'AGENTS.md'), 'utf8') |
| 91 | - expect(agentsMd).toMatch(/^# Global\n\n<!-- skilly:begin/) | 91 | + expect(agentsMd).toMatch(/^# Global\n\n<!-- panoplie:begin/) |
| 92 | expect(await readFile(path.join(home, '.codex', 'agents', 'reviewer.toml'), 'utf8')).toContain('model = "gpt-5"') | 92 | expect(await readFile(path.join(home, '.codex', 'agents', 'reviewer.toml'), 'utf8')).toContain('model = "gpt-5"') |
| 93 | 93 | ||
| 94 | const inv = await scanHome({ homeDir: home, codexHome: path.join(home, '.codex') }) | 94 | const inv = await scanHome({ homeDir: home, codexHome: path.join(home, '.codex') }) |
modified
packages/core/test/sync-merge.test.ts +3 -3 | @@ -98,9 +98,9 @@ describe('AGENTS.md managed block', () => { | ||
| 98 | 98 | }) |
| 99 | 99 | |
| 100 | 100 | it('keeps content after the block untouched', () => { |
| 101 | - const text = `${user}\n${BLOCK_BEGIN}\n<!-- skilly:rule a -->\n## a\n${BLOCK_END}\n\n## Footer\nkeep me\n` | |
| 101 | + const text = `${user}\n${BLOCK_BEGIN}\n<!-- panoplie:rule a -->\n## a\n${BLOCK_END}\n\n## Footer\nkeep me\n` | |
| 102 | 102 | const out = setSection(text, 'a', '## a\n\nnew') |
| 103 | - expect(out).toBe(`${user}\n${BLOCK_BEGIN}\n<!-- skilly:rule a -->\n## a\n\nnew\n${BLOCK_END}\n\n## Footer\nkeep me\n`) | |
| 103 | + expect(out).toBe(`${user}\n${BLOCK_BEGIN}\n<!-- panoplie:rule a -->\n## a\n\nnew\n${BLOCK_END}\n\n## Footer\nkeep me\n`) | |
| 104 | 104 | }) |
| 105 | 105 | }) |
| 106 | 106 | |
| @@ -115,7 +115,7 @@ describe('config entries', () => { | ||
| 115 | 115 | expect(parseConfigKey('AGENTS.md', 'agents-md-block:ts')).toMatchObject({ kind: 'agents-md-section', name: 'ts' }) |
| 116 | 116 | }) |
| 117 | 117 | |
| 118 | - it('finds, replaces and removes only skilly array items', () => { | |
| 118 | + it('finds, replaces and removes only panoplie array items', () => { | |
| 119 | 119 | const loc = jsonArrayItem('s.json', ['hooks', 'Stop'], 'x') |
| 120 | 120 | const user = { matcher: 'u', hooks: [{ type: 'command', command: 'user' }] } |
| 121 | 121 | const w1 = configWrite(loc, { hooks: [{ type: 'command', command: 'one' }] }) |
| @@ -98,9 +98,9 @@ describe('AGENTS.md managed block', () => { | |||
| 98 | }) | 98 | }) |
| 99 | 99 | ||
| 100 | it('keeps content after the block untouched', () => { | 100 | it('keeps content after the block untouched', () => { |
| 101 | - const text = `${user}\n${BLOCK_BEGIN}\n<!-- skilly:rule a -->\n## a\n${BLOCK_END}\n\n## Footer\nkeep me\n` | 101 | + const text = `${user}\n${BLOCK_BEGIN}\n<!-- panoplie:rule a -->\n## a\n${BLOCK_END}\n\n## Footer\nkeep me\n` |
| 102 | const out = setSection(text, 'a', '## a\n\nnew') | 102 | const out = setSection(text, 'a', '## a\n\nnew') |
| 103 | - expect(out).toBe(`${user}\n${BLOCK_BEGIN}\n<!-- skilly:rule a -->\n## a\n\nnew\n${BLOCK_END}\n\n## Footer\nkeep me\n`) | 103 | + expect(out).toBe(`${user}\n${BLOCK_BEGIN}\n<!-- panoplie:rule a -->\n## a\n\nnew\n${BLOCK_END}\n\n## Footer\nkeep me\n`) |
| 104 | }) | 104 | }) |
| 105 | }) | 105 | }) |
| 106 | 106 | ||
| @@ -115,7 +115,7 @@ describe('config entries', () => { | |||
| 115 | expect(parseConfigKey('AGENTS.md', 'agents-md-block:ts')).toMatchObject({ kind: 'agents-md-section', name: 'ts' }) | 115 | expect(parseConfigKey('AGENTS.md', 'agents-md-block:ts')).toMatchObject({ kind: 'agents-md-section', name: 'ts' }) |
| 116 | }) | 116 | }) |
| 117 | 117 | ||
| 118 | - it('finds, replaces and removes only skilly array items', () => { | 118 | + it('finds, replaces and removes only panoplie array items', () => { |
| 119 | const loc = jsonArrayItem('s.json', ['hooks', 'Stop'], 'x') | 119 | const loc = jsonArrayItem('s.json', ['hooks', 'Stop'], 'x') |
| 120 | const user = { matcher: 'u', hooks: [{ type: 'command', command: 'user' }] } | 120 | const user = { matcher: 'u', hooks: [{ type: 'command', command: 'user' }] } |
| 121 | const w1 = configWrite(loc, { hooks: [{ type: 'command', command: 'one' }] }) | 121 | const w1 = configWrite(loc, { hooks: [{ type: 'command', command: 'one' }] }) |
modified
packages/core/test/sync.test.ts +6 -6 | @@ -89,7 +89,7 @@ function find(inv: ScopeInventory, agent: string, type: string, name: string): I | ||
| 89 | 89 | } |
| 90 | 90 | |
| 91 | 91 | beforeAll(async () => { |
| 92 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-sync-')) | |
| 92 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-sync-')) | |
| 93 | 93 | home = path.join(tmp, 'home') |
| 94 | 94 | project = path.join(tmp, 'proj') |
| 95 | 95 | catalog = new FakeCatalog(path.join(tmp, 'catalog')) |
| @@ -144,7 +144,7 @@ describe('config and lock files', () => { | ||
| 144 | 144 | it('returns undefined when missing and normalizes defaults', async () => { |
| 145 | 145 | expect(await syncEngine.readConfig(target)).toBeUndefined() |
| 146 | 146 | expect(await syncEngine.readLock(target)).toBeUndefined() |
| 147 | - await put(p('skilly.json'), JSON.stringify({ presets: ['base'], add: { skills: ['x'] } })) | |
| 147 | + await put(p('panoplie.json'), JSON.stringify({ presets: ['base'], add: { skills: ['x'] } })) | |
| 148 | 148 | expect(await syncEngine.readConfig(target)).toEqual({ |
| 149 | 149 | version: 1, |
| 150 | 150 | targets: ['claude', 'codex'], |
| @@ -155,7 +155,7 @@ describe('config and lock files', () => { | ||
| 155 | 155 | hideGlobal: { skills: [], mcp: [] }, |
| 156 | 156 | }) |
| 157 | 157 | await syncEngine.writeConfig(target, config) |
| 158 | - const text = await read('skilly.json') | |
| 158 | + const text = await read('panoplie.json') | |
| 159 | 159 | expect(text.endsWith('}\n')).toBe(true) |
| 160 | 160 | expect(text).toContain('\n "targets": [') |
| 161 | 161 | expect(Object.keys(JSON.parse(text))).toEqual(['version', 'targets', 'presets', 'add', 'exclude', 'onDemand', 'hideGlobal']) |
| @@ -199,7 +199,7 @@ describe('project sync', () => { | ||
| 199 | 199 | expect(w).toMatch(/OTHER_KEY is forwarded as is, not as API_KEY/) |
| 200 | 200 | expect(w).toMatch(/Codex cannot hide global items per project; still visible to Codex: skill hyperframes-a/) |
| 201 | 201 | expect(await pathExists(p('.claude/skills'))).toBe(false) |
| 202 | - expect(await pathExists(p('skilly.lock'))).toBe(false) | |
| 202 | + expect(await pathExists(p('panoplie.lock'))).toBe(false) | |
| 203 | 203 | expect(await read('.claude/settings.json')).toBe(SETTINGS) |
| 204 | 204 | }) |
| 205 | 205 | |
| @@ -299,9 +299,9 @@ describe('project sync', () => { | ||
| 299 | 299 | expect(status[find(inv, 'claude', 'skill', 'nuxt').id]).toEqual({ managed: true, catalogRef: { type: 'skill', name: 'nuxt' }, state: 'in-sync' }) |
| 300 | 300 | expect(status[find(inv, 'codex', 'mcp', 'github').id]!.state).toBe('in-sync') |
| 301 | 301 | expect(status[find(inv, 'codex', 'agent', 'reviewer').id]!.managed).toBe(true) |
| 302 | - const skillyHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'npx prettier --write')! | |
| 302 | + const panoplieHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'npx prettier --write')! | |
| 303 | 303 | const userHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'echo user')! |
| 304 | - expect(status[skillyHook.id]).toMatchObject({ managed: true, state: 'in-sync' }) | |
| 304 | + expect(status[panoplieHook.id]).toMatchObject({ managed: true, state: 'in-sync' }) | |
| 305 | 305 | expect(status[userHook.id]).toEqual({ managed: false, state: 'not-in-catalog' }) |
| 306 | 306 | expect(status[find(inv, 'codex', 'instructions', 'AGENTS.md').id]!.managed).toBe(false) |
| 307 | 307 | }) |
| @@ -89,7 +89,7 @@ function find(inv: ScopeInventory, agent: string, type: string, name: string): I | |||
| 89 | } | 89 | } |
| 90 | 90 | ||
| 91 | beforeAll(async () => { | 91 | beforeAll(async () => { |
| 92 | - tmp = await mkdtemp(path.join(os.tmpdir(), 'skilly-sync-')) | 92 | + tmp = await mkdtemp(path.join(os.tmpdir(), 'panoplie-sync-')) |
| 93 | home = path.join(tmp, 'home') | 93 | home = path.join(tmp, 'home') |
| 94 | project = path.join(tmp, 'proj') | 94 | project = path.join(tmp, 'proj') |
| 95 | catalog = new FakeCatalog(path.join(tmp, 'catalog')) | 95 | catalog = new FakeCatalog(path.join(tmp, 'catalog')) |
| @@ -144,7 +144,7 @@ describe('config and lock files', () => { | |||
| 144 | it('returns undefined when missing and normalizes defaults', async () => { | 144 | it('returns undefined when missing and normalizes defaults', async () => { |
| 145 | expect(await syncEngine.readConfig(target)).toBeUndefined() | 145 | expect(await syncEngine.readConfig(target)).toBeUndefined() |
| 146 | expect(await syncEngine.readLock(target)).toBeUndefined() | 146 | expect(await syncEngine.readLock(target)).toBeUndefined() |
| 147 | - await put(p('skilly.json'), JSON.stringify({ presets: ['base'], add: { skills: ['x'] } })) | 147 | + await put(p('panoplie.json'), JSON.stringify({ presets: ['base'], add: { skills: ['x'] } })) |
| 148 | expect(await syncEngine.readConfig(target)).toEqual({ | 148 | expect(await syncEngine.readConfig(target)).toEqual({ |
| 149 | version: 1, | 149 | version: 1, |
| 150 | targets: ['claude', 'codex'], | 150 | targets: ['claude', 'codex'], |
| @@ -155,7 +155,7 @@ describe('config and lock files', () => { | |||
| 155 | hideGlobal: { skills: [], mcp: [] }, | 155 | hideGlobal: { skills: [], mcp: [] }, |
| 156 | }) | 156 | }) |
| 157 | await syncEngine.writeConfig(target, config) | 157 | await syncEngine.writeConfig(target, config) |
| 158 | - const text = await read('skilly.json') | 158 | + const text = await read('panoplie.json') |
| 159 | expect(text.endsWith('}\n')).toBe(true) | 159 | expect(text.endsWith('}\n')).toBe(true) |
| 160 | expect(text).toContain('\n "targets": [') | 160 | expect(text).toContain('\n "targets": [') |
| 161 | expect(Object.keys(JSON.parse(text))).toEqual(['version', 'targets', 'presets', 'add', 'exclude', 'onDemand', 'hideGlobal']) | 161 | expect(Object.keys(JSON.parse(text))).toEqual(['version', 'targets', 'presets', 'add', 'exclude', 'onDemand', 'hideGlobal']) |
| @@ -199,7 +199,7 @@ describe('project sync', () => { | |||
| 199 | expect(w).toMatch(/OTHER_KEY is forwarded as is, not as API_KEY/) | 199 | expect(w).toMatch(/OTHER_KEY is forwarded as is, not as API_KEY/) |
| 200 | expect(w).toMatch(/Codex cannot hide global items per project; still visible to Codex: skill hyperframes-a/) | 200 | expect(w).toMatch(/Codex cannot hide global items per project; still visible to Codex: skill hyperframes-a/) |
| 201 | expect(await pathExists(p('.claude/skills'))).toBe(false) | 201 | expect(await pathExists(p('.claude/skills'))).toBe(false) |
| 202 | - expect(await pathExists(p('skilly.lock'))).toBe(false) | 202 | + expect(await pathExists(p('panoplie.lock'))).toBe(false) |
| 203 | expect(await read('.claude/settings.json')).toBe(SETTINGS) | 203 | expect(await read('.claude/settings.json')).toBe(SETTINGS) |
| 204 | }) | 204 | }) |
| 205 | 205 | ||
| @@ -299,9 +299,9 @@ describe('project sync', () => { | |||
| 299 | expect(status[find(inv, 'claude', 'skill', 'nuxt').id]).toEqual({ managed: true, catalogRef: { type: 'skill', name: 'nuxt' }, state: 'in-sync' }) | 299 | expect(status[find(inv, 'claude', 'skill', 'nuxt').id]).toEqual({ managed: true, catalogRef: { type: 'skill', name: 'nuxt' }, state: 'in-sync' }) |
| 300 | expect(status[find(inv, 'codex', 'mcp', 'github').id]!.state).toBe('in-sync') | 300 | expect(status[find(inv, 'codex', 'mcp', 'github').id]!.state).toBe('in-sync') |
| 301 | expect(status[find(inv, 'codex', 'agent', 'reviewer').id]!.managed).toBe(true) | 301 | expect(status[find(inv, 'codex', 'agent', 'reviewer').id]!.managed).toBe(true) |
| 302 | - const skillyHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'npx prettier --write')! | 302 | + const panoplieHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'npx prettier --write')! |
| 303 | const userHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'echo user')! | 303 | const userHook = inv.items.find((i) => i.agent === 'claude' && i.type === 'hook' && (i.details as any).command === 'echo user')! |
| 304 | - expect(status[skillyHook.id]).toMatchObject({ managed: true, state: 'in-sync' }) | 304 | + expect(status[panoplieHook.id]).toMatchObject({ managed: true, state: 'in-sync' }) |
| 305 | expect(status[userHook.id]).toEqual({ managed: false, state: 'not-in-catalog' }) | 305 | expect(status[userHook.id]).toEqual({ managed: false, state: 'not-in-catalog' }) |
| 306 | expect(status[find(inv, 'codex', 'instructions', 'AGENTS.md').id]!.managed).toBe(false) | 306 | expect(status[find(inv, 'codex', 'instructions', 'AGENTS.md').id]!.managed).toBe(false) |
| 307 | }) | 307 | }) |
modified
packages/server/package.json +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | - "name": "@skilly/server", | |
| 2 | + "name": "@panoplie/server", | |
| 3 | 3 | "version": "0.0.0", |
| 4 | 4 | "private": true, |
| 5 | 5 | "license": "MIT", |
| @@ -12,7 +12,7 @@ | ||
| 12 | 12 | }, |
| 13 | 13 | "dependencies": { |
| 14 | 14 | "@hono/node-server": "^2.1.0", |
| 15 | - "@skilly/core": "workspace:*", | |
| 15 | + "@panoplie/core": "workspace:*", | |
| 16 | 16 | "hono": "^4.13.0" |
| 17 | 17 | } |
| 18 | 18 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | { | 1 | { |
| 2 | - "name": "@skilly/server", | 2 | + "name": "@panoplie/server", |
| 3 | "version": "0.0.0", | 3 | "version": "0.0.0", |
| 4 | "private": true, | 4 | "private": true, |
| 5 | "license": "MIT", | 5 | "license": "MIT", |
| @@ -12,7 +12,7 @@ | |||
| 12 | }, | 12 | }, |
| 13 | "dependencies": { | 13 | "dependencies": { |
| 14 | "@hono/node-server": "^2.1.0", | 14 | "@hono/node-server": "^2.1.0", |
| 15 | - "@skilly/core": "workspace:*", | 15 | + "@panoplie/core": "workspace:*", |
| 16 | "hono": "^4.13.0" | 16 | "hono": "^4.13.0" |
| 17 | } | 17 | } |
| 18 | } | 18 | } |
modified
packages/server/src/catalog-routes.ts +3 -3 | @@ -10,7 +10,7 @@ import { | ||
| 10 | 10 | type SaveItemInput, |
| 11 | 11 | type ScopeConfig, |
| 12 | 12 | type Settings, |
| 13 | -} from '@skilly/core' | |
| 13 | +} from '@panoplie/core' | |
| 14 | 14 | import type { Hono } from 'hono' |
| 15 | 15 | import path from 'node:path' |
| 16 | 16 | import { HttpError, isDirectory, isStringArray, readJsonBody, type AppContext } from './context.ts' |
| @@ -80,7 +80,7 @@ async function computeUsage(ctx: AppContext): Promise<Record<string, string[]>> | ||
| 80 | 80 | } |
| 81 | 81 | |
| 82 | 82 | /** |
| 83 | - * Points the scope configs (`skilly.json` / `home.json`) at the new name of a renamed item or preset, so that a | |
| 83 | + * Points the scope configs (`panoplie.json` / `home.json`) at the new name of a renamed item or preset, so that a | |
| 84 | 84 | * rename does not silently disable it. Exact names only: glob patterns are left as is. Nothing is applied. |
| 85 | 85 | */ |
| 86 | 86 | async function renameInScopeConfigs(ctx: AppContext, kind: CatalogItemType | 'preset', from: string, to: string): Promise<void> { |
| @@ -110,7 +110,7 @@ async function renameInScopeConfigs(ctx: AppContext, kind: CatalogItemType | 'pr | ||
| 110 | 110 | await ctx.syncEngine.writeConfig(scope.target, next) |
| 111 | 111 | ctx.invalidateScan(id) |
| 112 | 112 | } catch (e) { |
| 113 | - console.error(`skilly: could not update the config of scope ${id} after a rename:`, e instanceof Error ? e.message : e) | |
| 113 | + console.error(`panoplie: could not update the config of scope ${id} after a rename:`, e instanceof Error ? e.message : e) | |
| 114 | 114 | } |
| 115 | 115 | } |
| 116 | 116 | } |
| @@ -10,7 +10,7 @@ import { | |||
| 10 | type SaveItemInput, | 10 | type SaveItemInput, |
| 11 | type ScopeConfig, | 11 | type ScopeConfig, |
| 12 | type Settings, | 12 | type Settings, |
| 13 | -} from '@skilly/core' | 13 | +} from '@panoplie/core' |
| 14 | import type { Hono } from 'hono' | 14 | import type { Hono } from 'hono' |
| 15 | import path from 'node:path' | 15 | import path from 'node:path' |
| 16 | import { HttpError, isDirectory, isStringArray, readJsonBody, type AppContext } from './context.ts' | 16 | import { HttpError, isDirectory, isStringArray, readJsonBody, type AppContext } from './context.ts' |
| @@ -80,7 +80,7 @@ async function computeUsage(ctx: AppContext): Promise<Record<string, string[]>> | |||
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | /** | 82 | /** |
| 83 | - * Points the scope configs (`skilly.json` / `home.json`) at the new name of a renamed item or preset, so that a | 83 | + * Points the scope configs (`panoplie.json` / `home.json`) at the new name of a renamed item or preset, so that a |
| 84 | * rename does not silently disable it. Exact names only: glob patterns are left as is. Nothing is applied. | 84 | * rename does not silently disable it. Exact names only: glob patterns are left as is. Nothing is applied. |
| 85 | */ | 85 | */ |
| 86 | async function renameInScopeConfigs(ctx: AppContext, kind: CatalogItemType | 'preset', from: string, to: string): Promise<void> { | 86 | async function renameInScopeConfigs(ctx: AppContext, kind: CatalogItemType | 'preset', from: string, to: string): Promise<void> { |
| @@ -110,7 +110,7 @@ async function renameInScopeConfigs(ctx: AppContext, kind: CatalogItemType | 'pr | |||
| 110 | await ctx.syncEngine.writeConfig(scope.target, next) | 110 | await ctx.syncEngine.writeConfig(scope.target, next) |
| 111 | ctx.invalidateScan(id) | 111 | ctx.invalidateScan(id) |
| 112 | } catch (e) { | 112 | } catch (e) { |
| 113 | - console.error(`skilly: could not update the config of scope ${id} after a rename:`, e instanceof Error ? e.message : e) | 113 | + console.error(`panoplie: could not update the config of scope ${id} after a rename:`, e instanceof Error ? e.message : e) |
| 114 | } | 114 | } |
| 115 | } | 115 | } |
| 116 | } | 116 | } |
modified
packages/server/src/context.ts +1 -1 | @@ -13,7 +13,7 @@ import { | ||
| 13 | 13 | type Settings, |
| 14 | 14 | type SyncEngine, |
| 15 | 15 | type SyncTarget, |
| 16 | -} from '@skilly/core' | |
| 16 | +} from '@panoplie/core' | |
| 17 | 17 | import type { Context } from 'hono' |
| 18 | 18 | import type { ProjectStore } from './store.ts' |
| 19 | 19 | |
| @@ -13,7 +13,7 @@ import { | |||
| 13 | type Settings, | 13 | type Settings, |
| 14 | type SyncEngine, | 14 | type SyncEngine, |
| 15 | type SyncTarget, | 15 | type SyncTarget, |
| 16 | -} from '@skilly/core' | 16 | +} from '@panoplie/core' |
| 17 | import type { Context } from 'hono' | 17 | import type { Context } from 'hono' |
| 18 | import type { ProjectStore } from './store.ts' | 18 | import type { ProjectStore } from './store.ts' |
| 19 | 19 | ||
modified
packages/server/src/dev.ts +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | 1 | import { startServer } from './index.ts' |
| 2 | 2 | |
| 3 | 3 | const server = await startServer({ token: 'dev', port: 4300 }) |
| 4 | -console.log(`skilly dev API on ${server.url} (token: dev)`) | |
| 4 | +console.log(`panoplie dev API on ${server.url} (token: dev)`) | |
| @@ -1,4 +1,4 @@ | |||
| 1 | import { startServer } from './index.ts' | 1 | import { startServer } from './index.ts' |
| 2 | 2 | ||
| 3 | const server = await startServer({ token: 'dev', port: 4300 }) | 3 | const server = await startServer({ token: 'dev', port: 4300 }) |
| 4 | -console.log(`skilly dev API on ${server.url} (token: dev)`) | 4 | +console.log(`panoplie dev API on ${server.url} (token: dev)`) |
modified
packages/server/src/files.ts +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { open, realpath, stat } from 'node:fs/promises' |
| 2 | 2 | import path from 'node:path' |
| 3 | -import type { FileContent } from '@skilly/core' | |
| 3 | +import type { FileContent } from '@panoplie/core' | |
| 4 | 4 | |
| 5 | 5 | export const MAX_FILE_BYTES = 512 * 1024 |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | |||
| 1 | import { open, realpath, stat } from 'node:fs/promises' | 1 | import { open, realpath, stat } from 'node:fs/promises' |
| 2 | import path from 'node:path' | 2 | import path from 'node:path' |
| 3 | -import type { FileContent } from '@skilly/core' | 3 | +import type { FileContent } from '@panoplie/core' |
| 4 | 4 | ||
| 5 | export const MAX_FILE_BYTES = 512 * 1024 | 5 | export const MAX_FILE_BYTES = 512 * 1024 |
| 6 | 6 | ||
modified
packages/server/src/fslist.ts +1 -1 | @@ -2,7 +2,7 @@ import { existsSync } from 'node:fs' | ||
| 2 | 2 | import { readdir, stat } from 'node:fs/promises' |
| 3 | 3 | import os from 'node:os' |
| 4 | 4 | import path from 'node:path' |
| 5 | -import type { DiscoveredProject, FsDiscovery, FsEntry, FsListing } from '@skilly/core' | |
| 5 | +import type { DiscoveredProject, FsDiscovery, FsEntry, FsListing } from '@panoplie/core' | |
| 6 | 6 | |
| 7 | 7 | const EXCLUDED = new Set(['node_modules', '$recycle.bin', 'system volume information']) |
| 8 | 8 | const AGENT_MARKERS = ['.claude', '.agents', '.codex', 'AGENTS.md', 'CLAUDE.md', '.mcp.json'] |
| @@ -2,7 +2,7 @@ import { existsSync } from 'node:fs' | |||
| 2 | import { readdir, stat } from 'node:fs/promises' | 2 | import { readdir, stat } from 'node:fs/promises' |
| 3 | import os from 'node:os' | 3 | import os from 'node:os' |
| 4 | import path from 'node:path' | 4 | import path from 'node:path' |
| 5 | -import type { DiscoveredProject, FsDiscovery, FsEntry, FsListing } from '@skilly/core' | 5 | +import type { DiscoveredProject, FsDiscovery, FsEntry, FsListing } from '@panoplie/core' |
| 6 | 6 | ||
| 7 | const EXCLUDED = new Set(['node_modules', '$recycle.bin', 'system volume information']) | 7 | const EXCLUDED = new Set(['node_modules', '$recycle.bin', 'system volume information']) |
| 8 | const AGENT_MARKERS = ['.claude', '.agents', '.codex', 'AGENTS.md', 'CLAUDE.md', '.mcp.json'] | 8 | const AGENT_MARKERS = ['.claude', '.agents', '.codex', 'AGENTS.md', 'CLAUDE.md', '.mcp.json'] |
modified
packages/server/src/index.ts +9 -6 | @@ -8,6 +8,7 @@ import { serve } from '@hono/node-server' | ||
| 8 | 8 | import { |
| 9 | 9 | ITEM_TYPES, |
| 10 | 10 | defaultScanContext, |
| 11 | + migrateLegacyConfigDir, | |
| 11 | 12 | createMarketplace as realCreateMarketplace, |
| 12 | 13 | openCatalog as realOpenCatalog, |
| 13 | 14 | openCatalogGit as realOpenCatalogGit, |
| @@ -24,7 +25,7 @@ import { | ||
| 24 | 25 | type ProjectSummary, |
| 25 | 26 | type ScanContext, |
| 26 | 27 | type ScopeInventory, |
| 27 | -} from '@skilly/core' | |
| 28 | +} from '@panoplie/core' | |
| 28 | 29 | import { Hono } from 'hono' |
| 29 | 30 | import { registerCatalogRoutes } from './catalog-routes.ts' |
| 30 | 31 | import { HttpError, guardGitConflicts, isDirectory, makeScopeRef, type AppContext } from './context.ts' |
| @@ -41,7 +42,7 @@ const STARTUP_GRACE_MS = 60_000 | ||
| 41 | 42 | |
| 42 | 43 | export interface AppOptions { |
| 43 | 44 | token: string |
| 44 | - /** Defaults to `~/.skilly`. */ | |
| 45 | + /** Defaults to `~/.panoplie`. */ | |
| 45 | 46 | configDir?: string |
| 46 | 47 | scanContext?: ScanContext |
| 47 | 48 | /** Sync engine (defaults to the real one); injectable for tests. */ |
| @@ -153,7 +154,7 @@ export function createApp(options: AppOptions): Hono { | ||
| 153 | 154 | |
| 154 | 155 | export function createAppHandle(options: AppOptions): AppHandle { |
| 155 | 156 | const { token } = options |
| 156 | - const configDir = options.configDir ?? path.join(os.homedir(), '.skilly') | |
| 157 | + const configDir = options.configDir ?? path.join(os.homedir(), '.panoplie') | |
| 157 | 158 | const ctx = options.scanContext ?? defaultScanContext() |
| 158 | 159 | const store = new ProjectStore(configDir) |
| 159 | 160 | const scanCache = new Map<string, { at: number; inventory: ScopeInventory }>() |
| @@ -175,7 +176,7 @@ export function createAppHandle(options: AppOptions): AppHandle { | ||
| 175 | 176 | const status = await git.status() |
| 176 | 177 | if (!status.isRepo) return |
| 177 | 178 | if (status.remote) await git.sync() |
| 178 | - else await git.commitAll('skilly: update catalog') | |
| 179 | + else await git.commitAll('panoplie: update catalog') | |
| 179 | 180 | } |
| 180 | 181 | |
| 181 | 182 | const appCtx: AppContext = { |
| @@ -212,7 +213,7 @@ export function createAppHandle(options: AppOptions): AppHandle { | ||
| 212 | 213 | syncTimer = undefined |
| 213 | 214 | syncChain = syncChain |
| 214 | 215 | .then(runAutoSync) |
| 215 | - .catch((e: unknown) => console.error('skilly: catalog auto-sync failed:', e instanceof Error ? e.message : e)) | |
| 216 | + .catch((e: unknown) => console.error('panoplie: catalog auto-sync failed:', e instanceof Error ? e.message : e)) | |
| 216 | 217 | }, options.autoSyncDelayMs ?? 3000) |
| 217 | 218 | syncTimer.unref() |
| 218 | 219 | }) |
| @@ -229,7 +230,7 @@ export function createAppHandle(options: AppOptions): AppHandle { | ||
| 229 | 230 | }) |
| 230 | 231 | |
| 231 | 232 | app.use('/api/*', async (c, next) => { |
| 232 | - if (!tokenMatches(c.req.header('x-skilly-token'), token)) return c.json(err('Unauthorized'), 401) | |
| 233 | + if (!tokenMatches(c.req.header('x-panoplie-token'), token)) return c.json(err('Unauthorized'), 401) | |
| 233 | 234 | await next() |
| 234 | 235 | }) |
| 235 | 236 | |
| @@ -376,6 +377,8 @@ export function createAppHandle(options: AppOptions): AppHandle { | ||
| 376 | 377 | } |
| 377 | 378 | |
| 378 | 379 | export async function startServer(options: ServerOptions): Promise<RunningServer> { |
| 380 | + // Callers passing configDir (the CLI) migrate it themselves; the default folder is migrated here. | |
| 381 | + if (options.configDir === undefined) await migrateLegacyConfigDir(path.join(os.homedir(), '.panoplie')) | |
| 379 | 382 | let idleTimer: NodeJS.Timeout | undefined |
| 380 | 383 | let closed = false |
| 381 | 384 | const armWatchdog = (ms: number): void => { |
| @@ -8,6 +8,7 @@ import { serve } from '@hono/node-server' | |||
| 8 | import { | 8 | import { |
| 9 | ITEM_TYPES, | 9 | ITEM_TYPES, |
| 10 | defaultScanContext, | 10 | defaultScanContext, |
| 11 | + migrateLegacyConfigDir, | ||
| 11 | createMarketplace as realCreateMarketplace, | 12 | createMarketplace as realCreateMarketplace, |
| 12 | openCatalog as realOpenCatalog, | 13 | openCatalog as realOpenCatalog, |
| 13 | openCatalogGit as realOpenCatalogGit, | 14 | openCatalogGit as realOpenCatalogGit, |
| @@ -24,7 +25,7 @@ import { | |||
| 24 | type ProjectSummary, | 25 | type ProjectSummary, |
| 25 | type ScanContext, | 26 | type ScanContext, |
| 26 | type ScopeInventory, | 27 | type ScopeInventory, |
| 27 | -} from '@skilly/core' | 28 | +} from '@panoplie/core' |
| 28 | import { Hono } from 'hono' | 29 | import { Hono } from 'hono' |
| 29 | import { registerCatalogRoutes } from './catalog-routes.ts' | 30 | import { registerCatalogRoutes } from './catalog-routes.ts' |
| 30 | import { HttpError, guardGitConflicts, isDirectory, makeScopeRef, type AppContext } from './context.ts' | 31 | import { HttpError, guardGitConflicts, isDirectory, makeScopeRef, type AppContext } from './context.ts' |
| @@ -41,7 +42,7 @@ const STARTUP_GRACE_MS = 60_000 | |||
| 41 | 42 | ||
| 42 | export interface AppOptions { | 43 | export interface AppOptions { |
| 43 | token: string | 44 | token: string |
| 44 | - /** Defaults to `~/.skilly`. */ | 45 | + /** Defaults to `~/.panoplie`. */ |
| 45 | configDir?: string | 46 | configDir?: string |
| 46 | scanContext?: ScanContext | 47 | scanContext?: ScanContext |
| 47 | /** Sync engine (defaults to the real one); injectable for tests. */ | 48 | /** Sync engine (defaults to the real one); injectable for tests. */ |
| @@ -153,7 +154,7 @@ export function createApp(options: AppOptions): Hono { | |||
| 153 | 154 | ||
| 154 | export function createAppHandle(options: AppOptions): AppHandle { | 155 | export function createAppHandle(options: AppOptions): AppHandle { |
| 155 | const { token } = options | 156 | const { token } = options |
| 156 | - const configDir = options.configDir ?? path.join(os.homedir(), '.skilly') | 157 | + const configDir = options.configDir ?? path.join(os.homedir(), '.panoplie') |
| 157 | const ctx = options.scanContext ?? defaultScanContext() | 158 | const ctx = options.scanContext ?? defaultScanContext() |
| 158 | const store = new ProjectStore(configDir) | 159 | const store = new ProjectStore(configDir) |
| 159 | const scanCache = new Map<string, { at: number; inventory: ScopeInventory }>() | 160 | const scanCache = new Map<string, { at: number; inventory: ScopeInventory }>() |
| @@ -175,7 +176,7 @@ export function createAppHandle(options: AppOptions): AppHandle { | |||
| 175 | const status = await git.status() | 176 | const status = await git.status() |
| 176 | if (!status.isRepo) return | 177 | if (!status.isRepo) return |
| 177 | if (status.remote) await git.sync() | 178 | if (status.remote) await git.sync() |
| 178 | - else await git.commitAll('skilly: update catalog') | 179 | + else await git.commitAll('panoplie: update catalog') |
| 179 | } | 180 | } |
| 180 | 181 | ||
| 181 | const appCtx: AppContext = { | 182 | const appCtx: AppContext = { |
| @@ -212,7 +213,7 @@ export function createAppHandle(options: AppOptions): AppHandle { | |||
| 212 | syncTimer = undefined | 213 | syncTimer = undefined |
| 213 | syncChain = syncChain | 214 | syncChain = syncChain |
| 214 | .then(runAutoSync) | 215 | .then(runAutoSync) |
| 215 | - .catch((e: unknown) => console.error('skilly: catalog auto-sync failed:', e instanceof Error ? e.message : e)) | 216 | + .catch((e: unknown) => console.error('panoplie: catalog auto-sync failed:', e instanceof Error ? e.message : e)) |
| 216 | }, options.autoSyncDelayMs ?? 3000) | 217 | }, options.autoSyncDelayMs ?? 3000) |
| 217 | syncTimer.unref() | 218 | syncTimer.unref() |
| 218 | }) | 219 | }) |
| @@ -229,7 +230,7 @@ export function createAppHandle(options: AppOptions): AppHandle { | |||
| 229 | }) | 230 | }) |
| 230 | 231 | ||
| 231 | app.use('/api/*', async (c, next) => { | 232 | app.use('/api/*', async (c, next) => { |
| 232 | - if (!tokenMatches(c.req.header('x-skilly-token'), token)) return c.json(err('Unauthorized'), 401) | 233 | + if (!tokenMatches(c.req.header('x-panoplie-token'), token)) return c.json(err('Unauthorized'), 401) |
| 233 | await next() | 234 | await next() |
| 234 | }) | 235 | }) |
| 235 | 236 | ||
| @@ -376,6 +377,8 @@ export function createAppHandle(options: AppOptions): AppHandle { | |||
| 376 | } | 377 | } |
| 377 | 378 | ||
| 378 | export async function startServer(options: ServerOptions): Promise<RunningServer> { | 379 | export async function startServer(options: ServerOptions): Promise<RunningServer> { |
| 380 | + // Callers passing configDir (the CLI) migrate it themselves; the default folder is migrated here. | ||
| 381 | + if (options.configDir === undefined) await migrateLegacyConfigDir(path.join(os.homedir(), '.panoplie')) | ||
| 379 | let idleTimer: NodeJS.Timeout | undefined | 382 | let idleTimer: NodeJS.Timeout | undefined |
| 380 | let closed = false | 383 | let closed = false |
| 381 | const armWatchdog = (ms: number): void => { | 384 | const armWatchdog = (ms: number): void => { |
modified
packages/server/src/marketplace-routes.ts +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { readFile, readdir, rm } from 'node:fs/promises' |
| 2 | 2 | import path from 'node:path' |
| 3 | -import type { ImportResult, MarketplaceSkillDetail, MarketplaceSource } from '@skilly/core' | |
| 3 | +import type { ImportResult, MarketplaceSkillDetail, MarketplaceSource } from '@panoplie/core' | |
| 4 | 4 | import type { Hono } from 'hono' |
| 5 | 5 | import { HttpError, isStringArray, readJsonBody, type AppContext } from './context.ts' |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | |||
| 1 | import { readFile, readdir, rm } from 'node:fs/promises' | 1 | import { readFile, readdir, rm } from 'node:fs/promises' |
| 2 | import path from 'node:path' | 2 | import path from 'node:path' |
| 3 | -import type { ImportResult, MarketplaceSkillDetail, MarketplaceSource } from '@skilly/core' | 3 | +import type { ImportResult, MarketplaceSkillDetail, MarketplaceSource } from '@panoplie/core' |
| 4 | import type { Hono } from 'hono' | 4 | import type { Hono } from 'hono' |
| 5 | import { HttpError, isStringArray, readJsonBody, type AppContext } from './context.ts' | 5 | import { HttpError, isStringArray, readJsonBody, type AppContext } from './context.ts' |
| 6 | 6 | ||
modified
packages/server/src/scope-routes.ts +1 -1 | @@ -15,7 +15,7 @@ import { | ||
| 15 | 15 | type ScopeInventory, |
| 16 | 16 | type ScopeState, |
| 17 | 17 | type SyncPlan, |
| 18 | -} from '@skilly/core' | |
| 18 | +} from '@panoplie/core' | |
| 19 | 19 | import type { Hono } from 'hono' |
| 20 | 20 | import { HttpError, isStringArray, memoizeCatalogReads, readJsonBody, type AppContext, type ScopeRef } from './context.ts' |
| 21 | 21 | |
| @@ -15,7 +15,7 @@ import { | |||
| 15 | type ScopeInventory, | 15 | type ScopeInventory, |
| 16 | type ScopeState, | 16 | type ScopeState, |
| 17 | type SyncPlan, | 17 | type SyncPlan, |
| 18 | -} from '@skilly/core' | 18 | +} from '@panoplie/core' |
| 19 | import type { Hono } from 'hono' | 19 | import type { Hono } from 'hono' |
| 20 | import { HttpError, isStringArray, memoizeCatalogReads, readJsonBody, type AppContext, type ScopeRef } from './context.ts' | 20 | import { HttpError, isStringArray, memoizeCatalogReads, readJsonBody, type AppContext, type ScopeRef } from './context.ts' |
| 21 | 21 | ||
modified
packages/server/src/store.ts +1 -1 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { createHash } from 'node:crypto' |
| 2 | 2 | import { mkdir, readFile, rename, writeFile } from 'node:fs/promises' |
| 3 | 3 | import path from 'node:path' |
| 4 | -import { normalizePath, type MarketplaceSource, type Settings } from '@skilly/core' | |
| 4 | +import { normalizePath, type MarketplaceSource, type Settings } from '@panoplie/core' | |
| 5 | 5 | |
| 6 | 6 | export interface StoredProject { |
| 7 | 7 | id: string |
| @@ -1,7 +1,7 @@ | |||
| 1 | import { createHash } from 'node:crypto' | 1 | import { createHash } from 'node:crypto' |
| 2 | import { mkdir, readFile, rename, writeFile } from 'node:fs/promises' | 2 | import { mkdir, readFile, rename, writeFile } from 'node:fs/promises' |
| 3 | import path from 'node:path' | 3 | import path from 'node:path' |
| 4 | -import { normalizePath, type MarketplaceSource, type Settings } from '@skilly/core' | 4 | +import { normalizePath, type MarketplaceSource, type Settings } from '@panoplie/core' |
| 5 | 5 | ||
| 6 | export interface StoredProject { | 6 | export interface StoredProject { |
| 7 | id: string | 7 | id: string |
modified
packages/server/test/catalog.test.ts +3 -3 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { execFileSync } from 'node:child_process' |
| 2 | 2 | import { mkdir, readFile } from 'node:fs/promises' |
| 3 | 3 | import path from 'node:path' |
| 4 | -import type { CatalogItem, CatalogItemDetail, CatalogResponse, FileContent, GitStatus, ImportResult, Settings } from '@skilly/core' | |
| 4 | +import type { CatalogItem, CatalogItemDetail, CatalogResponse, FileContent, GitStatus, ImportResult, Settings } from '@panoplie/core' | |
| 5 | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 6 | 6 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' |
| 7 | 7 | |
| @@ -181,11 +181,11 @@ describe('catalog git', { timeout: 30_000 }, () => { | ||
| 181 | 181 | await env.send('PUT', '/api/settings', { autoSync: true }) |
| 182 | 182 | await env.send('PUT', '/api/catalog/items/skill/auto1', { content: skillMd('auto1') }) |
| 183 | 183 | await waitFor(() => git(env.catalogDir, '--no-optional-locks', 'status', '--porcelain').trim() === '') |
| 184 | - expect(git(env.catalogDir, 'log', '--oneline')).toContain('skilly') | |
| 184 | + expect(git(env.catalogDir, 'log', '--oneline')).toContain('panoplie') | |
| 185 | 185 | |
| 186 | 186 | await env.send('POST', '/api/catalog/git/remote', { url: remote }) |
| 187 | 187 | await env.send('PUT', '/api/catalog/items/skill/auto2', { content: skillMd('auto2') }) |
| 188 | - await waitFor(() => safe(() => git(remote, 'log', '--oneline', 'main')).includes('skilly')) | |
| 188 | + await waitFor(() => safe(() => git(remote, 'log', '--oneline', 'main')).includes('panoplie')) | |
| 189 | 189 | expect(git(remote, 'ls-tree', '-r', '--name-only', 'main')).toContain('skills/auto2/SKILL.md') |
| 190 | 190 | }) |
| 191 | 191 | |
| @@ -1,7 +1,7 @@ | |||
| 1 | import { execFileSync } from 'node:child_process' | 1 | import { execFileSync } from 'node:child_process' |
| 2 | import { mkdir, readFile } from 'node:fs/promises' | 2 | import { mkdir, readFile } from 'node:fs/promises' |
| 3 | import path from 'node:path' | 3 | import path from 'node:path' |
| 4 | -import type { CatalogItem, CatalogItemDetail, CatalogResponse, FileContent, GitStatus, ImportResult, Settings } from '@skilly/core' | 4 | +import type { CatalogItem, CatalogItemDetail, CatalogResponse, FileContent, GitStatus, ImportResult, Settings } from '@panoplie/core' |
| 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 6 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' | 6 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' |
| 7 | 7 | ||
| @@ -181,11 +181,11 @@ describe('catalog git', { timeout: 30_000 }, () => { | |||
| 181 | await env.send('PUT', '/api/settings', { autoSync: true }) | 181 | await env.send('PUT', '/api/settings', { autoSync: true }) |
| 182 | await env.send('PUT', '/api/catalog/items/skill/auto1', { content: skillMd('auto1') }) | 182 | await env.send('PUT', '/api/catalog/items/skill/auto1', { content: skillMd('auto1') }) |
| 183 | await waitFor(() => git(env.catalogDir, '--no-optional-locks', 'status', '--porcelain').trim() === '') | 183 | await waitFor(() => git(env.catalogDir, '--no-optional-locks', 'status', '--porcelain').trim() === '') |
| 184 | - expect(git(env.catalogDir, 'log', '--oneline')).toContain('skilly') | 184 | + expect(git(env.catalogDir, 'log', '--oneline')).toContain('panoplie') |
| 185 | 185 | ||
| 186 | await env.send('POST', '/api/catalog/git/remote', { url: remote }) | 186 | await env.send('POST', '/api/catalog/git/remote', { url: remote }) |
| 187 | await env.send('PUT', '/api/catalog/items/skill/auto2', { content: skillMd('auto2') }) | 187 | await env.send('PUT', '/api/catalog/items/skill/auto2', { content: skillMd('auto2') }) |
| 188 | - await waitFor(() => safe(() => git(remote, 'log', '--oneline', 'main')).includes('skilly')) | 188 | + await waitFor(() => safe(() => git(remote, 'log', '--oneline', 'main')).includes('panoplie')) |
| 189 | expect(git(remote, 'ls-tree', '-r', '--name-only', 'main')).toContain('skills/auto2/SKILL.md') | 189 | expect(git(remote, 'ls-tree', '-r', '--name-only', 'main')).toContain('skills/auto2/SKILL.md') |
| 190 | }) | 190 | }) |
| 191 | 191 | ||
modified
packages/server/test/helpers.ts +3 -3 | @@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process' | ||
| 2 | 2 | import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises' |
| 3 | 3 | import os from 'node:os' |
| 4 | 4 | import path from 'node:path' |
| 5 | -import type { ApplyResult, ConflictResolution, InventoryItem, ScopeConfig, SyncEngine, SyncPlan, SyncTarget } from '@skilly/core' | |
| 5 | +import type { ApplyResult, ConflictResolution, InventoryItem, ScopeConfig, SyncEngine, SyncPlan, SyncTarget } from '@panoplie/core' | |
| 6 | 6 | import type { Hono } from 'hono' |
| 7 | 7 | import { createApp, type AppOptions } from '../src/index.ts' |
| 8 | 8 | |
| @@ -43,7 +43,7 @@ process.env.GIT_COMMITTER_NAME = 'Test' | ||
| 43 | 43 | process.env.GIT_COMMITTER_EMAIL = 'test@example.com' |
| 44 | 44 | |
| 45 | 45 | export async function makeEnv(extra: Partial<AppOptions> = {}): Promise<Env> { |
| 46 | - const tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'skilly-server-'))) | |
| 46 | + const tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'panoplie-server-'))) | |
| 47 | 47 | const home = path.join(tmp, 'home') |
| 48 | 48 | const configDir = path.join(tmp, 'config') |
| 49 | 49 | const project = path.join(tmp, 'work', 'my-project') |
| @@ -52,7 +52,7 @@ export async function makeEnv(extra: Partial<AppOptions> = {}): Promise<Env> { | ||
| 52 | 52 | const app = createApp({ token: TOKEN, configDir, scanContext: { homeDir: home }, autoSyncDelayMs: 50, ...extra }) |
| 53 | 53 | const req = (url: string, init: RequestInit = {}) => { |
| 54 | 54 | const headers = new Headers(init.headers) |
| 55 | - headers.set('x-skilly-token', TOKEN) | |
| 55 | + headers.set('x-panoplie-token', TOKEN) | |
| 56 | 56 | return Promise.resolve(app.request(url, { ...init, headers })) |
| 57 | 57 | } |
| 58 | 58 | const send = (method: string, url: string, body?: unknown) => |
| @@ -2,7 +2,7 @@ import { execFileSync } from 'node:child_process' | |||
| 2 | import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises' | 2 | import { mkdir, mkdtemp, realpath, rm, writeFile } from 'node:fs/promises' |
| 3 | import os from 'node:os' | 3 | import os from 'node:os' |
| 4 | import path from 'node:path' | 4 | import path from 'node:path' |
| 5 | -import type { ApplyResult, ConflictResolution, InventoryItem, ScopeConfig, SyncEngine, SyncPlan, SyncTarget } from '@skilly/core' | 5 | +import type { ApplyResult, ConflictResolution, InventoryItem, ScopeConfig, SyncEngine, SyncPlan, SyncTarget } from '@panoplie/core' |
| 6 | import type { Hono } from 'hono' | 6 | import type { Hono } from 'hono' |
| 7 | import { createApp, type AppOptions } from '../src/index.ts' | 7 | import { createApp, type AppOptions } from '../src/index.ts' |
| 8 | 8 | ||
| @@ -43,7 +43,7 @@ process.env.GIT_COMMITTER_NAME = 'Test' | |||
| 43 | process.env.GIT_COMMITTER_EMAIL = 'test@example.com' | 43 | process.env.GIT_COMMITTER_EMAIL = 'test@example.com' |
| 44 | 44 | ||
| 45 | export async function makeEnv(extra: Partial<AppOptions> = {}): Promise<Env> { | 45 | export async function makeEnv(extra: Partial<AppOptions> = {}): Promise<Env> { |
| 46 | - const tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'skilly-server-'))) | 46 | + const tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'panoplie-server-'))) |
| 47 | const home = path.join(tmp, 'home') | 47 | const home = path.join(tmp, 'home') |
| 48 | const configDir = path.join(tmp, 'config') | 48 | const configDir = path.join(tmp, 'config') |
| 49 | const project = path.join(tmp, 'work', 'my-project') | 49 | const project = path.join(tmp, 'work', 'my-project') |
| @@ -52,7 +52,7 @@ export async function makeEnv(extra: Partial<AppOptions> = {}): Promise<Env> { | |||
| 52 | const app = createApp({ token: TOKEN, configDir, scanContext: { homeDir: home }, autoSyncDelayMs: 50, ...extra }) | 52 | const app = createApp({ token: TOKEN, configDir, scanContext: { homeDir: home }, autoSyncDelayMs: 50, ...extra }) |
| 53 | const req = (url: string, init: RequestInit = {}) => { | 53 | const req = (url: string, init: RequestInit = {}) => { |
| 54 | const headers = new Headers(init.headers) | 54 | const headers = new Headers(init.headers) |
| 55 | - headers.set('x-skilly-token', TOKEN) | 55 | + headers.set('x-panoplie-token', TOKEN) |
| 56 | return Promise.resolve(app.request(url, { ...init, headers })) | 56 | return Promise.resolve(app.request(url, { ...init, headers })) |
| 57 | } | 57 | } |
| 58 | const send = (method: string, url: string, body?: unknown) => | 58 | const send = (method: string, url: string, body?: unknown) => |
modified
packages/server/test/integration.test.ts +1 -1 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { execFileSync } from 'node:child_process' |
| 2 | 2 | import { readFile, writeFile } from 'node:fs/promises' |
| 3 | 3 | import path from 'node:path' |
| 4 | -import { syncEngine, type ApplyResponse, type ProjectSummary, type ScopeConfig, type ScopeState } from '@skilly/core' | |
| 4 | +import { syncEngine, type ApplyResponse, type ProjectSummary, type ScopeConfig, type ScopeState } from '@panoplie/core' | |
| 5 | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 6 | 6 | import { makeEnv, skillMd, type Env } from './helpers.ts' |
| 7 | 7 | |
| @@ -1,7 +1,7 @@ | |||
| 1 | import { execFileSync } from 'node:child_process' | 1 | import { execFileSync } from 'node:child_process' |
| 2 | import { readFile, writeFile } from 'node:fs/promises' | 2 | import { readFile, writeFile } from 'node:fs/promises' |
| 3 | import path from 'node:path' | 3 | import path from 'node:path' |
| 4 | -import { syncEngine, type ApplyResponse, type ProjectSummary, type ScopeConfig, type ScopeState } from '@skilly/core' | 4 | +import { syncEngine, type ApplyResponse, type ProjectSummary, type ScopeConfig, type ScopeState } from '@panoplie/core' |
| 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 6 | import { makeEnv, skillMd, type Env } from './helpers.ts' | 6 | import { makeEnv, skillMd, type Env } from './helpers.ts' |
| 7 | 7 | ||
modified
packages/server/test/marketplace.test.ts +1 -1 | @@ -1,6 +1,6 @@ | ||
| 1 | 1 | import { mkdir } from 'node:fs/promises' |
| 2 | 2 | import path from 'node:path' |
| 3 | -import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@skilly/core' | |
| 3 | +import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@panoplie/core' | |
| 4 | 4 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 5 | 5 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' |
| 6 | 6 | |
| @@ -1,6 +1,6 @@ | |||
| 1 | import { mkdir } from 'node:fs/promises' | 1 | import { mkdir } from 'node:fs/promises' |
| 2 | import path from 'node:path' | 2 | import path from 'node:path' |
| 3 | -import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@skilly/core' | 3 | +import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@panoplie/core' |
| 4 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' | 4 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 5 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' | 5 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' |
| 6 | 6 | ||
modified
packages/server/test/onboarding.test.ts +1 -1 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { execFileSync } from 'node:child_process' |
| 2 | 2 | import { mkdir, readFile } from 'node:fs/promises' |
| 3 | 3 | import path from 'node:path' |
| 4 | -import type { CatalogResponse, FsDiscovery, Settings } from '@skilly/core' | |
| 4 | +import type { CatalogResponse, FsDiscovery, Settings } from '@panoplie/core' | |
| 5 | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 6 | 6 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' |
| 7 | 7 | |
| @@ -1,7 +1,7 @@ | |||
| 1 | import { execFileSync } from 'node:child_process' | 1 | import { execFileSync } from 'node:child_process' |
| 2 | import { mkdir, readFile } from 'node:fs/promises' | 2 | import { mkdir, readFile } from 'node:fs/promises' |
| 3 | import path from 'node:path' | 3 | import path from 'node:path' |
| 4 | -import type { CatalogResponse, FsDiscovery, Settings } from '@skilly/core' | 4 | +import type { CatalogResponse, FsDiscovery, Settings } from '@panoplie/core' |
| 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' | 5 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 6 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' | 6 | import { fakeEngine, git, makeEnv, put, skillMd, type Env } from './helpers.ts' |
| 7 | 7 | ||
modified
packages/server/test/scopes.test.ts +2 -2 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { ApplyResponse, CatalogItem, ProjectSummary, RemoveFromGlobalResult, ScopeConfig, ScopeState } from '@skilly/core' | |
| 1 | +import type { ApplyResponse, CatalogItem, ProjectSummary, RemoveFromGlobalResult, ScopeConfig, ScopeState } from '@panoplie/core' | |
| 2 | 2 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 3 | 3 | import { fakeEngine, makeEnv, put, skillMd, type Env, type FakeEngine } from './helpers.ts' |
| 4 | 4 | |
| @@ -53,7 +53,7 @@ describe('scope state and config', () => { | ||
| 53 | 53 | expect(write.args[0]).toMatchObject({ kind: 'project', root: env.project, configDir: env.project, homeDir: env.home }) |
| 54 | 54 | }) |
| 55 | 55 | |
| 56 | - it('uses the skilly config dir as target for Home', async () => { | |
| 56 | + it('uses the panoplie config dir as target for Home', async () => { | |
| 57 | 57 | await env.send('PUT', '/api/scopes/home/config', config()) |
| 58 | 58 | const write = engine.calls.find((c) => c.method === 'writeConfig')! |
| 59 | 59 | expect(write.args[0]).toMatchObject({ kind: 'home', root: env.home, configDir: env.configDir }) |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { ApplyResponse, CatalogItem, ProjectSummary, RemoveFromGlobalResult, ScopeConfig, ScopeState } from '@skilly/core' | 1 | +import type { ApplyResponse, CatalogItem, ProjectSummary, RemoveFromGlobalResult, ScopeConfig, ScopeState } from '@panoplie/core' |
| 2 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' | 2 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 3 | import { fakeEngine, makeEnv, put, skillMd, type Env, type FakeEngine } from './helpers.ts' | 3 | import { fakeEngine, makeEnv, put, skillMd, type Env, type FakeEngine } from './helpers.ts' |
| 4 | 4 | ||
| @@ -53,7 +53,7 @@ describe('scope state and config', () => { | |||
| 53 | expect(write.args[0]).toMatchObject({ kind: 'project', root: env.project, configDir: env.project, homeDir: env.home }) | 53 | expect(write.args[0]).toMatchObject({ kind: 'project', root: env.project, configDir: env.project, homeDir: env.home }) |
| 54 | }) | 54 | }) |
| 55 | 55 | ||
| 56 | - it('uses the skilly config dir as target for Home', async () => { | 56 | + it('uses the panoplie config dir as target for Home', async () => { |
| 57 | await env.send('PUT', '/api/scopes/home/config', config()) | 57 | await env.send('PUT', '/api/scopes/home/config', config()) |
| 58 | const write = engine.calls.find((c) => c.method === 'writeConfig')! | 58 | const write = engine.calls.find((c) => c.method === 'writeConfig')! |
| 59 | expect(write.args[0]).toMatchObject({ kind: 'home', root: env.home, configDir: env.configDir }) | 59 | expect(write.args[0]).toMatchObject({ kind: 'home', root: env.home, configDir: env.configDir }) |
modified
packages/server/test/server.test.ts +4 -4 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises' |
| 2 | 2 | import os from 'node:os' |
| 3 | 3 | import path from 'node:path' |
| 4 | -import type { FileContent, FsListing, ProjectSummary } from '@skilly/core' | |
| 4 | +import type { FileContent, FsListing, ProjectSummary } from '@panoplie/core' | |
| 5 | 5 | import type { Hono } from 'hono' |
| 6 | 6 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 7 | 7 | import { createApp, startServer } from '../src/index.ts' |
| @@ -15,7 +15,7 @@ let app: Hono | ||
| 15 | 15 | |
| 16 | 16 | function req(url: string, init: RequestInit = {}, token: string | null = TOKEN) { |
| 17 | 17 | const headers = new Headers(init.headers) |
| 18 | - if (token) headers.set('x-skilly-token', token) | |
| 18 | + if (token) headers.set('x-panoplie-token', token) | |
| 19 | 19 | return app.request(url, { ...init, headers }) |
| 20 | 20 | } |
| 21 | 21 | |
| @@ -24,7 +24,7 @@ function post(url: string, body: unknown) { | ||
| 24 | 24 | } |
| 25 | 25 | |
| 26 | 26 | beforeEach(async () => { |
| 27 | - tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'skilly-server-'))) | |
| 27 | + tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'panoplie-server-'))) | |
| 28 | 28 | home = path.join(tmp, 'home') |
| 29 | 29 | configDir = path.join(tmp, 'config') |
| 30 | 30 | project = path.join(tmp, 'work', 'my-project') |
| @@ -216,7 +216,7 @@ describe('startServer', () => { | ||
| 216 | 216 | }) |
| 217 | 217 | try { |
| 218 | 218 | expect(server.port).toBeGreaterThan(0) |
| 219 | - const headers = { 'x-skilly-token': TOKEN } | |
| 219 | + const headers = { 'x-panoplie-token': TOKEN } | |
| 220 | 220 | const res = await fetch(`${server.url}/api/health`, { headers }) |
| 221 | 221 | expect(res.status).toBe(200) |
| 222 | 222 | expect(idle).toBe(0) // startup grace is at least 60 s |
| @@ -1,7 +1,7 @@ | |||
| 1 | import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises' | 1 | import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises' |
| 2 | import os from 'node:os' | 2 | import os from 'node:os' |
| 3 | import path from 'node:path' | 3 | import path from 'node:path' |
| 4 | -import type { FileContent, FsListing, ProjectSummary } from '@skilly/core' | 4 | +import type { FileContent, FsListing, ProjectSummary } from '@panoplie/core' |
| 5 | import type { Hono } from 'hono' | 5 | import type { Hono } from 'hono' |
| 6 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' | 6 | import { afterEach, beforeEach, describe, expect, it } from 'vitest' |
| 7 | import { createApp, startServer } from '../src/index.ts' | 7 | import { createApp, startServer } from '../src/index.ts' |
| @@ -15,7 +15,7 @@ let app: Hono | |||
| 15 | 15 | ||
| 16 | function req(url: string, init: RequestInit = {}, token: string | null = TOKEN) { | 16 | function req(url: string, init: RequestInit = {}, token: string | null = TOKEN) { |
| 17 | const headers = new Headers(init.headers) | 17 | const headers = new Headers(init.headers) |
| 18 | - if (token) headers.set('x-skilly-token', token) | 18 | + if (token) headers.set('x-panoplie-token', token) |
| 19 | return app.request(url, { ...init, headers }) | 19 | return app.request(url, { ...init, headers }) |
| 20 | } | 20 | } |
| 21 | 21 | ||
| @@ -24,7 +24,7 @@ function post(url: string, body: unknown) { | |||
| 24 | } | 24 | } |
| 25 | 25 | ||
| 26 | beforeEach(async () => { | 26 | beforeEach(async () => { |
| 27 | - tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'skilly-server-'))) | 27 | + tmp = await realpath(await mkdtemp(path.join(os.tmpdir(), 'panoplie-server-'))) |
| 28 | home = path.join(tmp, 'home') | 28 | home = path.join(tmp, 'home') |
| 29 | configDir = path.join(tmp, 'config') | 29 | configDir = path.join(tmp, 'config') |
| 30 | project = path.join(tmp, 'work', 'my-project') | 30 | project = path.join(tmp, 'work', 'my-project') |
| @@ -216,7 +216,7 @@ describe('startServer', () => { | |||
| 216 | }) | 216 | }) |
| 217 | try { | 217 | try { |
| 218 | expect(server.port).toBeGreaterThan(0) | 218 | expect(server.port).toBeGreaterThan(0) |
| 219 | - const headers = { 'x-skilly-token': TOKEN } | 219 | + const headers = { 'x-panoplie-token': TOKEN } |
| 220 | const res = await fetch(`${server.url}/api/health`, { headers }) | 220 | const res = await fetch(`${server.url}/api/health`, { headers }) |
| 221 | expect(res.status).toBe(200) | 221 | expect(res.status).toBe(200) |
| 222 | expect(idle).toBe(0) // startup grace is at least 60 s | 222 | expect(idle).toBe(0) // startup grace is at least 60 s |
modified
packages/ui/app/assets/css/main.css +1 -1 | @@ -75,7 +75,7 @@ | ||
| 75 | 75 | --text-sm--line-height: 1.45; |
| 76 | 76 | } |
| 77 | 77 | |
| 78 | -/* ------------------------------------------------------------------ skilly tokens (source of truth: design/BRIEF.md 1.2) */ | |
| 78 | +/* ------------------------------------------------------------------ panoplie tokens (source of truth: design/BRIEF.md 1.2) */ | |
| 79 | 79 | :root, |
| 80 | 80 | .light { |
| 81 | 81 | --sk-bg: #f5f4f1; |
| @@ -75,7 +75,7 @@ | |||
| 75 | --text-sm--line-height: 1.45; | 75 | --text-sm--line-height: 1.45; |
| 76 | } | 76 | } |
| 77 | 77 | ||
| 78 | -/* ------------------------------------------------------------------ skilly tokens (source of truth: design/BRIEF.md 1.2) */ | 78 | +/* ------------------------------------------------------------------ panoplie tokens (source of truth: design/BRIEF.md 1.2) */ |
| 79 | :root, | 79 | :root, |
| 80 | .light { | 80 | .light { |
| 81 | --sk-bg: #f5f4f1; | 81 | --sk-bg: #f5f4f1; |
modified
packages/ui/app/components/AddToCatalogModal.vue +4 -4 | @@ -1,9 +1,9 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ImportResult, InventoryItem, ItemStatus } from '@skilly/core' | |
| 2 | +import type { ImportResult, InventoryItem, ItemStatus } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * "Add to catalog": copies local items into the catalog (tags, "overwrite" when a different version exists). |
| 6 | - * The scope page then keeps them enabled where they came from, managed by skilly (`done` carries the result). | |
| 6 | + * The scope page then keeps them enabled where they came from, managed by panoplie (`done` carries the result). | |
| 7 | 7 | */ |
| 8 | 8 | const props = defineProps<{ scope: string, items: InventoryItem[], statuses: Record<string, ItemStatus> }>() |
| 9 | 9 | const open = defineModel<boolean>('open', { default: false }) |
| @@ -67,7 +67,7 @@ async function submit() { | ||
| 67 | 67 | }) |
| 68 | 68 | toast.add({ |
| 69 | 69 | title: `${result.value.imported.length} added to the catalog`, |
| 70 | - description: result.value.imported.length ? 'They stay here, now managed by skilly. Apply to take them over.' : undefined, | |
| 70 | + description: result.value.imported.length ? 'They stay here, now managed by panoplie. Apply to take them over.' : undefined, | |
| 71 | 71 | icon: 'i-lucide-check', |
| 72 | 72 | color: 'success' |
| 73 | 73 | }) |
| @@ -81,7 +81,7 @@ async function submit() { | ||
| 81 | 81 | </script> |
| 82 | 82 | |
| 83 | 83 | <template> |
| 84 | - <SkModal v-model:open="open" :width="items.some(i => statuses[i.id]?.state === 'differs-from-catalog') ? 'lg' : 'md'" title="Add to catalog" description="Copy these items into the catalog so they can be reused in other projects. They stay enabled here, managed by skilly."> | |
| 84 | + <SkModal v-model:open="open" :width="items.some(i => statuses[i.id]?.state === 'differs-from-catalog') ? 'lg' : 'md'" title="Add to catalog" description="Copy these items into the catalog so they can be reused in other projects. They stay enabled here, managed by panoplie."> | |
| 85 | 85 | <div v-if="result" class="flex flex-col gap-3"> |
| 86 | 86 | <SkNote v-if="result.imported.length" tone="ok"> |
| 87 | 87 | <span class="font-medium">{{ result.imported.length }} added</span> |
| @@ -1,9 +1,9 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ImportResult, InventoryItem, ItemStatus } from '@skilly/core' | 2 | +import type { ImportResult, InventoryItem, ItemStatus } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * "Add to catalog": copies local items into the catalog (tags, "overwrite" when a different version exists). | 5 | * "Add to catalog": copies local items into the catalog (tags, "overwrite" when a different version exists). |
| 6 | - * The scope page then keeps them enabled where they came from, managed by skilly (`done` carries the result). | 6 | + * The scope page then keeps them enabled where they came from, managed by panoplie (`done` carries the result). |
| 7 | */ | 7 | */ |
| 8 | const props = defineProps<{ scope: string, items: InventoryItem[], statuses: Record<string, ItemStatus> }>() | 8 | const props = defineProps<{ scope: string, items: InventoryItem[], statuses: Record<string, ItemStatus> }>() |
| 9 | const open = defineModel<boolean>('open', { default: false }) | 9 | const open = defineModel<boolean>('open', { default: false }) |
| @@ -67,7 +67,7 @@ async function submit() { | |||
| 67 | }) | 67 | }) |
| 68 | toast.add({ | 68 | toast.add({ |
| 69 | title: `${result.value.imported.length} added to the catalog`, | 69 | title: `${result.value.imported.length} added to the catalog`, |
| 70 | - description: result.value.imported.length ? 'They stay here, now managed by skilly. Apply to take them over.' : undefined, | 70 | + description: result.value.imported.length ? 'They stay here, now managed by panoplie. Apply to take them over.' : undefined, |
| 71 | icon: 'i-lucide-check', | 71 | icon: 'i-lucide-check', |
| 72 | color: 'success' | 72 | color: 'success' |
| 73 | }) | 73 | }) |
| @@ -81,7 +81,7 @@ async function submit() { | |||
| 81 | </script> | 81 | </script> |
| 82 | 82 | ||
| 83 | <template> | 83 | <template> |
| 84 | - <SkModal v-model:open="open" :width="items.some(i => statuses[i.id]?.state === 'differs-from-catalog') ? 'lg' : 'md'" title="Add to catalog" description="Copy these items into the catalog so they can be reused in other projects. They stay enabled here, managed by skilly."> | 84 | + <SkModal v-model:open="open" :width="items.some(i => statuses[i.id]?.state === 'differs-from-catalog') ? 'lg' : 'md'" title="Add to catalog" description="Copy these items into the catalog so they can be reused in other projects. They stay enabled here, managed by panoplie."> |
| 85 | <div v-if="result" class="flex flex-col gap-3"> | 85 | <div v-if="result" class="flex flex-col gap-3"> |
| 86 | <SkNote v-if="result.imported.length" tone="ok"> | 86 | <SkNote v-if="result.imported.length" tone="ok"> |
| 87 | <span class="font-medium">{{ result.imported.length }} added</span> | 87 | <span class="font-medium">{{ result.imported.length }} added</span> |
modified
packages/ui/app/components/AppHeader.vue +2 -2 | @@ -45,9 +45,9 @@ async function onPill() { | ||
| 45 | 45 | <header |
| 46 | 46 | class="sticky top-0 z-30 flex min-w-0 items-center gap-2.5 px-[18px] py-3 backdrop-blur-[8px] bg-[color-mix(in_oklch,var(--sk-bg)_82%,transparent)]" |
| 47 | 47 | > |
| 48 | - <NuxtLink to="/" class="flex shrink-0 items-center gap-2 text-sk-text hover:text-sk-text" aria-label="skilly"> | |
| 48 | + <NuxtLink to="/" class="flex shrink-0 items-center gap-2 text-sk-text hover:text-sk-text" aria-label="panoplie"> | |
| 49 | 49 | <SkLogo /> |
| 50 | - <span class="text-base font-semibold">skilly</span> | |
| 50 | + <span class="text-base font-semibold">panoplie</span> | |
| 51 | 51 | </NuxtLink> |
| 52 | 52 | |
| 53 | 53 | <span class="min-w-0 flex-1" /> |
| @@ -45,9 +45,9 @@ async function onPill() { | |||
| 45 | <header | 45 | <header |
| 46 | class="sticky top-0 z-30 flex min-w-0 items-center gap-2.5 px-[18px] py-3 backdrop-blur-[8px] bg-[color-mix(in_oklch,var(--sk-bg)_82%,transparent)]" | 46 | class="sticky top-0 z-30 flex min-w-0 items-center gap-2.5 px-[18px] py-3 backdrop-blur-[8px] bg-[color-mix(in_oklch,var(--sk-bg)_82%,transparent)]" |
| 47 | > | 47 | > |
| 48 | - <NuxtLink to="/" class="flex shrink-0 items-center gap-2 text-sk-text hover:text-sk-text" aria-label="skilly"> | 48 | + <NuxtLink to="/" class="flex shrink-0 items-center gap-2 text-sk-text hover:text-sk-text" aria-label="panoplie"> |
| 49 | <SkLogo /> | 49 | <SkLogo /> |
| 50 | - <span class="text-base font-semibold">skilly</span> | 50 | + <span class="text-base font-semibold">panoplie</span> |
| 51 | </NuxtLink> | 51 | </NuxtLink> |
| 52 | 52 | ||
| 53 | <span class="min-w-0 flex-1" /> | 53 | <span class="min-w-0 flex-1" /> |
modified
packages/ui/app/components/CatalogBrowser.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItem, CatalogItemType } from '@skilly/core' | |
| 2 | +import type { CatalogItem, CatalogItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Catalog items screen: header, left column (search, type chips, tag chips, list) and the detail panel. |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItem, CatalogItemType } from '@skilly/core' | 2 | +import type { CatalogItem, CatalogItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Catalog items screen: header, left column (search, type chips, tag chips, list) and the detail panel. | 5 | * Catalog items screen: header, left column (search, type chips, tag chips, list) and the detail panel. |
modified
packages/ui/app/components/CatalogHeader.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType } from '@skilly/core' | |
| 2 | +import type { CatalogItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Catalog header: title, "Your shared library" and the New split button (the main action creates the current type, |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType } from '@skilly/core' | 2 | +import type { CatalogItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Catalog header: title, "Your shared library" and the New split button (the main action creates the current type, | 5 | * Catalog header: title, "Your shared library" and the New split button (the main action creates the current type, |
modified
packages/ui/app/components/CatalogItemDiff.vue +2 -2 | @@ -1,8 +1,8 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemDetail, FileContent, InventoryItem, SkillDetails } from '@skilly/core' | |
| 2 | +import type { CatalogItemDetail, FileContent, InventoryItem, SkillDetails } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | - * Diff of a local item (not managed by skilly) against the catalog item of the same name, shown before an import | |
| 5 | + * Diff of a local item (not managed by panoplie) against the catalog item of the same name, shown before an import | |
| 6 | 6 | * replaces the catalog version. Compares the main file (SKILL.md, rule or subagent markdown) and, for skill folders, |
| 7 | 7 | * the file lists. A legacy command (single markdown file) is compared with the catalog SKILL.md it would become. Codex subagents (TOML rendering) and entries of shared configuration files have no standalone diff. |
| 8 | 8 | */ |
| @@ -1,8 +1,8 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemDetail, FileContent, InventoryItem, SkillDetails } from '@skilly/core' | 2 | +import type { CatalogItemDetail, FileContent, InventoryItem, SkillDetails } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | - * Diff of a local item (not managed by skilly) against the catalog item of the same name, shown before an import | 5 | + * Diff of a local item (not managed by panoplie) against the catalog item of the same name, shown before an import |
| 6 | * replaces the catalog version. Compares the main file (SKILL.md, rule or subagent markdown) and, for skill folders, | 6 | * replaces the catalog version. Compares the main file (SKILL.md, rule or subagent markdown) and, for skill folders, |
| 7 | * the file lists. A legacy command (single markdown file) is compared with the catalog SKILL.md it would become. Codex subagents (TOML rendering) and entries of shared configuration files have no standalone diff. | 7 | * the file lists. A legacy command (single markdown file) is compared with the catalog SKILL.md it would become. Codex subagents (TOML rendering) and entries of shared configuration files have no standalone diff. |
| 8 | */ | 8 | */ |
modified
packages/ui/app/components/CatalogItemEditor.vue +3 -3 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemDetail, CatalogItemType, HookTemplate, McpTemplate, SaveItemInput } from '@skilly/core' | |
| 2 | +import type { CatalogItemDetail, CatalogItemType, HookTemplate, McpTemplate, SaveItemInput } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Detail panel of a catalog item: read view, then edit mode (Markdown split editor for skill / rule / subagent, |
| @@ -262,7 +262,7 @@ const FORM_VIEWS = [ | ||
| 262 | 262 | |
| 263 | 263 | const originLabel = computed(() => { |
| 264 | 264 | const o = detail.value?.item.origin |
| 265 | - if (!o || o.kind === 'created') return 'Created in skilly' | |
| 265 | + if (!o || o.kind === 'created') return 'Created in panoplie' | |
| 266 | 266 | if (o.kind === 'import') return 'Imported' |
| 267 | 267 | return 'Marketplace' |
| 268 | 268 | }) |
| @@ -417,7 +417,7 @@ const TA = 'block w-full resize-none border-0 bg-sk-bg px-5 py-[18px] font-mono | ||
| 417 | 417 | <p v-if="envNames.length" class="m-0 text-pretty text-sk-muted"> |
| 418 | 418 | The value of {{ envNames.join(', ') }} is read from your environment. It is never stored in the catalog. |
| 419 | 419 | </p> |
| 420 | - <p v-if="type === 'hook'" class="m-0 text-xs text-sk-faint">skilly never runs the command: the agent does.</p> | |
| 420 | + <p v-if="type === 'hook'" class="m-0 text-xs text-sk-faint">panoplie never runs the command: the agent does.</p> | |
| 421 | 421 | </template> |
| 422 | 422 | |
| 423 | 423 | <div v-if="type === 'skill' && existingFiles.length" class="flex flex-col gap-2 pt-1"> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemDetail, CatalogItemType, HookTemplate, McpTemplate, SaveItemInput } from '@skilly/core' | 2 | +import type { CatalogItemDetail, CatalogItemType, HookTemplate, McpTemplate, SaveItemInput } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Detail panel of a catalog item: read view, then edit mode (Markdown split editor for skill / rule / subagent, | 5 | * Detail panel of a catalog item: read view, then edit mode (Markdown split editor for skill / rule / subagent, |
| @@ -262,7 +262,7 @@ const FORM_VIEWS = [ | |||
| 262 | 262 | ||
| 263 | const originLabel = computed(() => { | 263 | const originLabel = computed(() => { |
| 264 | const o = detail.value?.item.origin | 264 | const o = detail.value?.item.origin |
| 265 | - if (!o || o.kind === 'created') return 'Created in skilly' | 265 | + if (!o || o.kind === 'created') return 'Created in panoplie' |
| 266 | if (o.kind === 'import') return 'Imported' | 266 | if (o.kind === 'import') return 'Imported' |
| 267 | return 'Marketplace' | 267 | return 'Marketplace' |
| 268 | }) | 268 | }) |
| @@ -417,7 +417,7 @@ const TA = 'block w-full resize-none border-0 bg-sk-bg px-5 py-[18px] font-mono | |||
| 417 | <p v-if="envNames.length" class="m-0 text-pretty text-sk-muted"> | 417 | <p v-if="envNames.length" class="m-0 text-pretty text-sk-muted"> |
| 418 | The value of {{ envNames.join(', ') }} is read from your environment. It is never stored in the catalog. | 418 | The value of {{ envNames.join(', ') }} is read from your environment. It is never stored in the catalog. |
| 419 | </p> | 419 | </p> |
| 420 | - <p v-if="type === 'hook'" class="m-0 text-xs text-sk-faint">skilly never runs the command: the agent does.</p> | 420 | + <p v-if="type === 'hook'" class="m-0 text-xs text-sk-faint">panoplie never runs the command: the agent does.</p> |
| 421 | </template> | 421 | </template> |
| 422 | 422 | ||
| 423 | <div v-if="type === 'skill' && existingFiles.length" class="flex flex-col gap-2 pt-1"> | 423 | <div v-if="type === 'skill' && existingFiles.length" class="flex flex-col gap-2 pt-1"> |
modified
packages/ui/app/components/CatalogSetupCard.vue +1 -1 | @@ -32,7 +32,7 @@ async function create() { | ||
| 32 | 32 | <h2 class="m-0 text-base font-semibold">No catalog yet</h2> |
| 33 | 33 | <p class="m-0 max-w-md text-pretty text-sk-muted"> |
| 34 | 34 | The catalog is a git repository holding your skills, rules, MCP servers, hooks, subagents and presets. |
| 35 | - Create one at the location below, or point skilly to an existing repository in Settings. | |
| 35 | + Create one at the location below, or point panoplie to an existing repository in Settings. | |
| 36 | 36 | </p> |
| 37 | 37 | <p class="m-0 max-w-full truncate rounded-[7px] border border-sk-line2 bg-sk-bg px-3 py-1.5 font-mono text-xs" :title="path">{{ path || '...' }}</p> |
| 38 | 38 | <div class="flex gap-2"> |
| @@ -32,7 +32,7 @@ async function create() { | |||
| 32 | <h2 class="m-0 text-base font-semibold">No catalog yet</h2> | 32 | <h2 class="m-0 text-base font-semibold">No catalog yet</h2> |
| 33 | <p class="m-0 max-w-md text-pretty text-sk-muted"> | 33 | <p class="m-0 max-w-md text-pretty text-sk-muted"> |
| 34 | The catalog is a git repository holding your skills, rules, MCP servers, hooks, subagents and presets. | 34 | The catalog is a git repository holding your skills, rules, MCP servers, hooks, subagents and presets. |
| 35 | - Create one at the location below, or point skilly to an existing repository in Settings. | 35 | + Create one at the location below, or point panoplie to an existing repository in Settings. |
| 36 | </p> | 36 | </p> |
| 37 | <p class="m-0 max-w-full truncate rounded-[7px] border border-sk-line2 bg-sk-bg px-3 py-1.5 font-mono text-xs" :title="path">{{ path || '...' }}</p> | 37 | <p class="m-0 max-w-full truncate rounded-[7px] border border-sk-line2 bg-sk-bg px-3 py-1.5 font-mono text-xs" :title="path">{{ path || '...' }}</p> |
| 38 | <div class="flex gap-2"> | 38 | <div class="flex gap-2"> |
modified
packages/ui/app/components/EnvRows.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { McpEnvValue } from '@skilly/core' | |
| 2 | +import type { McpEnvValue } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Editable key -> (env var | literal) rows. Used for MCP `env` and `headers`. */ |
| 5 | 5 | const props = defineProps<{ modelValue: Record<string, McpEnvValue> | undefined, keyLabel: string, keyPlaceholder: string }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { McpEnvValue } from '@skilly/core' | 2 | +import type { McpEnvValue } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Editable key -> (env var | literal) rows. Used for MCP `env` and `headers`. */ | 4 | /** Editable key -> (env var | literal) rows. Used for MCP `env` and `headers`. */ |
| 5 | const props = defineProps<{ modelValue: Record<string, McpEnvValue> | undefined, keyLabel: string, keyPlaceholder: string }>() | 5 | const props = defineProps<{ modelValue: Record<string, McpEnvValue> | undefined, keyLabel: string, keyPlaceholder: string }>() |
modified
packages/ui/app/components/FolderBrowser.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { FsListing } from '@skilly/core' | |
| 2 | +import type { FsListing } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Folder browser backed by /api/fs/list. `selected` is the highlighted folder, falling back to the folder being browsed. */ |
| 5 | 5 | const props = defineProps<{ start?: string, height?: string }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { FsListing } from '@skilly/core' | 2 | +import type { FsListing } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Folder browser backed by /api/fs/list. `selected` is the highlighted folder, falling back to the folder being browsed. */ | 4 | /** Folder browser backed by /api/fs/list. `selected` is the highlighted folder, falling back to the folder being browsed. */ |
| 5 | const props = defineProps<{ start?: string, height?: string }>() | 5 | const props = defineProps<{ start?: string, height?: string }>() |
modified
packages/ui/app/components/HookForm.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId, HookTemplate } from '@skilly/core' | |
| 2 | +import type { AgentId, HookTemplate } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Form for a hook template. The parent owns the object (v-model) and renders the YAML. */ |
| 5 | 5 | const t = defineModel<HookTemplate>({ required: true }) |
| @@ -37,7 +37,7 @@ function toggleAgent(a: AgentId, on: boolean | 'indeterminate') { | ||
| 37 | 37 | </CatalogField> |
| 38 | 38 | </div> |
| 39 | 39 | |
| 40 | - <CatalogField label="Command" required hint="Run by the agent. skilly never executes it."> | |
| 40 | + <CatalogField label="Command" required hint="Run by the agent. panoplie never executes it."> | |
| 41 | 41 | <UInput :model-value="t.handler.command" class="w-full" placeholder="prettier --write $FILE" :ui="{ base: 'font-mono text-xs' }" @update:model-value="(v: string) => patchHandler({ command: v })" /> |
| 42 | 42 | </CatalogField> |
| 43 | 43 | |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId, HookTemplate } from '@skilly/core' | 2 | +import type { AgentId, HookTemplate } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Form for a hook template. The parent owns the object (v-model) and renders the YAML. */ | 4 | /** Form for a hook template. The parent owns the object (v-model) and renders the YAML. */ |
| 5 | const t = defineModel<HookTemplate>({ required: true }) | 5 | const t = defineModel<HookTemplate>({ required: true }) |
| @@ -37,7 +37,7 @@ function toggleAgent(a: AgentId, on: boolean | 'indeterminate') { | |||
| 37 | </CatalogField> | 37 | </CatalogField> |
| 38 | </div> | 38 | </div> |
| 39 | 39 | ||
| 40 | - <CatalogField label="Command" required hint="Run by the agent. skilly never executes it."> | 40 | + <CatalogField label="Command" required hint="Run by the agent. panoplie never executes it."> |
| 41 | <UInput :model-value="t.handler.command" class="w-full" placeholder="prettier --write $FILE" :ui="{ base: 'font-mono text-xs' }" @update:model-value="(v: string) => patchHandler({ command: v })" /> | 41 | <UInput :model-value="t.handler.command" class="w-full" placeholder="prettier --write $FILE" :ui="{ base: 'font-mono text-xs' }" @update:model-value="(v: string) => patchHandler({ command: v })" /> |
| 42 | </CatalogField> | 42 | </CatalogField> |
| 43 | 43 | ||
modified
packages/ui/app/components/ItemPicker.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItem, CatalogItemType } from '@skilly/core' | |
| 2 | +import type { CatalogItem, CatalogItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Checkbox picker over the catalog, grouped by type, with a filter. |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItem, CatalogItemType } from '@skilly/core' | 2 | +import type { CatalogItem, CatalogItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Checkbox picker over the catalog, grouped by type, with a filter. | 5 | * Checkbox picker over the catalog, grouped by type, with a filter. |
modified
packages/ui/app/components/MarkdownView.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { FileContent } from '@skilly/core' | |
| 2 | +import type { FileContent } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | const props = defineProps<{ |
| 5 | 5 | /** Absolute path of the file to display. */ |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { FileContent } from '@skilly/core' | 2 | +import type { FileContent } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | const props = defineProps<{ | 4 | const props = defineProps<{ |
| 5 | /** Absolute path of the file to display. */ | 5 | /** Absolute path of the file to display. */ |
modified
packages/ui/app/components/McpForm.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { McpTemplate } from '@skilly/core' | |
| 2 | +import type { McpTemplate } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Form for an MCP server template. The parent owns the object (v-model) and renders the YAML. */ |
| 5 | 5 | const t = defineModel<McpTemplate>({ required: true }) |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { McpTemplate } from '@skilly/core' | 2 | +import type { McpTemplate } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Form for an MCP server template. The parent owns the object (v-model) and renders the YAML. */ | 4 | /** Form for an MCP server template. The parent owns the object (v-model) and renders the YAML. */ |
| 5 | const t = defineModel<McpTemplate>({ required: true }) | 5 | const t = defineModel<McpTemplate>({ required: true }) |
modified
packages/ui/app/components/PresetEditor.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType, Preset } from '@skilly/core' | |
| 2 | +import type { CatalogItemType, Preset } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Preset editor: name, description, included presets, items; resolved preview on the right. `name` is undefined for a new preset. */ |
| 5 | 5 | const props = defineProps<{ name?: string }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType, Preset } from '@skilly/core' | 2 | +import type { CatalogItemType, Preset } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Preset editor: name, description, included presets, items; resolved preview on the right. `name` is undefined for a new preset. */ | 4 | /** Preset editor: name, description, included presets, items; resolved preview on the right. `name` is undefined for a new preset. */ |
| 5 | const props = defineProps<{ name?: string }>() | 5 | const props = defineProps<{ name?: string }>() |
modified
packages/ui/app/components/RemoveFromGlobalModal.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { InventoryItem, ItemStatus, RemoveFromGlobalResult } from '@skilly/core' | |
| 2 | +import type { InventoryItem, ItemStatus, RemoveFromGlobalResult } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * "Move {name} to projects" (mockup 7, "Remove from global"): three numbered steps adapted to the real API. |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { InventoryItem, ItemStatus, RemoveFromGlobalResult } from '@skilly/core' | 2 | +import type { InventoryItem, ItemStatus, RemoveFromGlobalResult } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * "Move {name} to projects" (mockup 7, "Remove from global"): three numbered steps adapted to the real API. | 5 | * "Move {name} to projects" (mockup 7, "Remove from global"): three numbered steps adapted to the real API. |
modified
packages/ui/app/components/dashboard/DashboardRecent.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ProjectSummary } from '@skilly/core' | |
| 2 | +import type { ProjectSummary } from '@panoplie/core' | |
| 3 | 3 | import type { ScopeSummary } from '../../composables/useScopeSummaries' |
| 4 | 4 | |
| 5 | 5 | /** Dashboard "Recent projects": the last projects opened (or the last added), with their sync status. */ |
| @@ -46,7 +46,7 @@ const statusOf = (p: ProjectSummary) => (p.exists ? props.summaries[p.id]?.statu | ||
| 46 | 46 | <USkeleton v-else class="h-4 w-20" /> |
| 47 | 47 | </SkRow> |
| 48 | 48 | <SkEmpty v-if="!projects.length" title="No project yet"> |
| 49 | - A project is a folder you work in. skilly shows what Claude Code and Codex load there and writes the items you enable. | |
| 49 | + A project is a folder you work in. panoplie shows what Claude Code and Codex load there and writes the items you enable. | |
| 50 | 50 | <template #actions><SkButton variant="primary" label="Add project" @click="addOpen = true" /></template> |
| 51 | 51 | </SkEmpty> |
| 52 | 52 | </SkPanel> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ProjectSummary } from '@skilly/core' | 2 | +import type { ProjectSummary } from '@panoplie/core' |
| 3 | import type { ScopeSummary } from '../../composables/useScopeSummaries' | 3 | import type { ScopeSummary } from '../../composables/useScopeSummaries' |
| 4 | 4 | ||
| 5 | /** Dashboard "Recent projects": the last projects opened (or the last added), with their sync status. */ | 5 | /** Dashboard "Recent projects": the last projects opened (or the last added), with their sync status. */ |
| @@ -46,7 +46,7 @@ const statusOf = (p: ProjectSummary) => (p.exists ? props.summaries[p.id]?.statu | |||
| 46 | <USkeleton v-else class="h-4 w-20" /> | 46 | <USkeleton v-else class="h-4 w-20" /> |
| 47 | </SkRow> | 47 | </SkRow> |
| 48 | <SkEmpty v-if="!projects.length" title="No project yet"> | 48 | <SkEmpty v-if="!projects.length" title="No project yet"> |
| 49 | - A project is a folder you work in. skilly shows what Claude Code and Codex load there and writes the items you enable. | 49 | + A project is a folder you work in. panoplie shows what Claude Code and Codex load there and writes the items you enable. |
| 50 | <template #actions><SkButton variant="primary" label="Add project" @click="addOpen = true" /></template> | 50 | <template #actions><SkButton variant="primary" label="Add project" @click="addOpen = true" /></template> |
| 51 | </SkEmpty> | 51 | </SkEmpty> |
| 52 | </SkPanel> | 52 | </SkPanel> |
modified
packages/ui/app/components/dev/DevKitSheet.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemStatus, PlanActionKind } from '@skilly/core' | |
| 2 | +import type { ItemStatus, PlanActionKind } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Every Sk primitive on one sheet. Rendered once per theme by /dev/kit. Keep in sync when adding a primitive. */ |
| 5 | 5 | const toast = useToast() |
| @@ -285,7 +285,7 @@ const typeOptions = [ | ||
| 285 | 285 | <SkButton label="Open modal" @click="modalOpen = true" /> |
| 286 | 286 | <SkButton label="Open drawer" @click="drawerOpen = true" /> |
| 287 | 287 | <SkButton label="Show toast" @click="toast.add({ title: 'Applied 4 changes · 2 files written' })" /> |
| 288 | - <UDropdownMenu :items="[[{ label: 'Rescan' }, { label: 'Copy path' }], [{ label: 'Remove from skilly', color: 'error' }]]"> | |
| 288 | + <UDropdownMenu :items="[[{ label: 'Rescan' }, { label: 'Copy path' }], [{ label: 'Remove from panoplie', color: 'error' }]]"> | |
| 289 | 289 | <SkButton icon="i-lucide-ellipsis" label="Menu" /> |
| 290 | 290 | </UDropdownMenu> |
| 291 | 291 | <UTooltip text="A tooltip"><SkButton variant="ghost" label="Hover me" /></UTooltip> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemStatus, PlanActionKind } from '@skilly/core' | 2 | +import type { ItemStatus, PlanActionKind } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Every Sk primitive on one sheet. Rendered once per theme by /dev/kit. Keep in sync when adding a primitive. */ | 4 | /** Every Sk primitive on one sheet. Rendered once per theme by /dev/kit. Keep in sync when adding a primitive. */ |
| 5 | const toast = useToast() | 5 | const toast = useToast() |
| @@ -285,7 +285,7 @@ const typeOptions = [ | |||
| 285 | <SkButton label="Open modal" @click="modalOpen = true" /> | 285 | <SkButton label="Open modal" @click="modalOpen = true" /> |
| 286 | <SkButton label="Open drawer" @click="drawerOpen = true" /> | 286 | <SkButton label="Open drawer" @click="drawerOpen = true" /> |
| 287 | <SkButton label="Show toast" @click="toast.add({ title: 'Applied 4 changes · 2 files written' })" /> | 287 | <SkButton label="Show toast" @click="toast.add({ title: 'Applied 4 changes · 2 files written' })" /> |
| 288 | - <UDropdownMenu :items="[[{ label: 'Rescan' }, { label: 'Copy path' }], [{ label: 'Remove from skilly', color: 'error' }]]"> | 288 | + <UDropdownMenu :items="[[{ label: 'Rescan' }, { label: 'Copy path' }], [{ label: 'Remove from panoplie', color: 'error' }]]"> |
| 289 | <SkButton icon="i-lucide-ellipsis" label="Menu" /> | 289 | <SkButton icon="i-lucide-ellipsis" label="Menu" /> |
| 290 | </UDropdownMenu> | 290 | </UDropdownMenu> |
| 291 | <UTooltip text="A tooltip"><SkButton variant="ghost" label="Hover me" /></UTooltip> | 291 | <UTooltip text="A tooltip"><SkButton variant="ghost" label="Hover me" /></UTooltip> |
modified
packages/ui/app/components/home/HomeSuggestions.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { InventoryItem, ItemStatus } from '@skilly/core' | |
| 2 | +import type { InventoryItem, ItemStatus } from '@panoplie/core' | |
| 3 | 3 | import type { ProjectSpecific } from '../../utils/inventoryView' |
| 4 | 4 | |
| 5 | 5 | /** |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { InventoryItem, ItemStatus } from '@skilly/core' | 2 | +import type { InventoryItem, ItemStatus } from '@panoplie/core' |
| 3 | import type { ProjectSpecific } from '../../utils/inventoryView' | 3 | import type { ProjectSpecific } from '../../utils/inventoryView' |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
modified
packages/ui/app/components/projects/ProjectsTable.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ProjectSummary } from '@skilly/core' | |
| 2 | +import type { ProjectSummary } from '@panoplie/core' | |
| 3 | 3 | import type { Tone } from '../../utils/status' |
| 4 | 4 | |
| 5 | 5 | /** |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ProjectSummary } from '@skilly/core' | 2 | +import type { ProjectSummary } from '@panoplie/core' |
| 3 | import type { Tone } from '../../utils/status' | 3 | import type { Tone } from '../../utils/status' |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
modified
packages/ui/app/components/scope/ScopeAddPresetsModal.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { Preset } from '@skilly/core' | |
| 2 | +import type { Preset } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** "Add presets to {scope}" modal (mockup 11): search, checkbox list of presets, "Create a new preset" link. */ |
| 5 | 5 | const props = defineProps<{ presets: Preset[], enabled: string[], scopeName: string, catalogExists: boolean }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { Preset } from '@skilly/core' | 2 | +import type { Preset } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** "Add presets to {scope}" modal (mockup 11): search, checkbox list of presets, "Create a new preset" link. */ | 4 | /** "Add presets to {scope}" modal (mockup 11): search, checkbox list of presets, "Create a new preset" link. */ |
| 5 | const props = defineProps<{ presets: Preset[], enabled: string[], scopeName: string, catalogExists: boolean }>() | 5 | const props = defineProps<{ presets: Preset[], enabled: string[], scopeName: string, catalogExists: boolean }>() |
modified
packages/ui/app/components/scope/ScopeConflictChoice.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ConflictResolution, PlanAction } from '@skilly/core' | |
| 2 | +import type { ConflictResolution, PlanAction } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * The three ways to resolve a conflict, as a segmented choice. Picking one only stages it: the Review & apply |
| @@ -15,7 +15,7 @@ const options = computed(() => [ | ||
| 15 | 15 | ]) |
| 16 | 16 | |
| 17 | 17 | const EFFECT: Record<ConflictResolution, string> = { |
| 18 | - 'keep-local': 'The files stay as they are. skilly stops overwriting this item until you resolve it.', | |
| 18 | + 'keep-local': 'The files stay as they are. panoplie stops overwriting this item until you resolve it.', | |
| 19 | 19 | 'take-catalog': 'The catalog version replaces the local one on Apply.', |
| 20 | 20 | 'push-to-catalog': 'The local version is copied into the catalog on Apply; other scopes that use it will get an update.' |
| 21 | 21 | } |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ConflictResolution, PlanAction } from '@skilly/core' | 2 | +import type { ConflictResolution, PlanAction } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * The three ways to resolve a conflict, as a segmented choice. Picking one only stages it: the Review & apply | 5 | * The three ways to resolve a conflict, as a segmented choice. Picking one only stages it: the Review & apply |
| @@ -15,7 +15,7 @@ const options = computed(() => [ | |||
| 15 | ]) | 15 | ]) |
| 16 | 16 | ||
| 17 | const EFFECT: Record<ConflictResolution, string> = { | 17 | const EFFECT: Record<ConflictResolution, string> = { |
| 18 | - 'keep-local': 'The files stay as they are. skilly stops overwriting this item until you resolve it.', | 18 | + 'keep-local': 'The files stay as they are. panoplie stops overwriting this item until you resolve it.', |
| 19 | 'take-catalog': 'The catalog version replaces the local one on Apply.', | 19 | 'take-catalog': 'The catalog version replaces the local one on Apply.', |
| 20 | 'push-to-catalog': 'The local version is copied into the catalog on Apply; other scopes that use it will get an update.' | 20 | 'push-to-catalog': 'The local version is copied into the catalog on Apply; other scopes that use it will get an update.' |
| 21 | } | 21 | } |
modified
packages/ui/app/components/scope/ScopeConflictCompare.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { FileContent, PlanAction } from '@skilly/core' | |
| 2 | +import type { FileContent, PlanAction } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Diff of a conflicting item against its catalog version (design/BRIEF.md 4.2). Only standalone files have one |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { FileContent, PlanAction } from '@skilly/core' | 2 | +import type { FileContent, PlanAction } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Diff of a conflicting item against its catalog version (design/BRIEF.md 4.2). Only standalone files have one | 5 | * Diff of a conflicting item against its catalog version (design/BRIEF.md 4.2). Only standalone files have one |
modified
packages/ui/app/components/scope/ScopeInheritedSection.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId, ItemType } from '@skilly/core' | |
| 2 | +import type { AgentId, ItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * "Inherited from Home" (projects only, mockup 9): Home items this project also sees, with hide switches. |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId, ItemType } from '@skilly/core' | 2 | +import type { AgentId, ItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * "Inherited from Home" (projects only, mockup 9): Home items this project also sees, with hide switches. | 5 | * "Inherited from Home" (projects only, mockup 9): Home items this project also sees, with hide switches. |
modified
packages/ui/app/components/scope/ScopeInstructionsTab.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { InstructionsDetails, InventoryItem } from '@skilly/core' | |
| 2 | +import type { InstructionsDetails, InventoryItem } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Instructions tab (mockup 12): CLAUDE.md / AGENTS.md of the project (or of Global: ~/.claude, ~/.codex), read-only, with loaded / not loaded, |
| @@ -73,7 +73,7 @@ const loadedText = (f: InstructionFile) => (f.loadedBy.length ? `Loaded by ${f.l | ||
| 73 | 73 | </template> |
| 74 | 74 | |
| 75 | 75 | <SkPanel v-else> |
| 76 | - <SkEmpty>No CLAUDE.md or AGENTS.md {{ home ? 'in ~/.claude or ~/.codex' : 'in this project' }}. Instruction files are written by you: skilly only reads them.</SkEmpty> | |
| 76 | + <SkEmpty>No CLAUDE.md or AGENTS.md {{ home ? 'in ~/.claude or ~/.codex' : 'in this project' }}. Instruction files are written by you: panoplie only reads them.</SkEmpty> | |
| 77 | 77 | </SkPanel> |
| 78 | 78 | </div> |
| 79 | 79 | </template> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { InstructionsDetails, InventoryItem } from '@skilly/core' | 2 | +import type { InstructionsDetails, InventoryItem } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Instructions tab (mockup 12): CLAUDE.md / AGENTS.md of the project (or of Global: ~/.claude, ~/.codex), read-only, with loaded / not loaded, | 5 | * Instructions tab (mockup 12): CLAUDE.md / AGENTS.md of the project (or of Global: ~/.claude, ~/.codex), read-only, with loaded / not loaded, |
| @@ -73,7 +73,7 @@ const loadedText = (f: InstructionFile) => (f.loadedBy.length ? `Loaded by ${f.l | |||
| 73 | </template> | 73 | </template> |
| 74 | 74 | ||
| 75 | <SkPanel v-else> | 75 | <SkPanel v-else> |
| 76 | - <SkEmpty>No CLAUDE.md or AGENTS.md {{ home ? 'in ~/.claude or ~/.codex' : 'in this project' }}. Instruction files are written by you: skilly only reads them.</SkEmpty> | 76 | + <SkEmpty>No CLAUDE.md or AGENTS.md {{ home ? 'in ~/.claude or ~/.codex' : 'in this project' }}. Instruction files are written by you: panoplie only reads them.</SkEmpty> |
| 77 | </SkPanel> | 77 | </SkPanel> |
| 78 | </div> | 78 | </div> |
| 79 | </template> | 79 | </template> |
modified
packages/ui/app/components/scope/ScopeInstructionsViewer.vue +3 -3 | @@ -1,9 +1,9 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { FileContent } from '@skilly/core' | |
| 2 | +import type { FileContent } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Read-only view of an instruction file (CLAUDE.md, AGENTS.md...) in a mono block. |
| 6 | - * The block managed by skilly (between `<!-- skilly:begin -->` and `<!-- skilly:end -->`) is highlighted. | |
| 6 | + * The block managed by panoplie (between `<!-- panoplie:begin -->` and `<!-- panoplie:end -->`) is highlighted. | |
| 7 | 7 | */ |
| 8 | 8 | const props = defineProps<{ path: string }>() |
| 9 | 9 | const { request } = useApi() |
| @@ -38,7 +38,7 @@ const hasManaged = computed(() => segments.value.some(s => s.managed)) | ||
| 38 | 38 | <SkNote v-if="file.truncated">Only the first part of this file is shown ({{ formatBytes(file.sizeBytes) }} in total).</SkNote> |
| 39 | 39 | <p v-if="hasManaged" class="m-0 flex items-center gap-2 text-xs text-sk-muted"> |
| 40 | 40 | <span class="inline-block h-3 w-1 rounded-full bg-sk-accent" aria-hidden="true" /> |
| 41 | - The highlighted block is generated by skilly from your enabled rules. Edit the rules, not the block. | |
| 41 | + The highlighted block is generated by panoplie from your enabled rules. Edit the rules, not the block. | |
| 42 | 42 | </p> |
| 43 | 43 | <p v-if="!file.content.trim()" class="m-0 py-6 text-center text-sk-faint">This file is empty.</p> |
| 44 | 44 | <pre v-else class="m-0 overflow-auto font-mono text-xs leading-[1.7] break-words whitespace-pre-wrap text-sk-text"><template v-for="(s, i) in segments" :key="i"><span v-if="s.managed" class="-mx-3 block rounded-[6px] border-l-2 border-sk-accent bg-sk-accent-bg px-[10px]">{{ s.text }}</span><template v-else>{{ s.text }}</template></template></pre> |
| @@ -1,9 +1,9 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { FileContent } from '@skilly/core' | 2 | +import type { FileContent } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Read-only view of an instruction file (CLAUDE.md, AGENTS.md...) in a mono block. | 5 | * Read-only view of an instruction file (CLAUDE.md, AGENTS.md...) in a mono block. |
| 6 | - * The block managed by skilly (between `<!-- skilly:begin -->` and `<!-- skilly:end -->`) is highlighted. | 6 | + * The block managed by panoplie (between `<!-- panoplie:begin -->` and `<!-- panoplie:end -->`) is highlighted. |
| 7 | */ | 7 | */ |
| 8 | const props = defineProps<{ path: string }>() | 8 | const props = defineProps<{ path: string }>() |
| 9 | const { request } = useApi() | 9 | const { request } = useApi() |
| @@ -38,7 +38,7 @@ const hasManaged = computed(() => segments.value.some(s => s.managed)) | |||
| 38 | <SkNote v-if="file.truncated">Only the first part of this file is shown ({{ formatBytes(file.sizeBytes) }} in total).</SkNote> | 38 | <SkNote v-if="file.truncated">Only the first part of this file is shown ({{ formatBytes(file.sizeBytes) }} in total).</SkNote> |
| 39 | <p v-if="hasManaged" class="m-0 flex items-center gap-2 text-xs text-sk-muted"> | 39 | <p v-if="hasManaged" class="m-0 flex items-center gap-2 text-xs text-sk-muted"> |
| 40 | <span class="inline-block h-3 w-1 rounded-full bg-sk-accent" aria-hidden="true" /> | 40 | <span class="inline-block h-3 w-1 rounded-full bg-sk-accent" aria-hidden="true" /> |
| 41 | - The highlighted block is generated by skilly from your enabled rules. Edit the rules, not the block. | 41 | + The highlighted block is generated by panoplie from your enabled rules. Edit the rules, not the block. |
| 42 | </p> | 42 | </p> |
| 43 | <p v-if="!file.content.trim()" class="m-0 py-6 text-center text-sk-faint">This file is empty.</p> | 43 | <p v-if="!file.content.trim()" class="m-0 py-6 text-center text-sk-faint">This file is empty.</p> |
| 44 | <pre v-else class="m-0 overflow-auto font-mono text-xs leading-[1.7] break-words whitespace-pre-wrap text-sk-text"><template v-for="(s, i) in segments" :key="i"><span v-if="s.managed" class="-mx-3 block rounded-[6px] border-l-2 border-sk-accent bg-sk-accent-bg px-[10px]">{{ s.text }}</span><template v-else>{{ s.text }}</template></template></pre> | 44 | <pre v-else class="m-0 overflow-auto font-mono text-xs leading-[1.7] break-words whitespace-pre-wrap text-sk-text"><template v-for="(s, i) in segments" :key="i"><span v-if="s.managed" class="-mx-3 block rounded-[6px] border-l-2 border-sk-accent bg-sk-accent-bg px-[10px]">{{ s.text }}</span><template v-else>{{ s.text }}</template></template></pre> |
modified
packages/ui/app/components/scope/ScopeItemDrawer.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { SkillDetails } from '@skilly/core' | |
| 2 | +import type { SkillDetails } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Item drawer (brief 3, "Item drawer"): metadata grid, warnings, then the file content (rendered markdown) or the |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { SkillDetails } from '@skilly/core' | 2 | +import type { SkillDetails } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Item drawer (brief 3, "Item drawer"): metadata grid, warnings, then the file content (rendered markdown) or the | 5 | * Item drawer (brief 3, "Item drawer"): metadata grid, warnings, then the file content (rendered markdown) or the |
modified
packages/ui/app/components/scope/ScopeItemRow.vue +2 -2 | @@ -3,7 +3,7 @@ import type { RowAction, ScopeRow } from '../../utils/scopeRows' | ||
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * One row of the Items list (mockup 9 and 10): switch (managed items), name + type + origin (and the path on disk |
| 6 | - * for items skilly doesn't manage), status cell with its inline action, Automatic / On demand for skills, a "…" menu | |
| 6 | + * for items panoplie doesn't manage), status cell with its inline action, Automatic / On demand for skills, a "…" menu | |
| 7 | 7 | * (Global: "Move to projects…") and the inline resolve box. Clicking the name opens the item drawer. |
| 8 | 8 | * Editing only changes the config (autosaved); files change on Apply. |
| 9 | 9 | */ |
| @@ -11,7 +11,7 @@ const props = defineProps<{ | ||
| 11 | 11 | row: ScopeRow |
| 12 | 12 | root: string |
| 13 | 13 | home?: boolean |
| 14 | - /** Path on disk, shown under the origin for items skilly doesn't manage. */ | |
| 14 | + /** Path on disk, shown under the origin for items panoplie doesn't manage. */ | |
| 15 | 15 | location?: string |
| 16 | 16 | /** "Project-specific" hint (Global). */ |
| 17 | 17 | flag?: string |
| @@ -3,7 +3,7 @@ import type { RowAction, ScopeRow } from '../../utils/scopeRows' | |||
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * One row of the Items list (mockup 9 and 10): switch (managed items), name + type + origin (and the path on disk | 5 | * One row of the Items list (mockup 9 and 10): switch (managed items), name + type + origin (and the path on disk |
| 6 | - * for items skilly doesn't manage), status cell with its inline action, Automatic / On demand for skills, a "…" menu | 6 | + * for items panoplie doesn't manage), status cell with its inline action, Automatic / On demand for skills, a "…" menu |
| 7 | * (Global: "Move to projects…") and the inline resolve box. Clicking the name opens the item drawer. | 7 | * (Global: "Move to projects…") and the inline resolve box. Clicking the name opens the item drawer. |
| 8 | * Editing only changes the config (autosaved); files change on Apply. | 8 | * Editing only changes the config (autosaved); files change on Apply. |
| 9 | */ | 9 | */ |
| @@ -11,7 +11,7 @@ const props = defineProps<{ | |||
| 11 | row: ScopeRow | 11 | row: ScopeRow |
| 12 | root: string | 12 | root: string |
| 13 | home?: boolean | 13 | home?: boolean |
| 14 | - /** Path on disk, shown under the origin for items skilly doesn't manage. */ | 14 | + /** Path on disk, shown under the origin for items panoplie doesn't manage. */ |
| 15 | location?: string | 15 | location?: string |
| 16 | /** "Project-specific" hint (Global). */ | 16 | /** "Project-specific" hint (Global). */ |
| 17 | flag?: string | 17 | flag?: string |
modified
packages/ui/app/components/scope/ScopeItemsSection.vue +4 -4 | @@ -1,10 +1,10 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType, InventoryItem } from '@skilly/core' | |
| 2 | +import type { CatalogItemType, InventoryItem } from '@panoplie/core' | |
| 3 | 3 | import type { HomeNames, ScopeRow } from '../../utils/scopeRows' |
| 4 | 4 | import type { ProjectSpecific } from '../../utils/inventoryView' |
| 5 | 5 | |
| 6 | 6 | /** |
| 7 | - * The single "Items" list of a project or of Global (mockup 9-10, merged with the old Files tab): what skilly manages | |
| 7 | + * The single "Items" list of a project or of Global (mockup 9-10, merged with the old Files tab): what panoplie manages | |
| 8 | 8 | * (switch, status, mode, inline resolution) and what is only on disk (local and plugin items, with "Import" to the |
| 9 | 9 | * catalog and, in Global, "Move to projects…"), grouped by type. Filters: type, origin (managed / on disk only), search. |
| 10 | 10 | * Works without a config: the scope then only lists what is on disk. |
| @@ -76,7 +76,7 @@ const typeOptions = computed(() => [ | ||
| 76 | 76 | ]) |
| 77 | 77 | const originOptions = computed(() => [ |
| 78 | 78 | { value: 'all' as const, label: 'All' }, |
| 79 | - { value: 'managed' as const, label: 'Managed by skilly', count: rows.value.filter(isManagedRow).length }, | |
| 79 | + { value: 'managed' as const, label: 'Managed by panoplie', count: rows.value.filter(isManagedRow).length }, | |
| 80 | 80 | { value: 'local' as const, label: 'Not managed', count: rows.value.filter(r => !isManagedRow(r)).length } |
| 81 | 81 | ]) |
| 82 | 82 | |
| @@ -199,7 +199,7 @@ function confirmPicker() { | ||
| 199 | 199 | <template> |
| 200 | 200 | <SkSection :title="home ? 'Items in Global' : 'Items in this project'"> |
| 201 | 201 | <template #actions> |
| 202 | - <SkButton v-if="manageable.length" :label="`Manage all in catalog (${manageable.length})`" title="These items are identical to the catalog version: let skilly manage them here" @click="scope.manage(manageable)" /> | |
| 202 | + <SkButton v-if="manageable.length" :label="`Manage all in catalog (${manageable.length})`" title="These items are identical to the catalog version: let panoplie manage them here" @click="scope.manage(manageable)" /> | |
| 203 | 203 | <SkButton v-if="importable.length" :label="`Import all to catalog (${importable.length})`" @click="scope.askImport(importable)" /> |
| 204 | 204 | <SkButton v-if="managed" label="Add items" :disabled="!pickable.length" @click="openPicker" /> |
| 205 | 205 | </template> |
| @@ -1,10 +1,10 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType, InventoryItem } from '@skilly/core' | 2 | +import type { CatalogItemType, InventoryItem } from '@panoplie/core' |
| 3 | import type { HomeNames, ScopeRow } from '../../utils/scopeRows' | 3 | import type { HomeNames, ScopeRow } from '../../utils/scopeRows' |
| 4 | import type { ProjectSpecific } from '../../utils/inventoryView' | 4 | import type { ProjectSpecific } from '../../utils/inventoryView' |
| 5 | 5 | ||
| 6 | /** | 6 | /** |
| 7 | - * The single "Items" list of a project or of Global (mockup 9-10, merged with the old Files tab): what skilly manages | 7 | + * The single "Items" list of a project or of Global (mockup 9-10, merged with the old Files tab): what panoplie manages |
| 8 | * (switch, status, mode, inline resolution) and what is only on disk (local and plugin items, with "Import" to the | 8 | * (switch, status, mode, inline resolution) and what is only on disk (local and plugin items, with "Import" to the |
| 9 | * catalog and, in Global, "Move to projects…"), grouped by type. Filters: type, origin (managed / on disk only), search. | 9 | * catalog and, in Global, "Move to projects…"), grouped by type. Filters: type, origin (managed / on disk only), search. |
| 10 | * Works without a config: the scope then only lists what is on disk. | 10 | * Works without a config: the scope then only lists what is on disk. |
| @@ -76,7 +76,7 @@ const typeOptions = computed(() => [ | |||
| 76 | ]) | 76 | ]) |
| 77 | const originOptions = computed(() => [ | 77 | const originOptions = computed(() => [ |
| 78 | { value: 'all' as const, label: 'All' }, | 78 | { value: 'all' as const, label: 'All' }, |
| 79 | - { value: 'managed' as const, label: 'Managed by skilly', count: rows.value.filter(isManagedRow).length }, | 79 | + { value: 'managed' as const, label: 'Managed by panoplie', count: rows.value.filter(isManagedRow).length }, |
| 80 | { value: 'local' as const, label: 'Not managed', count: rows.value.filter(r => !isManagedRow(r)).length } | 80 | { value: 'local' as const, label: 'Not managed', count: rows.value.filter(r => !isManagedRow(r)).length } |
| 81 | ]) | 81 | ]) |
| 82 | 82 | ||
| @@ -199,7 +199,7 @@ function confirmPicker() { | |||
| 199 | <template> | 199 | <template> |
| 200 | <SkSection :title="home ? 'Items in Global' : 'Items in this project'"> | 200 | <SkSection :title="home ? 'Items in Global' : 'Items in this project'"> |
| 201 | <template #actions> | 201 | <template #actions> |
| 202 | - <SkButton v-if="manageable.length" :label="`Manage all in catalog (${manageable.length})`" title="These items are identical to the catalog version: let skilly manage them here" @click="scope.manage(manageable)" /> | 202 | + <SkButton v-if="manageable.length" :label="`Manage all in catalog (${manageable.length})`" title="These items are identical to the catalog version: let panoplie manage them here" @click="scope.manage(manageable)" /> |
| 203 | <SkButton v-if="importable.length" :label="`Import all to catalog (${importable.length})`" @click="scope.askImport(importable)" /> | 203 | <SkButton v-if="importable.length" :label="`Import all to catalog (${importable.length})`" @click="scope.askImport(importable)" /> |
| 204 | <SkButton v-if="managed" label="Add items" :disabled="!pickable.length" @click="openPicker" /> | 204 | <SkButton v-if="managed" label="Add items" :disabled="!pickable.length" @click="openPicker" /> |
| 205 | </template> | 205 | </template> |
modified
packages/ui/app/components/scope/ScopeItemsTab.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { InventoryItem } from '@skilly/core' | |
| 2 | +import type { InventoryItem } from '@panoplie/core' | |
| 3 | 3 | import type { ProjectSpecific } from '../../utils/inventoryView' |
| 4 | 4 | |
| 5 | 5 | /** |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { InventoryItem } from '@skilly/core' | 2 | +import type { InventoryItem } from '@panoplie/core' |
| 3 | import type { ProjectSpecific } from '../../utils/inventoryView' | 3 | import type { ProjectSpecific } from '../../utils/inventoryView' |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
modified
packages/ui/app/components/scope/ScopeManageCard.vue +5 -5 | @@ -1,7 +1,7 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | |
| 2 | +import type { AgentId } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | -/** Card above the Items list of a scope without skilly.json / home.json: choose the target agents and start managing. */ | |
| 4 | +/** Card above the Items list of a scope without panoplie.json / home.json: choose the target agents and start managing. */ | |
| 5 | 5 | const scope = useScopeContext() |
| 6 | 6 | const home = scope.kind === 'home' |
| 7 | 7 | const AGENT_LIST: AgentId[] = ['claude', 'codex'] |
| @@ -23,13 +23,13 @@ function start() { | ||
| 23 | 23 | <template> |
| 24 | 24 | <SkPanel padded class="flex flex-col gap-5"> |
| 25 | 25 | <div class="flex flex-col gap-1"> |
| 26 | - <h2 class="m-0 text-[15px] font-semibold">{{ home ? 'skilly does not manage Global yet' : 'skilly does not manage this project yet' }}</h2> | |
| 26 | + <h2 class="m-0 text-[15px] font-semibold">{{ home ? 'panoplie does not manage Global yet' : 'panoplie does not manage this project yet' }}</h2> | |
| 27 | 27 | <p class="m-0 max-w-2xl text-pretty text-sk-muted"> |
| 28 | 28 | The list below is what the agents load here today. Start managing to enable presets and catalog items, switch |
| 29 | 29 | skills to on demand and keep the files in sync with the catalog. |
| 30 | 30 | {{ home |
| 31 | - ? 'Settings are stored in ~/.skilly/home.json (not in a repository). Nothing is written into ~/.claude, ~/.agents or ~/.codex until you apply.' | |
| 32 | - : 'A skilly.json file is created at the project root: commit it with the project. Nothing else is written until you apply.' }} | |
| 31 | + ? 'Settings are stored in ~/.panoplie/home.json (not in a repository). Nothing is written into ~/.claude, ~/.agents or ~/.codex until you apply.' | |
| 32 | + : 'A panoplie.json file is created at the project root: commit it with the project. Nothing else is written until you apply.' }} | |
| 33 | 33 | </p> |
| 34 | 34 | </div> |
| 35 | 35 | |
| @@ -1,7 +1,7 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | 2 | +import type { AgentId } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | -/** Card above the Items list of a scope without skilly.json / home.json: choose the target agents and start managing. */ | 4 | +/** Card above the Items list of a scope without panoplie.json / home.json: choose the target agents and start managing. */ |
| 5 | const scope = useScopeContext() | 5 | const scope = useScopeContext() |
| 6 | const home = scope.kind === 'home' | 6 | const home = scope.kind === 'home' |
| 7 | const AGENT_LIST: AgentId[] = ['claude', 'codex'] | 7 | const AGENT_LIST: AgentId[] = ['claude', 'codex'] |
| @@ -23,13 +23,13 @@ function start() { | |||
| 23 | <template> | 23 | <template> |
| 24 | <SkPanel padded class="flex flex-col gap-5"> | 24 | <SkPanel padded class="flex flex-col gap-5"> |
| 25 | <div class="flex flex-col gap-1"> | 25 | <div class="flex flex-col gap-1"> |
| 26 | - <h2 class="m-0 text-[15px] font-semibold">{{ home ? 'skilly does not manage Global yet' : 'skilly does not manage this project yet' }}</h2> | 26 | + <h2 class="m-0 text-[15px] font-semibold">{{ home ? 'panoplie does not manage Global yet' : 'panoplie does not manage this project yet' }}</h2> |
| 27 | <p class="m-0 max-w-2xl text-pretty text-sk-muted"> | 27 | <p class="m-0 max-w-2xl text-pretty text-sk-muted"> |
| 28 | The list below is what the agents load here today. Start managing to enable presets and catalog items, switch | 28 | The list below is what the agents load here today. Start managing to enable presets and catalog items, switch |
| 29 | skills to on demand and keep the files in sync with the catalog. | 29 | skills to on demand and keep the files in sync with the catalog. |
| 30 | {{ home | 30 | {{ home |
| 31 | - ? 'Settings are stored in ~/.skilly/home.json (not in a repository). Nothing is written into ~/.claude, ~/.agents or ~/.codex until you apply.' | 31 | + ? 'Settings are stored in ~/.panoplie/home.json (not in a repository). Nothing is written into ~/.claude, ~/.agents or ~/.codex until you apply.' |
| 32 | - : 'A skilly.json file is created at the project root: commit it with the project. Nothing else is written until you apply.' }} | 32 | + : 'A panoplie.json file is created at the project root: commit it with the project. Nothing else is written until you apply.' }} |
| 33 | </p> | 33 | </p> |
| 34 | </div> | 34 | </div> |
| 35 | 35 | ||
modified
packages/ui/app/components/scope/ScopePage.vue +8 -8 | @@ -3,7 +3,7 @@ | ||
| 3 | 3 | * Project page, also used for Global (the Home scope, `~`) (design/BRIEF.md 2.0, 2.4, 4.1), full width: header, tab |
| 4 | 4 | * segment (Items · Instructions), tab content, the sticky "pending changes" bar and the overlays (item drawer, review |
| 5 | 5 | * drawer, import and move modals). Global has the same tabs as a project; it differs by its "loaded in every project" |
| 6 | - * banner, its "to tidy up" panel and the absence of "Remove from skilly". | |
| 6 | + * banner, its "to tidy up" panel and the absence of "Remove from panoplie". | |
| 7 | 7 | * State lives in `createScope` and is shared with the scope components through `useScopeContext()`. |
| 8 | 8 | */ |
| 9 | 9 | const props = defineProps<{ kind: 'home' | 'project', projectId?: string }>() |
| @@ -67,7 +67,7 @@ async function confirmRemove() { | ||
| 67 | 67 | try { |
| 68 | 68 | await remove(props.projectId) |
| 69 | 69 | removeOpen.value = false |
| 70 | - toast.add({ title: 'Project removed from skilly', description: 'No file was deleted.', icon: 'i-lucide-check', color: 'success' }) | |
| 70 | + toast.add({ title: 'Project removed from panoplie', description: 'No file was deleted.', icon: 'i-lucide-check', color: 'success' }) | |
| 71 | 71 | await router.push('/projects') |
| 72 | 72 | } catch { |
| 73 | 73 | // The error toast comes from useApi. |
| @@ -79,7 +79,7 @@ async function confirmRemove() { | ||
| 79 | 79 | const menu = computed(() => [[ |
| 80 | 80 | { label: 'Rescan', icon: 'i-lucide-refresh-cw', onSelect: () => scope.rescan() }, |
| 81 | 81 | { label: 'Copy path', icon: 'i-lucide-copy', onSelect: copyPath }, |
| 82 | - ...(home ? [] : [{ label: 'Remove from skilly', icon: 'i-lucide-trash-2', color: 'error' as const, onSelect: () => { removeOpen.value = true } }]) | |
| 82 | + ...(home ? [] : [{ label: 'Remove from panoplie', icon: 'i-lucide-trash-2', color: 'error' as const, onSelect: () => { removeOpen.value = true } }]) | |
| 83 | 83 | ]]) |
| 84 | 84 | |
| 85 | 85 | const agentsText = computed(() => { |
| @@ -126,11 +126,11 @@ const barVisible = computed(() => !!config.value && (pending.value.length > 0 || | ||
| 126 | 126 | <template v-else-if="error && !inventory"> |
| 127 | 127 | <SkNote v-if="folderMissing" tone="accent"> |
| 128 | 128 | <p class="m-0 font-semibold">This folder no longer exists on disk</p> |
| 129 | - <p class="m-0 mt-1">{{ rootPath }} was moved or deleted. Remove the project from skilly, or retry once the folder is back.</p> | |
| 129 | + <p class="m-0 mt-1">{{ rootPath }} was moved or deleted. Remove the project from panoplie, or retry once the folder is back.</p> | |
| 130 | 130 | <template #aside> |
| 131 | 131 | <div class="flex gap-2"> |
| 132 | 132 | <SkButton size="sm" label="Retry" @click="scope.load()" /> |
| 133 | - <SkButton size="sm" variant="danger" label="Remove from skilly" @click="removeOpen = true" /> | |
| 133 | + <SkButton size="sm" variant="danger" label="Remove from panoplie" @click="removeOpen = true" /> | |
| 134 | 134 | </div> |
| 135 | 135 | </template> |
| 136 | 136 | </SkNote> |
| @@ -150,7 +150,7 @@ const barVisible = computed(() => !!config.value && (pending.value.length > 0 || | ||
| 150 | 150 | |
| 151 | 151 | <SkNote v-if="folderMissing" tone="accent"> |
| 152 | 152 | <p class="m-0 font-semibold">This folder no longer exists on disk</p> |
| 153 | - <p class="m-0 mt-1">The inventory below may be empty or outdated. You can remove the project from skilly.</p> | |
| 153 | + <p class="m-0 mt-1">The inventory below may be empty or outdated. You can remove the project from panoplie.</p> | |
| 154 | 154 | </SkNote> |
| 155 | 155 | |
| 156 | 156 | <SkNote v-if="inventory.errors.length" tone="accent"> |
| @@ -183,14 +183,14 @@ const barVisible = computed(() => !!config.value && (pending.value.length > 0 || | ||
| 183 | 183 | <AddToCatalogModal v-model:open="importOpen" :scope="scopeId" :items="importItems" :statuses="statuses" @done="scope.afterImport" /> |
| 184 | 184 | <RemoveFromGlobalModal v-model:open="moveOpen" :items="moveItems" :statuses="statuses" @done="scope.afterCatalogChange" /> |
| 185 | 185 | |
| 186 | - <SkModal v-model:open="removeOpen" title="Remove project from skilly" description="This only forgets the project in skilly." width="sm"> | |
| 186 | + <SkModal v-model:open="removeOpen" title="Remove project from panoplie" description="This only forgets the project in panoplie." width="sm"> | |
| 187 | 187 | <p class="m-0"> |
| 188 | 188 | <strong>{{ title }}</strong> will disappear from the project list. <strong>No file is deleted</strong>: |
| 189 | 189 | the folder and its agent configuration stay untouched. You can add it again at any time. |
| 190 | 190 | </p> |
| 191 | 191 | <template #footer> |
| 192 | 192 | <SkButton label="Cancel" @click="removeOpen = false" /> |
| 193 | - <SkButton variant="primary" label="Remove from skilly" :loading="removing" @click="confirmRemove" /> | |
| 193 | + <SkButton variant="primary" label="Remove from panoplie" :loading="removing" @click="confirmRemove" /> | |
| 194 | 194 | </template> |
| 195 | 195 | </SkModal> |
| 196 | 196 | </template> |
| @@ -3,7 +3,7 @@ | |||
| 3 | * Project page, also used for Global (the Home scope, `~`) (design/BRIEF.md 2.0, 2.4, 4.1), full width: header, tab | 3 | * Project page, also used for Global (the Home scope, `~`) (design/BRIEF.md 2.0, 2.4, 4.1), full width: header, tab |
| 4 | * segment (Items · Instructions), tab content, the sticky "pending changes" bar and the overlays (item drawer, review | 4 | * segment (Items · Instructions), tab content, the sticky "pending changes" bar and the overlays (item drawer, review |
| 5 | * drawer, import and move modals). Global has the same tabs as a project; it differs by its "loaded in every project" | 5 | * drawer, import and move modals). Global has the same tabs as a project; it differs by its "loaded in every project" |
| 6 | - * banner, its "to tidy up" panel and the absence of "Remove from skilly". | 6 | + * banner, its "to tidy up" panel and the absence of "Remove from panoplie". |
| 7 | * State lives in `createScope` and is shared with the scope components through `useScopeContext()`. | 7 | * State lives in `createScope` and is shared with the scope components through `useScopeContext()`. |
| 8 | */ | 8 | */ |
| 9 | const props = defineProps<{ kind: 'home' | 'project', projectId?: string }>() | 9 | const props = defineProps<{ kind: 'home' | 'project', projectId?: string }>() |
| @@ -67,7 +67,7 @@ async function confirmRemove() { | |||
| 67 | try { | 67 | try { |
| 68 | await remove(props.projectId) | 68 | await remove(props.projectId) |
| 69 | removeOpen.value = false | 69 | removeOpen.value = false |
| 70 | - toast.add({ title: 'Project removed from skilly', description: 'No file was deleted.', icon: 'i-lucide-check', color: 'success' }) | 70 | + toast.add({ title: 'Project removed from panoplie', description: 'No file was deleted.', icon: 'i-lucide-check', color: 'success' }) |
| 71 | await router.push('/projects') | 71 | await router.push('/projects') |
| 72 | } catch { | 72 | } catch { |
| 73 | // The error toast comes from useApi. | 73 | // The error toast comes from useApi. |
| @@ -79,7 +79,7 @@ async function confirmRemove() { | |||
| 79 | const menu = computed(() => [[ | 79 | const menu = computed(() => [[ |
| 80 | { label: 'Rescan', icon: 'i-lucide-refresh-cw', onSelect: () => scope.rescan() }, | 80 | { label: 'Rescan', icon: 'i-lucide-refresh-cw', onSelect: () => scope.rescan() }, |
| 81 | { label: 'Copy path', icon: 'i-lucide-copy', onSelect: copyPath }, | 81 | { label: 'Copy path', icon: 'i-lucide-copy', onSelect: copyPath }, |
| 82 | - ...(home ? [] : [{ label: 'Remove from skilly', icon: 'i-lucide-trash-2', color: 'error' as const, onSelect: () => { removeOpen.value = true } }]) | 82 | + ...(home ? [] : [{ label: 'Remove from panoplie', icon: 'i-lucide-trash-2', color: 'error' as const, onSelect: () => { removeOpen.value = true } }]) |
| 83 | ]]) | 83 | ]]) |
| 84 | 84 | ||
| 85 | const agentsText = computed(() => { | 85 | const agentsText = computed(() => { |
| @@ -126,11 +126,11 @@ const barVisible = computed(() => !!config.value && (pending.value.length > 0 || | |||
| 126 | <template v-else-if="error && !inventory"> | 126 | <template v-else-if="error && !inventory"> |
| 127 | <SkNote v-if="folderMissing" tone="accent"> | 127 | <SkNote v-if="folderMissing" tone="accent"> |
| 128 | <p class="m-0 font-semibold">This folder no longer exists on disk</p> | 128 | <p class="m-0 font-semibold">This folder no longer exists on disk</p> |
| 129 | - <p class="m-0 mt-1">{{ rootPath }} was moved or deleted. Remove the project from skilly, or retry once the folder is back.</p> | 129 | + <p class="m-0 mt-1">{{ rootPath }} was moved or deleted. Remove the project from panoplie, or retry once the folder is back.</p> |
| 130 | <template #aside> | 130 | <template #aside> |
| 131 | <div class="flex gap-2"> | 131 | <div class="flex gap-2"> |
| 132 | <SkButton size="sm" label="Retry" @click="scope.load()" /> | 132 | <SkButton size="sm" label="Retry" @click="scope.load()" /> |
| 133 | - <SkButton size="sm" variant="danger" label="Remove from skilly" @click="removeOpen = true" /> | 133 | + <SkButton size="sm" variant="danger" label="Remove from panoplie" @click="removeOpen = true" /> |
| 134 | </div> | 134 | </div> |
| 135 | </template> | 135 | </template> |
| 136 | </SkNote> | 136 | </SkNote> |
| @@ -150,7 +150,7 @@ const barVisible = computed(() => !!config.value && (pending.value.length > 0 || | |||
| 150 | 150 | ||
| 151 | <SkNote v-if="folderMissing" tone="accent"> | 151 | <SkNote v-if="folderMissing" tone="accent"> |
| 152 | <p class="m-0 font-semibold">This folder no longer exists on disk</p> | 152 | <p class="m-0 font-semibold">This folder no longer exists on disk</p> |
| 153 | - <p class="m-0 mt-1">The inventory below may be empty or outdated. You can remove the project from skilly.</p> | 153 | + <p class="m-0 mt-1">The inventory below may be empty or outdated. You can remove the project from panoplie.</p> |
| 154 | </SkNote> | 154 | </SkNote> |
| 155 | 155 | ||
| 156 | <SkNote v-if="inventory.errors.length" tone="accent"> | 156 | <SkNote v-if="inventory.errors.length" tone="accent"> |
| @@ -183,14 +183,14 @@ const barVisible = computed(() => !!config.value && (pending.value.length > 0 || | |||
| 183 | <AddToCatalogModal v-model:open="importOpen" :scope="scopeId" :items="importItems" :statuses="statuses" @done="scope.afterImport" /> | 183 | <AddToCatalogModal v-model:open="importOpen" :scope="scopeId" :items="importItems" :statuses="statuses" @done="scope.afterImport" /> |
| 184 | <RemoveFromGlobalModal v-model:open="moveOpen" :items="moveItems" :statuses="statuses" @done="scope.afterCatalogChange" /> | 184 | <RemoveFromGlobalModal v-model:open="moveOpen" :items="moveItems" :statuses="statuses" @done="scope.afterCatalogChange" /> |
| 185 | 185 | ||
| 186 | - <SkModal v-model:open="removeOpen" title="Remove project from skilly" description="This only forgets the project in skilly." width="sm"> | 186 | + <SkModal v-model:open="removeOpen" title="Remove project from panoplie" description="This only forgets the project in panoplie." width="sm"> |
| 187 | <p class="m-0"> | 187 | <p class="m-0"> |
| 188 | <strong>{{ title }}</strong> will disappear from the project list. <strong>No file is deleted</strong>: | 188 | <strong>{{ title }}</strong> will disappear from the project list. <strong>No file is deleted</strong>: |
| 189 | the folder and its agent configuration stay untouched. You can add it again at any time. | 189 | the folder and its agent configuration stay untouched. You can add it again at any time. |
| 190 | </p> | 190 | </p> |
| 191 | <template #footer> | 191 | <template #footer> |
| 192 | <SkButton label="Cancel" @click="removeOpen = false" /> | 192 | <SkButton label="Cancel" @click="removeOpen = false" /> |
| 193 | - <SkButton variant="primary" label="Remove from skilly" :loading="removing" @click="confirmRemove" /> | 193 | + <SkButton variant="primary" label="Remove from panoplie" :loading="removing" @click="confirmRemove" /> |
| 194 | </template> | 194 | </template> |
| 195 | </SkModal> | 195 | </SkModal> |
| 196 | </template> | 196 | </template> |
modified
packages/ui/app/components/scope/ScopeResolveBox.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ConflictResolution, PlanAction } from '@skilly/core' | |
| 2 | +import type { ConflictResolution, PlanAction } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Inline drift resolution under an item row (mockup 10): headline, diff, and a staged choice per conflicting agent. */ |
| 5 | 5 | defineProps<{ conflicts: PlanAction[], root: string, home?: boolean }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ConflictResolution, PlanAction } from '@skilly/core' | 2 | +import type { ConflictResolution, PlanAction } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Inline drift resolution under an item row (mockup 10): headline, diff, and a staged choice per conflicting agent. */ | 4 | /** Inline drift resolution under an item row (mockup 10): headline, diff, and a staged choice per conflicting agent. */ |
| 5 | defineProps<{ conflicts: PlanAction[], root: string, home?: boolean }>() | 5 | defineProps<{ conflicts: PlanAction[], root: string, home?: boolean }>() |
modified
packages/ui/app/components/scope/ScopeReviewDrawer.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ApplyResponse, ConflictResolution, PlanAction, PlanActionKind, ScopeState, SyncPlan } from '@skilly/core' | |
| 2 | +import type { ApplyResponse, ConflictResolution, PlanAction, PlanActionKind, ScopeState, SyncPlan } from '@panoplie/core' | |
| 3 | 3 | import type { HomeNames } from '../../utils/scopeRows' |
| 4 | 4 | |
| 5 | 5 | /** |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ApplyResponse, ConflictResolution, PlanAction, PlanActionKind, ScopeState, SyncPlan } from '@skilly/core' | 2 | +import type { ApplyResponse, ConflictResolution, PlanAction, PlanActionKind, ScopeState, SyncPlan } from '@panoplie/core' |
| 3 | import type { HomeNames } from '../../utils/scopeRows' | 3 | import type { HomeNames } from '../../utils/scopeRows' |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
modified
packages/ui/app/components/scope/ScopeScanErrors.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ScanError } from '@skilly/core' | |
| 2 | +import type { ScanError } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Details of the items the scanners could not read (inside a clay SkNote). */ |
| 5 | 5 | defineProps<{ errors: ScanError[], root: string }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ScanError } from '@skilly/core' | 2 | +import type { ScanError } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Details of the items the scanners could not read (inside a clay SkNote). */ | 4 | /** Details of the items the scanners could not read (inside a clay SkNote). */ |
| 5 | defineProps<{ errors: ScanError[], root: string }>() | 5 | defineProps<{ errors: ScanError[], root: string }>() |
modified
packages/ui/app/components/scope/ScopeTargets.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | |
| 2 | +import type { AgentId } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** "Writes for" + agent logos with a popover to change the target agents (ScopeConfig.targets). */ |
| 5 | 5 | const scope = useScopeContext() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | 2 | +import type { AgentId } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** "Writes for" + agent logos with a popover to change the target agents (ScopeConfig.targets). */ | 4 | /** "Writes for" + agent logos with a popover to change the target agents (ScopeConfig.targets). */ |
| 5 | const scope = useScopeContext() | 5 | const scope = useScopeContext() |
modified
packages/ui/app/components/sk/SkAgentBadge.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | |
| 2 | +import type { AgentId } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Agents have no colour in the design: plain muted text ("text", default) or a neutral mono tag ("tag"). |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | 2 | +import type { AgentId } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Agents have no colour in the design: plain muted text ("text", default) or a neutral mono tag ("tag"). | 5 | * Agents have no colour in the design: plain muted text ("text", default) or a neutral mono tag ("tag"). |
modified
packages/ui/app/components/sk/SkAgentIcons.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | |
| 2 | +import type { AgentId } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Agent logos (Claude Code, Codex) for at-a-glance lists; the name stays available as tooltip and accessible label. */ |
| 5 | 5 | withDefaults(defineProps<{ agents: AgentId[], empty?: string }>(), { empty: '—' }) |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { AgentId } from '@skilly/core' | 2 | +import type { AgentId } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Agent logos (Claude Code, Codex) for at-a-glance lists; the name stays available as tooltip and accessible label. */ | 4 | /** Agent logos (Claude Code, Codex) for at-a-glance lists; the name stays available as tooltip and accessible label. */ |
| 5 | withDefaults(defineProps<{ agents: AgentId[], empty?: string }>(), { empty: '—' }) | 5 | withDefaults(defineProps<{ agents: AgentId[], empty?: string }>(), { empty: '—' }) |
modified
packages/ui/app/components/sk/SkItemChip.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemType } from '@skilly/core' | |
| 2 | +import type { ItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Item chip: name + mono type (preset cards, "Used in"). */ |
| 5 | 5 | defineProps<{ name: string, type?: ItemType }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemType } from '@skilly/core' | 2 | +import type { ItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Item chip: name + mono type (preset cards, "Used in"). */ | 4 | /** Item chip: name + mono type (preset cards, "Used in"). */ |
| 5 | defineProps<{ name: string, type?: ItemType }>() | 5 | defineProps<{ name: string, type?: ItemType }>() |
modified
packages/ui/app/components/sk/SkLogo.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -/** skilly logo mark: a 2x2 grid (design/BRIEF.md 1.9). public/favicon.svg draws the same mark. */ | |
| 2 | +/** panoplie logo mark: a 2x2 grid (design/BRIEF.md 1.9). public/favicon.svg draws the same mark. */ | |
| 3 | 3 | withDefaults(defineProps<{ size?: number }>(), { size: 26 }) |
| 4 | 4 | </script> |
| 5 | 5 | |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -/** skilly logo mark: a 2x2 grid (design/BRIEF.md 1.9). public/favicon.svg draws the same mark. */ | 2 | +/** panoplie logo mark: a 2x2 grid (design/BRIEF.md 1.9). public/favicon.svg draws the same mark. */ |
| 3 | withDefaults(defineProps<{ size?: number }>(), { size: 26 }) | 3 | withDefaults(defineProps<{ size?: number }>(), { size: 26 }) |
| 4 | </script> | 4 | </script> |
| 5 | 5 | ||
modified
packages/ui/app/components/sk/SkStatus.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemStatus } from '@skilly/core' | |
| 2 | +import type { ItemStatus } from '@panoplie/core' | |
| 3 | 3 | import type { Tone } from '../../utils/status' |
| 4 | 4 | |
| 5 | 5 | /** |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemStatus } from '@skilly/core' | 2 | +import type { ItemStatus } from '@panoplie/core' |
| 3 | import type { Tone } from '../../utils/status' | 3 | import type { Tone } from '../../utils/status' |
| 4 | 4 | ||
| 5 | /** | 5 | /** |
modified
packages/ui/app/components/sk/SkTypeTag.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemType } from '@skilly/core' | |
| 2 | +import type { ItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Item type as a mono text tag: Skill, Rule, MCP, Hook, Subagent (no icons in the design). `label` overrides the text. */ |
| 5 | 5 | defineProps<{ type: ItemType, label?: string }>() |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ItemType } from '@skilly/core' | 2 | +import type { ItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Item type as a mono text tag: Skill, Rule, MCP, Hook, Subagent (no icons in the design). `label` overrides the text. */ | 4 | /** Item type as a mono text tag: Skill, Rule, MCP, Hook, Subagent (no icons in the design). `label` overrides the text. */ |
| 5 | defineProps<{ type: ItemType, label?: string }>() | 5 | defineProps<{ type: ItemType, label?: string }>() |
modified
packages/ui/app/composables/useApi.ts +4 -4 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { ApiError } from '@skilly/core' | |
| 1 | +import type { ApiError } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | export class ApiRequestError extends Error { |
| 4 | 4 | status: number |
| @@ -25,14 +25,14 @@ export function useApi() { | ||
| 25 | 25 | method: opts.method ?? 'GET', |
| 26 | 26 | body: opts.body as Record<string, unknown> | undefined, |
| 27 | 27 | query: opts.query, |
| 28 | - headers: { 'x-skilly-token': getToken() } | |
| 28 | + headers: { 'x-panoplie-token': getToken() } | |
| 29 | 29 | }) |
| 30 | 30 | } catch (e: any) { |
| 31 | 31 | const status: number = e?.response?.status ?? e?.statusCode ?? 0 |
| 32 | 32 | const data = e?.data as Partial<ApiError> | undefined |
| 33 | 33 | const message = data?.error |
| 34 | - ?? (status === 401 ? 'Unauthorized: the session token is missing or invalid. Relaunch skilly.' : undefined) | |
| 35 | - ?? (status === 0 ? 'Cannot reach the skilly server.' : e?.message ?? 'Request failed') | |
| 34 | + ?? (status === 401 ? 'Unauthorized: the session token is missing or invalid. Relaunch panoplie.' : undefined) | |
| 35 | + ?? (status === 0 ? 'Cannot reach the panoplie server.' : e?.message ?? 'Request failed') | |
| 36 | 36 | if (!opts.silent) { |
| 37 | 37 | toast.add({ title: 'Request failed', description: message, color: 'error', icon: 'i-lucide-circle-alert' }) |
| 38 | 38 | } |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { ApiError } from '@skilly/core' | 1 | +import type { ApiError } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | export class ApiRequestError extends Error { | 3 | export class ApiRequestError extends Error { |
| 4 | status: number | 4 | status: number |
| @@ -25,14 +25,14 @@ export function useApi() { | |||
| 25 | method: opts.method ?? 'GET', | 25 | method: opts.method ?? 'GET', |
| 26 | body: opts.body as Record<string, unknown> | undefined, | 26 | body: opts.body as Record<string, unknown> | undefined, |
| 27 | query: opts.query, | 27 | query: opts.query, |
| 28 | - headers: { 'x-skilly-token': getToken() } | 28 | + headers: { 'x-panoplie-token': getToken() } |
| 29 | }) | 29 | }) |
| 30 | } catch (e: any) { | 30 | } catch (e: any) { |
| 31 | const status: number = e?.response?.status ?? e?.statusCode ?? 0 | 31 | const status: number = e?.response?.status ?? e?.statusCode ?? 0 |
| 32 | const data = e?.data as Partial<ApiError> | undefined | 32 | const data = e?.data as Partial<ApiError> | undefined |
| 33 | const message = data?.error | 33 | const message = data?.error |
| 34 | - ?? (status === 401 ? 'Unauthorized: the session token is missing or invalid. Relaunch skilly.' : undefined) | 34 | + ?? (status === 401 ? 'Unauthorized: the session token is missing or invalid. Relaunch panoplie.' : undefined) |
| 35 | - ?? (status === 0 ? 'Cannot reach the skilly server.' : e?.message ?? 'Request failed') | 35 | + ?? (status === 0 ? 'Cannot reach the panoplie server.' : e?.message ?? 'Request failed') |
| 36 | if (!opts.silent) { | 36 | if (!opts.silent) { |
| 37 | toast.add({ title: 'Request failed', description: message, color: 'error', icon: 'i-lucide-circle-alert' }) | 37 | toast.add({ title: 'Request failed', description: message, color: 'error', icon: 'i-lucide-circle-alert' }) |
| 38 | } | 38 | } |
modified
packages/ui/app/composables/useCatalog.ts +4 -4 | @@ -1,11 +1,11 @@ | ||
| 1 | -import type { CatalogItem, CatalogResponse, GitStatus, Preset } from '@skilly/core' | |
| 1 | +import type { CatalogItem, CatalogResponse, GitStatus, Preset } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | /** Shared catalog snapshot (items, presets, usage, git status). */ |
| 4 | 4 | export function useCatalog() { |
| 5 | 5 | const { request } = useApi() |
| 6 | - const data = useState<CatalogResponse | null>('skilly-catalog', () => null) | |
| 7 | - const loading = useState('skilly-catalog-loading', () => false) | |
| 8 | - const error = useState<string | null>('skilly-catalog-error', () => null) | |
| 6 | + const data = useState<CatalogResponse | null>('panoplie-catalog', () => null) | |
| 7 | + const loading = useState('panoplie-catalog-loading', () => false) | |
| 8 | + const error = useState<string | null>('panoplie-catalog-error', () => null) | |
| 9 | 9 | |
| 10 | 10 | async function refresh() { |
| 11 | 11 | loading.value = true |
| @@ -1,11 +1,11 @@ | |||
| 1 | -import type { CatalogItem, CatalogResponse, GitStatus, Preset } from '@skilly/core' | 1 | +import type { CatalogItem, CatalogResponse, GitStatus, Preset } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | /** Shared catalog snapshot (items, presets, usage, git status). */ | 3 | /** Shared catalog snapshot (items, presets, usage, git status). */ |
| 4 | export function useCatalog() { | 4 | export function useCatalog() { |
| 5 | const { request } = useApi() | 5 | const { request } = useApi() |
| 6 | - const data = useState<CatalogResponse | null>('skilly-catalog', () => null) | 6 | + const data = useState<CatalogResponse | null>('panoplie-catalog', () => null) |
| 7 | - const loading = useState('skilly-catalog-loading', () => false) | 7 | + const loading = useState('panoplie-catalog-loading', () => false) |
| 8 | - const error = useState<string | null>('skilly-catalog-error', () => null) | 8 | + const error = useState<string | null>('panoplie-catalog-error', () => null) |
| 9 | 9 | ||
| 10 | async function refresh() { | 10 | async function refresh() { |
| 11 | loading.value = true | 11 | loading.value = true |
modified
packages/ui/app/composables/useCatalogGit.ts +3 -3 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { GitStatus } from '@skilly/core' | |
| 1 | +import type { GitStatus } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | export type GitAction = 'remote' | 'commit' | 'pull' | 'push' | 'sync' | 'resolve' |
| 4 | 4 | |
| @@ -16,8 +16,8 @@ export function useCatalogGit() { | ||
| 16 | 16 | get: () => route.path === '/sync', |
| 17 | 17 | set: (v: boolean) => { if (v && route.path !== '/sync') navigateTo('/sync') } |
| 18 | 18 | }) |
| 19 | - const busy = useState<GitAction | null>('skilly-git-busy', () => null) | |
| 20 | - const error = useState<string | null>('skilly-git-error', () => null) | |
| 19 | + const busy = useState<GitAction | null>('panoplie-git-busy', () => null) | |
| 20 | + const error = useState<string | null>('panoplie-git-error', () => null) | |
| 21 | 21 | |
| 22 | 22 | async function run(action: GitAction, body?: unknown, successTitle?: string): Promise<boolean> { |
| 23 | 23 | busy.value = action |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { GitStatus } from '@skilly/core' | 1 | +import type { GitStatus } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | export type GitAction = 'remote' | 'commit' | 'pull' | 'push' | 'sync' | 'resolve' | 3 | export type GitAction = 'remote' | 'commit' | 'pull' | 'push' | 'sync' | 'resolve' |
| 4 | 4 | ||
| @@ -16,8 +16,8 @@ export function useCatalogGit() { | |||
| 16 | get: () => route.path === '/sync', | 16 | get: () => route.path === '/sync', |
| 17 | set: (v: boolean) => { if (v && route.path !== '/sync') navigateTo('/sync') } | 17 | set: (v: boolean) => { if (v && route.path !== '/sync') navigateTo('/sync') } |
| 18 | }) | 18 | }) |
| 19 | - const busy = useState<GitAction | null>('skilly-git-busy', () => null) | 19 | + const busy = useState<GitAction | null>('panoplie-git-busy', () => null) |
| 20 | - const error = useState<string | null>('skilly-git-error', () => null) | 20 | + const error = useState<string | null>('panoplie-git-error', () => null) |
| 21 | 21 | ||
| 22 | async function run(action: GitAction, body?: unknown, successTitle?: string): Promise<boolean> { | 22 | async function run(action: GitAction, body?: unknown, successTitle?: string): Promise<boolean> { |
| 23 | busy.value = action | 23 | busy.value = action |
modified
packages/ui/app/composables/useProjects.ts +8 -8 | @@ -1,12 +1,12 @@ | ||
| 1 | -import type { ProjectSummary, ScopeInventory } from '@skilly/core' | |
| 1 | +import type { ProjectSummary, ScopeInventory } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | /** Shared list of added projects (sidebar, dashboard, scope pages). */ |
| 4 | 4 | export function useProjects() { |
| 5 | 5 | const { request } = useApi() |
| 6 | - const projects = useState<ProjectSummary[]>('skilly-projects', () => []) | |
| 7 | - const loading = useState('skilly-projects-loading', () => false) | |
| 8 | - const loaded = useState('skilly-projects-loaded', () => false) | |
| 9 | - const error = useState<string | null>('skilly-projects-error', () => null) | |
| 6 | + const projects = useState<ProjectSummary[]>('panoplie-projects', () => []) | |
| 7 | + const loading = useState('panoplie-projects-loading', () => false) | |
| 8 | + const loaded = useState('panoplie-projects-loaded', () => false) | |
| 9 | + const error = useState<string | null>('panoplie-projects-error', () => null) | |
| 10 | 10 | |
| 11 | 11 | async function refresh() { |
| 12 | 12 | loading.value = true |
| @@ -38,9 +38,9 @@ export function useProjects() { | ||
| 38 | 38 | /** Shared Home (Global scope) inventory: onboarding, inherited items of a project, the move modal. */ |
| 39 | 39 | export function useHomeInventory() { |
| 40 | 40 | const { request } = useApi() |
| 41 | - const home = useState<ScopeInventory | null>('skilly-home', () => null) | |
| 42 | - const loading = useState('skilly-home-loading', () => false) | |
| 43 | - const error = useState<string | null>('skilly-home-error', () => null) | |
| 41 | + const home = useState<ScopeInventory | null>('panoplie-home', () => null) | |
| 42 | + const loading = useState('panoplie-home-loading', () => false) | |
| 43 | + const error = useState<string | null>('panoplie-home-error', () => null) | |
| 44 | 44 | |
| 45 | 45 | async function refresh() { |
| 46 | 46 | loading.value = true |
| @@ -1,12 +1,12 @@ | |||
| 1 | -import type { ProjectSummary, ScopeInventory } from '@skilly/core' | 1 | +import type { ProjectSummary, ScopeInventory } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | /** Shared list of added projects (sidebar, dashboard, scope pages). */ | 3 | /** Shared list of added projects (sidebar, dashboard, scope pages). */ |
| 4 | export function useProjects() { | 4 | export function useProjects() { |
| 5 | const { request } = useApi() | 5 | const { request } = useApi() |
| 6 | - const projects = useState<ProjectSummary[]>('skilly-projects', () => []) | 6 | + const projects = useState<ProjectSummary[]>('panoplie-projects', () => []) |
| 7 | - const loading = useState('skilly-projects-loading', () => false) | 7 | + const loading = useState('panoplie-projects-loading', () => false) |
| 8 | - const loaded = useState('skilly-projects-loaded', () => false) | 8 | + const loaded = useState('panoplie-projects-loaded', () => false) |
| 9 | - const error = useState<string | null>('skilly-projects-error', () => null) | 9 | + const error = useState<string | null>('panoplie-projects-error', () => null) |
| 10 | 10 | ||
| 11 | async function refresh() { | 11 | async function refresh() { |
| 12 | loading.value = true | 12 | loading.value = true |
| @@ -38,9 +38,9 @@ export function useProjects() { | |||
| 38 | /** Shared Home (Global scope) inventory: onboarding, inherited items of a project, the move modal. */ | 38 | /** Shared Home (Global scope) inventory: onboarding, inherited items of a project, the move modal. */ |
| 39 | export function useHomeInventory() { | 39 | export function useHomeInventory() { |
| 40 | const { request } = useApi() | 40 | const { request } = useApi() |
| 41 | - const home = useState<ScopeInventory | null>('skilly-home', () => null) | 41 | + const home = useState<ScopeInventory | null>('panoplie-home', () => null) |
| 42 | - const loading = useState('skilly-home-loading', () => false) | 42 | + const loading = useState('panoplie-home-loading', () => false) |
| 43 | - const error = useState<string | null>('skilly-home-error', () => null) | 43 | + const error = useState<string | null>('panoplie-home-error', () => null) |
| 44 | 44 | ||
| 45 | async function refresh() { | 45 | async function refresh() { |
| 46 | loading.value = true | 46 | loading.value = true |
modified
packages/ui/app/composables/useScopeState.ts +4 -4 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { AgentId, CatalogItemType, ConflictResolution, ImportResult, InventoryItem, PlanAction, ScopeConfig, ScopeState, SyncPlan } from '@skilly/core' | |
| 1 | +import type { AgentId, CatalogItemType, ConflictResolution, ImportResult, InventoryItem, PlanAction, ScopeConfig, ScopeState, SyncPlan } from '@panoplie/core' | |
| 2 | 2 | import type { InjectionKey } from 'vue' |
| 3 | 3 | |
| 4 | 4 | /** |
| @@ -7,7 +7,7 @@ import type { InjectionKey } from 'vue' | ||
| 7 | 7 | * (item drawer, review drawer, import and remove-from-global modals). |
| 8 | 8 | * |
| 9 | 9 | * `ScopePage` creates it and provides it; the scope components read it with `useScopeContext()`. |
| 10 | - * Nothing is written to disk by this composable except `skilly.json` / `home.json` (the config); files change on Apply only. | |
| 10 | + * Nothing is written to disk by this composable except `panoplie.json` / `home.json` (the config); files change on Apply only. | |
| 11 | 11 | */ |
| 12 | 12 | export function createScope(kind: 'home' | 'project', projectId: () => string | undefined) { |
| 13 | 13 | const { request } = useApi() |
| @@ -194,7 +194,7 @@ export function createScope(kind: 'home' | 'project', projectId: () => string | | ||
| 194 | 194 | await flush() |
| 195 | 195 | } |
| 196 | 196 | |
| 197 | - /** After "Add to catalog" from this scope: the imported items stay here, now managed by skilly. */ | |
| 197 | + /** After "Add to catalog" from this scope: the imported items stay here, now managed by panoplie. */ | |
| 198 | 198 | async function afterImport(res: ImportResult | null) { |
| 199 | 199 | await catalog.refresh() |
| 200 | 200 | if (res && manage(res.imported.map(i => ({ type: i.type, name: i.name })))) await flush() |
| @@ -223,7 +223,7 @@ export function createScope(kind: 'home' | 'project', projectId: () => string | | ||
| 223 | 223 | |
| 224 | 224 | export type ScopeContext = ReturnType<typeof createScope> |
| 225 | 225 | |
| 226 | -const SCOPE_KEY: InjectionKey<ScopeContext> = Symbol('skilly-scope') | |
| 226 | +const SCOPE_KEY: InjectionKey<ScopeContext> = Symbol('panoplie-scope') | |
| 227 | 227 | |
| 228 | 228 | export function provideScope(scope: ScopeContext) { |
| 229 | 229 | provide(SCOPE_KEY, scope) |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { AgentId, CatalogItemType, ConflictResolution, ImportResult, InventoryItem, PlanAction, ScopeConfig, ScopeState, SyncPlan } from '@skilly/core' | 1 | +import type { AgentId, CatalogItemType, ConflictResolution, ImportResult, InventoryItem, PlanAction, ScopeConfig, ScopeState, SyncPlan } from '@panoplie/core' |
| 2 | import type { InjectionKey } from 'vue' | 2 | import type { InjectionKey } from 'vue' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| @@ -7,7 +7,7 @@ import type { InjectionKey } from 'vue' | |||
| 7 | * (item drawer, review drawer, import and remove-from-global modals). | 7 | * (item drawer, review drawer, import and remove-from-global modals). |
| 8 | * | 8 | * |
| 9 | * `ScopePage` creates it and provides it; the scope components read it with `useScopeContext()`. | 9 | * `ScopePage` creates it and provides it; the scope components read it with `useScopeContext()`. |
| 10 | - * Nothing is written to disk by this composable except `skilly.json` / `home.json` (the config); files change on Apply only. | 10 | + * Nothing is written to disk by this composable except `panoplie.json` / `home.json` (the config); files change on Apply only. |
| 11 | */ | 11 | */ |
| 12 | export function createScope(kind: 'home' | 'project', projectId: () => string | undefined) { | 12 | export function createScope(kind: 'home' | 'project', projectId: () => string | undefined) { |
| 13 | const { request } = useApi() | 13 | const { request } = useApi() |
| @@ -194,7 +194,7 @@ export function createScope(kind: 'home' | 'project', projectId: () => string | | |||
| 194 | await flush() | 194 | await flush() |
| 195 | } | 195 | } |
| 196 | 196 | ||
| 197 | - /** After "Add to catalog" from this scope: the imported items stay here, now managed by skilly. */ | 197 | + /** After "Add to catalog" from this scope: the imported items stay here, now managed by panoplie. */ |
| 198 | async function afterImport(res: ImportResult | null) { | 198 | async function afterImport(res: ImportResult | null) { |
| 199 | await catalog.refresh() | 199 | await catalog.refresh() |
| 200 | if (res && manage(res.imported.map(i => ({ type: i.type, name: i.name })))) await flush() | 200 | if (res && manage(res.imported.map(i => ({ type: i.type, name: i.name })))) await flush() |
| @@ -223,7 +223,7 @@ export function createScope(kind: 'home' | 'project', projectId: () => string | | |||
| 223 | 223 | ||
| 224 | export type ScopeContext = ReturnType<typeof createScope> | 224 | export type ScopeContext = ReturnType<typeof createScope> |
| 225 | 225 | ||
| 226 | -const SCOPE_KEY: InjectionKey<ScopeContext> = Symbol('skilly-scope') | 226 | +const SCOPE_KEY: InjectionKey<ScopeContext> = Symbol('panoplie-scope') |
| 227 | 227 | ||
| 228 | export function provideScope(scope: ScopeContext) { | 228 | export function provideScope(scope: ScopeContext) { |
| 229 | provide(SCOPE_KEY, scope) | 229 | provide(SCOPE_KEY, scope) |
modified
packages/ui/app/composables/useScopeSummaries.ts +4 -4 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { AgentId, ScopeState } from '@skilly/core' | |
| 1 | +import type { AgentId, ScopeState } from '@panoplie/core' | |
| 2 | 2 | import type { Tone } from '../utils/status' |
| 3 | 3 | |
| 4 | 4 | export interface ScopeSummary { |
| @@ -23,9 +23,9 @@ export function useScopeSummaries() { | ||
| 23 | 23 | const { request } = useApi() |
| 24 | 24 | const { projects } = useProjects() |
| 25 | 25 | const catalog = useCatalog() |
| 26 | - const summaries = useState<Record<string, ScopeSummary | null>>('skilly-scope-summaries', () => ({})) | |
| 27 | - const homeState = useState<ScopeState | null>('skilly-home-state', () => null) | |
| 28 | - const loading = useState('skilly-scope-summaries-loading', () => false) | |
| 26 | + const summaries = useState<Record<string, ScopeSummary | null>>('panoplie-scope-summaries', () => ({})) | |
| 27 | + const homeState = useState<ScopeState | null>('panoplie-home-state', () => null) | |
| 28 | + const loading = useState('panoplie-scope-summaries-loading', () => false) | |
| 29 | 29 | |
| 30 | 30 | function summarize(s: ScopeState): ScopeSummary { |
| 31 | 31 | const cfg = s.config |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { AgentId, ScopeState } from '@skilly/core' | 1 | +import type { AgentId, ScopeState } from '@panoplie/core' |
| 2 | import type { Tone } from '../utils/status' | 2 | import type { Tone } from '../utils/status' |
| 3 | 3 | ||
| 4 | export interface ScopeSummary { | 4 | export interface ScopeSummary { |
| @@ -23,9 +23,9 @@ export function useScopeSummaries() { | |||
| 23 | const { request } = useApi() | 23 | const { request } = useApi() |
| 24 | const { projects } = useProjects() | 24 | const { projects } = useProjects() |
| 25 | const catalog = useCatalog() | 25 | const catalog = useCatalog() |
| 26 | - const summaries = useState<Record<string, ScopeSummary | null>>('skilly-scope-summaries', () => ({})) | 26 | + const summaries = useState<Record<string, ScopeSummary | null>>('panoplie-scope-summaries', () => ({})) |
| 27 | - const homeState = useState<ScopeState | null>('skilly-home-state', () => null) | 27 | + const homeState = useState<ScopeState | null>('panoplie-home-state', () => null) |
| 28 | - const loading = useState('skilly-scope-summaries-loading', () => false) | 28 | + const loading = useState('panoplie-scope-summaries-loading', () => false) |
| 29 | 29 | ||
| 30 | function summarize(s: ScopeState): ScopeSummary { | 30 | function summarize(s: ScopeState): ScopeSummary { |
| 31 | const cfg = s.config | 31 | const cfg = s.config |
modified
packages/ui/app/composables/useShell.ts +2 -2 | @@ -2,10 +2,10 @@ | ||
| 2 | 2 | |
| 3 | 3 | /** "Add project" modal (mounted once in the default layout). */ |
| 4 | 4 | export function useAddProjectModal() { |
| 5 | - return useState('skilly-add-open', () => false) | |
| 5 | + return useState('panoplie-add-open', () => false) | |
| 6 | 6 | } |
| 7 | 7 | |
| 8 | 8 | /** Ctrl+K quick switcher (mounted once in the default layout). */ |
| 9 | 9 | export function useSwitcher() { |
| 10 | - return useState('skilly-switcher-open', () => false) | |
| 10 | + return useState('panoplie-switcher-open', () => false) | |
| 11 | 11 | } |
| @@ -2,10 +2,10 @@ | |||
| 2 | 2 | ||
| 3 | /** "Add project" modal (mounted once in the default layout). */ | 3 | /** "Add project" modal (mounted once in the default layout). */ |
| 4 | export function useAddProjectModal() { | 4 | export function useAddProjectModal() { |
| 5 | - return useState('skilly-add-open', () => false) | 5 | + return useState('panoplie-add-open', () => false) |
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | /** Ctrl+K quick switcher (mounted once in the default layout). */ | 8 | /** Ctrl+K quick switcher (mounted once in the default layout). */ |
| 9 | export function useSwitcher() { | 9 | export function useSwitcher() { |
| 10 | - return useState('skilly-switcher-open', () => false) | 10 | + return useState('panoplie-switcher-open', () => false) |
| 11 | } | 11 | } |
modified
packages/ui/app/pages/catalog/[type]/[name].vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType } from '@skilly/core' | |
| 2 | +import type { CatalogItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | const route = useRoute() |
| 5 | 5 | const selected = computed(() => ({ type: String(route.params.type) as CatalogItemType, name: String(route.params.name) })) |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType } from '@skilly/core' | 2 | +import type { CatalogItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | const route = useRoute() | 4 | const route = useRoute() |
| 5 | const selected = computed(() => ({ type: String(route.params.type) as CatalogItemType, name: String(route.params.name) })) | 5 | const selected = computed(() => ({ type: String(route.params.type) as CatalogItemType, name: String(route.params.name) })) |
modified
packages/ui/app/pages/catalog/new.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType } from '@skilly/core' | |
| 2 | +import type { CatalogItemType } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | const route = useRoute() |
| 5 | 5 | const type = computed<CatalogItemType>(() => (CAT_TYPES.includes(route.query.type as CatalogItemType) ? route.query.type as CatalogItemType : 'skill')) |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType } from '@skilly/core' | 2 | +import type { CatalogItemType } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | const route = useRoute() | 4 | const route = useRoute() |
| 5 | const type = computed<CatalogItemType>(() => (CAT_TYPES.includes(route.query.type as CatalogItemType) ? route.query.type as CatalogItemType : 'skill')) | 5 | const type = computed<CatalogItemType>(() => (CAT_TYPES.includes(route.query.type as CatalogItemType) ? route.query.type as CatalogItemType : 'skill')) |
modified
packages/ui/app/pages/index.vue +4 -4 | @@ -2,16 +2,16 @@ | ||
| 2 | 2 | import type { DashboardAction, OverviewCard } from '../utils/dashboard' |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | - * Dashboard (`/`): what skilly does, what needs attention ("To do now"), a "Getting started" checklist until it is | |
| 5 | + * Dashboard (`/`): what panoplie does, what needs attention ("To do now"), a "Getting started" checklist until it is | |
| 6 | 6 | * done, entry points per area and the recent projects. First run (no catalog and no project yet) goes to the |
| 7 | - * onboarding instead, unless the user skipped or finished it once (`skilly-onboarding-done`). | |
| 7 | + * onboarding instead, unless the user skipped or finished it once (`panoplie-onboarding-done`). | |
| 8 | 8 | */ |
| 9 | 9 | const { projects, refresh: refreshProjects } = useProjects() |
| 10 | 10 | const catalog = useCatalog() |
| 11 | 11 | |
| 12 | 12 | await Promise.all([refreshProjects(), catalog.refresh()]) |
| 13 | 13 | let onboarded = false |
| 14 | -try { onboarded = !!localStorage.getItem('skilly-onboarding-done') } catch { /* private mode */ } | |
| 14 | +try { onboarded = !!localStorage.getItem('panoplie-onboarding-done') } catch { /* private mode */ } | |
| 15 | 15 | if (!catalog.exists.value && !projects.value.length && !onboarded) await navigateTo('/onboarding', { replace: true }) |
| 16 | 16 | |
| 17 | 17 | const { summaries, homeState, load } = useScopeSummaries() |
| @@ -42,7 +42,7 @@ const todos = computed(() => dashboardTodos({ | ||
| 42 | 42 | |
| 43 | 43 | /* ---------------------------------------------------------------- getting started */ |
| 44 | 44 | |
| 45 | -const START_HIDDEN = 'skilly-dashboard-start-hidden' | |
| 45 | +const START_HIDDEN = 'panoplie-dashboard-start-hidden' | |
| 46 | 46 | const startHidden = ref(false) |
| 47 | 47 | try { startHidden.value = !!localStorage.getItem(START_HIDDEN) } catch { /* private mode */ } |
| 48 | 48 | function hideStart() { |
| @@ -2,16 +2,16 @@ | |||
| 2 | import type { DashboardAction, OverviewCard } from '../utils/dashboard' | 2 | import type { DashboardAction, OverviewCard } from '../utils/dashboard' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | - * Dashboard (`/`): what skilly does, what needs attention ("To do now"), a "Getting started" checklist until it is | 5 | + * Dashboard (`/`): what panoplie does, what needs attention ("To do now"), a "Getting started" checklist until it is |
| 6 | * done, entry points per area and the recent projects. First run (no catalog and no project yet) goes to the | 6 | * done, entry points per area and the recent projects. First run (no catalog and no project yet) goes to the |
| 7 | - * onboarding instead, unless the user skipped or finished it once (`skilly-onboarding-done`). | 7 | + * onboarding instead, unless the user skipped or finished it once (`panoplie-onboarding-done`). |
| 8 | */ | 8 | */ |
| 9 | const { projects, refresh: refreshProjects } = useProjects() | 9 | const { projects, refresh: refreshProjects } = useProjects() |
| 10 | const catalog = useCatalog() | 10 | const catalog = useCatalog() |
| 11 | 11 | ||
| 12 | await Promise.all([refreshProjects(), catalog.refresh()]) | 12 | await Promise.all([refreshProjects(), catalog.refresh()]) |
| 13 | let onboarded = false | 13 | let onboarded = false |
| 14 | -try { onboarded = !!localStorage.getItem('skilly-onboarding-done') } catch { /* private mode */ } | 14 | +try { onboarded = !!localStorage.getItem('panoplie-onboarding-done') } catch { /* private mode */ } |
| 15 | if (!catalog.exists.value && !projects.value.length && !onboarded) await navigateTo('/onboarding', { replace: true }) | 15 | if (!catalog.exists.value && !projects.value.length && !onboarded) await navigateTo('/onboarding', { replace: true }) |
| 16 | 16 | ||
| 17 | const { summaries, homeState, load } = useScopeSummaries() | 17 | const { summaries, homeState, load } = useScopeSummaries() |
| @@ -42,7 +42,7 @@ const todos = computed(() => dashboardTodos({ | |||
| 42 | 42 | ||
| 43 | /* ---------------------------------------------------------------- getting started */ | 43 | /* ---------------------------------------------------------------- getting started */ |
| 44 | 44 | ||
| 45 | -const START_HIDDEN = 'skilly-dashboard-start-hidden' | 45 | +const START_HIDDEN = 'panoplie-dashboard-start-hidden' |
| 46 | const startHidden = ref(false) | 46 | const startHidden = ref(false) |
| 47 | try { startHidden.value = !!localStorage.getItem(START_HIDDEN) } catch { /* private mode */ } | 47 | try { startHidden.value = !!localStorage.getItem(START_HIDDEN) } catch { /* private mode */ } |
| 48 | function hideStart() { | 48 | function hideStart() { |
modified
packages/ui/app/pages/marketplaces/index.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@skilly/core' | |
| 2 | +import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Marketplaces (brief 3.1): two panes like the Catalog. Left: sources (built-in + yours). Right: the skills of the |
| @@ -330,7 +330,7 @@ const plural = (n: number, w: string) => `${n} ${w}${n === 1 ? '' : 's'}` | ||
| 330 | 330 | |
| 331 | 331 | <SkNote v-if="fetching" tone="info"> |
| 332 | 332 | <p class="m-0 font-medium">{{ refreshing ? 'Fetching the repository…' : 'Loading the skills…' }} <span class="font-mono text-xs text-sk-faint">{{ elapsed }}s</span></p> |
| 333 | - <p class="m-0 text-xs text-sk-muted">The first load clones the repository, which can take a minute for large ones. You can keep using skilly meanwhile.</p> | |
| 333 | + <p class="m-0 text-xs text-sk-muted">The first load clones the repository, which can take a minute for large ones. You can keep using panoplie meanwhile.</p> | |
| 334 | 334 | </SkNote> |
| 335 | 335 | |
| 336 | 336 | <div class="flex flex-wrap items-center gap-2.5"> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@skilly/core' | 2 | +import type { ImportResult, MarketplaceSkill, MarketplaceSkillDetail, MarketplaceSource } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Marketplaces (brief 3.1): two panes like the Catalog. Left: sources (built-in + yours). Right: the skills of the | 5 | * Marketplaces (brief 3.1): two panes like the Catalog. Left: sources (built-in + yours). Right: the skills of the |
| @@ -330,7 +330,7 @@ const plural = (n: number, w: string) => `${n} ${w}${n === 1 ? '' : 's'}` | |||
| 330 | 330 | ||
| 331 | <SkNote v-if="fetching" tone="info"> | 331 | <SkNote v-if="fetching" tone="info"> |
| 332 | <p class="m-0 font-medium">{{ refreshing ? 'Fetching the repository…' : 'Loading the skills…' }} <span class="font-mono text-xs text-sk-faint">{{ elapsed }}s</span></p> | 332 | <p class="m-0 font-medium">{{ refreshing ? 'Fetching the repository…' : 'Loading the skills…' }} <span class="font-mono text-xs text-sk-faint">{{ elapsed }}s</span></p> |
| 333 | - <p class="m-0 text-xs text-sk-muted">The first load clones the repository, which can take a minute for large ones. You can keep using skilly meanwhile.</p> | 333 | + <p class="m-0 text-xs text-sk-muted">The first load clones the repository, which can take a minute for large ones. You can keep using panoplie meanwhile.</p> |
| 334 | </SkNote> | 334 | </SkNote> |
| 335 | 335 | ||
| 336 | <div class="flex flex-wrap items-center gap-2.5"> | 336 | <div class="flex flex-wrap items-center gap-2.5"> |
modified
packages/ui/app/pages/onboarding.vue +9 -9 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogResponse, DiscoveredProject, FsDiscovery, FsListing, Settings } from '@skilly/core' | |
| 2 | +import type { CatalogResponse, DiscoveredProject, FsDiscovery, FsListing, Settings } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Onboarding (design/BRIEF.md 2.1 + 4.8), bare layout. Five steps: Welcome, Catalog (restore = git clone, or start |
| @@ -188,7 +188,7 @@ async function addSelected(): Promise<boolean> { | ||
| 188 | 188 | |
| 189 | 189 | /* ------------------------------------------------ Navigation */ |
| 190 | 190 | |
| 191 | -const SKIP_KEY = 'skilly-onboarding-done' | |
| 191 | +const SKIP_KEY = 'panoplie-onboarding-done' | |
| 192 | 192 | function finish(to = '/') { |
| 193 | 193 | try { localStorage.setItem(SKIP_KEY, '1') } catch { /* private mode */ } |
| 194 | 194 | return router.push(to) |
| @@ -202,7 +202,7 @@ const canContinue = computed(() => { | ||
| 202 | 202 | const nextLabel = computed(() => { |
| 203 | 203 | if (step.value === 1 && mode.value === 'fresh' && !catalogReady.value) return 'Skip for now' |
| 204 | 204 | if (step.value === 3) return selectedCandidates.value.length ? `Add ${selectedCandidates.value.length} and continue` : 'Skip for now' |
| 205 | - if (step.value === 4) return 'Open skilly' | |
| 205 | + if (step.value === 4) return 'Open panoplie' | |
| 206 | 206 | return 'Continue' |
| 207 | 207 | }) |
| 208 | 208 | |
| @@ -229,7 +229,7 @@ const summary = computed(() => [ | ||
| 229 | 229 | <SkPanel hero class="grid w-full max-w-[860px] md:min-h-[520px] md:grid-cols-[200px_minmax(0,1fr)]"> |
| 230 | 230 | <!-- Steps --> |
| 231 | 231 | <aside class="hidden flex-col gap-5 border-r border-sk-line2 bg-sk-panel p-[18px] md:flex"> |
| 232 | - <div class="flex items-center gap-2"><SkLogo /><span class="text-base font-semibold">skilly</span></div> | |
| 232 | + <div class="flex items-center gap-2"><SkLogo /><span class="text-base font-semibold">panoplie</span></div> | |
| 233 | 233 | <ol class="m-0 flex list-none flex-col gap-3 p-0"> |
| 234 | 234 | <li v-for="(s, i) in STEPS" :key="s" class="flex items-center gap-2.5" :aria-current="i === step ? 'step' : undefined"> |
| 235 | 235 | <span |
| @@ -248,7 +248,7 @@ const summary = computed(() => [ | ||
| 248 | 248 | <div class="flex flex-1 flex-col gap-4"> |
| 249 | 249 | <!-- 0 Welcome --> |
| 250 | 250 | <template v-if="step === 0"> |
| 251 | - <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">Welcome to skilly</h1> | |
| 251 | + <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">Welcome to panoplie</h1> | |
| 252 | 252 | <p class="m-0 max-w-[460px] text-pretty text-sk-muted">Manage the skills, rules, MCP servers, hooks and subagents your coding agents use, across all your projects, from one place.</p> |
| 253 | 253 | <div class="mt-3 grid gap-3 sm:grid-cols-2"> |
| 254 | 254 | <button type="button" class="cursor-pointer rounded-[10px] border border-sk-line bg-sk-panel p-4 text-left transition-colors hover:border-sk-faint" @click="choose('restore')"> |
| @@ -266,7 +266,7 @@ const summary = computed(() => [ | ||
| 266 | 266 | <template v-else-if="step === 1"> |
| 267 | 267 | <SkSectionLabel>Step 1 of 3</SkSectionLabel> |
| 268 | 268 | <h1 class="m-0 font-mono text-[22px] font-normal tracking-[-0.01em]">{{ mode === 'restore' ? 'Connect your catalog' : 'Create your catalog' }}</h1> |
| 269 | - <p class="m-0 text-sk-muted">{{ mode === 'restore' ? 'skilly clones the repository and restores your items and presets on this machine.' : 'An empty catalog in a git repository. Add a remote later from the Sync page to share it.' }}</p> | |
| 269 | + <p class="m-0 text-sk-muted">{{ mode === 'restore' ? 'panoplie clones the repository and restores your items and presets on this machine.' : 'An empty catalog in a git repository. Add a remote later from the Sync page to share it.' }}</p> | |
| 270 | 270 | |
| 271 | 271 | <div class="flex flex-col gap-1.5"> |
| 272 | 272 | <span class="font-medium">Catalog folder</span> |
| @@ -317,7 +317,7 @@ const summary = computed(() => [ | ||
| 317 | 317 | <template v-else-if="step === 2"> |
| 318 | 318 | <SkSectionLabel>Step 2 of 3</SkSectionLabel> |
| 319 | 319 | <h1 class="m-0 font-mono text-[22px] font-normal tracking-[-0.01em]">Agents on this machine</h1> |
| 320 | - <p class="m-0 text-sk-muted">What skilly found in your home folder. Each project chooses the agents it writes for.</p> | |
| 320 | + <p class="m-0 text-sk-muted">What panoplie found in your home folder. Each project chooses the agents it writes for.</p> | |
| 321 | 321 | <SkPanel> |
| 322 | 322 | <SkRow v-for="a in agentRows" :key="a.id" cols="minmax(0,1fr) auto" class="!py-3"> |
| 323 | 323 | <div class="min-w-0"> |
| @@ -327,7 +327,7 @@ const summary = computed(() => [ | ||
| 327 | 327 | <SkStatus :tone="a.found ? 'ok' : 'faint'" :label="a.found ? `Detected · ${a.count} item${a.count === 1 ? '' : 's'}` : 'Not found'" /> |
| 328 | 328 | </SkRow> |
| 329 | 329 | </SkPanel> |
| 330 | - <p v-if="home && !home.agentsDetected.length" class="m-0 text-xs text-sk-faint">Nothing detected yet. You can still manage projects; skilly writes the agent folders when you apply.</p> | |
| 330 | + <p v-if="home && !home.agentsDetected.length" class="m-0 text-xs text-sk-faint">Nothing detected yet. You can still manage projects; panoplie writes the agent folders when you apply.</p> | |
| 331 | 331 | </template> |
| 332 | 332 | |
| 333 | 333 | <!-- 3 Projects --> |
| @@ -358,7 +358,7 @@ const summary = computed(() => [ | ||
| 358 | 358 | <!-- 4 Ready --> |
| 359 | 359 | <template v-else> |
| 360 | 360 | <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">You're set up</h1> |
| 361 | - <p class="m-0 max-w-[480px] text-pretty text-sk-muted">skilly writes the enabled items into each project. The files are committed with the project, so it works without skilly too.</p> | |
| 361 | + <p class="m-0 max-w-[480px] text-pretty text-sk-muted">panoplie writes the enabled items into each project. The files are committed with the project, so it works without panoplie too.</p> | |
| 362 | 362 | <SkPanel> |
| 363 | 363 | <SkRow v-for="[k, v] in summary" :key="k" cols="110px minmax(0,1fr)"> |
| 364 | 364 | <span class="text-sk-muted">{{ k }}</span> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogResponse, DiscoveredProject, FsDiscovery, FsListing, Settings } from '@skilly/core' | 2 | +import type { CatalogResponse, DiscoveredProject, FsDiscovery, FsListing, Settings } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Onboarding (design/BRIEF.md 2.1 + 4.8), bare layout. Five steps: Welcome, Catalog (restore = git clone, or start | 5 | * Onboarding (design/BRIEF.md 2.1 + 4.8), bare layout. Five steps: Welcome, Catalog (restore = git clone, or start |
| @@ -188,7 +188,7 @@ async function addSelected(): Promise<boolean> { | |||
| 188 | 188 | ||
| 189 | /* ------------------------------------------------ Navigation */ | 189 | /* ------------------------------------------------ Navigation */ |
| 190 | 190 | ||
| 191 | -const SKIP_KEY = 'skilly-onboarding-done' | 191 | +const SKIP_KEY = 'panoplie-onboarding-done' |
| 192 | function finish(to = '/') { | 192 | function finish(to = '/') { |
| 193 | try { localStorage.setItem(SKIP_KEY, '1') } catch { /* private mode */ } | 193 | try { localStorage.setItem(SKIP_KEY, '1') } catch { /* private mode */ } |
| 194 | return router.push(to) | 194 | return router.push(to) |
| @@ -202,7 +202,7 @@ const canContinue = computed(() => { | |||
| 202 | const nextLabel = computed(() => { | 202 | const nextLabel = computed(() => { |
| 203 | if (step.value === 1 && mode.value === 'fresh' && !catalogReady.value) return 'Skip for now' | 203 | if (step.value === 1 && mode.value === 'fresh' && !catalogReady.value) return 'Skip for now' |
| 204 | if (step.value === 3) return selectedCandidates.value.length ? `Add ${selectedCandidates.value.length} and continue` : 'Skip for now' | 204 | if (step.value === 3) return selectedCandidates.value.length ? `Add ${selectedCandidates.value.length} and continue` : 'Skip for now' |
| 205 | - if (step.value === 4) return 'Open skilly' | 205 | + if (step.value === 4) return 'Open panoplie' |
| 206 | return 'Continue' | 206 | return 'Continue' |
| 207 | }) | 207 | }) |
| 208 | 208 | ||
| @@ -229,7 +229,7 @@ const summary = computed(() => [ | |||
| 229 | <SkPanel hero class="grid w-full max-w-[860px] md:min-h-[520px] md:grid-cols-[200px_minmax(0,1fr)]"> | 229 | <SkPanel hero class="grid w-full max-w-[860px] md:min-h-[520px] md:grid-cols-[200px_minmax(0,1fr)]"> |
| 230 | <!-- Steps --> | 230 | <!-- Steps --> |
| 231 | <aside class="hidden flex-col gap-5 border-r border-sk-line2 bg-sk-panel p-[18px] md:flex"> | 231 | <aside class="hidden flex-col gap-5 border-r border-sk-line2 bg-sk-panel p-[18px] md:flex"> |
| 232 | - <div class="flex items-center gap-2"><SkLogo /><span class="text-base font-semibold">skilly</span></div> | 232 | + <div class="flex items-center gap-2"><SkLogo /><span class="text-base font-semibold">panoplie</span></div> |
| 233 | <ol class="m-0 flex list-none flex-col gap-3 p-0"> | 233 | <ol class="m-0 flex list-none flex-col gap-3 p-0"> |
| 234 | <li v-for="(s, i) in STEPS" :key="s" class="flex items-center gap-2.5" :aria-current="i === step ? 'step' : undefined"> | 234 | <li v-for="(s, i) in STEPS" :key="s" class="flex items-center gap-2.5" :aria-current="i === step ? 'step' : undefined"> |
| 235 | <span | 235 | <span |
| @@ -248,7 +248,7 @@ const summary = computed(() => [ | |||
| 248 | <div class="flex flex-1 flex-col gap-4"> | 248 | <div class="flex flex-1 flex-col gap-4"> |
| 249 | <!-- 0 Welcome --> | 249 | <!-- 0 Welcome --> |
| 250 | <template v-if="step === 0"> | 250 | <template v-if="step === 0"> |
| 251 | - <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">Welcome to skilly</h1> | 251 | + <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">Welcome to panoplie</h1> |
| 252 | <p class="m-0 max-w-[460px] text-pretty text-sk-muted">Manage the skills, rules, MCP servers, hooks and subagents your coding agents use, across all your projects, from one place.</p> | 252 | <p class="m-0 max-w-[460px] text-pretty text-sk-muted">Manage the skills, rules, MCP servers, hooks and subagents your coding agents use, across all your projects, from one place.</p> |
| 253 | <div class="mt-3 grid gap-3 sm:grid-cols-2"> | 253 | <div class="mt-3 grid gap-3 sm:grid-cols-2"> |
| 254 | <button type="button" class="cursor-pointer rounded-[10px] border border-sk-line bg-sk-panel p-4 text-left transition-colors hover:border-sk-faint" @click="choose('restore')"> | 254 | <button type="button" class="cursor-pointer rounded-[10px] border border-sk-line bg-sk-panel p-4 text-left transition-colors hover:border-sk-faint" @click="choose('restore')"> |
| @@ -266,7 +266,7 @@ const summary = computed(() => [ | |||
| 266 | <template v-else-if="step === 1"> | 266 | <template v-else-if="step === 1"> |
| 267 | <SkSectionLabel>Step 1 of 3</SkSectionLabel> | 267 | <SkSectionLabel>Step 1 of 3</SkSectionLabel> |
| 268 | <h1 class="m-0 font-mono text-[22px] font-normal tracking-[-0.01em]">{{ mode === 'restore' ? 'Connect your catalog' : 'Create your catalog' }}</h1> | 268 | <h1 class="m-0 font-mono text-[22px] font-normal tracking-[-0.01em]">{{ mode === 'restore' ? 'Connect your catalog' : 'Create your catalog' }}</h1> |
| 269 | - <p class="m-0 text-sk-muted">{{ mode === 'restore' ? 'skilly clones the repository and restores your items and presets on this machine.' : 'An empty catalog in a git repository. Add a remote later from the Sync page to share it.' }}</p> | 269 | + <p class="m-0 text-sk-muted">{{ mode === 'restore' ? 'panoplie clones the repository and restores your items and presets on this machine.' : 'An empty catalog in a git repository. Add a remote later from the Sync page to share it.' }}</p> |
| 270 | 270 | ||
| 271 | <div class="flex flex-col gap-1.5"> | 271 | <div class="flex flex-col gap-1.5"> |
| 272 | <span class="font-medium">Catalog folder</span> | 272 | <span class="font-medium">Catalog folder</span> |
| @@ -317,7 +317,7 @@ const summary = computed(() => [ | |||
| 317 | <template v-else-if="step === 2"> | 317 | <template v-else-if="step === 2"> |
| 318 | <SkSectionLabel>Step 2 of 3</SkSectionLabel> | 318 | <SkSectionLabel>Step 2 of 3</SkSectionLabel> |
| 319 | <h1 class="m-0 font-mono text-[22px] font-normal tracking-[-0.01em]">Agents on this machine</h1> | 319 | <h1 class="m-0 font-mono text-[22px] font-normal tracking-[-0.01em]">Agents on this machine</h1> |
| 320 | - <p class="m-0 text-sk-muted">What skilly found in your home folder. Each project chooses the agents it writes for.</p> | 320 | + <p class="m-0 text-sk-muted">What panoplie found in your home folder. Each project chooses the agents it writes for.</p> |
| 321 | <SkPanel> | 321 | <SkPanel> |
| 322 | <SkRow v-for="a in agentRows" :key="a.id" cols="minmax(0,1fr) auto" class="!py-3"> | 322 | <SkRow v-for="a in agentRows" :key="a.id" cols="minmax(0,1fr) auto" class="!py-3"> |
| 323 | <div class="min-w-0"> | 323 | <div class="min-w-0"> |
| @@ -327,7 +327,7 @@ const summary = computed(() => [ | |||
| 327 | <SkStatus :tone="a.found ? 'ok' : 'faint'" :label="a.found ? `Detected · ${a.count} item${a.count === 1 ? '' : 's'}` : 'Not found'" /> | 327 | <SkStatus :tone="a.found ? 'ok' : 'faint'" :label="a.found ? `Detected · ${a.count} item${a.count === 1 ? '' : 's'}` : 'Not found'" /> |
| 328 | </SkRow> | 328 | </SkRow> |
| 329 | </SkPanel> | 329 | </SkPanel> |
| 330 | - <p v-if="home && !home.agentsDetected.length" class="m-0 text-xs text-sk-faint">Nothing detected yet. You can still manage projects; skilly writes the agent folders when you apply.</p> | 330 | + <p v-if="home && !home.agentsDetected.length" class="m-0 text-xs text-sk-faint">Nothing detected yet. You can still manage projects; panoplie writes the agent folders when you apply.</p> |
| 331 | </template> | 331 | </template> |
| 332 | 332 | ||
| 333 | <!-- 3 Projects --> | 333 | <!-- 3 Projects --> |
| @@ -358,7 +358,7 @@ const summary = computed(() => [ | |||
| 358 | <!-- 4 Ready --> | 358 | <!-- 4 Ready --> |
| 359 | <template v-else> | 359 | <template v-else> |
| 360 | <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">You're set up</h1> | 360 | <h1 class="m-0 mt-3 font-mono text-[26px] leading-tight font-normal tracking-[-0.01em]">You're set up</h1> |
| 361 | - <p class="m-0 max-w-[480px] text-pretty text-sk-muted">skilly writes the enabled items into each project. The files are committed with the project, so it works without skilly too.</p> | 361 | + <p class="m-0 max-w-[480px] text-pretty text-sk-muted">panoplie writes the enabled items into each project. The files are committed with the project, so it works without panoplie too.</p> |
| 362 | <SkPanel> | 362 | <SkPanel> |
| 363 | <SkRow v-for="[k, v] in summary" :key="k" cols="110px minmax(0,1fr)"> | 363 | <SkRow v-for="[k, v] in summary" :key="k" cols="110px minmax(0,1fr)"> |
| 364 | <span class="text-sk-muted">{{ k }}</span> | 364 | <span class="text-sk-muted">{{ k }}</span> |
modified
packages/ui/app/pages/presets/index.vue +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType, Preset } from '@skilly/core' | |
| 2 | +import type { CatalogItemType, Preset } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | const { data, loading, error, refresh, exists, presets, presetUsedBy } = useCatalog() |
| 5 | 5 | |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { CatalogItemType, Preset } from '@skilly/core' | 2 | +import type { CatalogItemType, Preset } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | const { data, loading, error, refresh, exists, presets, presetUsedBy } = useCatalog() | 4 | const { data, loading, error, refresh, exists, presets, presetUsedBy } = useCatalog() |
| 5 | 5 | ||
modified
packages/ui/app/pages/projects/index.vue +1 -1 | @@ -4,7 +4,7 @@ const addOpen = useAddProjectModal() | ||
| 4 | 4 | |
| 5 | 5 | <template> |
| 6 | 6 | <SkPage> |
| 7 | - <SkPageHeader title="Projects" subtitle="Folders managed by skilly. Enabled items are written into each project."> | |
| 7 | + <SkPageHeader title="Projects" subtitle="Folders managed by panoplie. Enabled items are written into each project."> | |
| 8 | 8 | <template #actions> |
| 9 | 9 | <SkButton variant="primary" label="Add project" @click="addOpen = true" /> |
| 10 | 10 | </template> |
| @@ -4,7 +4,7 @@ const addOpen = useAddProjectModal() | |||
| 4 | 4 | ||
| 5 | <template> | 5 | <template> |
| 6 | <SkPage> | 6 | <SkPage> |
| 7 | - <SkPageHeader title="Projects" subtitle="Folders managed by skilly. Enabled items are written into each project."> | 7 | + <SkPageHeader title="Projects" subtitle="Folders managed by panoplie. Enabled items are written into each project."> |
| 8 | <template #actions> | 8 | <template #actions> |
| 9 | <SkButton variant="primary" label="Add project" @click="addOpen = true" /> | 9 | <SkButton variant="primary" label="Add project" @click="addOpen = true" /> |
| 10 | </template> | 10 | </template> |
modified
packages/ui/app/pages/settings.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { Settings } from '@skilly/core' | |
| 2 | +import type { Settings } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** Settings (brief 3.1): catalog location + automatic sync, appearance, run the setup again, about. */ |
| 5 | 5 | const { request } = useApi() |
| @@ -112,7 +112,7 @@ const APPEARANCE = [ | ||
| 112 | 112 | <SkMeta label="Version" mono>{{ version ?? '-' }}</SkMeta> |
| 113 | 113 | <SkMeta label="Projects" mono>{{ projects.length }}</SkMeta> |
| 114 | 114 | </SkMetaGrid> |
| 115 | - <p class="m-0 text-xs text-sk-faint">skilly runs locally. The catalog only leaves this machine through the git remote you set.</p> | |
| 115 | + <p class="m-0 text-xs text-sk-faint">panoplie runs locally. The catalog only leaves this machine through the git remote you set.</p> | |
| 116 | 116 | </SkPanel> |
| 117 | 117 | </template> |
| 118 | 118 | |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { Settings } from '@skilly/core' | 2 | +import type { Settings } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** Settings (brief 3.1): catalog location + automatic sync, appearance, run the setup again, about. */ | 4 | /** Settings (brief 3.1): catalog location + automatic sync, appearance, run the setup again, about. */ |
| 5 | const { request } = useApi() | 5 | const { request } = useApi() |
| @@ -112,7 +112,7 @@ const APPEARANCE = [ | |||
| 112 | <SkMeta label="Version" mono>{{ version ?? '-' }}</SkMeta> | 112 | <SkMeta label="Version" mono>{{ version ?? '-' }}</SkMeta> |
| 113 | <SkMeta label="Projects" mono>{{ projects.length }}</SkMeta> | 113 | <SkMeta label="Projects" mono>{{ projects.length }}</SkMeta> |
| 114 | </SkMetaGrid> | 114 | </SkMetaGrid> |
| 115 | - <p class="m-0 text-xs text-sk-faint">skilly runs locally. The catalog only leaves this machine through the git remote you set.</p> | 115 | + <p class="m-0 text-xs text-sk-faint">panoplie runs locally. The catalog only leaves this machine through the git remote you set.</p> |
| 116 | </SkPanel> | 116 | </SkPanel> |
| 117 | </template> | 117 | </template> |
| 118 | 118 | ||
modified
packages/ui/app/pages/sync.vue +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <script setup lang="ts"> |
| 2 | -import type { Settings } from '@skilly/core' | |
| 2 | +import type { Settings } from '@panoplie/core' | |
| 3 | 3 | |
| 4 | 4 | /** |
| 5 | 5 | * Sync (brief 3.1): the catalog git repository in one page. Status, remote, commit, pull / push / sync, conflicts |
| @@ -224,7 +224,7 @@ const noRemoteHint = 'Set a remote first' | ||
| 224 | 224 | </div> |
| 225 | 225 | </SkRowCard> |
| 226 | 226 | </SkTray> |
| 227 | - <p class="m-0 text-xs text-sk-muted">Keep mine keeps the version on this machine. Use remote takes the version from the remote. Once every conflict is resolved, skilly commits the merge.</p> | |
| 227 | + <p class="m-0 text-xs text-sk-muted">Keep mine keeps the version on this machine. Use remote takes the version from the remote. Once every conflict is resolved, panoplie commits the merge.</p> | |
| 228 | 228 | </SkPanel> |
| 229 | 229 | |
| 230 | 230 | <!-- Changes --> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <script setup lang="ts"> | 1 | <script setup lang="ts"> |
| 2 | -import type { Settings } from '@skilly/core' | 2 | +import type { Settings } from '@panoplie/core' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| 5 | * Sync (brief 3.1): the catalog git repository in one page. Status, remote, commit, pull / push / sync, conflicts | 5 | * Sync (brief 3.1): the catalog git repository in one page. Status, remote, commit, pull / push / sync, conflicts |
| @@ -224,7 +224,7 @@ const noRemoteHint = 'Set a remote first' | |||
| 224 | </div> | 224 | </div> |
| 225 | </SkRowCard> | 225 | </SkRowCard> |
| 226 | </SkTray> | 226 | </SkTray> |
| 227 | - <p class="m-0 text-xs text-sk-muted">Keep mine keeps the version on this machine. Use remote takes the version from the remote. Once every conflict is resolved, skilly commits the merge.</p> | 227 | + <p class="m-0 text-xs text-sk-muted">Keep mine keeps the version on this machine. Use remote takes the version from the remote. Once every conflict is resolved, panoplie commits the merge.</p> |
| 228 | </SkPanel> | 228 | </SkPanel> |
| 229 | 229 | ||
| 230 | <!-- Changes --> | 230 | <!-- Changes --> |
modified
packages/ui/app/plugins/02.heartbeat.client.ts +1 -1 | @@ -3,7 +3,7 @@ export default defineNuxtPlugin(() => { | ||
| 3 | 3 | const ping = () => { |
| 4 | 4 | fetch('/api/heartbeat', { |
| 5 | 5 | method: 'POST', |
| 6 | - headers: { 'x-skilly-token': getToken() }, | |
| 6 | + headers: { 'x-panoplie-token': getToken() }, | |
| 7 | 7 | keepalive: true |
| 8 | 8 | }).catch(() => {}) |
| 9 | 9 | } |
| @@ -3,7 +3,7 @@ export default defineNuxtPlugin(() => { | |||
| 3 | const ping = () => { | 3 | const ping = () => { |
| 4 | fetch('/api/heartbeat', { | 4 | fetch('/api/heartbeat', { |
| 5 | method: 'POST', | 5 | method: 'POST', |
| 6 | - headers: { 'x-skilly-token': getToken() }, | 6 | + headers: { 'x-panoplie-token': getToken() }, |
| 7 | keepalive: true | 7 | keepalive: true |
| 8 | }).catch(() => {}) | 8 | }).catch(() => {}) |
| 9 | } | 9 | } |
modified
packages/ui/app/utils/catalog.ts +2 -2 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { CatalogItemType, HookTemplate, ItemStatus, McpEnvValue, McpTemplate } from '@skilly/core' | |
| 1 | +import type { CatalogItemType, HookTemplate, ItemStatus, McpEnvValue, McpTemplate } from '@panoplie/core' | |
| 2 | 2 | import { splitFrontmatter } from './markdown' |
| 3 | 3 | |
| 4 | 4 | export const CAT_TYPES: CatalogItemType[] = ['skill', 'rule', 'agent', 'mcp', 'hook'] |
| @@ -23,7 +23,7 @@ export const STATUS_META: Record<ItemStatus['state'], { label: string, color: 's | ||
| 23 | 23 | 'modified': { label: 'modified', color: 'warning', icon: 'i-lucide-pencil', hint: 'Edited in this scope since the last sync.' }, |
| 24 | 24 | 'catalog-updated': { label: 'catalog updated', color: 'info', icon: 'i-lucide-arrow-down-to-line', hint: 'The catalog has a newer version.' }, |
| 25 | 25 | 'not-in-catalog': { label: 'not in catalog', color: 'neutral', icon: 'i-lucide-circle-dashed', hint: 'Local item, absent from the catalog.' }, |
| 26 | - 'differs-from-catalog': { label: 'differs from catalog', color: 'warning', icon: 'i-lucide-git-compare', hint: 'Same name as a catalog item, different content, not managed by skilly.' } | |
| 26 | + 'differs-from-catalog': { label: 'differs from catalog', color: 'warning', icon: 'i-lucide-git-compare', hint: 'Same name as a catalog item, different content, not managed by panoplie.' } | |
| 27 | 27 | } |
| 28 | 28 | |
| 29 | 29 | /** `*` wildcard match on names, same semantics as the config globs. */ |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { CatalogItemType, HookTemplate, ItemStatus, McpEnvValue, McpTemplate } from '@skilly/core' | 1 | +import type { CatalogItemType, HookTemplate, ItemStatus, McpEnvValue, McpTemplate } from '@panoplie/core' |
| 2 | import { splitFrontmatter } from './markdown' | 2 | import { splitFrontmatter } from './markdown' |
| 3 | 3 | ||
| 4 | export const CAT_TYPES: CatalogItemType[] = ['skill', 'rule', 'agent', 'mcp', 'hook'] | 4 | export const CAT_TYPES: CatalogItemType[] = ['skill', 'rule', 'agent', 'mcp', 'hook'] |
| @@ -23,7 +23,7 @@ export const STATUS_META: Record<ItemStatus['state'], { label: string, color: 's | |||
| 23 | 'modified': { label: 'modified', color: 'warning', icon: 'i-lucide-pencil', hint: 'Edited in this scope since the last sync.' }, | 23 | 'modified': { label: 'modified', color: 'warning', icon: 'i-lucide-pencil', hint: 'Edited in this scope since the last sync.' }, |
| 24 | 'catalog-updated': { label: 'catalog updated', color: 'info', icon: 'i-lucide-arrow-down-to-line', hint: 'The catalog has a newer version.' }, | 24 | 'catalog-updated': { label: 'catalog updated', color: 'info', icon: 'i-lucide-arrow-down-to-line', hint: 'The catalog has a newer version.' }, |
| 25 | 'not-in-catalog': { label: 'not in catalog', color: 'neutral', icon: 'i-lucide-circle-dashed', hint: 'Local item, absent from the catalog.' }, | 25 | 'not-in-catalog': { label: 'not in catalog', color: 'neutral', icon: 'i-lucide-circle-dashed', hint: 'Local item, absent from the catalog.' }, |
| 26 | - 'differs-from-catalog': { label: 'differs from catalog', color: 'warning', icon: 'i-lucide-git-compare', hint: 'Same name as a catalog item, different content, not managed by skilly.' } | 26 | + 'differs-from-catalog': { label: 'differs from catalog', color: 'warning', icon: 'i-lucide-git-compare', hint: 'Same name as a catalog item, different content, not managed by panoplie.' } |
| 27 | } | 27 | } |
| 28 | 28 | ||
| 29 | /** `*` wildcard match on names, same semantics as the config globs. */ | 29 | /** `*` wildcard match on names, same semantics as the config globs. */ |
modified
packages/ui/app/utils/dashboard.ts +3 -3 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { GitStatus, InventoryItem, ItemStatus } from '@skilly/core' | |
| 1 | +import type { GitStatus, InventoryItem, ItemStatus } from '@panoplie/core' | |
| 2 | 2 | import type { Tone } from './status' |
| 3 | 3 | import { canMoveOutOfHome, projectSpecific } from './inventoryView' |
| 4 | 4 | import { GLOBAL_PATH, scopeLink } from './catalog' |
| @@ -144,14 +144,14 @@ export function startSteps(input: { | ||
| 144 | 144 | { |
| 145 | 145 | id: 'project', |
| 146 | 146 | title: 'Add a project', |
| 147 | - hint: 'Pick a folder: skilly shows what Claude Code and Codex load there.', | |
| 147 | + hint: 'Pick a folder: panoplie shows what Claude Code and Codex load there.', | |
| 148 | 148 | done: input.projects.length > 0, |
| 149 | 149 | action: { label: 'Add project', run: 'add-project' } |
| 150 | 150 | }, |
| 151 | 151 | { |
| 152 | 152 | id: 'manage', |
| 153 | 153 | title: 'Choose what a project uses', |
| 154 | - hint: 'Enable presets and catalog items, then review and apply: skilly writes the files.', | |
| 154 | + hint: 'Enable presets and catalog items, then review and apply: panoplie writes the files.', | |
| 155 | 155 | done: input.managedProjects > 0, |
| 156 | 156 | action: { label: 'Open', to: first ? scopeLink(first.id) : '/projects' } |
| 157 | 157 | }, |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { GitStatus, InventoryItem, ItemStatus } from '@skilly/core' | 1 | +import type { GitStatus, InventoryItem, ItemStatus } from '@panoplie/core' |
| 2 | import type { Tone } from './status' | 2 | import type { Tone } from './status' |
| 3 | import { canMoveOutOfHome, projectSpecific } from './inventoryView' | 3 | import { canMoveOutOfHome, projectSpecific } from './inventoryView' |
| 4 | import { GLOBAL_PATH, scopeLink } from './catalog' | 4 | import { GLOBAL_PATH, scopeLink } from './catalog' |
| @@ -144,14 +144,14 @@ export function startSteps(input: { | |||
| 144 | { | 144 | { |
| 145 | id: 'project', | 145 | id: 'project', |
| 146 | title: 'Add a project', | 146 | title: 'Add a project', |
| 147 | - hint: 'Pick a folder: skilly shows what Claude Code and Codex load there.', | 147 | + hint: 'Pick a folder: panoplie shows what Claude Code and Codex load there.', |
| 148 | done: input.projects.length > 0, | 148 | done: input.projects.length > 0, |
| 149 | action: { label: 'Add project', run: 'add-project' } | 149 | action: { label: 'Add project', run: 'add-project' } |
| 150 | }, | 150 | }, |
| 151 | { | 151 | { |
| 152 | id: 'manage', | 152 | id: 'manage', |
| 153 | title: 'Choose what a project uses', | 153 | title: 'Choose what a project uses', |
| 154 | - hint: 'Enable presets and catalog items, then review and apply: skilly writes the files.', | 154 | + hint: 'Enable presets and catalog items, then review and apply: panoplie writes the files.', |
| 155 | done: input.managedProjects > 0, | 155 | done: input.managedProjects > 0, |
| 156 | action: { label: 'Open', to: first ? scopeLink(first.id) : '/projects' } | 156 | action: { label: 'Open', to: first ? scopeLink(first.id) : '/projects' } |
| 157 | }, | 157 | }, |
modified
packages/ui/app/utils/format.ts +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { AgentId, ItemType } from '@skilly/core' | |
| 1 | +import type { AgentId, ItemType } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | export const AGENT_LABELS: Record<AgentId, string> = { claude: 'Claude Code', codex: 'Codex' } |
| 4 | 4 | |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { AgentId, ItemType } from '@skilly/core' | 1 | +import type { AgentId, ItemType } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | export const AGENT_LABELS: Record<AgentId, string> = { claude: 'Claude Code', codex: 'Codex' } | 3 | export const AGENT_LABELS: Record<AgentId, string> = { claude: 'Claude Code', codex: 'Codex' } |
| 4 | 4 | ||
modified
packages/ui/app/utils/inventoryView.ts +9 -5 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { AgentId, CatalogItemType, HookDetails, InstructionsDetails, InventoryItem, ItemStatus } from '@skilly/core' | |
| 1 | +import type { AgentId, CatalogItemType, HookDetails, InstructionsDetails, InventoryItem, ItemStatus } from '@panoplie/core' | |
| 2 | 2 | import type { Tone } from './status' |
| 3 | 3 | |
| 4 | 4 | /** |
| @@ -56,7 +56,7 @@ export function homeStateMeta(state: ItemStatus['state'] | undefined): { tone: T | ||
| 56 | 56 | /** Types that "Remove from global" supports. MCP servers and hooks of Home are read-only in v1. */ |
| 57 | 57 | export const MOVABLE_TYPES: ReadonlySet<string> = new Set(['skill', 'rule', 'agent']) |
| 58 | 58 | |
| 59 | -/** True when the item can be moved out of Home (skills, rules, subagents not provided by a plugin nor managed by skilly). */ | |
| 59 | +/** True when the item can be moved out of Home (skills, rules, subagents not provided by a plugin nor managed by panoplie). */ | |
| 60 | 60 | export function canMoveOutOfHome(i: InventoryItem, status?: ItemStatus): boolean { |
| 61 | 61 | return MOVABLE_TYPES.has(i.type) && !i.source.plugin && !status?.managed |
| 62 | 62 | } |
| @@ -99,12 +99,16 @@ export function agentsOf(items: { agent: AgentId }[]): AgentId[] { | ||
| 99 | 99 | return (['claude', 'codex'] as AgentId[]).filter(a => set.has(a)) |
| 100 | 100 | } |
| 101 | 101 | |
| 102 | -/** Splits an instruction file into plain and skilly-managed segments (the AGENTS.md block between skilly:begin and skilly:end). */ | |
| 102 | +/** | |
| 103 | + * Splits an instruction file into plain and panoplie-managed segments (the AGENTS.md block between panoplie:begin | |
| 104 | + * and panoplie:end). Blocks written under the previous name (skilly:begin / skilly:end) are recognized too. | |
| 105 | + */ | |
| 103 | 106 | export function splitManagedBlock(text: string): { text: string, managed: boolean }[] { |
| 104 | 107 | const lines = text.split('\n') |
| 105 | - const begin = lines.findIndex(l => l.startsWith('<!-- skilly:begin')) | |
| 108 | + const begin = lines.findIndex(l => /^<!-- (panoplie|skilly):begin/.test(l)) | |
| 106 | 109 | if (begin === -1) return [{ text, managed: false }] |
| 107 | - let end = lines.findIndex((l, idx) => idx > begin && l.startsWith('<!-- skilly:end')) | |
| 110 | + const name = lines[begin]!.startsWith('<!-- skilly:') ? 'skilly' : 'panoplie' | |
| 111 | + let end = lines.findIndex((l, idx) => idx > begin && l.startsWith(`<!-- ${name}:end`)) | |
| 108 | 112 | if (end === -1) end = lines.length - 1 |
| 109 | 113 | const out: { text: string, managed: boolean }[] = [] |
| 110 | 114 | if (begin > 0) out.push({ text: lines.slice(0, begin).join('\n') + '\n', managed: false }) |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { AgentId, CatalogItemType, HookDetails, InstructionsDetails, InventoryItem, ItemStatus } from '@skilly/core' | 1 | +import type { AgentId, CatalogItemType, HookDetails, InstructionsDetails, InventoryItem, ItemStatus } from '@panoplie/core' |
| 2 | import type { Tone } from './status' | 2 | import type { Tone } from './status' |
| 3 | 3 | ||
| 4 | /** | 4 | /** |
| @@ -56,7 +56,7 @@ export function homeStateMeta(state: ItemStatus['state'] | undefined): { tone: T | |||
| 56 | /** Types that "Remove from global" supports. MCP servers and hooks of Home are read-only in v1. */ | 56 | /** Types that "Remove from global" supports. MCP servers and hooks of Home are read-only in v1. */ |
| 57 | export const MOVABLE_TYPES: ReadonlySet<string> = new Set(['skill', 'rule', 'agent']) | 57 | export const MOVABLE_TYPES: ReadonlySet<string> = new Set(['skill', 'rule', 'agent']) |
| 58 | 58 | ||
| 59 | -/** True when the item can be moved out of Home (skills, rules, subagents not provided by a plugin nor managed by skilly). */ | 59 | +/** True when the item can be moved out of Home (skills, rules, subagents not provided by a plugin nor managed by panoplie). */ |
| 60 | export function canMoveOutOfHome(i: InventoryItem, status?: ItemStatus): boolean { | 60 | export function canMoveOutOfHome(i: InventoryItem, status?: ItemStatus): boolean { |
| 61 | return MOVABLE_TYPES.has(i.type) && !i.source.plugin && !status?.managed | 61 | return MOVABLE_TYPES.has(i.type) && !i.source.plugin && !status?.managed |
| 62 | } | 62 | } |
| @@ -99,12 +99,16 @@ export function agentsOf(items: { agent: AgentId }[]): AgentId[] { | |||
| 99 | return (['claude', 'codex'] as AgentId[]).filter(a => set.has(a)) | 99 | return (['claude', 'codex'] as AgentId[]).filter(a => set.has(a)) |
| 100 | } | 100 | } |
| 101 | 101 | ||
| 102 | -/** Splits an instruction file into plain and skilly-managed segments (the AGENTS.md block between skilly:begin and skilly:end). */ | 102 | +/** |
| 103 | + * Splits an instruction file into plain and panoplie-managed segments (the AGENTS.md block between panoplie:begin | ||
| 104 | + * and panoplie:end). Blocks written under the previous name (skilly:begin / skilly:end) are recognized too. | ||
| 105 | + */ | ||
| 103 | export function splitManagedBlock(text: string): { text: string, managed: boolean }[] { | 106 | export function splitManagedBlock(text: string): { text: string, managed: boolean }[] { |
| 104 | const lines = text.split('\n') | 107 | const lines = text.split('\n') |
| 105 | - const begin = lines.findIndex(l => l.startsWith('<!-- skilly:begin')) | 108 | + const begin = lines.findIndex(l => /^<!-- (panoplie|skilly):begin/.test(l)) |
| 106 | if (begin === -1) return [{ text, managed: false }] | 109 | if (begin === -1) return [{ text, managed: false }] |
| 107 | - let end = lines.findIndex((l, idx) => idx > begin && l.startsWith('<!-- skilly:end')) | 110 | + const name = lines[begin]!.startsWith('<!-- skilly:') ? 'skilly' : 'panoplie' |
| 111 | + let end = lines.findIndex((l, idx) => idx > begin && l.startsWith(`<!-- ${name}:end`)) | ||
| 108 | if (end === -1) end = lines.length - 1 | 112 | if (end === -1) end = lines.length - 1 |
| 109 | const out: { text: string, managed: boolean }[] = [] | 113 | const out: { text: string, managed: boolean }[] = [] |
| 110 | if (begin > 0) out.push({ text: lines.slice(0, begin).join('\n') + '\n', managed: false }) | 114 | if (begin > 0) out.push({ text: lines.slice(0, begin).join('\n') + '\n', managed: false }) |
modified
packages/ui/app/utils/planView.ts +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { PlanAction, ScopeState } from '@skilly/core' | |
| 1 | +import type { PlanAction, ScopeState } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | /** Plan helpers shared by the Projects table, the pending bar and the review drawer. */ |
| 4 | 4 | |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { PlanAction, ScopeState } from '@skilly/core' | 1 | +import type { PlanAction, ScopeState } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | /** Plan helpers shared by the Projects table, the pending bar and the review drawer. */ | 3 | /** Plan helpers shared by the Projects table, the pending bar and the review drawer. */ |
| 4 | 4 | ||
modified
packages/ui/app/utils/recent.ts +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | /** Recently opened projects (dashboard "Recent projects"), most recent first. A UI preference kept in localStorage. */ |
| 2 | -const KEY = 'skilly-recent-projects' | |
| 2 | +const KEY = 'panoplie-recent-projects' | |
| 3 | 3 | const MAX = 8 |
| 4 | 4 | |
| 5 | 5 | export function recentProjectIds(): string[] { |
| @@ -1,5 +1,5 @@ | |||
| 1 | /** Recently opened projects (dashboard "Recent projects"), most recent first. A UI preference kept in localStorage. */ | 1 | /** Recently opened projects (dashboard "Recent projects"), most recent first. A UI preference kept in localStorage. */ |
| 2 | -const KEY = 'skilly-recent-projects' | 2 | +const KEY = 'panoplie-recent-projects' |
| 3 | const MAX = 8 | 3 | const MAX = 8 |
| 4 | 4 | ||
| 5 | export function recentProjectIds(): string[] { | 5 | export function recentProjectIds(): string[] { |
modified
packages/ui/app/utils/resolve.ts +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { CatalogItemType, Preset } from '@skilly/core' | |
| 1 | +import type { CatalogItemType, Preset } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | export interface ResolvedEntry { |
| 4 | 4 | type: CatalogItemType |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { CatalogItemType, Preset } from '@skilly/core' | 1 | +import type { CatalogItemType, Preset } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | export interface ResolvedEntry { | 3 | export interface ResolvedEntry { |
| 4 | type: CatalogItemType | 4 | type: CatalogItemType |
modified
packages/ui/app/utils/scopeConfig.ts +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { AgentId, CatalogItemType, PlanAction, Preset, ScopeConfig } from '@skilly/core' | |
| 1 | +import type { AgentId, CatalogItemType, PlanAction, Preset, ScopeConfig } from '@panoplie/core' | |
| 2 | 2 | import { resolveItems } from './resolve' |
| 3 | 3 | |
| 4 | 4 | /** Types Global's config (home.json) can hold: its MCP servers and hooks are read-only in v1. */ |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { AgentId, CatalogItemType, PlanAction, Preset, ScopeConfig } from '@skilly/core' | 1 | +import type { AgentId, CatalogItemType, PlanAction, Preset, ScopeConfig } from '@panoplie/core' |
| 2 | import { resolveItems } from './resolve' | 2 | import { resolveItems } from './resolve' |
| 3 | 3 | ||
| 4 | /** Types Global's config (home.json) can hold: its MCP servers and hooks are read-only in v1. */ | 4 | /** Types Global's config (home.json) can hold: its MCP servers and hooks are read-only in v1. */ |
modified
packages/ui/app/utils/scopeRows.ts +6 -6 | @@ -1,19 +1,19 @@ | ||
| 1 | 1 | import type { |
| 2 | 2 | AgentId, CatalogItem, CatalogItemType, ConflictKind, ItemStatus, PlanAction, Preset, ScopeConfig, ScopeInventory, SyncPlan |
| 3 | -} from '@skilly/core' | |
| 3 | +} from '@panoplie/core' | |
| 4 | 4 | import { ITEM_STATE_META, ITEM_STATE_SHORT, type Tone } from './status' |
| 5 | 5 | import { resolveItems } from './resolve' |
| 6 | 6 | import { itemDisplayName } from './inventoryView' |
| 7 | 7 | import { HOME_CONFIG_TYPES } from './scopeConfig' |
| 8 | 8 | |
| 9 | 9 | /** |
| 10 | - * View model of the single "Items" list of a project or of Global: what skilly manages (config entries, removals) | |
| 10 | + * View model of the single "Items" list of a project or of Global: what panoplie manages (config entries, removals) | |
| 11 | 11 | * and what is only on disk (local and plugin items), in one list. One row per logical item (`type/name`; hooks: one |
| 12 | 12 | * per inventory entry), with one status per agent. Built from the local config copy (null when the scope is not |
| 13 | 13 | * managed yet), the sync plan and the inventory. Pure and unit-tested. |
| 14 | 14 | */ |
| 15 | 15 | |
| 16 | -/** `manage`: the local item is identical to a catalog item; add it to the config so skilly manages it. */ | |
| 16 | +/** `manage`: the local item is identical to a catalog item; add it to the config so panoplie manages it. */ | |
| 17 | 17 | export type RowAction = 'resolve' | 'review' | 'import' | 'manage' |
| 18 | 18 | |
| 19 | 19 | export interface AgentRowStatus { |
| @@ -132,7 +132,7 @@ export interface BuildRowsInput { | ||
| 132 | 132 | statuses: Record<string, ItemStatus> |
| 133 | 133 | /** Names of the Home skills / MCP servers, to tell "hide global" actions from item actions. */ |
| 134 | 134 | homeNames?: HomeNames |
| 135 | - /** Also list the items on disk that skilly does not manage (local and plugin items). */ | |
| 135 | + /** Also list the items on disk that panoplie does not manage (local and plugin items). */ | |
| 136 | 136 | includeLocal: boolean |
| 137 | 137 | /** Global: its config only takes skills, rules and subagents, so its MCP servers and hooks can't be managed. */ |
| 138 | 138 | home?: boolean |
| @@ -215,7 +215,7 @@ export function buildRows(input: BuildRowsInput): ScopeRow[] { | ||
| 215 | 215 | const groups = new Map<string, ScopeRow>() |
| 216 | 216 | for (const it of inventory?.items ?? []) { |
| 217 | 217 | const st = statuses[it.id] |
| 218 | - // Written by skilly: already an entry or a removal row when there is a config. | |
| 218 | + // Written by panoplie: already an entry or a removal row when there is a config. | |
| 219 | 219 | if (it.type === 'instructions' || (config && st?.managed)) continue |
| 220 | 220 | const t = it.type as CatalogItemType |
| 221 | 221 | const plugin = it.source.plugin |
| @@ -239,7 +239,7 @@ export function buildRows(input: BuildRowsInput): ScopeRow[] { | ||
| 239 | 239 | const row = groups.get(key) ?? { |
| 240 | 240 | key, kind: 'local' as const, type: t, name: it.name, label: itemDisplayName(it), itemIds: [], plugin, |
| 241 | 241 | presets: [], added: false, excluded: false, excludedExact: false, inCatalog: !plugin && inCatalog(t, it.name), |
| 242 | - origin: plugin ? `Plugin · ${plugin}` : 'Local · not managed by skilly', | |
| 242 | + origin: plugin ? `Plugin · ${plugin}` : 'Local · not managed by panoplie', | |
| 243 | 243 | statuses: [], conflicts: [], viaPreset: false, onDemand: { on: false, exact: true } |
| 244 | 244 | } |
| 245 | 245 | row.itemIds.push(it.id) |
| @@ -1,19 +1,19 @@ | |||
| 1 | import type { | 1 | import type { |
| 2 | AgentId, CatalogItem, CatalogItemType, ConflictKind, ItemStatus, PlanAction, Preset, ScopeConfig, ScopeInventory, SyncPlan | 2 | AgentId, CatalogItem, CatalogItemType, ConflictKind, ItemStatus, PlanAction, Preset, ScopeConfig, ScopeInventory, SyncPlan |
| 3 | -} from '@skilly/core' | 3 | +} from '@panoplie/core' |
| 4 | import { ITEM_STATE_META, ITEM_STATE_SHORT, type Tone } from './status' | 4 | import { ITEM_STATE_META, ITEM_STATE_SHORT, type Tone } from './status' |
| 5 | import { resolveItems } from './resolve' | 5 | import { resolveItems } from './resolve' |
| 6 | import { itemDisplayName } from './inventoryView' | 6 | import { itemDisplayName } from './inventoryView' |
| 7 | import { HOME_CONFIG_TYPES } from './scopeConfig' | 7 | import { HOME_CONFIG_TYPES } from './scopeConfig' |
| 8 | 8 | ||
| 9 | /** | 9 | /** |
| 10 | - * View model of the single "Items" list of a project or of Global: what skilly manages (config entries, removals) | 10 | + * View model of the single "Items" list of a project or of Global: what panoplie manages (config entries, removals) |
| 11 | * and what is only on disk (local and plugin items), in one list. One row per logical item (`type/name`; hooks: one | 11 | * and what is only on disk (local and plugin items), in one list. One row per logical item (`type/name`; hooks: one |
| 12 | * per inventory entry), with one status per agent. Built from the local config copy (null when the scope is not | 12 | * per inventory entry), with one status per agent. Built from the local config copy (null when the scope is not |
| 13 | * managed yet), the sync plan and the inventory. Pure and unit-tested. | 13 | * managed yet), the sync plan and the inventory. Pure and unit-tested. |
| 14 | */ | 14 | */ |
| 15 | 15 | ||
| 16 | -/** `manage`: the local item is identical to a catalog item; add it to the config so skilly manages it. */ | 16 | +/** `manage`: the local item is identical to a catalog item; add it to the config so panoplie manages it. */ |
| 17 | export type RowAction = 'resolve' | 'review' | 'import' | 'manage' | 17 | export type RowAction = 'resolve' | 'review' | 'import' | 'manage' |
| 18 | 18 | ||
| 19 | export interface AgentRowStatus { | 19 | export interface AgentRowStatus { |
| @@ -132,7 +132,7 @@ export interface BuildRowsInput { | |||
| 132 | statuses: Record<string, ItemStatus> | 132 | statuses: Record<string, ItemStatus> |
| 133 | /** Names of the Home skills / MCP servers, to tell "hide global" actions from item actions. */ | 133 | /** Names of the Home skills / MCP servers, to tell "hide global" actions from item actions. */ |
| 134 | homeNames?: HomeNames | 134 | homeNames?: HomeNames |
| 135 | - /** Also list the items on disk that skilly does not manage (local and plugin items). */ | 135 | + /** Also list the items on disk that panoplie does not manage (local and plugin items). */ |
| 136 | includeLocal: boolean | 136 | includeLocal: boolean |
| 137 | /** Global: its config only takes skills, rules and subagents, so its MCP servers and hooks can't be managed. */ | 137 | /** Global: its config only takes skills, rules and subagents, so its MCP servers and hooks can't be managed. */ |
| 138 | home?: boolean | 138 | home?: boolean |
| @@ -215,7 +215,7 @@ export function buildRows(input: BuildRowsInput): ScopeRow[] { | |||
| 215 | const groups = new Map<string, ScopeRow>() | 215 | const groups = new Map<string, ScopeRow>() |
| 216 | for (const it of inventory?.items ?? []) { | 216 | for (const it of inventory?.items ?? []) { |
| 217 | const st = statuses[it.id] | 217 | const st = statuses[it.id] |
| 218 | - // Written by skilly: already an entry or a removal row when there is a config. | 218 | + // Written by panoplie: already an entry or a removal row when there is a config. |
| 219 | if (it.type === 'instructions' || (config && st?.managed)) continue | 219 | if (it.type === 'instructions' || (config && st?.managed)) continue |
| 220 | const t = it.type as CatalogItemType | 220 | const t = it.type as CatalogItemType |
| 221 | const plugin = it.source.plugin | 221 | const plugin = it.source.plugin |
| @@ -239,7 +239,7 @@ export function buildRows(input: BuildRowsInput): ScopeRow[] { | |||
| 239 | const row = groups.get(key) ?? { | 239 | const row = groups.get(key) ?? { |
| 240 | key, kind: 'local' as const, type: t, name: it.name, label: itemDisplayName(it), itemIds: [], plugin, | 240 | key, kind: 'local' as const, type: t, name: it.name, label: itemDisplayName(it), itemIds: [], plugin, |
| 241 | presets: [], added: false, excluded: false, excludedExact: false, inCatalog: !plugin && inCatalog(t, it.name), | 241 | presets: [], added: false, excluded: false, excludedExact: false, inCatalog: !plugin && inCatalog(t, it.name), |
| 242 | - origin: plugin ? `Plugin · ${plugin}` : 'Local · not managed by skilly', | 242 | + origin: plugin ? `Plugin · ${plugin}` : 'Local · not managed by panoplie', |
| 243 | statuses: [], conflicts: [], viaPreset: false, onDemand: { on: false, exact: true } | 243 | statuses: [], conflicts: [], viaPreset: false, onDemand: { on: false, exact: true } |
| 244 | } | 244 | } |
| 245 | row.itemIds.push(it.id) | 245 | row.itemIds.push(it.id) |
modified
packages/ui/app/utils/status.ts +3 -3 | @@ -1,4 +1,4 @@ | ||
| 1 | -import type { ConflictKind, GitStatus, ItemStatus, ItemType, PlanActionKind } from '@skilly/core' | |
| 1 | +import type { ConflictKind, GitStatus, ItemStatus, ItemType, PlanActionKind } from '@panoplie/core' | |
| 2 | 2 | |
| 3 | 3 | /** |
| 4 | 4 | * Status vocabulary shared by every screen (design/BRIEF.md 1.2 "Status colour semantics" and 3, 4.1, 4.2). |
| @@ -34,7 +34,7 @@ export const ITEM_STATE_META: Record<ItemStatus['state'], StatusMeta> = { | ||
| 34 | 34 | 'in-sync': { tone: 'ok', label: 'Up to date', hint: 'Identical to the catalog version.' }, |
| 35 | 35 | 'catalog-updated': { tone: 'info', label: 'Update available', hint: 'The catalog has a newer version.' }, |
| 36 | 36 | 'modified': { tone: 'accent', label: 'Drift', hint: 'Edited in this scope since the last sync.' }, |
| 37 | - 'differs-from-catalog': { tone: 'accent', label: 'Differs from catalog', hint: 'Same name as a catalog item, different content, not managed by skilly.' }, | |
| 37 | + 'differs-from-catalog': { tone: 'accent', label: 'Differs from catalog', hint: 'Same name as a catalog item, different content, not managed by panoplie.' }, | |
| 38 | 38 | 'not-in-catalog': { tone: 'faint', label: 'Local only', hint: 'Local item, absent from the catalog.' } |
| 39 | 39 | } |
| 40 | 40 | |
| @@ -75,7 +75,7 @@ export function planActionMeta(kind: PlanActionKind, opts: { catalogUpdated?: bo | ||
| 75 | 75 | /** Headline per conflict kind (brief 4.2). */ |
| 76 | 76 | export const CONFLICT_HEADLINE: Record<ConflictKind, string> = { |
| 77 | 77 | 'modified-locally': 'Edited in this project since the last sync', |
| 78 | - 'exists-unmanaged': 'A file already exists here and is not managed by skilly', | |
| 78 | + 'exists-unmanaged': 'A file already exists here and is not managed by panoplie', | |
| 79 | 79 | 'missing-in-catalog': 'No longer in the catalog' |
| 80 | 80 | } |
| 81 | 81 | |
| @@ -1,4 +1,4 @@ | |||
| 1 | -import type { ConflictKind, GitStatus, ItemStatus, ItemType, PlanActionKind } from '@skilly/core' | 1 | +import type { ConflictKind, GitStatus, ItemStatus, ItemType, PlanActionKind } from '@panoplie/core' |
| 2 | 2 | ||
| 3 | /** | 3 | /** |
| 4 | * Status vocabulary shared by every screen (design/BRIEF.md 1.2 "Status colour semantics" and 3, 4.1, 4.2). | 4 | * Status vocabulary shared by every screen (design/BRIEF.md 1.2 "Status colour semantics" and 3, 4.1, 4.2). |
| @@ -34,7 +34,7 @@ export const ITEM_STATE_META: Record<ItemStatus['state'], StatusMeta> = { | |||
| 34 | 'in-sync': { tone: 'ok', label: 'Up to date', hint: 'Identical to the catalog version.' }, | 34 | 'in-sync': { tone: 'ok', label: 'Up to date', hint: 'Identical to the catalog version.' }, |
| 35 | 'catalog-updated': { tone: 'info', label: 'Update available', hint: 'The catalog has a newer version.' }, | 35 | 'catalog-updated': { tone: 'info', label: 'Update available', hint: 'The catalog has a newer version.' }, |
| 36 | 'modified': { tone: 'accent', label: 'Drift', hint: 'Edited in this scope since the last sync.' }, | 36 | 'modified': { tone: 'accent', label: 'Drift', hint: 'Edited in this scope since the last sync.' }, |
| 37 | - 'differs-from-catalog': { tone: 'accent', label: 'Differs from catalog', hint: 'Same name as a catalog item, different content, not managed by skilly.' }, | 37 | + 'differs-from-catalog': { tone: 'accent', label: 'Differs from catalog', hint: 'Same name as a catalog item, different content, not managed by panoplie.' }, |
| 38 | 'not-in-catalog': { tone: 'faint', label: 'Local only', hint: 'Local item, absent from the catalog.' } | 38 | 'not-in-catalog': { tone: 'faint', label: 'Local only', hint: 'Local item, absent from the catalog.' } |
| 39 | } | 39 | } |
| 40 | 40 | ||
| @@ -75,7 +75,7 @@ export function planActionMeta(kind: PlanActionKind, opts: { catalogUpdated?: bo | |||
| 75 | /** Headline per conflict kind (brief 4.2). */ | 75 | /** Headline per conflict kind (brief 4.2). */ |
| 76 | export const CONFLICT_HEADLINE: Record<ConflictKind, string> = { | 76 | export const CONFLICT_HEADLINE: Record<ConflictKind, string> = { |
| 77 | 'modified-locally': 'Edited in this project since the last sync', | 77 | 'modified-locally': 'Edited in this project since the last sync', |
| 78 | - 'exists-unmanaged': 'A file already exists here and is not managed by skilly', | 78 | + 'exists-unmanaged': 'A file already exists here and is not managed by panoplie', |
| 79 | 'missing-in-catalog': 'No longer in the catalog' | 79 | 'missing-in-catalog': 'No longer in the catalog' |
| 80 | } | 80 | } |
| 81 | 81 | ||
modified
packages/ui/app/utils/token.ts +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -const STORAGE_KEY = 'skilly-token' | |
| 1 | +const STORAGE_KEY = 'panoplie-token' | |
| 2 | 2 | |
| 3 | 3 | /** Reads the session token from sessionStorage, falling back to `dev` in development. */ |
| 4 | 4 | export function getToken(): string { |
| @@ -1,4 +1,4 @@ | |||
| 1 | -const STORAGE_KEY = 'skilly-token' | 1 | +const STORAGE_KEY = 'panoplie-token' |
| 2 | 2 | ||
| 3 | /** Reads the session token from sessionStorage, falling back to `dev` in development. */ | 3 | /** Reads the session token from sessionStorage, falling back to `dev` in development. */ |
| 4 | export function getToken(): string { | 4 | export function getToken(): string { |
modified
packages/ui/nuxt.config.ts +1 -1 | @@ -6,7 +6,7 @@ export default defineNuxtConfig({ | ||
| 6 | 6 | devtools: { enabled: false }, |
| 7 | 7 | app: { |
| 8 | 8 | head: { |
| 9 | - title: 'skilly', | |
| 9 | + title: 'panoplie', | |
| 10 | 10 | link: [{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }] |
| 11 | 11 | } |
| 12 | 12 | }, |
| @@ -6,7 +6,7 @@ export default defineNuxtConfig({ | |||
| 6 | devtools: { enabled: false }, | 6 | devtools: { enabled: false }, |
| 7 | app: { | 7 | app: { |
| 8 | head: { | 8 | head: { |
| 9 | - title: 'skilly', | 9 | + title: 'panoplie', |
| 10 | link: [{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }] | 10 | link: [{ rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }] |
| 11 | } | 11 | } |
| 12 | }, | 12 | }, |
modified
packages/ui/package.json +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | { |
| 2 | - "name": "@skilly/ui", | |
| 2 | + "name": "@panoplie/ui", | |
| 3 | 3 | "version": "0.0.0", |
| 4 | 4 | "private": true, |
| 5 | 5 | "license": "MIT", |
| @@ -15,7 +15,7 @@ | ||
| 15 | 15 | "@iconify-json/lucide": "^1.2.0", |
| 16 | 16 | "@iconify-json/simple-icons": "^1.2.100", |
| 17 | 17 | "@nuxt/ui": "^4.12.0", |
| 18 | - "@skilly/core": "workspace:*", | |
| 18 | + "@panoplie/core": "workspace:*", | |
| 19 | 19 | "marked": "^18.1.1", |
| 20 | 20 | "nuxt": "^4.6.0", |
| 21 | 21 | "tailwindcss": "^4.1.0", |
| @@ -1,5 +1,5 @@ | |||
| 1 | { | 1 | { |
| 2 | - "name": "@skilly/ui", | 2 | + "name": "@panoplie/ui", |
| 3 | "version": "0.0.0", | 3 | "version": "0.0.0", |
| 4 | "private": true, | 4 | "private": true, |
| 5 | "license": "MIT", | 5 | "license": "MIT", |
| @@ -15,7 +15,7 @@ | |||
| 15 | "@iconify-json/lucide": "^1.2.0", | 15 | "@iconify-json/lucide": "^1.2.0", |
| 16 | "@iconify-json/simple-icons": "^1.2.100", | 16 | "@iconify-json/simple-icons": "^1.2.100", |
| 17 | "@nuxt/ui": "^4.12.0", | 17 | "@nuxt/ui": "^4.12.0", |
| 18 | - "@skilly/core": "workspace:*", | 18 | + "@panoplie/core": "workspace:*", |
| 19 | "marked": "^18.1.1", | 19 | "marked": "^18.1.1", |
| 20 | "nuxt": "^4.6.0", | 20 | "nuxt": "^4.6.0", |
| 21 | "tailwindcss": "^4.1.0", | 21 | "tailwindcss": "^4.1.0", |
modified
packages/ui/public/favicon.svg +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> |
| 2 | - <!-- The skilly mark (SkLogo): a 2x2 grid on ink, one clay square --> | |
| 2 | + <!-- The panoplie mark (SkLogo): a 2x2 grid on ink, one clay square --> | |
| 3 | 3 | <rect width="32" height="32" rx="8" fill="#1c1b19"/> |
| 4 | 4 | <rect x="6" y="6" width="8.5" height="8.5" rx="1.5" fill="#ffffff"/> |
| 5 | 5 | <rect x="6" y="17.5" width="8.5" height="8.5" rx="1.5" fill="#e2774c"/> |
| @@ -1,5 +1,5 @@ | |||
| 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> | 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"> |
| 2 | - <!-- The skilly mark (SkLogo): a 2x2 grid on ink, one clay square --> | 2 | + <!-- The panoplie mark (SkLogo): a 2x2 grid on ink, one clay square --> |
| 3 | <rect width="32" height="32" rx="8" fill="#1c1b19"/> | 3 | <rect width="32" height="32" rx="8" fill="#1c1b19"/> |
| 4 | <rect x="6" y="6" width="8.5" height="8.5" rx="1.5" fill="#ffffff"/> | 4 | <rect x="6" y="6" width="8.5" height="8.5" rx="1.5" fill="#ffffff"/> |
| 5 | <rect x="6" y="17.5" width="8.5" height="8.5" rx="1.5" fill="#e2774c"/> | 5 | <rect x="6" y="17.5" width="8.5" height="8.5" rx="1.5" fill="#e2774c"/> |
modified
packages/ui/test/dashboard.test.ts +1 -1 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | import { describe, expect, it } from 'vitest' |
| 2 | -import type { GitStatus } from '@skilly/core' | |
| 2 | +import type { GitStatus } from '@panoplie/core' | |
| 3 | 3 | import { dashboardTodos, startSteps, type ScopeHealth } from '../app/utils/dashboard' |
| 4 | 4 | |
| 5 | 5 | const git = (over: Partial<GitStatus> = {}): GitStatus => ({ |
| @@ -1,5 +1,5 @@ | |||
| 1 | import { describe, expect, it } from 'vitest' | 1 | import { describe, expect, it } from 'vitest' |
| 2 | -import type { GitStatus } from '@skilly/core' | 2 | +import type { GitStatus } from '@panoplie/core' |
| 3 | import { dashboardTodos, startSteps, type ScopeHealth } from '../app/utils/dashboard' | 3 | import { dashboardTodos, startSteps, type ScopeHealth } from '../app/utils/dashboard' |
| 4 | 4 | ||
| 5 | const git = (over: Partial<GitStatus> = {}): GitStatus => ({ | 5 | const git = (over: Partial<GitStatus> = {}): GitStatus => ({ |
modified
packages/ui/test/scope-views.test.ts +2 -2 | @@ -1,5 +1,5 @@ | ||
| 1 | 1 | import { describe, expect, it } from 'vitest' |
| 2 | -import type { InventoryItem, ScopeConfig, SyncPlan } from '@skilly/core' | |
| 2 | +import type { InventoryItem, ScopeConfig, SyncPlan } from '@panoplie/core' | |
| 3 | 3 | import { buildRows, matchIn, mergeStatuses } from '../app/utils/scopeRows' |
| 4 | 4 | import { disableInConfig, manageInConfig, undoInConfig, undoKind } from '../app/utils/scopeConfig' |
| 5 | 5 | import { projectSpecific, splitManagedBlock, homeDirOf } from '../app/utils/inventoryView' |
| @@ -114,7 +114,7 @@ describe('inventory view helpers', () => { | ||
| 114 | 114 | expect(homeDirOf(item, '/h')).toBe('~/.claude') |
| 115 | 115 | }) |
| 116 | 116 | it('splits the managed AGENTS.md block', () => { |
| 117 | - const segs = splitManagedBlock('a\n<!-- skilly:begin (x) -->\nr\n<!-- skilly:end -->\nb') | |
| 117 | + const segs = splitManagedBlock('a\n<!-- panoplie:begin (x) -->\nr\n<!-- panoplie:end -->\nb') | |
| 118 | 118 | expect(segs.map(s => s.managed)).toEqual([false, true, false]) |
| 119 | 119 | }) |
| 120 | 120 | }) |
| @@ -1,5 +1,5 @@ | |||
| 1 | import { describe, expect, it } from 'vitest' | 1 | import { describe, expect, it } from 'vitest' |
| 2 | -import type { InventoryItem, ScopeConfig, SyncPlan } from '@skilly/core' | 2 | +import type { InventoryItem, ScopeConfig, SyncPlan } from '@panoplie/core' |
| 3 | import { buildRows, matchIn, mergeStatuses } from '../app/utils/scopeRows' | 3 | import { buildRows, matchIn, mergeStatuses } from '../app/utils/scopeRows' |
| 4 | import { disableInConfig, manageInConfig, undoInConfig, undoKind } from '../app/utils/scopeConfig' | 4 | import { disableInConfig, manageInConfig, undoInConfig, undoKind } from '../app/utils/scopeConfig' |
| 5 | import { projectSpecific, splitManagedBlock, homeDirOf } from '../app/utils/inventoryView' | 5 | import { projectSpecific, splitManagedBlock, homeDirOf } from '../app/utils/inventoryView' |
| @@ -114,7 +114,7 @@ describe('inventory view helpers', () => { | |||
| 114 | expect(homeDirOf(item, '/h')).toBe('~/.claude') | 114 | expect(homeDirOf(item, '/h')).toBe('~/.claude') |
| 115 | }) | 115 | }) |
| 116 | it('splits the managed AGENTS.md block', () => { | 116 | it('splits the managed AGENTS.md block', () => { |
| 117 | - const segs = splitManagedBlock('a\n<!-- skilly:begin (x) -->\nr\n<!-- skilly:end -->\nb') | 117 | + const segs = splitManagedBlock('a\n<!-- panoplie:begin (x) -->\nr\n<!-- panoplie:end -->\nb') |
| 118 | expect(segs.map(s => s.managed)).toEqual([false, true, false]) | 118 | expect(segs.map(s => s.managed)).toEqual([false, true, false]) |
| 119 | }) | 119 | }) |
| 120 | }) | 120 | }) |
modified
pnpm-lock.yaml +4 -4 | @@ -23,10 +23,10 @@ importers: | ||
| 23 | 23 | |
| 24 | 24 | packages/cli: |
| 25 | 25 | dependencies: |
| 26 | - '@skilly/core': | |
| 26 | + '@panoplie/core': | |
| 27 | 27 | specifier: workspace:* |
| 28 | 28 | version: link:../core |
| 29 | - '@skilly/server': | |
| 29 | + '@panoplie/server': | |
| 30 | 30 | specifier: workspace:* |
| 31 | 31 | version: link:../server |
| 32 | 32 | |
| @@ -47,7 +47,7 @@ importers: | ||
| 47 | 47 | '@hono/node-server': |
| 48 | 48 | specifier: ^2.1.0 |
| 49 | 49 | version: 2.1.5(hono@4.13.13) |
| 50 | - '@skilly/core': | |
| 50 | + '@panoplie/core': | |
| 51 | 51 | specifier: workspace:* |
| 52 | 52 | version: link:../core |
| 53 | 53 | hono: |
| @@ -71,7 +71,7 @@ importers: | ||
| 71 | 71 | '@nuxt/ui': |
| 72 | 72 | specifier: ^4.12.0 |
| 73 | 73 | version: 4.12.0(5b14c4ff6cb0908ddbfab51a24207b59) |
| 74 | - '@skilly/core': | |
| 74 | + '@panoplie/core': | |
| 75 | 75 | specifier: workspace:* |
| 76 | 76 | version: link:../core |
| 77 | 77 | marked: |
| @@ -23,10 +23,10 @@ importers: | |||
| 23 | 23 | ||
| 24 | packages/cli: | 24 | packages/cli: |
| 25 | dependencies: | 25 | dependencies: |
| 26 | - '@skilly/core': | 26 | + '@panoplie/core': |
| 27 | specifier: workspace:* | 27 | specifier: workspace:* |
| 28 | version: link:../core | 28 | version: link:../core |
| 29 | - '@skilly/server': | 29 | + '@panoplie/server': |
| 30 | specifier: workspace:* | 30 | specifier: workspace:* |
| 31 | version: link:../server | 31 | version: link:../server |
| 32 | 32 | ||
| @@ -47,7 +47,7 @@ importers: | |||
| 47 | '@hono/node-server': | 47 | '@hono/node-server': |
| 48 | specifier: ^2.1.0 | 48 | specifier: ^2.1.0 |
| 49 | version: 2.1.5(hono@4.13.13) | 49 | version: 2.1.5(hono@4.13.13) |
| 50 | - '@skilly/core': | 50 | + '@panoplie/core': |
| 51 | specifier: workspace:* | 51 | specifier: workspace:* |
| 52 | version: link:../core | 52 | version: link:../core |
| 53 | hono: | 53 | hono: |
| @@ -71,7 +71,7 @@ importers: | |||
| 71 | '@nuxt/ui': | 71 | '@nuxt/ui': |
| 72 | specifier: ^4.12.0 | 72 | specifier: ^4.12.0 |
| 73 | version: 4.12.0(5b14c4ff6cb0908ddbfab51a24207b59) | 73 | version: 4.12.0(5b14c4ff6cb0908ddbfab51a24207b59) |
| 74 | - '@skilly/core': | 74 | + '@panoplie/core': |
| 75 | specifier: workspace:* | 75 | specifier: workspace:* |
| 76 | version: link:../core | 76 | version: link:../core |
| 77 | marked: | 77 | marked: |
modified
site/public/index.html +18 -18 | @@ -3,18 +3,18 @@ | ||
| 3 | 3 | <head> |
| 4 | 4 | <meta charset="utf-8"> |
| 5 | 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | - <title>skilly: stop copying agent skills between projects</title> | |
| 6 | + <title>panoplie: stop copying agent skills between projects</title> | |
| 7 | 7 | <meta name="description" content="One catalog for your Claude Code and Codex skills, rules and MCP servers. Each project gets only what it needs. Open source."> |
| 8 | - <link rel="canonical" href="https://skilly.eventuallycoding.com/"> | |
| 8 | + <link rel="canonical" href="https://panoplie.eventuallycoding.com/"> | |
| 9 | 9 | <meta name="color-scheme" content="light dark"> |
| 10 | 10 | <meta name="theme-color" content="#f5f4f1" media="(prefers-color-scheme: light)"> |
| 11 | 11 | <meta name="theme-color" content="#121110" media="(prefers-color-scheme: dark)"> |
| 12 | 12 | |
| 13 | 13 | <meta property="og:type" content="website"> |
| 14 | - <meta property="og:url" content="https://skilly.eventuallycoding.com/"> | |
| 15 | - <meta property="og:title" content="skilly: stop copying agent skills between projects"> | |
| 14 | + <meta property="og:url" content="https://panoplie.eventuallycoding.com/"> | |
| 15 | + <meta property="og:title" content="panoplie: stop copying agent skills between projects"> | |
| 16 | 16 | <meta property="og:description" content="One catalog for your Claude Code and Codex skills, rules and MCP servers. Each project gets only what it needs."> |
| 17 | - <meta property="og:image" content="https://skilly.eventuallycoding.com/screenshots/project.png"> | |
| 17 | + <meta property="og:image" content="https://panoplie.eventuallycoding.com/screenshots/project.png"> | |
| 18 | 18 | <meta name="twitter:card" content="summary_large_image"> |
| 19 | 19 | |
| 20 | 20 | <link rel="icon" type="image/svg+xml" href="favicon.svg"> |
| @@ -28,16 +28,16 @@ | ||
| 28 | 28 | <a class="skip" href="#main">Skip to content</a> |
| 29 | 29 | |
| 30 | 30 | <header class="top"> |
| 31 | - <a class="brand" href="./" aria-label="skilly, home"> | |
| 31 | + <a class="brand" href="./" aria-label="panoplie, home"> | |
| 32 | 32 | <span class="mark" aria-hidden="true"><i></i><i></i><i></i><i></i></span> |
| 33 | - <span>skilly</span> | |
| 33 | + <span>panoplie</span> | |
| 34 | 34 | </a> |
| 35 | 35 | <nav class="top-nav" aria-label="Sections"> |
| 36 | 36 | <a href="#tour">Tour</a> |
| 37 | 37 | <a href="#how">How it works</a> |
| 38 | 38 | <a href="#try">Try it</a> |
| 39 | 39 | </nav> |
| 40 | - <a class="btn btn-ink btn-sm" href="https://rickub.com/hlassiege/skilly"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> | |
| 40 | + <a class="btn btn-ink btn-sm" href="https://rickub.com/hlassiege/panoplie"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> | |
| 41 | 41 | </header> |
| 42 | 42 | |
| 43 | 43 | <main id="main"> |
| @@ -47,7 +47,7 @@ | ||
| 47 | 47 | <p class="lead">One catalog for your skills, rules and MCP servers.</p> |
| 48 | 48 | <div class="actions"> |
| 49 | 49 | <a class="btn btn-ink" href="#try">Try it</a> |
| 50 | - <a class="btn btn-line" href="https://rickub.com/hlassiege/skilly"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> | |
| 50 | + <a class="btn btn-line" href="https://rickub.com/hlassiege/panoplie"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> | |
| 51 | 51 | </div> |
| 52 | 52 | <p class="perk"> |
| 53 | 53 | <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg> |
| @@ -186,15 +186,15 @@ | ||
| 186 | 186 | </section> |
| 187 | 187 | |
| 188 | 188 | <section class="tour" id="tour" aria-labelledby="tour-title"> |
| 189 | - <h2 class="sr-only" id="tour-title">A tour of skilly</h2> | |
| 189 | + <h2 class="sr-only" id="tour-title">A tour of panoplie</h2> | |
| 190 | 190 | <div class="tour-tabs" role="tablist" aria-label="Screens" aria-orientation="vertical"> |
| 191 | 191 | <button role="tab" id="tab-project" aria-controls="shot-project" aria-selected="true"> |
| 192 | 192 | <span class="tab-title">Pick what each project uses</span> |
| 193 | - <span class="tab-desc">Add a preset or a single skill. skilly writes the files.</span> | |
| 193 | + <span class="tab-desc">Add a preset or a single skill. panoplie writes the files.</span> | |
| 194 | 194 | </button> |
| 195 | 195 | <button role="tab" id="tab-review" aria-controls="shot-review" aria-selected="false" tabindex="-1"> |
| 196 | 196 | <span class="tab-title">See every change first</span> |
| 197 | - <span class="tab-desc">Nothing is written until you apply. Edited a file by hand? skilly asks first.</span> | |
| 197 | + <span class="tab-desc">Nothing is written until you apply. Edited a file by hand? panoplie asks first.</span> | |
| 198 | 198 | </button> |
| 199 | 199 | <button role="tab" id="tab-global" aria-controls="shot-global" aria-selected="false" tabindex="-1"> |
| 200 | 200 | <span class="tab-title">Slim down your global config</span> |
| @@ -214,7 +214,7 @@ | ||
| 214 | 214 | </button> |
| 215 | 215 | </div> |
| 216 | 216 | <div class="tour-shots"> |
| 217 | - <div class="frame" id="shot-project" role="tabpanel" aria-labelledby="tab-project"><img src="screenshots/project.png" width="2880" height="1800" alt="A project in skilly with its presets and items" fetchpriority="high"></div> | |
| 217 | + <div class="frame" id="shot-project" role="tabpanel" aria-labelledby="tab-project"><img src="screenshots/project.png" width="2880" height="1800" alt="A project in panoplie with its presets and items" fetchpriority="high"></div> | |
| 218 | 218 | <div class="frame" id="shot-review" role="tabpanel" aria-labelledby="tab-review"><img src="screenshots/review.png" width="2880" height="1800" alt="The review screen listing the files that will change" loading="lazy"></div> |
| 219 | 219 | <div class="frame" id="shot-global" role="tabpanel" aria-labelledby="tab-global"><img src="screenshots/global.png" width="2880" height="1800" alt="Global items with suggestions to move them to projects" loading="lazy"></div> |
| 220 | 220 | <div class="frame" id="shot-catalog" role="tabpanel" aria-labelledby="tab-catalog"><img src="screenshots/catalog.png" width="2880" height="1800" alt="The catalog with a skill opened in the editor" loading="lazy"></div> |
| @@ -243,7 +243,7 @@ | ||
| 243 | 243 | |
| 244 | 244 | <section class="how" id="how" aria-labelledby="how-title"> |
| 245 | 245 | <h2 id="how-title">One catalog, plain files in each project</h2> |
| 246 | - <p class="section-lead">Your projects keep working without skilly.</p> | |
| 246 | + <p class="section-lead">Your projects keep working without panoplie.</p> | |
| 247 | 247 | |
| 248 | 248 | <div class="flow" data-flow> |
| 249 | 249 | <div class="panel cat"> |
| @@ -311,18 +311,18 @@ | ||
| 311 | 311 | <section class="try" id="try" aria-labelledby="try-title"> |
| 312 | 312 | <h2 id="try-title">Try it</h2> |
| 313 | 313 | <div class="code"> |
| 314 | - <pre><code id="install-cmd">git clone https://rickub.com/hlassiege/skilly.git | |
| 315 | -cd skilly | |
| 314 | + <pre><code id="install-cmd">git clone https://rickub.com/hlassiege/panoplie.git | |
| 315 | +cd panoplie | |
| 316 | 316 | pnpm install && pnpm build && pnpm start</code></pre> |
| 317 | 317 | <button class="btn btn-line btn-sm copy" type="button" data-copy="install-cmd">Copy</button> |
| 318 | 318 | </div> |
| 319 | - <p class="note"><a href="https://rickub.com/hlassiege/skilly">Read the README</a> for everything else.</p> | |
| 319 | + <p class="note"><a href="https://rickub.com/hlassiege/panoplie">Read the README</a> for everything else.</p> | |
| 320 | 320 | </section> |
| 321 | 321 | </main> |
| 322 | 322 | |
| 323 | 323 | <footer class="foot"> |
| 324 | 324 | <p>Open source, MIT. Made by <a href="https://eventuallycoding.com">Hugo Lassiège</a>.</p> |
| 325 | - <a href="https://rickub.com/hlassiege/skilly">Source code</a> | |
| 325 | + <a href="https://rickub.com/hlassiege/panoplie">Source code</a> | |
| 326 | 326 | </footer> |
| 327 | 327 | </body> |
| 328 | 328 | </html> |
| @@ -3,18 +3,18 @@ | |||
| 3 | <head> | 3 | <head> |
| 4 | <meta charset="utf-8"> | 4 | <meta charset="utf-8"> |
| 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> | 5 | <meta name="viewport" content="width=device-width, initial-scale=1"> |
| 6 | - <title>skilly: stop copying agent skills between projects</title> | 6 | + <title>panoplie: stop copying agent skills between projects</title> |
| 7 | <meta name="description" content="One catalog for your Claude Code and Codex skills, rules and MCP servers. Each project gets only what it needs. Open source."> | 7 | <meta name="description" content="One catalog for your Claude Code and Codex skills, rules and MCP servers. Each project gets only what it needs. Open source."> |
| 8 | - <link rel="canonical" href="https://skilly.eventuallycoding.com/"> | 8 | + <link rel="canonical" href="https://panoplie.eventuallycoding.com/"> |
| 9 | <meta name="color-scheme" content="light dark"> | 9 | <meta name="color-scheme" content="light dark"> |
| 10 | <meta name="theme-color" content="#f5f4f1" media="(prefers-color-scheme: light)"> | 10 | <meta name="theme-color" content="#f5f4f1" media="(prefers-color-scheme: light)"> |
| 11 | <meta name="theme-color" content="#121110" media="(prefers-color-scheme: dark)"> | 11 | <meta name="theme-color" content="#121110" media="(prefers-color-scheme: dark)"> |
| 12 | 12 | ||
| 13 | <meta property="og:type" content="website"> | 13 | <meta property="og:type" content="website"> |
| 14 | - <meta property="og:url" content="https://skilly.eventuallycoding.com/"> | 14 | + <meta property="og:url" content="https://panoplie.eventuallycoding.com/"> |
| 15 | - <meta property="og:title" content="skilly: stop copying agent skills between projects"> | 15 | + <meta property="og:title" content="panoplie: stop copying agent skills between projects"> |
| 16 | <meta property="og:description" content="One catalog for your Claude Code and Codex skills, rules and MCP servers. Each project gets only what it needs."> | 16 | <meta property="og:description" content="One catalog for your Claude Code and Codex skills, rules and MCP servers. Each project gets only what it needs."> |
| 17 | - <meta property="og:image" content="https://skilly.eventuallycoding.com/screenshots/project.png"> | 17 | + <meta property="og:image" content="https://panoplie.eventuallycoding.com/screenshots/project.png"> |
| 18 | <meta name="twitter:card" content="summary_large_image"> | 18 | <meta name="twitter:card" content="summary_large_image"> |
| 19 | 19 | ||
| 20 | <link rel="icon" type="image/svg+xml" href="favicon.svg"> | 20 | <link rel="icon" type="image/svg+xml" href="favicon.svg"> |
| @@ -28,16 +28,16 @@ | |||
| 28 | <a class="skip" href="#main">Skip to content</a> | 28 | <a class="skip" href="#main">Skip to content</a> |
| 29 | 29 | ||
| 30 | <header class="top"> | 30 | <header class="top"> |
| 31 | - <a class="brand" href="./" aria-label="skilly, home"> | 31 | + <a class="brand" href="./" aria-label="panoplie, home"> |
| 32 | <span class="mark" aria-hidden="true"><i></i><i></i><i></i><i></i></span> | 32 | <span class="mark" aria-hidden="true"><i></i><i></i><i></i><i></i></span> |
| 33 | - <span>skilly</span> | 33 | + <span>panoplie</span> |
| 34 | </a> | 34 | </a> |
| 35 | <nav class="top-nav" aria-label="Sections"> | 35 | <nav class="top-nav" aria-label="Sections"> |
| 36 | <a href="#tour">Tour</a> | 36 | <a href="#tour">Tour</a> |
| 37 | <a href="#how">How it works</a> | 37 | <a href="#how">How it works</a> |
| 38 | <a href="#try">Try it</a> | 38 | <a href="#try">Try it</a> |
| 39 | </nav> | 39 | </nav> |
| 40 | - <a class="btn btn-ink btn-sm" href="https://rickub.com/hlassiege/skilly"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> | 40 | + <a class="btn btn-ink btn-sm" href="https://rickub.com/hlassiege/panoplie"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> |
| 41 | </header> | 41 | </header> |
| 42 | 42 | ||
| 43 | <main id="main"> | 43 | <main id="main"> |
| @@ -47,7 +47,7 @@ | |||
| 47 | <p class="lead">One catalog for your skills, rules and MCP servers.</p> | 47 | <p class="lead">One catalog for your skills, rules and MCP servers.</p> |
| 48 | <div class="actions"> | 48 | <div class="actions"> |
| 49 | <a class="btn btn-ink" href="#try">Try it</a> | 49 | <a class="btn btn-ink" href="#try">Try it</a> |
| 50 | - <a class="btn btn-line" href="https://rickub.com/hlassiege/skilly"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> | 50 | + <a class="btn btn-line" href="https://rickub.com/hlassiege/panoplie"><svg class="btn-icon" viewBox="0 0 64 64" aria-hidden="true"><path d="M32 8 A24 24 0 1 1 12 44"/><path d="M32 18 A14 14 0 1 0 46 32"/><circle cx="32" cy="32" r="6.5"/></svg>Source code</a> |
| 51 | </div> | 51 | </div> |
| 52 | <p class="perk"> | 52 | <p class="perk"> |
| 53 | <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg> | 53 | <svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true"><path d="M3.5 8.5l3 3 6-7"/></svg> |
| @@ -186,15 +186,15 @@ | |||
| 186 | </section> | 186 | </section> |
| 187 | 187 | ||
| 188 | <section class="tour" id="tour" aria-labelledby="tour-title"> | 188 | <section class="tour" id="tour" aria-labelledby="tour-title"> |
| 189 | - <h2 class="sr-only" id="tour-title">A tour of skilly</h2> | 189 | + <h2 class="sr-only" id="tour-title">A tour of panoplie</h2> |
| 190 | <div class="tour-tabs" role="tablist" aria-label="Screens" aria-orientation="vertical"> | 190 | <div class="tour-tabs" role="tablist" aria-label="Screens" aria-orientation="vertical"> |
| 191 | <button role="tab" id="tab-project" aria-controls="shot-project" aria-selected="true"> | 191 | <button role="tab" id="tab-project" aria-controls="shot-project" aria-selected="true"> |
| 192 | <span class="tab-title">Pick what each project uses</span> | 192 | <span class="tab-title">Pick what each project uses</span> |
| 193 | - <span class="tab-desc">Add a preset or a single skill. skilly writes the files.</span> | 193 | + <span class="tab-desc">Add a preset or a single skill. panoplie writes the files.</span> |
| 194 | </button> | 194 | </button> |
| 195 | <button role="tab" id="tab-review" aria-controls="shot-review" aria-selected="false" tabindex="-1"> | 195 | <button role="tab" id="tab-review" aria-controls="shot-review" aria-selected="false" tabindex="-1"> |
| 196 | <span class="tab-title">See every change first</span> | 196 | <span class="tab-title">See every change first</span> |
| 197 | - <span class="tab-desc">Nothing is written until you apply. Edited a file by hand? skilly asks first.</span> | 197 | + <span class="tab-desc">Nothing is written until you apply. Edited a file by hand? panoplie asks first.</span> |
| 198 | </button> | 198 | </button> |
| 199 | <button role="tab" id="tab-global" aria-controls="shot-global" aria-selected="false" tabindex="-1"> | 199 | <button role="tab" id="tab-global" aria-controls="shot-global" aria-selected="false" tabindex="-1"> |
| 200 | <span class="tab-title">Slim down your global config</span> | 200 | <span class="tab-title">Slim down your global config</span> |
| @@ -214,7 +214,7 @@ | |||
| 214 | </button> | 214 | </button> |
| 215 | </div> | 215 | </div> |
| 216 | <div class="tour-shots"> | 216 | <div class="tour-shots"> |
| 217 | - <div class="frame" id="shot-project" role="tabpanel" aria-labelledby="tab-project"><img src="screenshots/project.png" width="2880" height="1800" alt="A project in skilly with its presets and items" fetchpriority="high"></div> | 217 | + <div class="frame" id="shot-project" role="tabpanel" aria-labelledby="tab-project"><img src="screenshots/project.png" width="2880" height="1800" alt="A project in panoplie with its presets and items" fetchpriority="high"></div> |
| 218 | <div class="frame" id="shot-review" role="tabpanel" aria-labelledby="tab-review"><img src="screenshots/review.png" width="2880" height="1800" alt="The review screen listing the files that will change" loading="lazy"></div> | 218 | <div class="frame" id="shot-review" role="tabpanel" aria-labelledby="tab-review"><img src="screenshots/review.png" width="2880" height="1800" alt="The review screen listing the files that will change" loading="lazy"></div> |
| 219 | <div class="frame" id="shot-global" role="tabpanel" aria-labelledby="tab-global"><img src="screenshots/global.png" width="2880" height="1800" alt="Global items with suggestions to move them to projects" loading="lazy"></div> | 219 | <div class="frame" id="shot-global" role="tabpanel" aria-labelledby="tab-global"><img src="screenshots/global.png" width="2880" height="1800" alt="Global items with suggestions to move them to projects" loading="lazy"></div> |
| 220 | <div class="frame" id="shot-catalog" role="tabpanel" aria-labelledby="tab-catalog"><img src="screenshots/catalog.png" width="2880" height="1800" alt="The catalog with a skill opened in the editor" loading="lazy"></div> | 220 | <div class="frame" id="shot-catalog" role="tabpanel" aria-labelledby="tab-catalog"><img src="screenshots/catalog.png" width="2880" height="1800" alt="The catalog with a skill opened in the editor" loading="lazy"></div> |
| @@ -243,7 +243,7 @@ | |||
| 243 | 243 | ||
| 244 | <section class="how" id="how" aria-labelledby="how-title"> | 244 | <section class="how" id="how" aria-labelledby="how-title"> |
| 245 | <h2 id="how-title">One catalog, plain files in each project</h2> | 245 | <h2 id="how-title">One catalog, plain files in each project</h2> |
| 246 | - <p class="section-lead">Your projects keep working without skilly.</p> | 246 | + <p class="section-lead">Your projects keep working without panoplie.</p> |
| 247 | 247 | ||
| 248 | <div class="flow" data-flow> | 248 | <div class="flow" data-flow> |
| 249 | <div class="panel cat"> | 249 | <div class="panel cat"> |
| @@ -311,18 +311,18 @@ | |||
| 311 | <section class="try" id="try" aria-labelledby="try-title"> | 311 | <section class="try" id="try" aria-labelledby="try-title"> |
| 312 | <h2 id="try-title">Try it</h2> | 312 | <h2 id="try-title">Try it</h2> |
| 313 | <div class="code"> | 313 | <div class="code"> |
| 314 | - <pre><code id="install-cmd">git clone https://rickub.com/hlassiege/skilly.git | 314 | + <pre><code id="install-cmd">git clone https://rickub.com/hlassiege/panoplie.git |
| 315 | -cd skilly | 315 | +cd panoplie |
| 316 | pnpm install && pnpm build && pnpm start</code></pre> | 316 | pnpm install && pnpm build && pnpm start</code></pre> |
| 317 | <button class="btn btn-line btn-sm copy" type="button" data-copy="install-cmd">Copy</button> | 317 | <button class="btn btn-line btn-sm copy" type="button" data-copy="install-cmd">Copy</button> |
| 318 | </div> | 318 | </div> |
| 319 | - <p class="note"><a href="https://rickub.com/hlassiege/skilly">Read the README</a> for everything else.</p> | 319 | + <p class="note"><a href="https://rickub.com/hlassiege/panoplie">Read the README</a> for everything else.</p> |
| 320 | </section> | 320 | </section> |
| 321 | </main> | 321 | </main> |
| 322 | 322 | ||
| 323 | <footer class="foot"> | 323 | <footer class="foot"> |
| 324 | <p>Open source, MIT. Made by <a href="https://eventuallycoding.com">Hugo Lassiège</a>.</p> | 324 | <p>Open source, MIT. Made by <a href="https://eventuallycoding.com">Hugo Lassiège</a>.</p> |
| 325 | - <a href="https://rickub.com/hlassiege/skilly">Source code</a> | 325 | + <a href="https://rickub.com/hlassiege/panoplie">Source code</a> |
| 326 | </footer> | 326 | </footer> |
| 327 | </body> | 327 | </body> |
| 328 | </html> | 328 | </html> |
modified
site/public/styles.css +1 -1 | @@ -1,4 +1,4 @@ | ||
| 1 | -/* skilly site. Tokens mirror the app (packages/ui/app/assets/css/main.css) so screenshots sit at home. */ | |
| 1 | +/* panoplie site. Tokens mirror the app (packages/ui/app/assets/css/main.css) so screenshots sit at home. */ | |
| 2 | 2 | |
| 3 | 3 | @font-face { font-family: 'Instrument Sans'; font-weight: 400; font-display: swap; src: url(fonts/instrument-sans-latin-400-normal.woff2) format('woff2'); } |
| 4 | 4 | @font-face { font-family: 'Instrument Sans'; font-weight: 500; font-display: swap; src: url(fonts/instrument-sans-latin-500-normal.woff2) format('woff2'); } |
| @@ -1,4 +1,4 @@ | |||
| 1 | -/* skilly site. Tokens mirror the app (packages/ui/app/assets/css/main.css) so screenshots sit at home. */ | 1 | +/* panoplie site. Tokens mirror the app (packages/ui/app/assets/css/main.css) so screenshots sit at home. */ |
| 2 | 2 | ||
| 3 | @font-face { font-family: 'Instrument Sans'; font-weight: 400; font-display: swap; src: url(fonts/instrument-sans-latin-400-normal.woff2) format('woff2'); } | 3 | @font-face { font-family: 'Instrument Sans'; font-weight: 400; font-display: swap; src: url(fonts/instrument-sans-latin-400-normal.woff2) format('woff2'); } |
| 4 | @font-face { font-family: 'Instrument Sans'; font-weight: 500; font-display: swap; src: url(fonts/instrument-sans-latin-500-normal.woff2) format('woff2'); } | 4 | @font-face { font-family: 'Instrument Sans'; font-weight: 500; font-display: swap; src: url(fonts/instrument-sans-latin-500-normal.woff2) format('woff2'); } |