freddie 0.0.105 → 0.0.107

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/AGENTS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Freddie — Agent Guide
2
2
 
3
- Instructions for AI coding assistants working on Freddie.
3
+ Instructions for AI coding assistants working on Freddie. Present-tense rules only — history lives in `git log` and `CHANGELOG.md`.
4
4
 
5
5
  ## Substrate (do not reimplement)
6
6
 
@@ -8,121 +8,156 @@ Instructions for AI coding assistants working on Freddie.
8
8
  - `@mariozechner/pi-agent-core` — `Agent`, `agentLoop`, `runAgentLoop`, `streamProxy`. Wrap in xstate, do not rewrite.
9
9
  - `@mariozechner/pi-ai` — `complete`, `completeSimple`, `AssistantMessageEventStream`, `registerApiProvider`, `getModel`, `calculateCost`, `parseStreamingJson`, `isContextOverflow`. THE provider layer.
10
10
  - `@mariozechner/pi-tui` — TUI primitives (Ink-equivalent).
11
- - `floosie` v0.6.14 — `ProcessorMachine` (xstate). Use for gateway pipelines.
12
- - `anentrypoint-design` ^0.0.94 — webjsx + ripple-ui. Use for any web UI; do NOT add React. Source in C:/dev/anentrypoint-design; freddie depends on the registry build (^0.0.94). For local SDK iteration, swap to `file:../anentrypoint-design` and rebuild via `node scripts/build.mjs`.
11
+ - `floosie` — `ProcessorMachine` (xstate). Use for gateway pipelines. Compose, don't fork.
12
+ - `anentrypoint-design` — webjsx + ripple-ui. **All GUI for freddie and thebird lives here.** Source in `C:/dev/anentrypoint-design`; freddie pins from npm registry. For local SDK iteration, swap to `file:../anentrypoint-design` and rebuild via `node scripts/build.mjs`. Do NOT add React.
13
+ - `acptoapi` — THE LLM SDK (see "acptoapi is THE SDK" below).
13
14
  - `xstate` v5 — every long-lived state machine (agent turns, gateway lifecycle, approvals).
14
15
 
15
- ## Dynamic stack contract (2026-05-13)
16
+ ## Dynamic stack contract
16
17
 
17
18
  The stack is **thebird → freddie → acptoapi**. Each layer owns one concern:
18
19
 
19
20
  - **acptoapi** owns all upstream LLM/provider connectivity: HTTP/SSE to OpenAI, Anthropic, Gemini, brand providers, ACP daemons, Claude CLI. Plus chain/queue/sampler/matrix.
20
- - **freddie** owns agent-loop orchestration: tools, skills, sessions, memory. It calls *only* acptoapi for LLM access. No direct `fetch('https://api.openai.com/...')` etc. — those are migration debt to move into acptoapi (currently still present in `plugins/vision`, `plugins/image_gen`, `plugins/tts`, `plugins/transcription`, `src/agent/codex_responses_adapter.js`, `src/agent/image_gen_provider.js`, `src/agent/model-discovery.js`).
21
+ - **freddie** owns agent-loop orchestration: tools, skills, sessions, memory. Calls *only* acptoapi for LLM access. No direct `fetch('https://api.openai.com/...')`. Migration debt still present in `plugins/vision`, `plugins/image_gen`, `plugins/tts`, `plugins/transcription`, `src/agent/codex_responses_adapter.js`, `src/agent/image_gen_provider.js`, `src/agent/model-discovery.js` — when you touch one, add the matching endpoint to acptoapi and call through acptoapi.
21
22
  - **thebird** owns browser presentation: webjsx UI, pyodide hermes shell. Talks to freddie for everything LLM-related when freddie is reachable; falls back to direct acptoapi only when there is no freddie.
22
23
 
23
24
  Versioning: freddie pins `acptoapi: "latest"` so `npm install` always picks up the newest published acptoapi. Thebird vendors freddie via `scripts/sync-upstream.mjs` against upstream main. No manual version-bump churn between sibling repos.
24
25
 
25
- When you touch one of the four direct-fetch utility plugins above, the right fix is to add the corresponding endpoint to acptoapi (POST /v1/images/generations, POST /v1/audio/transcriptions, etc.) and have freddie call acptoapi instead of the upstream vendor.
26
+ ## acptoapi is THE SDK
26
27
 
27
- ## acptoapi is THE SDK (since 1.0.59 / 2026-05-13)
28
+ **Do not reimplement LLM resolution, chain fallback, sampler backoff, or matrix-aware scoring in freddie.** acptoapi is the single source of truth. `src/agent/llm_resolver.js` is a thin shim over `acptoapi.chat({model, messages, tools, queuesMap, matrixSource, onFallback, output})` that builds a comma-list model string from `[explicit, input.model, agent.model_preference, keyed buildAutoChain]` and delegates everything else.
28
29
 
29
- **Do not reimplement LLM resolution, chain fallback, sampler backoff, or matrix-aware scoring in freddie.** acptoapi is the single source of truth. `src/agent/llm_resolver.js` is now a 61-line thin shim over `acptoapi.chat({model, messages, tools, queuesMap, matrixSource, onFallback, output})`. It builds a single comma-list model string from `[explicit, input.model, agent.model_preference, keyed buildAutoChain]` and delegates everything else.
30
+ Consume top-level acptoapi exports directly (no re-export shim, no helper module): `chat`, `stream`, `chain`, `chatChain`, `streamChain`, `fallback`, `buildAutoChain`, `resolveModel`, `parseCommaList`, `splitPrefix`, `listAllModelsAndQueues`, `resolveQueue`, `listAllQueues`, `loadMatrix`, `matrixScore`, `clearMatrixCache`, `peekStatus`, `getStatus`, `isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `startSampler`, `stopSampler`, `createSampler`, `probe`, `probeModels`, `getCachedModels`, `getRunHistory`, `PROVIDER_KEYS`, `PROVIDER_DEFAULTS`.
30
31
 
31
- Consume these top-level acptoapi exports directly (no re-export shim, no helper module): `chat`, `stream`, `chain`, `chatChain`, `streamChain`, `fallback`, `buildAutoChain`, `resolveModel`, `parseCommaList`, `splitPrefix`, `listAllModelsAndQueues`, `resolveQueue`, `listAllQueues`, `loadMatrix`, `matrixScore`, `clearMatrixCache`, `peekStatus`, `getStatus`, `isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `startSampler`, `stopSampler`, `createSampler`, `probe`, `probeModels`, `getCachedModels`, `getRunHistory`, `PROVIDER_KEYS`, `PROVIDER_DEFAULTS`.
32
+ Public surface reference: `node_modules/acptoapi/AGENTS.md` "Public API unified chain SDK".
32
33
 
