diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 8567794..6ca6052 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -6,13 +6,13 @@ }, "metadata": { "description": "Create, build, test, and deploy AppOS plugins using the @appos.space SDK. Skills for the full plugin API, commands for scaffolding/building/deploying, and specialized agents for architecture and WebView panel implementation.", - "version": "2.0.0" + "version": "2.0.1" }, "plugins": [ { "name": "appos-dev", "description": "Create, build, test, and deploy AppOS plugins using the @appos.space SDK. Skills for the full plugin API (22 namespaces, 34 permissions, WebView panels, workspaces, menubar, smart folders), commands for scaffolding/building/deploying, and specialized agents for architecture and WebView panel implementation. Canonical reference: appos-plugin-ytdlp.", - "version": "2.0.0", + "version": "2.0.1", "author": { "name": "AppOS", "url": "https://github.com/appos" diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json deleted file mode 100644 index 72ff0e2..0000000 --- a/.claude-plugin/plugin.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "name": "appos-dev", - "description": "Create, build, test, and deploy AppOS plugins using the @appos.space SDK. Skills for the full plugin API (22 namespaces, 34 permissions, WebView panels, workspaces, menubar, smart folders), commands for scaffolding/building/deploying, and specialized agents for architecture and WebView panel implementation. Canonical reference: appos-plugin-ytdlp.", - "version": "2.0.0", - "homepage": "https://github.com/appos/appos-dev-plugin" -} diff --git a/CLAUDE.md b/CLAUDE.md index e5d891d..8a4fa0a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,31 +4,36 @@ Project instructions for Claude Code when working inside this repo. ## What this is -A Claude Code plugin that gives Claude Code the skills, commands, and agents needed to create, build, test, and deploy AppOS workspace manager plugins. Canonical reference: `~/Documents/GitHub/AppOS/appos-plugin-ytdlp` — the flagship plugin that exercises every supported SDK feature. +A Claude Code plugin that gives Claude Code the skills, commands, and agents needed to create, build, test, and deploy AppOS workspace manager plugins. Canonical references: the developer docs at https://docs.appos.space (always reachable) and the flagship plugin https://github.com/appos/appos-plugin-ytdlp (public as of AppOS launch) — the plugin that exercises every supported SDK feature. -This plugin is versioned at **2.0.0** because it was fully rewritten to target the SDK+WebView flagship pattern. The legacy ViewDescriptor-only model is still supported but is no longer the primary pattern. +This plugin is versioned at **2.x** (current: see `.claude-plugin/marketplace.json`) because it was fully rewritten to target the SDK+WebView flagship pattern. The legacy ViewDescriptor-only model is still supported but is no longer the primary pattern. ## Repository layout ``` appos-dev-plugin/ ├── .claude-plugin/ -│ ├── plugin.json # Claude Code plugin manifest -│ └── marketplace.json # Single-plugin marketplace catalog -├── commands/ -│ ├── new-plugin.md # /appos-dev:new-plugin -│ ├── build.md # /appos-dev:build -│ ├── deploy.md # /appos-dev:deploy -│ └── validate.md # /appos-dev:validate -├── agents/ -│ ├── plugin-architect.md # Designs plugin architecture from requirements -│ └── webview-panel-builder.md # Builds WebView panels end-to-end -├── skills/ -│ ├── appos-plugin-dev/ -│ │ ├── SKILL.md # Main SDK+WebView skill -│ │ └── reference/ # Full API spec, patterns, type definitions -│ └── webview-panels/ -│ └── SKILL.md # Focused WebView authoring skill +│ └── marketplace.json # Marketplace catalog — points at ./plugins/appos-dev +├── plugins/ +│ └── appos-dev/ # The actual Claude Code plugin +│ ├── commands/ +│ │ ├── new-plugin.md # /appos-dev:new-plugin +│ │ ├── build.md # /appos-dev:build +│ │ ├── deploy.md # /appos-dev:deploy +│ │ └── validate.md # /appos-dev:validate +│ ├── agents/ +│ │ ├── plugin-architect.md # Designs plugin architecture from requirements +│ │ ├── viewdescriptor-builder.md # Builds ViewDescriptor UI trees +│ │ └── webview-panel-builder.md # Builds WebView panels end-to-end +│ ├── skills/ +│ │ ├── appos-plugin-dev/ +│ │ │ ├── SKILL.md # Main SDK+WebView skill +│ │ │ └── reference/ # Full API spec, patterns, type definitions +│ │ ├── viewdescriptor-authoring/ +│ │ │ └── SKILL.md # ViewDescriptor authoring skill +│ │ └── webview-panels/ +│ │ └── SKILL.md # Focused WebView authoring skill +│ └── compiled/ # Concatenated context artifacts for the AppOS host ├── README.md └── LICENSE ``` @@ -37,14 +42,15 @@ appos-dev-plugin/ Before editing anything, be aware of these external sources of truth: -- **Flagship reference plugin**: `~/Documents/GitHub/AppOS/appos-plugin-ytdlp/` — all patterns in the skills match this plugin's implementation. When in doubt, read its source files. -- **SDK source**: `~/Documents/GitHub/AppOS/plugin-sdk/packages/plugin-types/dist/` — the current declaration-only TypeScript types. Use for reference when validating API shapes in skills or commands. -- **Host version**: read `/Applications/2Panez.app/Contents/Info.plist` → `CFBundleShortVersionString` (currently `1.7.0`). This is the host app's on-disk bundle (legacy name, do not rename); its version is what `plugin.json` `minHostVersion` is compared against, NOT the SDK package version. +- **Developer docs**: https://docs.appos.space — the canonical AppOS plugin developer documentation. +- **Flagship reference plugin**: https://github.com/appos/appos-plugin-ytdlp — all patterns in the skills match this plugin's implementation. When in doubt, read its source files, in this order: (1) a local clone if one exists (preferred); (2) the GitHub repo — public as of AppOS launch; fetch raw files from `https://raw.githubusercontent.com/appos/appos-plugin-ytdlp/main/`; (3) https://docs.appos.space — always reachable; its getting-started/first-plugin pages carry the same canonical patterns. +- **SDK source**: https://github.com/appos/plugin-sdk (`packages/plugin-types`) — the current declaration-only TypeScript types, also published on npm as `@appos.space/plugin-types`. Use for reference when validating API shapes in skills or commands. +- **Host version**: read `/Applications/AppOS.app/Contents/Info.plist` → `CFBundleShortVersionString` (currently `1.0.0`). The host version is what `plugin.json` `minHostVersion` is compared against, NOT the SDK package version. ## Conventions - **Plugin IDs**: use `space.appos.*` for flagship plugins (the ones shipped with AppOS), `com.community.*` for community plugins, other reverse-domain for private/personal plugins. -- **minHostVersion**: always default to `"1.0.0"`. The single biggest cause of "plugin installed but not appearing in Settings" is conflating the SDK version (`2.4.x`) with the host version (`1.7.x`). The `appos-plugin-dev` skill has a "minHostVersion landmine" section; keep it prominent. +- **minHostVersion**: always default to `"1.0.0"`. The single biggest cause of "plugin installed but not appearing in Settings" is conflating the SDK version (`2.4.x`) with the host version (`1.0.x`). The `appos-plugin-dev` skill has a "minHostVersion landmine" section; keep it prominent. - **tsconfig**: always include `verbatimModuleSyntax: true`. Without it, TypeScript emits broken runtime imports from the declaration-only `@appos.space/plugin-types` package. - **Entry point**: always `globalThis.activate = activate` + `globalThis.deactivate = deactivate`, never ESM `export`. ESM exports disappear inside the IIFE closure and the host can't find them. - **Parameter naming**: always `ctx`, never `pluginContext`. Matches `appos-plugin-ytdlp` and every reference plugin. @@ -52,8 +58,8 @@ Before editing anything, be aware of these external sources of truth: ## When updating skills or commands -1. Read the corresponding code in `appos-plugin-ytdlp` first — that's the ground truth. -2. If the SDK surface changed, check `~/Documents/GitHub/AppOS/plugin-sdk/packages/plugin-types/dist/` for the current type definitions. +1. Read the corresponding code in `appos-plugin-ytdlp` first — that's the ground truth (local clone preferred; see Key references for the fallback order). +2. If the SDK surface changed, check https://github.com/appos/plugin-sdk (`packages/plugin-types`) — or the published `@appos.space/plugin-types` npm package — for the current type definitions. 3. Keep examples copyable — prefer full working snippets over fragments. 4. When documenting a gotcha, include a `**Why**:` line with the root cause so future-you can judge edge cases. @@ -61,14 +67,16 @@ Before editing anything, be aware of these external sources of truth: Follow Claude Code plugin conventions: -- Commands go in `commands/*.md` with frontmatter declaring `allowed-tools` and `argument-hint` -- Agents go in `agents/*.md` with frontmatter declaring `name`, `description`, `whenToUse`, `tools` -- Skills go in `skills/{name}/SKILL.md` with frontmatter declaring `description` (include the trigger phrases that should activate it) +- Commands go in `plugins/appos-dev/commands/*.md` with frontmatter declaring `allowed-tools` and `argument-hint` +- Agents go in `plugins/appos-dev/agents/*.md` with frontmatter declaring `name`, `description`, `whenToUse`, `tools` +- Skills go in `plugins/appos-dev/skills/{name}/SKILL.md` with frontmatter declaring `description` (include the trigger phrases that should activate it) ## Testing locally +From the repo root (the plugin lives at `plugins/appos-dev`, not the repo root): + ```bash -claude --plugin-dir ~/Documents/GitHub/appos-dev-plugin/ +claude --plugin-dir ./plugins/appos-dev ``` Then verify: diff --git a/README.md b/README.md index 40dc66b..251ca38 100644 --- a/README.md +++ b/README.md @@ -22,14 +22,19 @@ v2.0 is a full rewrite targeting the SDK+WebView flagship pattern used by `appos ### From marketplace -```bash -claude /plugin marketplace add appos/appos-dev-plugin +In a Claude Code session: + +``` +/plugin marketplace add appos/appos-dev-plugin +/plugin install appos-dev@appos-dev ``` ### Local development +The plugin lives at `plugins/appos-dev` inside this repo (marketplace layout): + ```bash -claude --plugin-dir /path/to/appos-dev-plugin/ +claude --plugin-dir /path/to/appos-dev-plugin/plugins/appos-dev ``` ## Commands @@ -72,7 +77,7 @@ claude --plugin-dir /path/to/appos-dev-plugin/ - Max 2 WebView panels per plugin, 6 globally - WebView CSP blocks inline scripts/styles/handlers — everything external, ES modules only - `pipeShellToWebPanel` lives on `ctx.ui`, NOT `ctx.shell` (stale docs are wrong) -- Install path: `~/Library/Application Support/com.twopanez/plugins/{plugin-id}/` (see `/appos-dev:deploy`) +- Install path: `~/Library/Application Support/AppOS/plugins/{plugin-id}/` (see `/appos-dev:deploy`) ## License diff --git a/plugins/appos-dev/agents/plugin-architect.md b/plugins/appos-dev/agents/plugin-architect.md index b1c1d29..a61cf80 100644 --- a/plugins/appos-dev/agents/plugin-architect.md +++ b/plugins/appos-dev/agents/plugin-architect.md @@ -29,7 +29,7 @@ whenToUse: | assistant: "I'll use the plugin-architect agent to map your requirements to permissions." User needs permission mapping for their plugin. -tools: [Read, Grep, Glob, Skill] +tools: [Read, Grep, Glob, Skill, WebFetch] --- You are an AppOS plugin design specialist. You understand the full SDK surface (`@appos.space/plugin-types` + `@appos.space/plugin-utils` + `@appos.space/view-builders`) and help users architect plugins before implementation. @@ -43,7 +43,7 @@ Before responding, invoke these skills to load the current API surface and patte Also use Glob to find `**/reference/extension-api.md` and `**/reference/patterns.md` in the appos-dev plugin directory if they exist — they contain deeper API details. -The canonical flagship reference is `~/Documents/GitHub/AppOS/appos-plugin-ytdlp` — it uses every major SDK feature and should be read when the user asks "how does a real plugin do X". +The canonical flagship reference is `appos-plugin-ytdlp` (https://github.com/appos/appos-plugin-ytdlp) — it uses every major SDK feature and should be read when the user asks "how does a real plugin do X". Use a local clone if one exists; otherwise WebFetch individual files from `https://raw.githubusercontent.com/appos/appos-plugin-ytdlp/main/` (the repo is public as of AppOS launch). If neither is reachable, fall back to https://docs.appos.space — always reachable, with the same canonical patterns on its getting-started/first-plugin pages. ## Your responsibilities @@ -104,7 +104,7 @@ The plugin can render UI in two ways, and you can mix them per panel: **ViewDescriptor panel** (`ctx.ui.registerPanel` with a JSON view tree rendered by SwiftUI) - Use when: static or low-frequency lists of items, simple forms, content that should feel indistinguishable from the host's own sidebar panels, when you need native `menuActions` context menus - Canonical reference: any of the 12 community plugins in `community-plugins/plugins/` -- 13 view types: `vstack`, `hstack`, `scroll`, `list`, `text`, `label`, `image`, `badge`, `button`, `listItem`, `section`, `divider`, `spacer` +- 17 view types: `vstack`, `hstack`, `scroll`, `list`, `grid`, `text`, `label`, `image`, `remoteImage`, `badge`, `button`, `listItem`, `textField`, `progress`, `section`, `divider`, `spacer` **Help the user pick**: - "Does the UI need to update faster than once per second?" → WebView @@ -134,7 +134,7 @@ Subscribe to `ctx.lifecycle.onDependencyStatusChanged` to react to install/unins ### 5. Settings schema design -Recommend settings based on what should be user-configurable. Setting types: `string`, `enum`, `boolean`, `number`. Keep the list short — each setting is a maintenance surface. +Recommend settings based on what should be user-configurable. Setting types: `string`, `enum`, `bool`, `number` (the manifest schema's type enum is `bool` — writing `boolean` fails schema validation). Keep the list short — each setting is a maintenance surface. ### 6. Plugin ID convention @@ -144,7 +144,7 @@ Recommend settings based on what should be user-configurable. Setting types: `st ### 7. minHostVersion -**Always default to `"1.0.0"`.** Do NOT use the SDK version from `@appos.space/plugin-types` — that's a different number. The host compares `minHostVersion` against its `CFBundleShortVersionString` (currently `1.7.0`), and too-high values cause silent plugin rejection. See `appos-plugin-dev` skill → "minHostVersion landmine". +**Always default to `"1.0.0"`.** Do NOT use the SDK version from `@appos.space/plugin-types` — that's a different number. The host compares `minHostVersion` against its `CFBundleShortVersionString` (currently `1.0.0`), and too-high values cause silent plugin rejection. See `appos-plugin-dev` skill → "minHostVersion landmine". ### 8. Output format diff --git a/plugins/appos-dev/agents/viewdescriptor-builder.md b/plugins/appos-dev/agents/viewdescriptor-builder.md index 63c28f6..d8a3365 100644 --- a/plugins/appos-dev/agents/viewdescriptor-builder.md +++ b/plugins/appos-dev/agents/viewdescriptor-builder.md @@ -2,7 +2,7 @@ name: viewdescriptor-builder description: > Builds ViewDescriptor JSON trees for AppOS plugin UI. Use when creating sidebar panels, - activity views, or any plugin UI that needs ViewDescriptor trees. Specializes in all 13 + activity views, or any plugin UI that needs ViewDescriptor trees. Specializes in all 17 types, column alignment, menuActions, section/badge patterns, button hover states, and empty/loading states. Triggers on: "build a ViewDescriptor", "create sidebar UI", "design the panel view", "listItem with columns", "menuActions menu", "section with badge". @@ -37,15 +37,15 @@ You are a ViewDescriptor specialist for AppOS plugins. You build native SwiftUI ## Your knowledge Use Glob to find `**/viewdescriptor-authoring/SKILL.md` and `**/reference/patterns.md` in the appos-dev plugin directory, then read them before building: -- `SKILL.md` — All 13 ViewDescriptor types, properties, patterns +- `SKILL.md` — All 17 ViewDescriptor types, properties, patterns - `patterns.md` — Working examples from 7 plugins -## The 13 ViewDescriptor types +## The 17 ViewDescriptor types -Layout: `vstack`, `hstack`, `scroll`, `list` -Content: `text`, `label`, `image`, `badge` -Interactive: `button`, `listItem`, `section` -Structural: `divider`, `spacer` +Layout: `vstack`, `hstack`, `scroll`, `list`, `grid` +Content: `text`, `label`, `image`, `remoteImage`, `badge` +Interactive: `button`, `listItem`, `textField`, `progress` +Structural: `section`, `divider`, `spacer` ## Your responsibilities @@ -104,5 +104,5 @@ Always produce complete, runnable TypeScript code that can be pasted directly in - Badges are **strings** — always `"" + count` - Wrap outer content in `{ type: "scroll", children: [{ type: "vstack", children }] }` - Set `id` on sections for persistent collapse state -- There are exactly 13 ViewDescriptor types — no others exist +- There are exactly 17 ViewDescriptor types — no others exist - No HTML, WebView, or DOM — everything is native SwiftUI diff --git a/plugins/appos-dev/agents/webview-panel-builder.md b/plugins/appos-dev/agents/webview-panel-builder.md index 0e68ef0..7a54912 100644 --- a/plugins/appos-dev/agents/webview-panel-builder.md +++ b/plugins/appos-dev/agents/webview-panel-builder.md @@ -31,7 +31,7 @@ whenToUse: | assistant: "I'll use the webview-panel-builder agent to add correlation IDs and instance filtering." User has a multi-instance broadcast bug. -tools: [Read, Grep, Glob, Skill, Write, Edit] +tools: [Read, Grep, Glob, Skill, WebFetch, Write, Edit] --- You are a WebView panel specialist for AppOS plugins. You build rich HTML/CSS/JS panels loaded via `plugin-panel://` into WKWebView, with typed message protocols between the panel and the plugin's TypeScript main. @@ -43,7 +43,13 @@ Before building anything, invoke these skills to load the current patterns: - `webview-panels` — The full WebView authoring skill: bridge pattern, CSP rules, message protocol, pipeShellToWebPanel, throttled broadcasts, multi-instance isolation - `appos-plugin-dev` — SDK structure, permissions, build/deploy, entry point pattern -The canonical reference plugin is `~/Documents/GitHub/AppOS/appos-plugin-ytdlp`. When in doubt about any pattern, read: +The canonical reference plugin is `appos-plugin-ytdlp` (https://github.com/appos/appos-plugin-ytdlp). Locate its source in this order: + +1. **Local clone** (preferred) — Glob for `**/appos-plugin-ytdlp/plugin.json` and Read files directly. +2. **Raw GitHub fetch** — WebFetch `https://raw.githubusercontent.com/appos/appos-plugin-ytdlp/main/` (e.g. `.../main/webview/shared/bridge.js`). The repo is public as of AppOS launch; if it is not reachable, fall through to (3). +3. **Developer docs** — https://docs.appos.space (always reachable); the getting-started/first-plugin pages carry the same canonical patterns, and the `webview-panels` skill embeds the bridge. + +Never invent a pattern you could not read from one of these sources. When in doubt about any pattern, read: - `src/panels/download-panel.ts` — Message routing, throttled broadcasts, multi-instance correlation, error handling - `src/panels/library-panel.ts` — Simpler panel for comparison @@ -95,7 +101,7 @@ Use `addEventListener` inside the ES modules for all event handling. ### 3. Build the bridge (if not already present) -Copy `webview/shared/bridge.js` from `appos-plugin-ytdlp` verbatim. It provides: +Copy `webview/shared/bridge.js` from `appos-plugin-ytdlp` verbatim (local clone, raw-URL WebFetch, or the copy embedded in the `webview-panels` skill). It provides: - `bridge.send(msg)` — fire-and-forget to the plugin - `bridge.onMessage(handler)` — subscribe to protocol messages from `postToWebPanel` - `bridge.onShellChunk(handler)` — subscribe to `pipeShellToWebPanel` output diff --git a/plugins/appos-dev/commands/deploy.md b/plugins/appos-dev/commands/deploy.md index 36f40f7..c881e78 100644 --- a/plugins/appos-dev/commands/deploy.md +++ b/plugins/appos-dev/commands/deploy.md @@ -5,9 +5,7 @@ description: Deploy the current AppOS plugin to the local plugins directory # Deploy the current AppOS plugin -Deploy the built plugin to `~/Library/Application Support/com.twopanez/plugins/`. - -**Destination path note**: The host app's on-disk bundle ID is `com.twopanez` (legacy identifier). Do not change this path; the installer has not been renamed. +Deploy the built plugin to `~/Library/Application Support/AppOS/plugins/`. ## 1. Find and read plugin.json @@ -28,7 +26,7 @@ If the plugin has a `webview/` directory, also verify at least one `webview/*/in ```bash PLUGIN_ID="{id-from-plugin.json}" -INSTALL_DIR="$HOME/Library/Application Support/com.twopanez/plugins/$PLUGIN_ID" +INSTALL_DIR="$HOME/Library/Application Support/AppOS/plugins/$PLUGIN_ID" mkdir -p "$INSTALL_DIR" rsync -av --delete --delete-excluded \ @@ -74,7 +72,7 @@ Report the deployment path and next steps: ``` Deployed {plugin-name} ({plugin-id}) to: - ~/Library/Application Support/com.twopanez/plugins/{plugin-id}/ + ~/Library/Application Support/AppOS/plugins/{plugin-id}/ Files installed: - plugin.json diff --git a/plugins/appos-dev/commands/new-plugin.md b/plugins/appos-dev/commands/new-plugin.md index 8751de1..1380bd4 100644 --- a/plugins/appos-dev/commands/new-plugin.md +++ b/plugins/appos-dev/commands/new-plugin.md @@ -15,7 +15,8 @@ Create a new AppOS plugin targeting the SDK pattern (`@appos.space/plugin-types` If a plugin name was provided as an argument, use it. Otherwise ask for: - **Plugin name** (kebab-case, e.g. `file-stats`) - **One-sentence description** -- **Flagship or community?** — flagships use `space.appos.*` IDs and live under `~/Documents/GitHub/AppOS/appos-plugin-{name}/`. Community plugins use `com.community.*` IDs and live under a community-plugins repo of the user's choice. +- **Flagship or community?** — flagships (first-party, shipped with AppOS) use `space.appos.*` IDs; community plugins use `com.community.*` IDs. +- **Target directory** — where to create the project (any location works; ask if not obvious from context). - **Rendering mode** — WebView panel (rich UI, streaming progress, media), ViewDescriptor sidebar (simple lists, native feel), or both. If unsure, ask the user what the primary UI looks like. Generate the plugin ID: @@ -43,7 +44,7 @@ These skills contain the canonical APIs, file layout, and gotchas. Do not procee From the one-sentence description, decide: - **Permissions** — start minimal. `ui.sidebar` for any panel, `ui.webPanel` + `webview` for WebView panels (BOTH required), `shell.execute` + `shellCommands: [...]` for CLI wrappers, `filesystem.read`/`filesystem.write` for file work, `cache` for persistence. - **System dependencies** — CLIs to probe on startup (with `check.command`, `check.args`, `versionPattern`, `minVersion`, `installHint`, `installUrl`). -- **Settings** — `string`, `enum`, or `boolean` keys shown in the plugin settings sheet. +- **Settings** — `string`, `enum`, `bool`, or `number` keys shown in the plugin settings sheet. The manifest schema's settings type enum is `bool` — writing `boolean` fails `/appos-dev:validate` schema validation. - **Rendering mode** — confirms from step 1. Drives whether you create `webview/` or not. ## 4. Create the project directory @@ -57,9 +58,7 @@ If WebView panels are needed, also create `$TARGET/webview/{panelId}/` and `$TAR ## 5. Write package.json -**CRITICAL**: `@appos.space/plugin-types` is the declaration-only SDK. `@appos.space/plugin-utils` and `@appos.space/view-builders` are runtime packages. - -For local development against the SDK in the AppOS repo, use `file:` dependencies. Adjust the relative paths to reach `~/Documents/GitHub/AppOS/plugin-sdk/packages/*` from the target directory. +**CRITICAL**: `@appos.space/plugin-types` is the declaration-only SDK. `@appos.space/plugin-utils` and `@appos.space/view-builders` are runtime packages. All three are published on npm — depend on them by version: ```json { @@ -73,19 +72,26 @@ For local development against the SDK in the AppOS repo, use `file:` dependencie "typecheck": "tsc --noEmit" }, "devDependencies": { - "@appos.space/plugin-types": "file:../plugin-sdk/packages/plugin-types", + "@appos.space/plugin-types": "^2.4.0", "esbuild": "^0.20.0", "typescript": "^5.4.0" }, "dependencies": { - "@appos.space/plugin-utils": "file:../plugin-sdk/packages/plugin-utils", - "@appos.space/view-builders": "file:../plugin-sdk/packages/view-builders" + "@appos.space/plugin-utils": "^2.4.0", + "@appos.space/view-builders": "^2.4.0" } } ``` If the plugin doesn't use ViewDescriptor panels at all, you can drop `@appos.space/view-builders` from `dependencies`. If it doesn't need runtime helpers, drop `@appos.space/plugin-utils` too. **`@appos.space/plugin-types` stays in `devDependencies` always** — it's type-only. +
+SDK contributors only: developing against a local plugin-sdk checkout + +If you are working on the SDK itself, point the three `@appos.space/*` entries at your clone with `file:` dependencies instead of npm versions — e.g. `"@appos.space/plugin-types": "file:../plugin-sdk/packages/plugin-types"` (adjust the relative path to reach your clone's `packages/*` from the plugin directory). Re-run `npm install` after switching. Do not scaffold `file:` paths for third-party plugin authors. + +
+ ## 6. Write tsconfig.json **MANDATORY**: `verbatimModuleSyntax: true` is required because `@appos.space/plugin-types` is declaration-only. Without this flag, TypeScript emits runtime `import` statements that try to resolve a non-existent module at runtime. @@ -150,7 +156,7 @@ if (isWatch) { ## 8. Write plugin.json -**LANDMINE**: `minHostVersion` refers to the host app's `CFBundleShortVersionString` (currently `1.7.0`), NOT the `@appos.space/plugin-types` SDK version. Defaulting to the SDK version (e.g. `"2.4.0"`) will cause `DependencyResolver.swift` to silently reject the plugin before it reaches the plugins sheet. **Always default to `"1.0.0"`.** +**LANDMINE**: `minHostVersion` refers to the host app's `CFBundleShortVersionString` (currently `1.0.0`), NOT the `@appos.space/plugin-types` SDK version. Defaulting to the SDK version (e.g. `"2.4.0"`) will cause `DependencyResolver.swift` to silently reject the plugin before it reaches the plugins sheet. **Always default to `"1.0.0"`.** ```json { @@ -251,12 +257,15 @@ ctx.ui.registerWebPanel('{panelId}', { allowNavigation: false, }); -disposables.push(ctx.ui.onWebPanelMessage('{panelId}', (envelope) => { +let panelDisposed = false; +ctx.ui.onWebPanelMessage('{panelId}', (envelope) => { + if (panelDisposed) return; // handle messages from webview -})); +}); +disposables.push(() => { panelDisposed = true; }); ``` -`onWebPanelMessage` returns `void`, not a disposer — track a `disposed` flag inside your handler instead, or push a no-op cleanup. See the `webview-panels` skill → "Cleanup" section. +`onWebPanelMessage` does NOT return a disposer — never push its return value into `disposables`. Use a `disposed` flag as above. See the `webview-panels` skill → "Cleanup" section. ## 11. Build diff --git a/plugins/appos-dev/commands/validate.md b/plugins/appos-dev/commands/validate.md index e64633b..7938586 100644 --- a/plugins/appos-dev/commands/validate.md +++ b/plugins/appos-dev/commands/validate.md @@ -9,11 +9,11 @@ Run comprehensive validation on the plugin in the current directory. ## 1. Find plugin root -Locate `plugin.json` in the current directory or parent directories. +Locate `plugin.json` in the current directory or parent directories. The directory containing it is the **plugin root** — referred to as `$PLUGIN_ROOT` below. All later steps (manifest, layout, build, and audit checks) resolve paths against `$PLUGIN_ROOT`, not the current working directory, so validation works when invoked from `src/` or any other subdirectory. ## 2. Manifest validation -Read `plugin.json` and check: +Read `$PLUGIN_ROOT/plugin.json` and check: - **Required fields present**: `id`, `name`, `version`, `runtime`, `entrypoint`, `minHostVersion` - **ID format**: reverse-domain (`space.appos.*` for flagships, `com.community.*` for community) @@ -22,12 +22,37 @@ Read `plugin.json` and check: - **Version**: valid semver (MAJOR.MINOR.PATCH) - **minHostVersion**: **LANDMINE CHECK** — this must be the host app `CFBundleShortVersionString`, NOT the `@appos.space/plugin-types` SDK version. If it's set to `"2.0.0"`, `"2.1.0"`, `"2.2.0"`, `"2.3.0"`, `"2.4.0"`, or similar SDK-like values, flag it as an ERROR. Default safe value: `"1.0.0"`. Host version can be read via: ```bash - defaults read /Applications/2Panez.app/Contents/Info.plist CFBundleShortVersionString + defaults read /Applications/AppOS.app/Contents/Info.plist CFBundleShortVersionString ``` -- **Permissions**: must be from the valid set (see below) +- **Permissions**: validated by the manifest schema (next step). Do NOT hand-check against a hardcoded list — the authoritative permission set lives in the SDK's `schemas/plugin-v1.json` and grows with each SDK release. -Valid permissions (33 total, from `@appos.space/plugin-types`): -`ui.sidebar`, `ui.statusBar`, `ui.contextMenu`, `ui.notifications`, `ui.sheets`, `ui.shortcuts`, `ui.themes`, `ui.preview`, `ui.webPanel`, `ui.quickActions`, `filesystem.read`, `filesystem.write`, `filesystem.watch`, `filesystem.readAll`, `filesystem.writeAll`, `shell.execute`, `clipboard.read`, `clipboard.write`, `network`, `network.outbound`, `network.unrestricted`, `cache`, `feedback`, `feedback.confirm`, `workspaces`, `menubar`, `smartFolders`, `webview`, `keychain.plugin`, `interPlugin.declare`, `interPlugin.contribute`, `interPlugin.query`, `interPlugin.emit` +### Schema validation (authoritative) + +Validate the whole manifest against the SDK's published JSON Schema. This checks required fields, field shapes, and the full permissions enum in one step. + +**Preferred (no clone needed)** — fetch the schema from the public `appos/plugin-sdk` repo and validate with ajv: + +```bash +curl -fsSL -o /tmp/appos-plugin-v1.schema.json \ + https://raw.githubusercontent.com/appos/plugin-sdk/main/schemas/plugin-v1.json +npx --yes -p ajv-cli -p ajv-formats ajv validate \ + --spec=draft2020 -c ajv-formats \ + -s /tmp/appos-plugin-v1.schema.json -d "$PLUGIN_ROOT/plugin.json" +``` + +Exit 0 with `plugin.json valid` = pass. On failure, ajv prints the offending JSON paths — e.g., an unknown permission string fails the `permissions` items enum. + +> **Why `main` and not a pinned SDK release tag:** the schema anchor is chosen for *install-time* truth. The shipped AppOS host validates and gates permissions against its full current scope surface (`PermissionScope.allKnown` — the superset the `main` schema mirrors), so `main` matches what the host actually accepts. The `v2.4.0` tag's schema predates the core-plugin waves: it knows only 37 of the current 140 permission strings and rejects the `extensions` field entirely, so it falsely FAILS valid manifests — including the flagship `appos-plugin-ytdlp`, which declares `actions.register`, `actions.invoke`, and `notifications.emit`. (The published `@appos.space/plugin-types` npm package ships only `.d.ts` files — there is no per-release package schema to fetch.) Note the separate compile-time bound: the SDK version you compile against (`^2.4.0`) limits which *typed APIs* your TypeScript sees, not which manifest permissions the host accepts. If a future SDK `main` ever moves ahead of your installed host, cross-check `minHostVersion` guidance above — manifest validity is anchored to the host, not the npm package. + +**Alternative (plugin-sdk clone available)** — validate offline with the clone's own validator, passing your manifest path as a positional argument: + +```bash +node "$SDK_CLONE/scripts/validate-schema.mjs" "$PLUGIN_ROOT/plugin.json" +``` + +Exit 0 = pass; on failure it prints the offending JSON paths. This runs fully offline — both the schema and the validator live in the clone — with one precondition: the clone's dev dependencies (`ajv`, `ajv-formats`) must be installed. Run `npm install` in `$SDK_CLONE` once while online; if that never happened and you're offline now, use the structural fallback below instead. Do NOT substitute the `npx ajv` recipe here — `npx` resolves its packages from npm, so it is not offline-capable. + +If you're offline and have no clone, fall back to the structural checks above (required fields, ID format, minHostVersion landmine) and state in the report that the permission set could NOT be authoritatively verified. If the manifest declares `shell.execute`, verify `"shellCommands"` is present and non-empty. The AppOS sandbox blocks any command not in that list. @@ -121,7 +146,7 @@ Plugin Validation: {plugin-name} Manifest: ✓ Required fields present ✓ Valid ID format (space.appos.filestats) ✓ minHostVersion: 1.0.0 (safe) - ✓ Valid permissions + ✓ Schema validation passed (plugin-v1.json) SDK Layout: ✓ build.mjs present ✓ tsconfig verbatimModuleSyntax: true diff --git a/plugins/appos-dev/compiled/plugin-factory-context.md b/plugins/appos-dev/compiled/plugin-factory-context.md index 81b4c74..c96e7d5 100644 --- a/plugins/appos-dev/compiled/plugin-factory-context.md +++ b/plugins/appos-dev/compiled/plugin-factory-context.md @@ -116,7 +116,7 @@ The leading `;` on the globalThis assignments prevents ASI hazards when the prec | Need | API | Rendering | Notes | |---|---|---|---| -| Rich forms, streaming progress, media playback | `ctx.ui.registerWebPanel()` | WKWebView | Bundle HTML/CSS/JS under `panels//`. Use for yt-dlp-style UIs. | +| Rich forms, streaming progress, media playback | `ctx.ui.registerWebPanel()` | WKWebView | Bundle HTML/CSS/JS under `webview//`. Use for yt-dlp-style UIs. | | Lightweight sidebar (file annotations, git status, file stats) | `ctx.ui.registerPanel()` | SwiftUI via ViewDescriptor | Cheap, reactive, composes well. | | Activity bar icon + sidebar | `ctx.ui.registerActivityView()` | SwiftUI via ViewDescriptor | Use for primary feature entry points. | | Menu bar status item (with badge) | `ctx.menubar.register()` / `setBadge()` | NSStatusItem | Subscribe to `menubar.clicked` event to handle clicks. | @@ -126,7 +126,7 @@ The leading `;` on the globalThis assignments prevents ASI hazards when the prec | Keyboard shortcuts | `ctx.shortcuts.register({ commandId, keys })` | — | Must bind to an already-registered `ctx.commands.register()`. | | Persistent state (queue, library, history) | `ctx.cache.set(key, value, { persist: true })` | SQLite write-through | Default is memory-only — pass `persist: true` for durability. `cache.get()` returns deserialized values — do NOT `JSON.parse`. | | Run a CLI with streaming output | `ctx.ui.pipeShellToWebPanel(panelId, shellOpts)` | Chunks flow into the webview | **Method lives on `ctx.ui`, NOT `ctx.shell`.** Hard 120s timeout — use resume-loops for long jobs. | -| React to dependency changes | `ctx.lifecycle.onDependencyStatusChanged(fn)` | — | Host pushes status at activation. `getDependencyStatus()` / `recheckDependencies()` are types-only — do NOT call. | +| React to dependency changes | `ctx.lifecycle.onDependencyStatusChanged(fn)` | — | Host pushes status at activation. `getDependencyStatus()` reads on demand; `recheckDependencies()` re-probes (both host-wired). | ### Two WebView panels maximum per plugin @@ -152,8 +152,8 @@ The SDK defines **17 ViewDescriptor types**: `vstack`, `hstack`, `scroll`, `list ``` my-plugin/ - src/main.ts # registerWebPanel('main-panel', { htmlPath: 'panels/main/index.html' }) - panels/ + src/main.ts # registerWebPanel('main-panel', { htmlPath: 'webview/main/index.html' }) + webview/ main/ index.html # Entry point (NO inline