33
- Public surface reference: `node_modules/acptoapi/AGENTS.md` "Public API — unified chain SDK". Acceptable freddie-side adapters (cannot be deleted yet): `model-discovery.js` (claude-cli/ACP/ollama probing breadth acptoapi doesn't cover; `listKnownProviders` merges `agent.discovered_models` keys + acptoapi `PROVIDER_KEYS` + `[claude-cli,kilo,opencode,ollama]`), `model-matrix.js` (28L MATRIX_FILE path helper + `matrixUsable` predicate — freddie-side because the matrix file path is repo-local), `acptoapi-bridge.js` (HTTP daemon passthrough at FREDDIE_LLM_URL when reachable, for `claude/*` etc that need the OAuth-managed daemon). Removed 2026-05-17: `src/agent/model-sampler.js` (was 13L re-export shim — callers now import sampler funcs directly via `createRequire('acptoapi')`).
34
+ Acceptable freddie-side adapters:
35
+ - `model-discovery.js` — claude-cli/ACP/ollama probing breadth acptoapi doesn't cover. `listKnownProviders` merges `agent.discovered_models` keys + acptoapi `PROVIDER_KEYS` + `[claude-cli,kilo,opencode,ollama]`.
36
+ - `model-matrix.js` — MATRIX_FILE path helper + `matrixUsable` predicate. Freddie-side because the matrix file path is repo-local.
37
+ - `acptoapi-bridge.js` — HTTP daemon passthrough at `FREDDIE_LLM_URL` when reachable, for `claude/*` etc that need the OAuth-managed daemon.
34
38
 
35
- Matrix wired: shim passes `matrixSource: process.env.FREDDIE_MATRIX_URL || <repo>/.gm/model-availability.json` only for comma-list or `queue/<name>` model strings (single-shot omits to avoid leaking chain opts into upstream HTTP body bug fixed in acptoapi 1.0.62 buildParams/_stripChainOpts, but the conditional pass-through stays as defense-in-depth).
39
+ Sampler funcs (`isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `getStatus`, `probe`, `startSampler`, `stopSampler`, `createSampler`) come straight from `acptoapi` via `createRequire`. Backoff logic (5-step 30s→480s, createSampler factory, singleton) lives in `acptoapi/lib/sampler.js`. CJS/ESM boundary bridged via `createRequire(import.meta.url)`.
36
40
 
37
- ## Plugin architecture (2026-05-03, pre-v1, no compat shims)
41
+ Matrix wired: shim passes `matrixSource: process.env.FREDDIE_MATRIX_URL || <repo>/.gm/model-availability.json` only for comma-list or `queue/<name>` model strings; single-shot omits to avoid leaking chain opts into upstream HTTP body.
38
42
 
39
- The monolith was decomposed into a universal plugin contract. Every tool, platform, memory provider, GUI route, and core subsystem is a plugin under `plugins/<name>/`. The old paths (`src/tools/registry.js`, `src/tools/*.js`, `src/gateway/platforms/*.js`, `src/plugins/memory/*.js`) are GONE — do not reach for them.
43
+ ## LLM resolver priority
40
44
 
41
- Contract: `{ name, version?, surfaces: 'pi'|'gui'|'both', requires?: [...names], register(ctx) }` — defined in `src/host/contract.js` (39L).
42
- - PI_VERBS: tool, env, command, cron, platform, memory, skill, context, agentExt, cli
43
- - GUI_VERBS: route, page, nav, debug, api, asset
44
- - HOOK_NAMES: preToolCall, postToolCall, preLlmCall, postLlmCall, onSessionStart, onSessionEnd, onTurnStart, onTurnEnd, onMessageInbound, onMessageOutbound
45
- - Surface guard throws `plugin <name>: surface verb '<verb>' not allowed (declared surfaces=<name>)` at load
46
- - `requires` cycles throw `plugin cycle: a -> b -> a` synchronously
45
+ 1. explicit provider+key
46
+ 2. acptoapi if `/v1/models` returns 200
47
+ 3. `agent.model_preference` config array (ordered failover, sampler-gated)
48
+ 4. `sdk.buildAutoChain()` env-key scan
49
+ 5. throw
47
50
 
48
- Host: `src/host/host.js` (157L) `createHost({surfaces, configStore, env})` + `discoverPlugins(roots)`. Singleton in `src/host/index.js`: `host()`, `bootHost(extraRoots)`, `resetHostForTests()`. Roots walked: `<repo>/plugins`, `~/.freddie/plugins/`, `<cwd>/.freddie/plugins/`.
51
+ `PROVIDER_KEYS` and `PROVIDER_DEFAULTS` come from acptoapi never maintained in freddie. `sdk.chat()` returns OpenAI `{choices:[{message}]}`; `sdkChat()` adapter in llm_resolver converts to freddie's `{content, tool_calls, raw}`.
52
+
53
+ `agent.model_preference: []` in `~/.freddie/config.yaml` is an array of `{ provider, model? }` objects; `resolveCallLLM` tries each in order, skipping unavailable (sampler-gated) and marking failures with backoff.
54
+
55
+ `src/agent/acptoapi-bridge.js` `max_tokens` defaults to 4096 — never lower. 1024 silently truncates generation tasks.
56
+
57
+ `src/agent/llm_resolver.js::acpChat()` speaks the kilo ACP protocol: POST `/session` → GET `/event` (SSE) → POST `/session/<id>/message`. Streams `message.part.updated` events to assemble content; terminates on `session.idle`. **`/event` must be opened BEFORE `/message` POST or messages drop.** `ACP_BACKENDS`: kilo on `http://localhost:4780`, opencode on `http://localhost:4790`. kilo + opencode ACP backends return content only, no tool_calls — for multi-iteration tool-using loops, use OpenAI-compatible providers (mistral, openrouter, sambanova, groq).
58
+
59
+ ## Plugin architecture
60
+
61
+ Every tool, platform, memory provider, GUI route, and core subsystem is a plugin under `plugins/<name>/`. There is no `src/tools/registry.js`, `src/tools/<tool>.js`, `src/gateway/platforms/*.js`, or `src/plugins/memory/*.js` — do not reach for those paths.
62
+
63
+ Contract: `{ name, version?, surfaces: 'pi'|'gui'|'both', requires?: [...names], register(ctx) }` — defined in `src/host/contract.js`.
64
+
65
+ - PI_VERBS: `tool, env, command, cron, platform, memory, skill, context, agentExt, cli`
66
+ - GUI_VERBS: `route, page, nav, debug, api, asset`
67
+ - HOOK_NAMES: `preToolCall, postToolCall, preLlmCall, postLlmCall, onSessionStart, onSessionEnd, onTurnStart, onTurnEnd, onMessageInbound, onMessageOutbound`
68
+ - Surface guard throws `plugin <name>: surface verb '<verb>' not allowed` at load.
69
+ - `requires` cycles throw `plugin cycle: a -> b -> a` synchronously.
70
+
71
+ Host: `src/host/host.js` — `createHost({surfaces, configStore, env})` + `discoverPlugins(roots)`. Singleton in `src/host/index.js`: `host()`, `bootHost(extraRoots)`, `resetHostForTests()`. Roots walked: `<repo>/plugins`, `~/.freddie/plugins/`, `<cwd>/.freddie/plugins/`.
49
72
 
50
73
  `register(ctx)` receives `{ pi, gui, hooks, log, config, host, env }`:
51
74
  - `log` — scoped JSONL with plugin name
52
75
  - `config` — scoped under `plugins.<name>` (`get/set/all`)
53
76
  - `host` — `{plugins(), get(name)}`
54
77
 
55
- Migrated 120+ in-tree plugins: 70 tools, 27 platforms, 8 memory providers, 11 GUI dashboard plugins (`gui-sessions/tools/cron/skills/config/env/debug/chat/batch/gateway/profiles-commands-health`), 6 core plugins (`core-cli/skills/cron/commands/agent-machine/context-engine/compressor`). Tool plugins lay out as `plugins/<name>/{plugin,handler}.js` where handler exports `_tool` or `_tool0`/`_tool1` for multi-tool files; `plugin.js` calls `pi.tools.register(_tool)`.
56
-
57
- Thin shims (still resolved through host, do not bypass):
58
- - `src/plugins/manager.js` — over the host
59
- - `src/web/server.js` (23L) — iterates `host.gui.routes.list()`
60
- - `bin/freddie.js` (19L) — iterates `host.pi.cli.list()` and registers commander commands
61
- - `src/gateway/platforms.js` — `makePlatform/getPlatformAdapter/listPlatformNames` (finds adapter by `*Adapter$` name match)
62
- - `src/plugins/memory/provider.js` — host-router (`createMemoryProvider`, `listMemoryProviders`, `registerMemoryProvider`, `MemoryProvider`)
63
- - All consumers (`acp/server.js`, `acp/tools.js`, `mcp/server.js`, `agent/machine.js`, `toolsets.js`, `cli/gateway_cli.js`) resolve via `bootHost()`
78
+ Thin shims (resolved through host, do not bypass): `src/plugins/manager.js`, `src/web/server.js` (iterates `host.gui.routes.list()`), `bin/freddie.js` (iterates `host.pi.cli.list()`), `src/gateway/platforms.js` (`*Adapter$` name match), `src/plugins/memory/provider.js` (host-router).
64
79
 
65
- Witness 2026-05-03: test.js 12/12 green @ 195L (asserts `host.plugins().length>=100`, `platforms.list>=18`, `memory.list>=8`, surface guard throws, cycle throws). `node bin/freddie.js tools` shows 70. `help-all` 32 lines. 11 dashboard `/api/*` routes return 200.
80
+ ## gm-skill plugin
66
81
 
67
- **gm-skill plugin integration** — `plugins/gm-skill/plugin.js` registers ONE canonical skill named `gm-skill`. Resolution order: (1) `~/.claude/skills/gm-skill/SKILL.md` (the `bun x skills add AnEntrypoint/gm-skill -y -g` install), (2) `node_modules/gm-cc/skills/gm-skill/SKILL.md` (npm fallback). All other `gm-*` platform variants (gm-cc, gm-codex, gm-cursor, gm-jetbrains, gm-kilo, gm-oc, gm-vscode, gm-zed, gm-gc, gm-copilot-cli) are DEPRECATED — do not register them. `src/host/host_helpers.js::loadCcFromNodeModules` carries `CC_EXCLUDE = new Set(['gm-cc'])` so the gm-cc npm package is not auto-discovered as a cc-plugin (which would otherwise inject all 25 deprecated variants). test.js asserts exactly one gm-prefixed skill is registered, named `gm-skill`.
82
+ `plugins/gm-skill/plugin.js` registers ONE canonical skill named `gm-skill`. Resolution order: (1) `~/.claude/skills/gm-skill/SKILL.md`, (2) `node_modules/gm-cc/skills/gm-skill/SKILL.md`. All other `gm-*` platform variants (gm-cc, gm-codex, gm-cursor, gm-jetbrains, gm-kilo, gm-oc, gm-vscode, gm-zed, gm-gc, gm-copilot-cli) are DEPRECATED — do not register them. `src/host/host_helpers.js::loadCcFromNodeModules` carries `CC_EXCLUDE = new Set(['gm-cc'])` so the gm-cc npm package is not auto-discovered as a cc-plugin. test.js asserts exactly one gm-prefixed skill is registered, named `gm-skill`.
68
83
 
69
- ## Multi-project workspace system (2026-05-04)
84
+ ## Multi-project workspace
70
85
 
71
86
  Freddie supports multiple isolated projects, each with its own home directory and plugin set. Registry at `~/.freddie/projects.json` stores `{ active, projects: [{name, path, created_at}] }`. Default project (`~/.freddie`) is protected from deletion.
72
87
 
73
- Code:
74
- - `src/projects.js` — CRUD: `loadRegistry()`, `listProjects()`, `getActiveProject()`, `createProject({name, projectPath})`, `deleteProject(name)`, `setActiveProject(name)`, `applyActiveProjectFromRegistry()`.
75
- - `src/home.js` added `applyHomeOverride(absPath)` to set `FREDDIE_HOME` env and clear cached home.
76
- - `src/host/index.js` — `bootHost()` calls `applyActiveProjectFromRegistry()` before plugin discovery, so plugins resolve against active project root.
77
- - `plugins/gui-projects/plugin.js` — GUI plugin exposing `GET /api/projects`, `POST /api/projects` (create), `DELETE /api/projects/:name`, `POST /api/projects/active` (switch).
78
- - `src/web/app.js` `#/projects` route, project pill in topbar, full CRUD UI.
88
+ - `src/projects.js` — `loadRegistry()`, `listProjects()`, `getActiveProject()`, `createProject({name, projectPath})`, `deleteProject(name)`, `setActiveProject(name)`, `applyActiveProjectFromRegistry()`.
89
+ - `src/home.js::applyHomeOverride(absPath)` sets `FREDDIE_HOME` and clears cached home.
90
+ - `src/host/index.js::bootHost()` calls `applyActiveProjectFromRegistry()` before plugin discovery.
91
+ - `plugins/gui-projects/plugin.js` — `GET/POST/DELETE /api/projects`, `POST /api/projects/active`.
92
+
93
+ Isolation boundary: each project gets its own sessions DB, config.json, skills/, plugins/, cron.db, batches/, logs/, auth.json (all under `getFreddieHome()`). Plugins re-read paths per-request.
94
+
95
+ **Runtime switch caveat**: switching active project calls `resetHostForTests()` and clears caches but does NOT re-discover plugins in the running dashboard. UI alerts user to restart dashboard for plugin reload. New processes pick up active project automatically.
96
+
97
+ ## GUI surface (anentrypoint-design)
98
+
99
+ All web UI for freddie + thebird lives in `anentrypoint-design`. Consumers must not duplicate components inline.
79
100
 
80
- Isolation boundary: Each project gets its own sessions DB, config.json, skills/, plugins/, cron.db, batches/, logs/, auth.json (all under `getFreddieHome()`). Plugins re-read paths per-request via `getFreddieHome()`.
101
+ - **freddie dashboard** (`src/web/`) is minimal: `index.html` (importmap), `app.js` (~100L thin mount), `state.js` (HTTP client), `routes.js` (re-exports SDK's `FREDDIE_PAGES`), `server.js`. No inline components. No inline CSS beyond reset. Any new page goes into `anentrypoint-design`'s `FREDDIE_PAGES`, not into `app.js`.
102
+ - **thebird** consumes the same SDK. Bespoke windowing (`wm.js`, `launcher.js`, `shell.js`) and any context-menu / theme-toggle DOM should migrate into the SDK as reusable kits; do not extend them in thebird.
103
+ - Theme toggle: SDK owns the controller. Consumers import it; they do NOT reimplement localStorage + `prefers-color-scheme` listeners.
81
104
 
82
- **Runtime switch caveat** — Switching active project calls `resetHostForTests()` and clears caches but does NOT re-discover plugins in the running dashboard. UI alerts user to restart dashboard for plugin reload. New process auto-picks up active project via `applyActiveProjectFromRegistry()` on `bootHost()`. Gap: if user switches project then uses a plugin-registered tool before restarting, the OLD project's tool set loads. `/api/health` returns `{ freddie_home: "<active-project-path>" }` after project switch. Needs improvement: in-process plugin re-discovery on project switch.
105
+ Build: `node scripts/build.mjs` in `C:/dev/anentrypoint-design` emits `dist/247420.js` + `dist/247420.css`. Rebuild after SDK edits or `component is not a function` kills mount silently. `server.js` serves SDK from `node_modules/anentrypoint-design/dist/`.
106
+
107
+ Theme attribute scoping: `class="ds-247420"` on `<html>`, `data-theme="dark|light"` on `<body>`. Putting both on the same node breaks the descendant selector and themes do not switch.
108
+
109
+ Live page rerender: `AppState.body` is cached per navigation. Live routes (e.g. `#/chat` with SSE updates) must recompute body in `rerender()`:
110
+ ```js
111
+ if (AppState.hash === '#/chat') { Promise.resolve(PAGES['#/chat']()).then(b => { AppState.body = b; _mount() }); return }
112
+ ```
113
+ Any future live-streaming pages (cron output, traces) need the same treatment.
114
+
115
+ Inline `<script type="module">` parse errors swallow file:line in browsers. Extract the script body to a `.js` file and `node --check` it to get the exact line.
83
116
 
84
117
  ## Layout
85
118
 
86
119
  ```
87
120
  src/home.js # getFreddieHome, applyProfileOverride, applyHomeOverride
88
- src/projects.js # Multi-project registry CRUD (loadRegistry, createProject, deleteProject, setActiveProject)
121
+ src/projects.js # Multi-project registry CRUD
89
122
  src/config.js # loadConfig, saveConfigValue, DEFAULT_CONFIG, _config_version migrations
90
- src/sessions.js # better-sqlite3 + FTS5
123
+ src/sessions.js # libsql + FTS5 (async API — every callsite must await)
91
124
  src/auth.js # FileAuthStore for credentials
92
- src/tools/registry.js # tool registration + dispatch
93
- src/tools/{bash,read,write,edit,grep,todo,memory,delegate,web_search,image_gen,browser}.js
94
- src/tools/environments/{local,docker,ssh,index}.js # execution environments
95
125
  src/toolsets.js # _FREDDIE_CORE_TOOLS, getEnabledToolSchemas
96
- src/agent/machine.js # xstate turn machine
126
+ src/agent/machine.js # xstate turn machine + writeTrajectory
127
+ src/agent/llm_resolver.js # thin shim over acptoapi.chat
128
+ src/agent/acptoapi-bridge.js # HTTP passthrough to FREDDIE_LLM_URL daemon
129
+ src/agent/model-discovery.js # claude-cli/ACP/ollama discovery beyond acptoapi
130
+ src/agent/model-matrix.js # MATRIX_FILE path + matrixUsable predicate
97
131
  src/agent/pi-bridge.js # @mariozechner/pi-ai callLLM adapter
98
- src/agent/compress/{tokens,policy,prompt,prune,fallback,compressor,index}.js # context compressor
132
+ src/agent/compress/{tokens,policy,prompt,prune,fallback,compressor,index}.js
99
133
  src/commands/registry.js # CommandDef + resolveCommand + gateway/telegram/slack views
100
134
  src/commands/profile.js # profile CRUD
101
135
  src/cli/interactive.js # readline REPL, skin-aware
102
136
  src/context/engine.js # context block builders (file, skills, memory)
103
- src/cron/{scheduler,cron-parse}.js # persistent cron jobs
137
+ src/cron/{scheduler,cron-parse}.js # persistent cron jobs (async API)
104
138
  src/batch.js # parallel batch runner
105
- src/web/{server,index.html} # dashboard (express + anentrypoint-design webjsx)
139
+ src/web/{server,app,state,routes,index.html} # thin dashboard mount over SDK
106
140
  src/gateway/run.js # Gateway + hooks
107
- src/gateway/platforms/*.js # webhook + api_server + 16 functional adapters
108
141
  src/acp/server.js # JSON-RPC stdio
109
- src/plugins/manager.js # PluginManager
110
- src/plugins/memory/{provider,_index,honcho,mem0,supermemory,byterover,hindsight,holographic,openviking,retaindb}.js
142
+ src/plugins/manager.js # thin shim over host
143
+ src/plugins/memory/provider.js # host-router
111
144
  src/skills/index.js # SKILL.md loader
112
145
  src/skin/engine.js # _BUILTIN_SKINS + load/get/set
113
146
  src/observability/log.js # structured logs
114
147
  src/observability/debug.js # /debug registry
148
+ src/host/{contract,host,host_helpers,index}.js # plugin contract + discovery + singleton
149
+ plugins/<name>/{plugin,handler}.js # ~150 plugins: tools, platforms, memory, gui, core
115
150
  skills/ # bundled skill bundles (creative/, software-development/, ops/, data/, planning/)
116
- website/ # flatspace docs site: flatspace.config.mjs + theme.mjs + content/pages/*.yaml + docs/ (build output)
117
- bin/freddie.js # commander CLI: tools, skills, profile, skin, sessions, search, gateway, acp, run, cron, batch, dashboard, help-all
151
+ website/ # flatspace docs site: flatspace.config.mjs + theme.mjs + content/pages/*.yaml
152
+ bin/freddie.js # commander CLI: tools, skills, profile, skin, sessions, search, gateway, acp, run, cron, batch, dashboard, help-all
118
153
  ```
119
154
 
120
155
  ## Adding a tool
121
156
 
122
- Tools are now plugins. Create `plugins/<name>/plugin.js` + `plugins/<name>/handler.js`:
157
+ Tools are plugins. Create `plugins/<name>/plugin.js` + `plugins/<name>/handler.js`:
123
158
 
124
159
  ```js
125
- // plugins/my-tool/handler.js
160
+ // handler.js
126
161
  export const _tool = {
127
162
  name: 'my_tool',
128
163
  toolset: 'core',
@@ -132,7 +167,7 @@ export const _tool = {
132
167
  requiresEnv: ['MY_KEY'],
133
168
  }
134
169
 
135
- // plugins/my-tool/plugin.js
170
+ // plugin.js
136
171
  import { _tool } from './handler.js'
137
172
  export default {
138
173
  name: 'my-tool',
@@ -151,7 +186,7 @@ Add a `CommandDef` to `COMMAND_REGISTRY` in `src/commands/registry.js`:
151
186
  { name: 'mycmd', description: '…', category: 'Session', aliases: ['mc'], args_hint: '[arg]' }
152
187
  ```
153
188
 
154
- Dispatch happens against the canonical name resolved via `resolveCommand()`. Gateway/telegram/slack views derive automatically.
189
+ Dispatch resolves against the canonical name via `resolveCommand()`. Gateway/telegram/slack views derive automatically.
155
190
 
156
191
  ## Adding a gateway platform
157
192
 
@@ -174,7 +209,7 @@ export default {
174
209
  }
175
210
  ```
176
211
 
177
- `makePlatform('myname', opts)` (from `src/gateway/platforms.js`) instantiates the adapter via `*Adapter$` name match.
212
+ `makePlatform('myname', opts)` in `src/gateway/platforms.js` instantiates the adapter via `*Adapter$` name match.
178
213
 
179
214
  ## Profile-safe code
180
215
 
@@ -188,24 +223,23 @@ Slash commands that mutate system-prompt state default to deferred invalidation;
188
223
 
189
224
  ## Testing
190
225
 
191
- One `test.js` at project root. ≤200 lines. Plain assertions, real data, real services. No mocks. No fixtures. No `tests/` dir. New behavior = extend `test.js`, not a new test file.
226
+ One `test.js` at project root. ≤200 lines. Plain assertions, real data, real services. No mocks. No fixtures. No `tests/` dir. New behavior extends `test.js`, not a new test file.
227
+
228
+ test.js can pass while the CLI is broken — exercise every cli verb that wraps async-API modules (sessions, cron) explicitly, or smoke `node bin/freddie.js <verb>` after changes.
229
+
230
+ On Windows, test.js must call `closeDb()` and log-stream `closeAll()` before exit, otherwise libuv handle teardown crashes the process.
192
231
 
193
232
  ## Substrate gotchas
194
233
 
195
- - `pi-coding-agent` ships a photon-rs wasm; install needs network. Verified working on Windows.
234
+ - `pi-coding-agent` ships a photon-rs wasm; install needs network.
196
235
  - `pi-ai` reads provider keys via `findEnvKeys` / `getEnvApiKey`. Match its env var names (`ANTHROPIC_API_KEY`, etc.).
197
- - `floosie.ProcessorMachine` is an xstate machine. Compose, don't fork.
198
- - **Browser inline `<script type="module">` syntax errors** — When a pageerror reports "missing ) after argument list" with no file:line info, extract the script body to a separate `.js` file and run `node --check path/to/file.js`. Browsers swallow line numbers for inline modules; node's V8 parser prints exact line. Essential for debugging unbalanced parens in webjsx-style nested `h()` calls. (Confirmed 2026-04-30: freddie dashboard app.js, line 133.)
199
- - **src/web/app.js 200-line policy violation** File is 548 lines, violating gm hard cap (2. over). Only file in 283-file codebase over limit. Likely waived intentionally or is drift to fix. When touching app.js, prefer splitting into `{app,routes,components,state}.js` over expanding further. Do not add 50+ more lines without addressing the split.
200
- - **libsql async debt class** `src/sessions.js` (listSessions/search/getMessages/createSession/appendMessage) and `src/cron/scheduler.js` (listJobs/createJob/cancelJob/deleteJob) are async after the libsql migration. Sync callsites silently wrap each call in a Promise that rejects on iteration, surfacing as `TypeError: ... is not iterable` via `node bin/freddie.js sessions` or `freddie cron list`. Rule: every call into those modules must be awaited; tool ACTIONS inner functions async + handler awaits dispatched fn. Fixed 2026-05-03 across bin/freddie.js, src/web/server.js, src/cli/dump.js, src/cli/status.js, src/tools/session_search.js, src/tools/cronjob.js, src/acp/session.js. test.js can pass while CLI is broken — exercise the cli verb in test.js or smoke `node bin/freddie.js <verb>` after changes.
201
- - **Bulk-rename: git grep is case-sensitive on literal patterns** — `git grep -lI <name>` only matches lowercase. For case-variant sweep during rename refactors, use `git grep -liI -e <lower> -e <Title> -e <UPPER>` (per-pattern `-i` requires `-e` form). Single-form check is a false-clean trap.
202
- - **codeinsight detector limits — regex-only** (2026-05-12) `🔐 hardcoded secrets` and `🔐 SQL injection` flags are pure regex matches on identifier substrings, not value or AST analysis. Confirmed false positives across this codebase: env-var names like `DAYTONA_API_KEY` in `process.env.X` references; function param names like `secret` in HMAC helpers; URL query keys like `?appkey=&appsecret=` (DingTalk API spec); error-message string literals containing `_API_KEY`; HTTP `DELETE` URL paths flagged as SQL `DELETE FROM`. Treat hits as starting points, not findings — always read the actual line. Recurrence tell: if the flagged line is a `process.env.X` reference, a `fetch(...)` URL, or a function-signature parameter name, it's almost certainly an FP.
203
- - **codeinsight orphan detector blind spots** (2026-05-12) Misses three reachability paths: (1) `await import('./path/' + variable + '.js')` dynamic strings (test.js line 143 enumerates 10 agent adapters this way); (2) plugin auto-discovery walking `plugins/<dir>/plugin.js` from `discoverPlugins()` in `src/host/host.js`; (3) HTTP-served static files like `src/web/app.js` referenced only from `src/web/index.html`. Real audit needs: scan dynamic-import strings in test.js, exempt plugin/handler files, exempt browser-served paths. 2026-05-12 cleanup: 19 confirmed-dead files deleted (zero references anywhere) `src/cli/{mcp_config,auth_commands,voice,tips,skills_config,env_loader,plugins_cmd}.js` and `src/agent/{onboarding,skill_preprocessing,skill_utils,subdirectory_hints,lmstudio_reasoning,manual_compression_feedback,memory_manager,insights,prompt_builder,shell_hooks,moonshot_schema,copilot_acp_client}.js`. All were pre-plugin-migration CLI files no longer wired.
204
- - **createHost decomposition** (2026-05-12) — `src/host/host.js` was 197L with a 111L `createHost` body. Refactored: helper factories `makePi`, `makeGui`, `makeCcHooks`, `makeHooksRegistry`, `makeCcLoaders` (+ `reg`, `guard`, `scopedCfg`, `nullStore`) extracted to sibling `src/host/host_helpers.js` (152L). host.js shrinks to 64L; createHost body ~24L. Both well under 200L cap. Public API unchanged: `import { createHost, discoverPlugins } from './host/host.js'` still works. test.js 12/12 green post-refactor.
205
- - **freddie exec command Windows invocation** — `plugins/core-cli/plugin.js` registers the `exec` command (commit e5fb1b7) for non-interactive scripted use. Correct invocation on Windows: `bun run bin/freddie.js exec --prompt "..."`. Do NOT use `bun x freddie` — it hangs on Windows due to npm registry fetch timeouts. The command takes `--prompt` (required), `--model` (default ''), `--timeout` (default 60000ms) and is the validated entry point for CI pipelines.
206
- - **acptoapi-bridge max_tokens silent truncation** — `src/agent/acptoapi-bridge.js` line 20 controls max_tokens passed to the LLM. Prior to commit e5fb1b7, this was set to 1024, which silently truncated responses on generation tasks. Raised to 4096 to prevent hidden content loss. If generation output appears incomplete, verify max_tokens is 4096 or higher.
207
- - **GitHub Actions deploy-pages@v5 duplicate artifact rejection** — When using `actions/deploy-pages@v5` in a workflow, the action rejects if 2+ artifacts are named "github-pages" (e.g. from a previous failed run re-uploaded by `gh run rerun --failed`). Symptom: deployment step fails with "artifact with name github-pages already exists" or similar. Fix: trigger a fresh run via empty commit instead of rerunning the failed deploy step. `gh run rerun` can silently re-upload transient failures; avoid for deploy failures in particular.
208
- - **Rebase regression trap — ROOT FIX applied 2026-05-04** — After `git pull --rebase` following a rejected push, a CI auto-bump commit on remote (based on pre-fix tree) can silently revert local fixes. The `anentrypoint-design: file:../anentrypoint-design → ^0.0.40` fix in commit `d469d25` was reverted this way. Underlying cause: `.github/workflows/restore-package.cjs` rewrote the dep back to `file:../anentrypoint-design` after every release, and `publish.yml` ran `git checkout -- package-lock.json`, discarding lockfile re-sync. **ROOT FIX (2026-05-04)**: restore-package.cjs now does `npm view anentrypoint-design version` and pins `^<latest>` instead; publish.yml runs `npm install --package-lock-only` to keep lockfile valid. Verification: after any rebase, check `package.json` + `package-lock.json` match expected values. Sanity check: `git show HEAD:package.json | node -e "const d=JSON.parse(require('fs').readFileSync('/dev/stdin','utf8')); console.log(d.dependencies['anentrypoint-design'])"`. **Recurrence tell** — if pages CI fails with "lock file's anentrypoint-design@X.Y.Z does not satisfy anentrypoint-design@" (empty version = file: dep), do not manually re-pin in package.json forever; audit `.github/workflows/restore-package.cjs` and `publish.yml` for reversion.
236
+ - **libsql async debt**: every call into `src/sessions.js` (listSessions/search/getMessages/createSession/appendMessage) and `src/cron/scheduler.js` (listJobs/createJob/cancelJob/deleteJob) must be `await`ed. Sync callsites silently wrap each call in a Promise that rejects on iteration, surfacing as `TypeError: ... is not iterable`. Tool ACTIONS inner functions are async; handlers await dispatched fn.
237
+ - **Bulk-rename: git grep is case-sensitive on literal patterns**: `git grep -lI <name>` only matches lowercase. For case-variant sweep, use `git grep -liI -e <lower> -e <Title> -e <UPPER>`. Single-form check is a false-clean trap.
238
+ - **codeinsight `🔐 hardcoded secrets` / `🔐 SQL injection` are regex-only**, not value/AST. False positives: env-var names like `DAYTONA_API_KEY` in `process.env.X` references; function param names like `secret` in HMAC helpers; URL query keys like `?appkey=&appsecret=`; HTTP `DELETE` URL paths flagged as SQL `DELETE FROM`. Always read the actual line before treating as a finding.
239
+ - **codeinsight orphan detector misses three reachability paths**: (1) `await import('./path/' + variable + '.js')` dynamic strings (test.js enumerates 10 agent adapters this way); (2) plugin auto-discovery walking `plugins/<dir>/plugin.js` from `discoverPlugins()` in `src/host/host.js`; (3) HTTP-served static files like `src/web/app.js` referenced only from `src/web/index.html`. Exempt these before deleting "dead" files.
240
+ - **freddie exec Windows invocation**: `bun run bin/freddie.js exec --prompt "..."`. Do NOT use `bun x freddie` hangs on Windows from npm registry fetch timeouts. Args: `--prompt` (required), `--model` (default ''), `--timeout` (default 60000ms). Validated CI entry point.
241
+ - **GitHub Actions `deploy-pages@v5`**: rejects if 2+ artifacts named "github-pages" exist. `gh run rerun --failed` can silently re-upload transients; trigger a fresh run via empty commit instead.
242
+ - **Rebase regression trap**: after `git pull --rebase` following a rejected push, a CI auto-bump commit on remote can revert local fixes. `.github/workflows/restore-package.cjs` pins anentrypoint-design via `npm view` + `^<latest>`; `publish.yml` runs `npm install --package-lock-only` to keep lockfile valid. Recurrence tell: if pages CI fails with `lock file's anentrypoint-design@X.Y.Z does not satisfy anentrypoint-design@` (empty version = file: dep), audit `restore-package.cjs` and `publish.yml` for reversion do not manually re-pin in `package.json` forever.
209
243
 
210
244
  ## Subsystem guide
211
245
 
@@ -213,126 +247,57 @@ One `test.js` at project root. ≤200 lines. Plain assertions, real data, real s
213
247
  |---|---|
214
248
  | Agent loop | `src/agent/machine.js` (xstate) + `@mariozechner/pi-agent-core` |
215
249
  | CLI entry | `bin/freddie.js` (commander) + pi-coding-agent InteractiveMode |
216
- | Tool registry | `src/tools/registry.js` + `src/tools/{bash,read,write,edit,grep}.js` |
250
+ | Tools | `plugins/<name>/{plugin,handler}.js` (no `src/tools/`) |
217
251
  | Toolsets | `src/toolsets.js` |
218
- | Session store | `src/sessions.js` (better-sqlite3 + FTS5) |
252
+ | Session store | `src/sessions.js` (libsql + FTS5, async API) |
219
253
  | Home + profiles | `src/home.js` |
220
254
  | Multi-project registry | `src/projects.js` (isolated FREDDIE_HOME per project) |
221
255
  | Structured logging | `src/observability/log.js` |
222
256
  | Config | `src/config.js` |
223
257
  | Commands | `src/commands/registry.js` |
224
258
  | Skin engine | `src/skin/engine.js` |
225
- | Gateway + platforms | `src/gateway/run.js` + `src/gateway/platforms/*.js` |
259
+ | Gateway + platforms | `src/gateway/run.js` + `plugins/platform-*/` |
226
260
  | ACP (JSON-RPC stdio) | `src/acp/server.js` |
227
261
  | TUI | substrate (`pi-tui` + pi-coding-agent) |
228
- | Plugins + memory | `src/plugins/manager.js` + `src/plugins/memory/provider.js` |
262
+ | Plugins + memory | `src/plugins/manager.js` + `src/plugins/memory/provider.js` + `plugins/memory-*/` |
229
263
  | Skills loader | `src/skills/index.js` — content drops into `~/.freddie/skills/` |
230
264
  | Context compressor | `src/agent/compress/{tokens,policy,prompt,prune,fallback,compressor,index}.js` |
231
265
  | Documentation site | `website/` (flatspace + content/pages/*.yaml + theme.mjs) |
232
- | Cron scheduler | `src/cron/{scheduler,cron-parse}.js` |
266
+ | Cron scheduler | `src/cron/{scheduler,cron-parse}.js` (async API) |
233
267
  | Batch runner | `src/batch.js` |
234
- | Execution environments | `src/tools/environments/{local,docker,ssh}.js` (modal/daytona/singularity = explicit residual) |
235
- | Dashboard | `src/web/{server,index.html}` (anentrypoint-design webjsx) |
268
+ | Execution environments | `src/tools/environments/{local,docker,ssh}.js` (modal/daytona/singularity are explicit residual) |
269
+ | Dashboard | `src/web/{server,app,state,routes,index.html}` — thin mount over `anentrypoint-design` SDK |
236
270
  | Auth store | `src/auth.js` (FileAuthStore) + pi-ai key resolution |
237
271
  | Context engine | `src/context/engine.js` |
238
- | Browser tool | `src/tools/browser.js` (puppeteer-core, lazy) |
239
- | Image gen | `src/tools/image_gen.js` (openai/replicate) |
240
- | Web search | `src/tools/web_search.js` (DDG/SerpAPI) |
241
- | Todo | `src/tools/todo.js` |
242
- | Memory tool | `src/tools/memory.js` |
243
- | Delegate | `src/tools/delegate.js` |
244
- | Bundled skills | `skills/` (5 categories, 12 SKILL.md placeholders) |
245
- | Integration tests | one `test.js` at root per gm policy |
272
+ | Browser tool | `plugins/browser/` (puppeteer-core, lazy) |
273
+ | LLM resolver | `src/agent/llm_resolver.js` (thin shim over `acptoapi.chat`) |
274
+ | Bundled skills | `skills/` (5 categories) |
275
+ | Integration tests | one `test.js` at root |
246
276
 
247
277
  ## Cross-project Rust gotchas
248
278
 
249
- - **rs-plugkit exec utility verbs** (2026-04-30) — The plugkit.exe binary advertises `exec:status`, `exec:close`, `exec:sleep` in hook help, but the Cmd enum was missing Status/Close/Sleep variants. Fix applied to c:\dev\rs-plugkit\src\main.rs; awaiting CI rebuild. Until rebuilt: use `exec:wait <secs>` for waits, read task output files directly via fs.readFileSync instead of exec:status.
250
- - **rs-exec timeout alias** Both `--timeout` (long-form) and `--timeout-ms` (plugin convention) are accepted due to alias added to c:\dev\rs-exec\src\main.rs. Both Cmd::Exec and Cmd::Bash support either form.
251
-
279
+ - **rs-plugkit exec utility verbs**: binary advertises `exec:status`, `exec:close`, `exec:sleep` in hook help. If the Cmd enum is missing variants, use `exec:wait <secs>` for waits and read task output files via `fs.readFileSync` instead of `exec:status`.
280
+ - **rs-exec timeout alias**: both `--timeout` (long-form) and `--timeout-ms` (plugin convention) are accepted on `Cmd::Exec` and `Cmd::Bash`.
252
281
 
253
282
  ## Plugsdk integration
254
283
 
255
- - **plugsdk peerDependencies zod conflict** (2026-05-03) plugsdk v1.0.6 declared `peerDependencies: { zod: "^3.23.0" }`, causing ERESOLVE when freddie installs with zod@^4.0.0. Fixed in plugsdk v1.0.7 by relaxing peer to `^3.23.0 || ^4.0.0`. Freddie now pins plugsdk@^1.0.7 (currently 1.0.8 on npm registry).
256
- - **plugsdk package-lock.json symlink blockage** (2026-05-03) freddie's package-lock.json contained a stale symlink entry from a prior `file:` dependency: `"resolved": "../plugsdk", "link": true`. This blocked `npm ci` in CI (non-registry installs fail when symlink target is missing or differs). Fix: removed the symlink entry, ran `npm install` to sync lockfile, then committed. Always install plugsdk from registry, not via file: dep.
257
- - **plugsdk auto-publish workflow** — plugsdk publishes automatically to npm registry on push to main branch. Current version 1.0.8. freddie's contract.js re-exports `piAdapter`, `HookType`, `allowResult`, `blockResult`, `modifyResult` from plugsdk + uses `HookType` constants in `FREDDIE_TO_SDK_HOOK` mapping.
258
- ## Integration test status (2026-04-30)
259
-
260
- All 21 named integration tests in `test.js` pass (exit 0). Subsystem coverage:
261
- - agent loop, CLI, gateway, plugins, skills, sessions, cron, batch, dashboard, ACP, web, context-engine, compressor, auth, observability
262
-
263
- ## Windows libuv cleanup caveat
264
-
265
- `test.js` adds explicit cleanup hooks before exit (`closeDb()`, `closeAll()` for log streams) to prevent libuv handle-teardown crash on Windows. Without these, exit hangs or returns non-zero. Critical for stability on Windows hosts.
284
+ - plugsdk publishes automatically to npm registry on push to main.
285
+ - Freddie installs plugsdk from registry, NOT via `file:` dep a stale `"link": true` entry in package-lock.json blocks `npm ci`.
286
+ - `src/host/contract.js` re-exports `piAdapter`, `HookType`, `allowResult`, `blockResult`, `modifyResult` from plugsdk and uses `HookType` constants in `FREDDIE_TO_SDK_HOOK` mapping.
266
287
 
267
- ## LLM backends and acptoapi
288
+ ## opencode CLI shim
268
289
 
269
- - **acptoapi bridge** Integrated at `src/agent/acptoapi-bridge.js` + `src/agent/llm_resolver.js` (commit 5f55f1e). Localhost API (default port 4800) converting OpenAI/Anthropic SDK calls to multiple backends: Kilo Code, opencode, Claude CLI, Anthropic API, Gemini, Ollama, Bedrock. Endpoint `/v1/chat/completions`, OpenAI-compatible, accepts `Bearer none` auth.
270
- - **acptoapi dep pattern** (2026-05-12) — `package.json` now pins `"acptoapi": "^1.0.55"` from the npm registry (CI auto-bumps on each acptoapi push; restore-package.cjs ROOT FIX prevents file: regressions). For local SDK iteration, swap to `file:../acptoapi` temporarily. CJS/ESM boundary bridged via `createRequire(import.meta.url)` in freddie ESM files that import acptoapi CJS exports.
271
- - **LLM resolver priority** (2026-05-10) — (1) explicit provider+key, (2) acptoapi if `/v1/models` returns 200, (3) `agent.model_preference` config array (ordered failover, sampler-gated), (4) `sdk.buildAutoChain()` env-key scan, (5) throw. `PROVIDER_KEYS` and `PROVIDER_DEFAULTS` imported from `acptoapi` — not maintained in freddie. `sdk.chat()` returns OpenAI `{choices:[{message}]}` format; `sdkChat()` adapter in llm_resolver converts to freddie's `{content, tool_calls, raw}`.
272
- - **Model sampler — direct acptoapi import** (2026-05-17) — `src/agent/model-sampler.js` was deleted as redundant. Sampler funcs (`isAvailable`, `markFailed`, `markOk`, `resetAvailability`, `getStatus`, `probe`, `startSampler`, `stopSampler`, `createSampler`) come straight from `acptoapi` via `createRequire`. Backoff logic (5-step 30s→480s, createSampler factory, singleton) lives in `c:\dev\acptoapi\lib\sampler.js`.
273
- - **model_preference config key** (2026-05-10) — `agent.model_preference: []` in `~/.freddie/config.yaml`. Array of `{ provider, model? }` objects; `resolveCallLLM` tries each in order, skipping unavailable (sampler-gated) and marking failures with backoff. Config v2 migration adds the key on upgrade from v1.
274
- - **acptoapi Claude backend verified** (2026-05-03) — Live agent loop working: start acptoapi server `node bin/agentapi.js --port 4800` (c:\dev\acptoapi), then set `FREDDIE_LLM_URL=http://localhost:4800/v1 + FREDDIE_LLM_MODEL=claude/haiku`. Model prefix `claude/` routes to Claude CLI subprocess. freddie test.js 12/12 green confirming integration production-ready. Dashboard `/api/chat` and `/api/batch` are POST-only; GET returns 404 (correct).
290
+ On Windows, use the npm install: `npm install -g opencode-ai` `C:\Users\user\AppData\Roaming\npm\opencode.cmd`. The bun-installed shim (`C:\Users\user\.bun\bin\opencode.exe`) is broken its wrapper looks for `C:\Users\user\node_modules\opencode-ai\bin\opencode` and fails with `MODULE_NOT_FOUND`. Do NOT `bun install -g opencode-ai`.
275
291
 
276
- ## Pre-rename validation snapshot (2026-05-03)
292
+ Start ACP daemon: `& 'C:\Users\user\AppData\Roaming\npm\opencode.cmd' serve --port 4790 --hostname 127.0.0.1`. Verify via `GET http://127.0.0.1:4790/` returning 200. `OPENCODE_SERVER_PASSWORD is not set` warning is harmless for localhost.
277
293
 
278
- All 12 test.js named groups passing: home+config+skin, sessions+FTS5, tools+toolsets, agent-machine, gateway+platforms+hooks, acp-full, plugins+memory, profiles+observability+auth+env+context+cron+batch+slash+skills, utils+time+redact+model-meta+agent-helpers, mcp+swe+distributions+account+credpool, compressor+trajectory, env+pi+cli+tui+setup+website+helpers. CLI boots (`node bin/freddie.js --version` → 0.1.0), tools list 25+ across core/browse/creative, commander 14 commands. 284 source files, test.js 198/200 lines, pkg.version 0.0.39 but bin reports 0.1.0. Node_modules installed, lockfile present. Baseline established before rename; re-run test.js post-rename to isolate rename-induced failures.
294
+ ## scripts/sync-upstream.mjs
279
295
 
280
- ## Learning audit
296
+ `node scripts/sync-upstream.mjs [--dry-run] [pkg ...]` bumps sibling dep entries (plugsdk, acptoapi, anentrypoint-design, gm-cc) in `package.json` to `^<latest>` from npm registry, then runs `npm install --package-lock-only`. Skips `file:` deps. Wired into `.github/workflows/sync-upstream.yml` (weekly cron + workflow_dispatch); opens a PR via `peter-evans/create-pull-request@v6` on changes.
281
297
 
282
- - 2026-05-04 (session 1): Multi-project workspace system documented. Registry, CRUD ops, isolation boundaries, GUI plugin, and runtime plugin switch caveat added. 4 facts ingested to rs-learn: project/freddie-multi-project-registry, reference/freddie-projects-module, reference/freddie-gui-projects-endpoints, feedback/freddie-project-switch-reload-limitation. Reach check passed (in-reach). AGENTS.md updated with new subsystem section + Layout entries + subsystem guide row.
283
- - 2026-05-04 (session 2): Audit cycle — 5 queries fired (pi-ai env keys, profile safe paths, libsql async debt, browser syntax errors, plugin architecture contract). rs-learn store still returning "No recall results" or off-topic trajectory entries. All 5 recalls failed. 0 items migrated; all AGENTS.md facts retained (safe default). Ingest path confirmed live (4 facts accepted), but retrieval side empty. Likely requires backend indexing rebuild or cross-session propagation.
284
- - 2026-05-01: 5 items queried (pi-ai keys, profile paths, cache safety, floosie composition, browser errors); rs-learn store unavailable (exec:recall returned no results). 0 items migrated. New facts (anentrypoint-design build, dashboard live-rerender caveat, libuv spawn caveat) ingested directly into rs-learn; audit will retry in future sessions.
285
- - 2026-05-01 (session 2): 5 items queried (pi-ai env keys, profile safe paths, cache safety, floosie composition, browser syntax errors). rs-learn store still empty. 0 items migrated. Refined anentrypoint-design source/dist skew entry in AGENTS.md to include silent-failure pageerror diagnostic. New fact `reference/anentrypoint-design-dist-rebuild` ingested.
286
- - 2026-05-03: Pre-rename validation snapshot recorded (all 12 test.js groups, CLI, tools, 284 files, version drift). Baseline stored to isolate post-rename regressions.
287
- - 2026-05-03 (session 2): Ingested feedback/app-js-size-violation (src/web/app.js 548L violation) into AGENTS.md Substrate gotchas. rs-learn store unavailable (exec:memorize missing binary). 0 migration audit items queried. 1 new fact added.
288
- - 2026-05-03 (session 3): Added Website theme + YAML caveats section (3 items: structured-YAML rendering, YAML colon-space trap, SSR innerHTML injection). rs-learn store still unavailable (exec:memorize → exit 127, command not found). 0 migration audit items queried. 3 new facts added to AGENTS.md only.
289
- - 2026-05-03 (session 3): Ingested libsql-async-debt-class into AGENTS.md Substrate gotchas (sessions.js + cron/scheduler.js async callsites; silent TypeError class; test.js passes while CLI broken). rs-learn store still unavailable (exec:memorize/exec:recall not on PATH). 0 migration audit items queried. 1 new fact added.
290
- - 2026-05-03 (session 4): Plugin-architecture decomposition recorded — added "Plugin architecture" section before Layout, rewrote "Adding a tool" + "Adding a gateway platform" for plugins/<name>/{plugin,handler}.js shape. Ingested 6 facts to rs-learn (project/freddie-plugin-architecture, reference/freddie-host-contract, reference/freddie-plugin-ctx, project/freddie-migrated-subsystems, reference/freddie-thin-shims, project/freddie-plugin-witness). Audit: 5 queries fired (pi-ai env keys, profile safe paths, libsql async debt, browser inline module errors, yaml colon space trap, plus self-test on freddie-plugin-architecture) — all returned "No recall results". rs-learn ingest path live but retrieval side empty for this session (likely needs learn-build propagation). 0 items migrated; AGENTS.md items retained.
291
- - 2026-05-04: Recorded freddie publish workflow root fix — `.github/workflows/restore-package.cjs` now pins anentrypoint-design via `npm view` + version (`^<latest>`) instead of file: dep; `publish.yml` runs `npm install --package-lock-only` to sync lockfile. Updated "Rebase regression trap" entry with detailed causation + recurrence tell. Added new "GitHub Actions deploy-pages@v5 duplicate artifact rejection" caveat (rerun --failed silently re-uploads; trigger fresh run instead). rs-learn store still unavailable. 0 items migrated; 2 facts added/refined in AGENTS.md.
292
- - 2026-05-10: 15-provider LLM resolver expansion. Added model-sampler.js (backoff), PROVIDER_KEYS/DEFAULTS expansion, model_preference config key, config v2 migration. Updated "LLM resolver priority" + "15-provider support" + "Model sampler" + "model_preference" entries in AGENTS.md. test.js 12/12 green at exactly 200 lines. rs-learn store still unavailable. 4 new facts added to AGENTS.md.
293
- - 2026-05-17 (continuation): +2 memorizes for uncommitted working-tree state — freddie-llm-resolver-anthropic-fallback-removed (project) captures the ~95L deletion of @anthropic-ai/sdk fallback in src/agent/llm_resolver.js (aligns with 'acptoapi is THE SDK' policy); freddie-stray-test-files-policy-violation (feedback) flags untracked src/agent/acptoapi-provider.js + test-acptoapi.js as violating the 'one test.js at root' rule. Total session exfiltration: 20 KV keys.
294
- - 2026-05-17: AGENTS.md exfiltration to learning store. 18 memorize dispatches landed (KV keys mem-1779041362557 through mem-1779041487836, 21KB total) covering: substrate stack, dynamic stack contract, acptoapi SDK surface, plugin architecture, multi-project workspace, 3 gotcha batches (browser/async, codeinsight FPs, exec/CI), dashboard caveats, website YAML, LLM resolver+sampler, trajectory v2 + validation witness, opencode+kilo ACP, gm-cc + sync-upstream, model availability matrix, plugsdk + Rust gotchas, testing/profile/cache policies, layout map. learn-status confirms wasm-via-KV mode. In-session recall still returns empty (consistent with documented retrieval-side gap; index propagation is cross-session). PRD 18/18 resolved; phases PLAN -> EXECUTE -> VERIFY -> COMPLETE.
295
- - 2026-05-10 (session 2): Deep integration audit between freddie and acptoapi SDK. Moved sampler logic into acptoapi lib/sampler.js (createSampler factory + singleton, 5-step backoff). Added lib/provider-maps.js (PROVIDER_KEYS + PROVIDER_DEFAULTS, 17 providers, derived from BRANDS+auto-chain). freddie: switched to file:../acptoapi dep, model-sampler.js replaced with 13-line re-export shim, llm_resolver.js simplified 131→95L (OPENAI_COMPAT removed, PROVIDER_KEYS/DEFAULTS from SDK, buildAutoChain() for auto-scan, sdkChat() adapter for OpenAI→freddie format). test.js 12/12 green. Both repos pushed. AGENTS.md updated with acptoapi dep pattern, re-export shim pattern, CJS/ESM bridge via createRequire.
298
+ ## Trajectory recorder
296
299
 
297
- ## Dashboard web UI caveats
298
-
299
- - **anentrypoint-design source/dist pattern** (2026-05-10) — No vendor dir in freddie (`src/web/vendor/` removed, commit 52d0732). `server.js` always serves SDK from `node_modules/anentrypoint-design/dist/`. freddie's `package.json` pins `"anentrypoint-design": "file:../anentrypoint-design"` so `npm install` symlinks directly to the live source — no publish cycle needed. Dist is rebuilt by CI on push to the SDK repo; rebuild locally after SDK edits via `node scripts/build.mjs` in `C:/dev/anentrypoint-design` (emits dist/247420.js ~441KB + 247420.css; build ~150ms; warning "[247420] missing css: vendor/rippleui-1.12.1.css" is benign). Skip rebuild before using a new component and the pageerror "component is not a function" kills app mount silently.
300
- - **Live page rerender caveat** — AppState.body caching (page computed once at navigation, body saved) breaks for live routes like #/chat where AppState is mutated mid-flight (SSE pushes new messages). Fix: detect live routes in rerender(), recompute body: `if (AppState.hash === '#/chat') { Promise.resolve(PAGES['#/chat']()).then(b => { AppState.body = b; _mount() }); return }`. Any future live-streaming pages (cron output, traces) need the same treatment.
301
- - **libuv spawn caveat** — Spawning createDashboard() from exec:nodejs and keeping process alive triggers libuv UV_HANDLE_CLOSING crash on shutdown. Reliable alternative: boot via `node bin/freddie.js dashboard --port <port>`. Liveness checks: exec:browser → page.goto → window.__debug.dashboard() returns {booted, ts, framework, route}; window.__debug.chat() exposes {messages, streaming, draft}; window.__debug.sendChat(text) drives round-trips.
302
- - **anentrypoint-design theme token descendant selector fix** (2026-05-04) — SDK CSS scopes light/dark theme variables under descendant selectors `.ds-247420 [data-theme="dark"]` and `.ds-247420 [data-theme="light"]`. Placing both `class="ds-247420"` and `data-theme="dark"` on the same node (e.g. `<html>`) breaks theming: the descendant selector does NOT match when both attributes are on the same element. Fix: scope `class="ds-247420"` on `<html>` and `data-theme="dark"` on `<body>`. Theme toggle must write to `document.body`, not `document.documentElement`. Browser-witnessed commit 17dfce0: pre-fix dark/light backgrounds identical (rgb 26,27,30); post-fix dark=rgb(26,27,30), light=rgb(245,240,228). Symptom: toggle theme in dashboard, page background does not change.
303
-
304
- ## Website theme + YAML caveats (2026-05-03)
305
-
306
- - **Structured-YAML rendering** — `website/theme.mjs` (164L) renders structured YAML via 247420 design vocabulary, not raw markdown. Consumes `page.hero` (heading/subheading/accent/body/badges/ctas), `page.sections[]` (rotating rail color green→purple→mascot→sun→flame→sky by section index, optional `lede` + per-item `benefit` italic), `page.examples[]` (railed link list with mono numeric ranks + ↗ glyph). Falls back to `page.body` markdown for prose. Style block inlined so rail/dot/chip/btn classes work without ds-247420 SDK CSS loading first. To get a specific rail color, reorder sections. Prefer enriching hero+sections+examples over expanding body markdown; copy existing YAML structure as template for new pages.
307
- - **YAML colon-space trap** — In `website/content/pages/*.yaml`, any value containing `: ` outside backticks (e.g. `[linux, macos, windows]`, `requiresEnv: ['MY_KEY']` code snippets) MUST be double-quoted. The parser otherwise interprets the embedded colon as a mapping and the file fails to load. Hit twice: tools.yaml line 72, skills.yaml line 40. Fix is wrapping the whole value in `"..."`.
308
- - **SSR innerHTML injection beats client dispatch** — anentrypoint-design exposes Hero/HomeView/Panel/Row/Section/WorksList (and more), but pre-mounted SSR injection via innerHTML is more reliable than dispatching components client-side at build — avoids depending on SDK loading before the static HTML paints. Emitted HTML carries rail/dot/chip/btn classes with inline styles to be self-sufficient.
309
-
310
- ## Residual complement (NOT ported this session)
311
-
312
- Genuinely out of session reach, with reasons:
313
-
314
- - **Real credentials per platform** — adapters work; setup needs you to provide TELEGRAM_BOT_TOKEN, DISCORD_BOT_TOKEN, SLACK_BOT_TOKEN, etc. before `start()` succeeds. Listed in each adapter's `getRequiredEnv()`.
315
- - **Memory provider API accounts** — 8 provider modules call real endpoints. Test runs construct objects but don't hit external APIs without keys (HONCHO_API_KEY etc.).
316
- - **modal / daytona / singularity environments** — only local/docker/ssh ported. The other three are heavyweight remote-execution deps (Modal SDK, Daytona Cloud, Singularity containers).
317
- - **Bedrock / codex provider adapters** — `pi-ai` covers Anthropic/OpenAI/Groq. Adding bedrock/codex requires registering custom providers via `pi-ai`'s `registerApiProvider`.
318
- - **TUI Ink rewrite** — `pi-tui` IS the substrate (architectural choice, not a port).
319
- - **15k pytest tests** — single `test.js` per gm policy.
320
-
321
- ## Dashboard Agents Section — Design Decision Needed (2026-05-04)
322
-
323
- User requested "agents section" for dashboard. Exploration result: agent state is **not exposed** via HTTP API. Dashboard is client-side UI consuming only HTTP endpoints. Current endpoints: `/api/sessions`, `/api/tools`, `/api/health`. No `/api/agents`.
324
-
325
- To implement:
326
- 1. Export agent machine state (xstate snapshot) from `src/agent/machine.js`
327
- 2. Create new HTTP endpoint `/api/agents` returning count, active agent, metrics
328
- 3. Add `#/agents` route + new PAGES entry to `src/web/app.js`
329
- 4. Register `window.__debug.agents()` observability global
330
-
331
- **Blocked on**: Design decision (what metrics? count only? session associations? perf data?). Deferred pending user clarification.
332
-
333
- ## Trajectory recorder schema v2 (2026-05-12)
334
-
335
- `src/agent/machine.js::writeTrajectory()` writes one JSON per turn under `<FREDDIE_HOME>/trajectories/<ts>-<slug>.json` whenever `agent.save_trajectories=true` OR `--witness <path>` is set on `freddie exec`. Schema (`schema_version: 2`):
300
+ `src/agent/machine.js::writeTrajectory()` writes one JSON per turn under `<FREDDIE_HOME>/trajectories/<ts>-<slug>.json` when `agent.save_trajectories=true` OR `--witness <path>` is set on `freddie exec`. Schema (`schema_version: 2`):
336
301
 
337
302
  ```
338
303
  {
@@ -346,20 +311,11 @@ To implement:
346
311
  }
347
312
  ```
348
313
 
349
- Optional `--witness <path>` writes a parallel JSONL stream with one event per line (`session_start`, `message`, `llm_call`, `session_end`) for downstream tail/grep. `runTurn({witnessPath})` is the in-code equivalent.
350
-
351
- Captured fields per acceptance bar:
352
- - (a) tool_call args → `tool_calls[].arguments` + `messages[].tool_calls`
353
- - (b) tool_result → `tool_results[]` + `messages[role:'tool']`
354
- - (c) LLM call timing/duration/provider/content_length/tool_calls_count → `llm_calls[]`
355
- - (d) compressor invocations → counted from `messages[role:'system']` matching `[trajectory.compressed]`
356
- - (e) errors with stack → `error_stack` + per-`llm_call` `stack` field
314
+ `--witness <path>` writes a parallel JSONL stream with one event per line (`session_start`, `message`, `llm_call`, `session_end`). `runTurn({witnessPath})` is the in-code equivalent.
357
315
 
358
- Witnessed 2026-05-12: mistral-large 4-iteration loop on penguins repo produced 4 successful llm_calls + 1 failing (429 rate-limit) all captured with full stack trace. See `.gm/agent-loop-witness.jsonl` for canonical example.
316
+ ## LLM validation witness
359
317
 
360
- ## LLM validation witness format (.gm/llm-validation.json)
361
-
362
- `.gm/llm-validation.json` is the canonical witness for provider reachability. Generated by an out-of-band validator script (per session) that probes every key in `process.env` matching `<PROVIDER>_API_KEY`, plus the acp-daemon endpoints (kilo on 4780, opencode on 4790) and claude-cli subprocess. Shape:
318
+ `.gm/llm-validation.json` is the canonical witness for provider reachability. Generated by an out-of-band validator that probes every `<PROVIDER>_API_KEY` in `process.env`, plus acp-daemon endpoints (kilo on 4780, opencode on 4790) and claude-cli subprocess. Shape:
363
319
 
364
320
  ```
365
321
  {
@@ -370,29 +326,9 @@ Witnessed 2026-05-12: mistral-large 4-iteration loop on penguins repo produced 4
370
326
  }
371
327
  ```
372
328
 
373
- Witnessed 2026-05-12: 7/15 pass — groq, google, mistral, openrouter, sambanova, kilo, claude-cli green. opencode-via-acp daemonUp=false until `opencode serve --port 4790` is started (see opencode caveat below).
374
-
375
- ## opencode CLI shim caveat (2026-05-12)
376
-
377
- `opencode-ai@1.14.48` installs successfully via `npm install -g opencode-ai` and exposes a working binary at `C:\Users\user\AppData\Roaming\npm\opencode.cmd` (Windows). The bun-installed shim at `C:\Users\user\.bun\bin\opencode.exe` is BROKEN — its wrapper looks for `C:\Users\user\node_modules\opencode-ai\bin\opencode` (wrong path) and fails with `MODULE_NOT_FOUND`. **Workflow**: use npm version; do NOT `bun install -g opencode-ai`. To start ACP daemon: `& 'C:\Users\user\AppData\Roaming\npm\opencode.cmd' serve --port 4790 --hostname 127.0.0.1`. Verified by GET http://127.0.0.1:4790/ returning 200 (HTML shell). Warning `OPENCODE_SERVER_PASSWORD is not set` is harmless for localhost use.
378
-
379
- ## gm-cc skill registry (2026-05-12)
380
-
381
- `plugins/gm-cc/plugin.js` auto-discovers 12 SKILL.md files from npm `gm-cc` package and registers them via `pi.skills.register({name: 'gm:<name>', description, content, source: 'gm-cc'})`. Registered skills: browser, code-search, create-lang-plugin, gm, gm-complete, gm-emit, gm-execute, governance, pages, planning, ssh, update-docs. Inspect with `node bin/freddie.js skills | findstr gm:`.
382
-
383
- ## kilo ACP integration (2026-05-12)
329
+ ## Model availability matrix
384
330
 
385
- `src/agent/llm_resolver.js::acpChat()` (line 33) speaks the kilo ACP protocol: POST `/session` GET `/event` (SSE) POST `/session/<id>/message`. Streams `message.part.updated` events to assemble content; terminates on `session.idle`. Required ordering: `/event` must be opened BEFORE `/message` POST or messages drop. Configured in `ACP_BACKENDS` at line 14: kilo on `http://localhost:4780`, opencode on `http://localhost:4790`.
386
-
387
- Note: kilo + opencode ACP backends return content only, no tool_calls (the LLM-side tool layer is opaque to freddie). For multi-iteration tool-using loops, use OpenAI-compatible providers (mistral, openrouter, sambanova, groq) instead.
388
-
389
- ## scripts/sync-upstream.mjs (2026-05-12)
390
-
391
- `node scripts/sync-upstream.mjs [--dry-run] [pkg ...]` bumps sibling dep entries (plugsdk, acptoapi, anentrypoint-design, gm-cc) in package.json to `^<latest>` from npm registry, then runs `npm install --package-lock-only`. Skips `file:` deps (local-dev pattern). Wired into `.github/workflows/sync-upstream.yml` (weekly cron + workflow_dispatch) which opens a PR via peter-evans/create-pull-request@v6 when changes land. Dry-run validated: detected `acptoapi: ^1.0.52 -> ^1.0.54`.
392
-
393
- ## Model availability matrix (2026-05-13)
394
-
395
- `scripts/build-model-availability.js` writes `.gm/model-availability.json` — a Cartesian witness of every (provider × model × mode) cell. Per-cell probe with `PER_CELL_TIMEOUT_MS` (15s default) and `MAX_MODELS_PER_PROVIDER` (5 default). Provider keys read from `.env`. Feeds acptoapi sampler: `probeDirect` and `probeAgentLoop` both call `markOk`/`markFailed` on outcome; both skip when `isAvailable(provider)===false` (sampler-backoff respected).
331
+ `scripts/build-model-availability.js` writes `.gm/model-availability.json` a Cartesian witness of every (provider × model × mode) cell. Per-cell probe with `PER_CELL_TIMEOUT_MS` (15s default) and `MAX_MODELS_PER_PROVIDER` (5 default). Provider keys read from `.env`. Feeds acptoapi sampler: `probeDirect` and `probeAgentLoop` call `markOk`/`markFailed`; both skip when `isAvailable(provider)===false`.
396
332
 
397
333
  Schema:
398
334
 
@@ -410,10 +346,15 @@ Schema:
410
346
 
411
347
  6 skipped-reasons: `no_api_key_for_provider`, `sampler_backoff_active`, `daemon_not_running:<PORT>`, `mode_mismatch:<detail>`, `claude_cli_not_installed`, `unknown_mode`.
412
348
 
413
- 3 dashboard endpoints (registered in `plugins/gui-models-discover/plugin.js`):
349
+ Dashboard endpoints (registered in `plugins/gui-models-discover/plugin.js`):
414
350
  - `GET /api/models/availability` → full JSON (404 with `{error,hint}` if file absent)
415
351
  - `GET /api/models/availability/summary` → `{timestamp, daemons, summary}` (truncated)
416
- - `POST /api/models/availability/rebuild` → 202 `{ok, pid, jobId}` (spawns matrix build; 409 if rebuild already in flight)
352
+ - `POST /api/models/availability/rebuild` → 202 `{ok, pid, jobId}` (spawns build; 409 if rebuild in flight)
353
+
354
+ Sampler integration: agent-loop failures feed acptoapi's per-provider backoff (5-step 30s→480s). Provider-scoped granularity only — per-(provider,model) would need an acptoapi sampler schema change.
417
355
 
418
- Sampler integration: agent-loop failures now feed acptoapi's per-provider backoff (5-step 30s→480s). Provider-scoped granularity only — per-(provider,model) would need acptoapi sampler schema change.
356
+ ## Website theme + YAML
419
357
 
358
+ - `website/theme.mjs` renders structured YAML via 247420 design vocabulary, not raw markdown. Consumes `page.hero` (heading/subheading/accent/body/badges/ctas), `page.sections[]` (rotating rail color green→purple→mascot→sun→flame→sky by section index, optional `lede` + per-item `benefit` italic), `page.examples[]` (railed link list with mono numeric ranks + ↗ glyph). Falls back to `page.body` markdown for prose. Style block inlined so rail/dot/chip/btn classes work without ds-247420 SDK CSS loading first. Prefer enriching hero+sections+examples over expanding body markdown.
359
+ - **YAML colon-space trap**: in `website/content/pages/*.yaml`, any value containing `: ` outside backticks (e.g. `[linux, macos, windows]`, `requiresEnv: ['MY_KEY']` snippets) MUST be double-quoted. The parser otherwise interprets the embedded colon as a mapping and the file fails to load.
360
+ - SSR innerHTML injection beats client dispatch for site pages — emit HTML with rail/dot/chip/btn classes + inline styles so it paints before the SDK loads.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "freddie",
3
- "version": "0.0.105",
3
+ "version": "0.0.107",
4
4
  "type": "module",
5
5
  "description": "Open JS agent harness built on pi-mono, floosie, xstate, and anentrypoint-design",
6
6
  "bin": {
@@ -26,13 +26,13 @@
26
26
  "@mariozechner/pi-ai": "^0.70.6",
27
27
  "@mariozechner/pi-coding-agent": "^0.70.6",
28
28
  "@mariozechner/pi-tui": "^0.70.6",
29
- "acptoapi": "^1.0.95",
30
- "anentrypoint-design": "^0.0.117",
29
+ "acptoapi": "^1.0.104",
30
+ "anentrypoint-design": "^0.0.127",
31
31
  "commander": "^14.0.0",
32
32
  "express": "^5.0.0",
33
33
  "flatspace": "^1.0.18",
34
34
  "floosie": "^0.6.14",
35
- "gm-cc": "^2.0.1081",
35
+ "gm-skill": "latest",
36
36
  "js-yaml": "^4.1.0",
37
37
  "libsql-plugkit-client": "^0.0.10",
38
38
  "plugsdk": "^1.0.20",
@@ -7,14 +7,21 @@ const _require = createRequire(import.meta.url)
7
7
  function resolveSkillMd() {
8
8
  const home = process.env.USERPROFILE || process.env.HOME
9
9
  if (home) {
10
- const userSkill = path.join(home, '.claude', 'skills', 'gm-skill', 'SKILL.md')
11
- if (fs.existsSync(userSkill)) return userSkill
10
+ const candidates = [
11
+ path.join(home, '.agents', 'skills', 'gm-skill', 'SKILL.md'),
12
+ path.join(home, '.claude', 'skills', 'gm-skill', 'SKILL.md'),
13
+ ]
14
+ for (const p of candidates) {
15
+ if (fs.existsSync(p)) return p
16
+ }
17
+ }
18
+ for (const pkg of ['gm-skill', 'gm-cc']) {
19
+ try {
20
+ const pkgPath = _require.resolve(`${pkg}/package.json`)
21
+ const candidate = path.join(path.dirname(pkgPath), 'skills', 'gm-skill', 'SKILL.md')
22
+ if (fs.existsSync(candidate)) return candidate
23
+ } catch {}
12
24
  }
13
- try {
14
- const pkgPath = _require.resolve('gm-cc/package.json')
15
- const candidate = path.join(path.dirname(pkgPath), 'skills', 'gm-skill', 'SKILL.md')
16
- if (fs.existsSync(candidate)) return candidate
17
- } catch {}
18
25
  return null
19
26
  }
20
27