@otto-assistant/bridge 0.4.101 → 0.4.103
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/dist/agent-model.e2e.test.js +1 -0
- package/dist/anthropic-auth-plugin.js +22 -1
- package/dist/anthropic-auth-state.js +31 -0
- package/dist/btw-prefix-detection.js +17 -0
- package/dist/btw-prefix-detection.test.js +63 -0
- package/dist/cli.js +101 -15
- package/dist/commands/agent.js +21 -2
- package/dist/commands/ask-question.js +50 -4
- package/dist/commands/ask-question.test.js +92 -0
- package/dist/commands/btw.js +71 -66
- package/dist/commands/new-worktree.js +92 -35
- package/dist/commands/queue.js +17 -0
- package/dist/commands/worktrees.js +196 -139
- package/dist/context-awareness-plugin.js +16 -8
- package/dist/context-awareness-plugin.test.js +4 -2
- package/dist/discord-bot.js +35 -2
- package/dist/discord-command-registration.js +9 -2
- package/dist/memory-overview-plugin.js +3 -1
- package/dist/opencode.js +24 -1
- package/dist/queue-question-select-drain.e2e.test.js +135 -10
- package/dist/session-handler/thread-runtime-state.js +27 -0
- package/dist/session-handler/thread-session-runtime.js +58 -28
- package/dist/session-title-rename.test.js +12 -0
- package/dist/skill-filter.js +31 -0
- package/dist/skill-filter.test.js +65 -0
- package/dist/store.js +2 -0
- package/dist/system-message.js +12 -3
- package/dist/system-message.test.js +10 -6
- package/dist/thread-message-queue.e2e.test.js +109 -0
- package/dist/worktree-lifecycle.e2e.test.js +4 -1
- package/dist/worktrees.js +106 -12
- package/dist/worktrees.test.js +232 -6
- package/package.json +2 -2
- package/skills/goke/SKILL.md +13 -619
- package/skills/new-skill/SKILL.md +34 -10
- package/skills/npm-package/SKILL.md +336 -2
- package/skills/profano/SKILL.md +24 -0
- package/skills/zele/SKILL.md +50 -21
- package/src/agent-model.e2e.test.ts +1 -0
- package/src/anthropic-auth-plugin.ts +24 -4
- package/src/anthropic-auth-state.ts +45 -0
- package/src/btw-prefix-detection.test.ts +73 -0
- package/src/btw-prefix-detection.ts +23 -0
- package/src/cli.ts +138 -46
- package/src/commands/agent.ts +24 -2
- package/src/commands/ask-question.test.ts +111 -0
- package/src/commands/ask-question.ts +69 -4
- package/src/commands/btw.ts +105 -85
- package/src/commands/new-worktree.ts +107 -40
- package/src/commands/queue.ts +22 -0
- package/src/commands/worktrees.ts +246 -154
- package/src/context-awareness-plugin.test.ts +4 -2
- package/src/context-awareness-plugin.ts +16 -8
- package/src/discord-bot.ts +40 -2
- package/src/discord-command-registration.ts +12 -2
- package/src/memory-overview-plugin.ts +3 -1
- package/src/opencode.ts +31 -1
- package/src/queue-question-select-drain.e2e.test.ts +174 -10
- package/src/session-handler/thread-runtime-state.ts +36 -1
- package/src/session-handler/thread-session-runtime.ts +72 -32
- package/src/session-title-rename.test.ts +18 -0
- package/src/skill-filter.test.ts +83 -0
- package/src/skill-filter.ts +42 -0
- package/src/store.ts +17 -0
- package/src/system-message.test.ts +10 -6
- package/src/system-message.ts +12 -3
- package/src/thread-message-queue.e2e.test.ts +126 -0
- package/src/worktree-lifecycle.e2e.test.ts +6 -1
- package/src/worktrees.test.ts +274 -9
- package/src/worktrees.ts +144 -23
|
@@ -26,7 +26,7 @@ The folder name should match the skill name in kebab-case. Each skill gets its o
|
|
|
26
26
|
For personal skills that follow you across all repos and are not meant for distribution in a GitHub repository, place them in:
|
|
27
27
|
|
|
28
28
|
```
|
|
29
|
-
~/.opencode/skills/<skill-name>/SKILL.md
|
|
29
|
+
~/.config/opencode/skills/<skill-name>/SKILL.md
|
|
30
30
|
```
|
|
31
31
|
|
|
32
32
|
Personal skills are only available on your machine. Repository skills are shared with everyone who clones the repo.
|
|
@@ -133,18 +133,41 @@ A good skill captures **hard-won knowledge** that is not obvious from reading do
|
|
|
133
133
|
|
|
134
134
|
A bad skill is just a copy of the tool's README or man page. If the agent could figure it out from `--help`, it does not need a skill for it.
|
|
135
135
|
|
|
136
|
-
##
|
|
136
|
+
## Keep the SKILL.md thin — point at canonical docs
|
|
137
137
|
|
|
138
|
-
|
|
138
|
+
The best skills are **thin**. They contain almost no documentation themselves. Their only job is to tell the agent where to find the full, fresh docs and to forbid truncation. This keeps docs in one place and stops the skill from going stale.
|
|
139
|
+
|
|
140
|
+
There are two variants:
|
|
141
|
+
|
|
142
|
+
**1. CLI tools → run `<tool> --help`**
|
|
143
|
+
|
|
144
|
+
Put as much documentation as possible into the CLI itself — command descriptions, option help text, examples. The skill then says:
|
|
139
145
|
|
|
140
146
|
```markdown
|
|
141
|
-
|
|
142
|
-
|
|
147
|
+
Every time you use mytool, you MUST run:
|
|
148
|
+
|
|
149
|
+
\`\`\`bash
|
|
150
|
+
mytool --help # NEVER pipe to head/tail, read the full output
|
|
151
|
+
\`\`\`
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
Exception: some CLIs have a dedicated `<tool> skill` subcommand when `--help` is not rich enough (e.g. `playwriter skill`). Prefer `--help` by default and only use a custom subcommand when the CLI ships one.
|
|
155
|
+
|
|
156
|
+
**2. Libraries and projects → curl the raw README**
|
|
157
|
+
|
|
158
|
+
For libraries, frameworks, and pattern skills, keep the canonical docs in `README.md` and have the skill curl the raw file from the main branch so the agent always reads the latest version:
|
|
159
|
+
|
|
160
|
+
```markdown
|
|
161
|
+
Every time you work with myproject, you MUST fetch the latest README:
|
|
162
|
+
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
curl -s https://raw.githubusercontent.com/owner/repo/main/README.md # NEVER pipe to head/tail
|
|
165
|
+
\`\`\`
|
|
143
166
|
```
|
|
144
167
|
|
|
145
|
-
|
|
168
|
+
If the README lives in a subdirectory (e.g. a package inside a monorepo), include that subpath: `.../main/packagename/README.md`.
|
|
146
169
|
|
|
147
|
-
|
|
170
|
+
**Never truncate docs output.** The agent must read `--help` and curl'd README output **in full**. Never pipe through `head`, `tail`, `sed -n`, `awk`, `| less`, or any command that strips or limits lines. Critical rules are spread throughout the doc, not just at the top. Agents truncate frequently and miss important context — forbid it explicitly in the skill body.
|
|
148
171
|
|
|
149
172
|
## Examples from real skills
|
|
150
173
|
|
|
@@ -206,6 +229,7 @@ Before saving a new skill:
|
|
|
206
229
|
|
|
207
230
|
1. Does the **description** clearly state when to load this skill? Would an agent reading just the description know whether to load it?
|
|
208
231
|
2. Does the **name** match the folder name?
|
|
209
|
-
3.
|
|
210
|
-
4.
|
|
211
|
-
5.
|
|
232
|
+
3. Does the skill **point at a single source of truth** (README curl URL or `--help` command) instead of duplicating docs inline?
|
|
233
|
+
4. Is there an explicit **"never truncate"** rule next to any docs command?
|
|
234
|
+
5. Are there **concrete code examples** for the main workflows?
|
|
235
|
+
6. Did you capture the **gotchas** — the things that took trial and error to figure out?
|
|
@@ -38,6 +38,9 @@ Use this skill when scaffolding or fixing npm packages.
|
|
|
38
38
|
- `dist`
|
|
39
39
|
- any runtime-required extra files (for example `schema.prisma`)
|
|
40
40
|
- docs like `README.md` and `CHANGELOG.md`
|
|
41
|
+
- `skills/` directory if the package ships an agent skill (see "Agent
|
|
42
|
+
skill" section below). Skill files live at `skills/<name>/SKILL.md`,
|
|
43
|
+
never at the package root.
|
|
41
44
|
- if tests are inside src and gets included in dist, it's fine. don't try to exclude them
|
|
42
45
|
10. `scripts.build` should be `tsc && chmod +x dist/cli.js` (skip the chmod if
|
|
43
46
|
the package has no bin). No bundling. Do not delete `dist/` in `build` by
|
|
@@ -111,8 +114,9 @@ import path from "node:path";
|
|
|
111
114
|
|
|
112
115
|
const __dirname = path.dirname(url.fileURLToPath(import.meta.url));
|
|
113
116
|
|
|
114
|
-
// e.g. from src/cli.ts → read SKILL.md
|
|
115
|
-
|
|
117
|
+
// e.g. from src/cli.ts → read SKILL.md under skills/<name>/SKILL.md
|
|
118
|
+
// (skill files always live in skills/<name>/SKILL.md, never at the package root)
|
|
119
|
+
const skillPath = path.resolve(__dirname, "../skills/mypkg/SKILL.md");
|
|
116
120
|
|
|
117
121
|
// from dist/cli.js (after tsc) → reach back to src/
|
|
118
122
|
const srcFile = path.resolve(__dirname, "../src/template.md");
|
|
@@ -218,11 +222,341 @@ Use Node ESM-compatible compiler settings:
|
|
|
218
222
|
```
|
|
219
223
|
|
|
220
224
|
|
|
225
|
+
## Package.json `imports` map (internal `#` aliases)
|
|
226
|
+
|
|
227
|
+
Use `imports` when you need a package to swap between different implementations
|
|
228
|
+
based on runtime (Node vs Bun vs browser vs SQLite vs better-sqlite3, etc.).
|
|
229
|
+
Internal imports are `#`-prefixed, scoped to the package itself, and never
|
|
230
|
+
leak to consumers. Consumers resolve through `exports`, not `imports`.
|
|
231
|
+
|
|
232
|
+
### Point `types` at `dist`, not `src`
|
|
233
|
+
|
|
234
|
+
The TypeScript docs are explicit about this:
|
|
235
|
+
|
|
236
|
+
> If the package.json is part of the local project, an additional remapping
|
|
237
|
+
> step is performed in order to find the **input** TypeScript implementation
|
|
238
|
+
> file... This remapping uses the `outDir`/`declarationDir` and `rootDir`
|
|
239
|
+
> from the tsconfig.json, so using `"imports"` usually requires an explicit
|
|
240
|
+
> `rootDir` to be set.
|
|
241
|
+
>
|
|
242
|
+
> This variation allows package authors to write `"imports"` and `"exports"`
|
|
243
|
+
> fields that reference only the compilation outputs that will be published
|
|
244
|
+
> to npm, while still allowing local development to use the original
|
|
245
|
+
> TypeScript source files.
|
|
246
|
+
|
|
247
|
+
In other words, TypeScript automatically walks from `./dist/foo.d.ts` back
|
|
248
|
+
to `./src/foo.ts` using `outDir` → `rootDir` during compilation. You do not
|
|
249
|
+
need to point `types` at `src` manually — **let TypeScript remap it**.
|
|
250
|
+
|
|
251
|
+
```json
|
|
252
|
+
{
|
|
253
|
+
"imports": {
|
|
254
|
+
"#sqlite": {
|
|
255
|
+
"bun": "./src/platform/bun/sqlite.ts",
|
|
256
|
+
"node": {
|
|
257
|
+
"types": "./dist/platform/node/sqlite.d.ts",
|
|
258
|
+
"default": "./dist/platform/node/sqlite.js"
|
|
259
|
+
},
|
|
260
|
+
"default": {
|
|
261
|
+
"types": "./dist/platform/node/sqlite.d.ts",
|
|
262
|
+
"default": "./dist/platform/node/sqlite.js"
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
Resolution flow when `tsc` sees `import db from '#sqlite'`:
|
|
270
|
+
|
|
271
|
+
1. `imports["#sqlite"].node.types` → `./dist/platform/node/sqlite.d.ts`
|
|
272
|
+
2. package.json is in the local project → apply the remap.
|
|
273
|
+
3. Replace `outDir` (`dist`) with `rootDir` (`src`) → `./src/platform/node/sqlite.d.ts`
|
|
274
|
+
4. Replace `.d.ts` with the source extension `.ts` → `./src/platform/node/sqlite.ts`
|
|
275
|
+
5. Return `./src/platform/node/sqlite.ts` (it exists on a fresh clone, no build needed).
|
|
276
|
+
6. Otherwise fall back to `./dist/platform/node/sqlite.d.ts`.
|
|
277
|
+
|
|
278
|
+
### Why dist-first is correct
|
|
279
|
+
|
|
280
|
+
- **No chicken-and-egg.** The remap is compile-time, so `tsc` works on a fresh
|
|
281
|
+
clone without `dist/` existing yet.
|
|
282
|
+
- **Published map describes shipped files.** Every `imports` entry points at
|
|
283
|
+
something that will actually be in the npm tarball. No stale src paths
|
|
284
|
+
leaking into the published package.json.
|
|
285
|
+
- **Works under plain Node.** If the package is loaded by Node without
|
|
286
|
+
TypeScript involvement, Node reads the same `imports` map at runtime and
|
|
287
|
+
resolves to real `dist/*.js` files that exist.
|
|
288
|
+
- **Bun / browser runtime conditions can still point at `src`**, because
|
|
289
|
+
those runtimes execute `.ts` directly and skip the build step.
|
|
290
|
+
|
|
291
|
+
### Requirements
|
|
292
|
+
|
|
293
|
+
This only works when:
|
|
294
|
+
|
|
295
|
+
- `moduleResolution` is `node16`, `nodenext`, or `bundler`
|
|
296
|
+
- `rootDir` is set explicitly in `tsconfig.json` (the skill's tsconfig rules
|
|
297
|
+
already require `"rootDir": "src"`)
|
|
298
|
+
- `outDir` is set (already in the template)
|
|
299
|
+
- `resolvePackageJsonImports` is not disabled (it is on by default for the
|
|
300
|
+
supported `moduleResolution` modes)
|
|
301
|
+
|
|
302
|
+
### Anti-pattern: pointing `types` at `src` manually
|
|
303
|
+
|
|
304
|
+
```json
|
|
305
|
+
{
|
|
306
|
+
"imports": {
|
|
307
|
+
"#sqlite": {
|
|
308
|
+
"node": {
|
|
309
|
+
"types": "./src/platform/node/sqlite.ts", // ❌ don't do this
|
|
310
|
+
"default": "./dist/platform/node/sqlite.js"
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
This works but:
|
|
318
|
+
|
|
319
|
+
1. The published `package.json` advertises `src/*.ts` paths that may or may
|
|
320
|
+
not exist depending on what you include in `files`.
|
|
321
|
+
2. It bypasses TypeScript's built-in remapping, which is the whole point of
|
|
322
|
+
the local-project `imports` feature.
|
|
323
|
+
3. It is inconsistent with `default` — mixing source (for types) and dist
|
|
324
|
+
(for runtime) paths in the same entry is easy to get wrong.
|
|
325
|
+
|
|
326
|
+
Source of truth: [TypeScript Modules Reference — package.json "imports" and self-name imports](https://www.typescriptlang.org/docs/handbook/modules/reference.html#packagejson-imports-and-self-name-imports).
|
|
327
|
+
|
|
221
328
|
## tests location
|
|
222
329
|
|
|
223
330
|
test files should be close with the associated source files. for example if you have an utils.ts file you will create utils.test.ts file next to it. with tests, importing from utils. preferred testing framework is vitest (or bun if project already using `bun test` or depends on bun APIs, rare)
|
|
224
331
|
|
|
225
332
|
|
|
333
|
+
## Agent skill
|
|
334
|
+
|
|
335
|
+
If the package ships an agent skill (SKILL.md for AI coding agents), place it
|
|
336
|
+
at:
|
|
337
|
+
|
|
338
|
+
```
|
|
339
|
+
skills/<package-name>/SKILL.md
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Never put `SKILL.md` at the package root. The `skills/<name>/SKILL.md` layout
|
|
343
|
+
matches the convention used by the [`skills`](https://skills.sh) CLI so users
|
|
344
|
+
can install it with:
|
|
345
|
+
|
|
346
|
+
```bash
|
|
347
|
+
npx -y skills add owner/repo
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
Add this installation snippet to the README so users know how to get the skill:
|
|
351
|
+
|
|
352
|
+
```markdown
|
|
353
|
+
## Agent Skill
|
|
354
|
+
|
|
355
|
+
This package ships a skill file that teaches AI coding agents how and when to
|
|
356
|
+
use it. Install it with:
|
|
357
|
+
|
|
358
|
+
\`\`\`bash
|
|
359
|
+
npx -y skills add owner/repo
|
|
360
|
+
\`\`\`
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
Remember to add `skills` to the `files` array in `package.json` so the skill
|
|
364
|
+
directory is included when publishing.
|
|
365
|
+
|
|
366
|
+
### Keep the SKILL.md thin
|
|
367
|
+
|
|
368
|
+
The SKILL.md body should be a **few lines**, not a full docs dump. Put all
|
|
369
|
+
real documentation in `README.md` (which already lives in `files`) and have
|
|
370
|
+
the skill tell the agent to fetch it. This way agents always read the latest
|
|
371
|
+
docs and the skill never goes stale.
|
|
372
|
+
|
|
373
|
+
The body stays thin, but the **frontmatter `description` must be rich**. It
|
|
374
|
+
is what the agent sees in its main context, and it is the only signal the
|
|
375
|
+
agent uses to decide whether to load the skill. Make it long enough to
|
|
376
|
+
cover: what the package is, the core concepts and APIs, concrete trigger
|
|
377
|
+
phrases the user might say, and explicit "ALWAYS load this skill when..."
|
|
378
|
+
conditions. A one-sentence description is almost always too short. See the
|
|
379
|
+
`new-skill` skill for full guidance on writing descriptions.
|
|
380
|
+
|
|
381
|
+
**CLI package template:**
|
|
382
|
+
|
|
383
|
+
```md
|
|
384
|
+
---
|
|
385
|
+
name: mypkg
|
|
386
|
+
description: |
|
|
387
|
+
mypkg is <what it does and the core concepts>. It exposes <main commands
|
|
388
|
+
or APIs> and is used for <typical tasks>. ALWAYS load this skill when
|
|
389
|
+
the user mentions mypkg, runs <binary>, edits files that import mypkg,
|
|
390
|
+
or asks about <trigger keywords>. Load it before writing any code that
|
|
391
|
+
touches mypkg so you know the correct usage patterns and gotchas.
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
# mypkg
|
|
395
|
+
|
|
396
|
+
Every time you use mypkg, you MUST run:
|
|
397
|
+
|
|
398
|
+
\`\`\`bash
|
|
399
|
+
mypkg --help # NEVER pipe to head/tail, read the full output
|
|
400
|
+
\`\`\`
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
**Library package template:**
|
|
404
|
+
|
|
405
|
+
```md
|
|
406
|
+
---
|
|
407
|
+
name: mypkg
|
|
408
|
+
description: |
|
|
409
|
+
mypkg is <what it does and the core concepts>. It exports <main APIs>
|
|
410
|
+
and is used for <typical tasks>. ALWAYS load this skill when the user
|
|
411
|
+
mentions mypkg, imports from mypkg, edits files that depend on it, or
|
|
412
|
+
asks about <trigger keywords>. Load it before writing any code that
|
|
413
|
+
touches mypkg so you know the correct usage patterns and gotchas.
|
|
414
|
+
---
|
|
415
|
+
|
|
416
|
+
# mypkg
|
|
417
|
+
|
|
418
|
+
Every time you work with mypkg, you MUST fetch the latest README:
|
|
419
|
+
|
|
420
|
+
\`\`\`bash
|
|
421
|
+
curl -s https://raw.githubusercontent.com/owner/repo/main/README.md # NEVER pipe to head/tail
|
|
422
|
+
\`\`\`
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
Because the SKILL.md body points at the README, the README must contain
|
|
426
|
+
everything the agent needs: API reference, examples, gotchas, and rules.
|
|
427
|
+
See the `new-skill` skill for the full pattern.
|
|
428
|
+
|
|
429
|
+
## pnpm workspaces
|
|
430
|
+
|
|
431
|
+
When the project is a monorepo, use pnpm workspaces with flat `./*` glob paths
|
|
432
|
+
in `pnpm-workspace.yaml`. All packages live at the repo root as siblings, no
|
|
433
|
+
nested `packages/` directory:
|
|
434
|
+
|
|
435
|
+
```yaml
|
|
436
|
+
packages:
|
|
437
|
+
- ./*
|
|
438
|
+
```
|
|
439
|
+
|
|
440
|
+
This means the repo looks like:
|
|
441
|
+
|
|
442
|
+
```
|
|
443
|
+
my-monorepo/
|
|
444
|
+
package.json # root (private: true)
|
|
445
|
+
pnpm-workspace.yaml
|
|
446
|
+
cli/ # workspace package
|
|
447
|
+
website/ # workspace package
|
|
448
|
+
db/ # workspace package
|
|
449
|
+
errore/ # workspace package (submodule)
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
### Common dev dependencies at root
|
|
453
|
+
|
|
454
|
+
Install shared dev tooling **only at the root** `package.json` so every
|
|
455
|
+
workspace package uses the same version without duplicating installs:
|
|
456
|
+
|
|
457
|
+
```json
|
|
458
|
+
{
|
|
459
|
+
"private": true,
|
|
460
|
+
"devDependencies": {
|
|
461
|
+
"typescript": "^5.9.2",
|
|
462
|
+
"tsx": "^4.20.5",
|
|
463
|
+
"vitest": "^3.2.4",
|
|
464
|
+
"oxfmt": "^0.24.0"
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Packages that need these tools (like `tsc` or `vitest`) will resolve them
|
|
470
|
+
from the root `node_modules` via pnpm's hoisting. Do **not** add
|
|
471
|
+
`typescript`, `tsx`, `vitest`, or `oxfmt` as devDependencies in individual
|
|
472
|
+
workspace packages — only add them at root.
|
|
473
|
+
|
|
474
|
+
Package-specific dev dependencies (for example `@types/node`, `rimraf`,
|
|
475
|
+
`prisma`) still go in each package's own `devDependencies`.
|
|
476
|
+
|
|
477
|
+
### Cross-workspace dependencies
|
|
478
|
+
|
|
479
|
+
Use `workspace:^` (not `workspace:*`) for local package versions so that
|
|
480
|
+
when published, the dependency resolves to a caret range instead of a pinned
|
|
481
|
+
version:
|
|
482
|
+
|
|
483
|
+
```json
|
|
484
|
+
{
|
|
485
|
+
"dependencies": {
|
|
486
|
+
"my-utils": "workspace:^"
|
|
487
|
+
}
|
|
488
|
+
}
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
Use `pnpm install package@workspace:^` to add a workspace dependency, or
|
|
492
|
+
add it to `package.json` manually with the `workspace:^` protocol.
|
|
493
|
+
|
|
494
|
+
## CI (GitHub Actions)
|
|
495
|
+
|
|
496
|
+
Standard CI workflow for pnpm workspace monorepos. Key points:
|
|
497
|
+
|
|
498
|
+
- **Checkout submodules** with `submodules: recursive` if the repo uses git
|
|
499
|
+
submodules (common for shared libraries like errore).
|
|
500
|
+
- **Use `pnpm/action-setup@v4`** with the pnpm version matching your lockfile.
|
|
501
|
+
- **Use Node 24** (or latest LTS) via `actions/setup-node@v4` with `cache: pnpm`.
|
|
502
|
+
- **Build workspace packages** that export from `dist/` before running tests,
|
|
503
|
+
since submodules and some packages have `dist/` gitignored.
|
|
504
|
+
- **Run tests from the package directory**, not root.
|
|
505
|
+
|
|
506
|
+
Example `.github/workflows/ci.yml`:
|
|
507
|
+
|
|
508
|
+
```yaml
|
|
509
|
+
name: CI
|
|
510
|
+
|
|
511
|
+
on:
|
|
512
|
+
push:
|
|
513
|
+
branches: [main]
|
|
514
|
+
pull_request:
|
|
515
|
+
branches: [main]
|
|
516
|
+
|
|
517
|
+
jobs:
|
|
518
|
+
test:
|
|
519
|
+
name: Tests
|
|
520
|
+
runs-on: ubuntu-latest
|
|
521
|
+
timeout-minutes: 30
|
|
522
|
+
|
|
523
|
+
steps:
|
|
524
|
+
- uses: actions/checkout@v4
|
|
525
|
+
with:
|
|
526
|
+
submodules: recursive
|
|
527
|
+
|
|
528
|
+
- uses: pnpm/action-setup@v4
|
|
529
|
+
with:
|
|
530
|
+
version: 9
|
|
531
|
+
|
|
532
|
+
- uses: actions/setup-node@v4
|
|
533
|
+
with:
|
|
534
|
+
node-version: 24
|
|
535
|
+
cache: pnpm
|
|
536
|
+
|
|
537
|
+
- name: Install dependencies
|
|
538
|
+
run: pnpm install
|
|
539
|
+
|
|
540
|
+
# Submodules and workspace packages with dist/ gitignored
|
|
541
|
+
# need to be built after checkout before anything can import them.
|
|
542
|
+
- name: Build workspace packages with dist/ exports
|
|
543
|
+
run: |
|
|
544
|
+
pnpm --filter my-lib run build
|
|
545
|
+
pnpm --filter my-utils run build
|
|
546
|
+
|
|
547
|
+
- name: Run tests
|
|
548
|
+
run: pnpm test -- --run
|
|
549
|
+
working-directory: cli
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
If the repo has Prisma schemas, add generate steps before tests:
|
|
553
|
+
|
|
554
|
+
```yaml
|
|
555
|
+
- name: Generate Prisma client
|
|
556
|
+
run: pnpm generate
|
|
557
|
+
working-directory: cli
|
|
558
|
+
```
|
|
559
|
+
|
|
226
560
|
## .gitignore
|
|
227
561
|
|
|
228
562
|
For non-workspace (standalone) packages, always create a `.gitignore` with:
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: profano
|
|
3
|
+
description: CLI tool to analyze V8 .cpuprofile files and print top functions by self-time or total-time in the terminal. ALWAYS load this skill when CPU profiling JavaScript or TypeScript programs (Node, Vitest, Bun, Chrome DevTools exports) — it shows how to generate .cpuprofile files and how to inspect them from the terminal without opening Chrome DevTools.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# profano
|
|
7
|
+
|
|
8
|
+
`profano` is a terminal CLI that reads V8 `.cpuprofile` files and prints the heaviest functions as a table sorted by self-time or total (inclusive) time. Use it to quickly identify CPU hotspots from the terminal without loading the profile into Chrome DevTools or cpupro.
|
|
9
|
+
|
|
10
|
+
## When to use
|
|
11
|
+
|
|
12
|
+
- You have a `.cpuprofile` file (from `node --cpu-prof`, Vitest, Chrome DevTools export, etc.) and want a quick top-N readout of the biggest offenders.
|
|
13
|
+
- You are an agent debugging slow code and need a grep-able, text-only view of where CPU time is spent.
|
|
14
|
+
- You want to compare hotspots across many profile files in one shot.
|
|
15
|
+
|
|
16
|
+
## How to use
|
|
17
|
+
|
|
18
|
+
**Always run `profano --help` first.** The help output is the source of truth for all commands, options, and examples. Read the full untruncated output — do not pipe it through `head`, `tail`, or `sed`.
|
|
19
|
+
|
|
20
|
+
For full setup, usage examples, how to generate `.cpuprofile` files (Node, Vitest with a ready-to-copy `vitest.config.ts`, Chrome DevTools), and how to read the output columns, fetch the README:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
curl -s https://raw.githubusercontent.com/remorses/profano/main/README.md
|
|
24
|
+
```
|
package/skills/zele/SKILL.md
CHANGED
|
@@ -3,13 +3,14 @@ name: zele
|
|
|
3
3
|
description: >
|
|
4
4
|
Control Gmail and Google Calendar via CLI. Read, search, send, reply, and forward
|
|
5
5
|
emails. Create, update, and delete calendar events. Manage drafts, labels, and attachments.
|
|
6
|
-
Supports multiple Google accounts
|
|
7
|
-
send messages, schedule meetings,
|
|
6
|
+
Supports multiple Google accounts and IMAP/SMTP accounts (Fastmail, Outlook, any provider).
|
|
7
|
+
Use this skill whenever the user asks to check email, send messages, schedule meetings,
|
|
8
|
+
or manage their calendar.
|
|
8
9
|
---
|
|
9
10
|
|
|
10
|
-
# zele —
|
|
11
|
+
# zele — Email & Calendar CLI
|
|
11
12
|
|
|
12
|
-
A multi-account
|
|
13
|
+
A multi-account email and calendar client supporting **Google OAuth** and **IMAP/SMTP** (Fastmail, Outlook, any provider). Output is YAML, pipe-friendly.
|
|
13
14
|
|
|
14
15
|
## Setup
|
|
15
16
|
|
|
@@ -20,11 +21,20 @@ bun install -g zele
|
|
|
20
21
|
# show connected accounts
|
|
21
22
|
zele whoami
|
|
22
23
|
|
|
23
|
-
# authenticate (opens browser, supports multiple accounts)
|
|
24
|
+
# authenticate with Google (opens browser, supports multiple accounts)
|
|
24
25
|
zele login
|
|
26
|
+
|
|
27
|
+
# authenticate with IMAP/SMTP (non-interactive, designed for agents)
|
|
28
|
+
zele login imap \
|
|
29
|
+
--email you@fastmail.com \
|
|
30
|
+
--imap-host imap.fastmail.com --imap-port 993 \
|
|
31
|
+
--smtp-host smtp.fastmail.com --smtp-port 465 \
|
|
32
|
+
--password "your-app-password"
|
|
25
33
|
```
|
|
26
34
|
|
|
27
|
-
**
|
|
35
|
+
**IMAP/SMTP login options:** `--imap-user` / `--smtp-user` if the login username differs from email. Omit `--smtp-host` for read-only (no sending). Use `--imap-password` / `--smtp-password` for separate credentials.
|
|
36
|
+
|
|
37
|
+
**Remote/headless Google login:** `zele login` is interactive — it prints an authorization URL and waits for a redirect URL to be pasted back. In agent/headless environments, run it inside tmux so the process persists:
|
|
28
38
|
|
|
29
39
|
```bash
|
|
30
40
|
# start login in a tmux session
|
|
@@ -49,16 +59,17 @@ Running `zele` with no subcommand launches a human-friendly TUI for browsing ema
|
|
|
49
59
|
|
|
50
60
|
## Capabilities
|
|
51
61
|
|
|
52
|
-
- **Mail:** list, search, read, send, reply, forward, star, archive, trash,
|
|
53
|
-
- **Drafts:** list, create, get, send, delete
|
|
54
|
-
- **
|
|
55
|
-
- **Labels:** list, create, delete, unread counts
|
|
56
|
-
- **
|
|
57
|
-
- **
|
|
62
|
+
- **Mail:** list, search, read, send, reply, forward, star, archive, trash, watch for new emails (Google + IMAP)
|
|
63
|
+
- **Drafts:** list, create, get, send, delete (Google + IMAP)
|
|
64
|
+
- **Attachments:** list per thread, download (Google + IMAP)
|
|
65
|
+
- **Labels:** list, create, delete, unread counts (Google only)
|
|
66
|
+
- **Filters:** list server-side filters (Google only)
|
|
67
|
+
- **Calendar:** list calendars, list/search events, create/update/delete events, RSVP, free/busy (Google only)
|
|
68
|
+
- **Multi-account:** all commands support `--account <email>` to filter; list/search merge across all account types
|
|
58
69
|
|
|
59
70
|
## Account discovery
|
|
60
71
|
|
|
61
|
-
When the user asks to check emails **for a specific account** (e.g. "check my work email", "what's new on my personal Gmail?"), always run `zele whoami` first to list the connected accounts and find the exact email address to pass to `--account`. Never guess the email — use the output of `zele whoami` to pick the right one.
|
|
72
|
+
When the user asks to check emails **for a specific account** (e.g. "check my work email", "what's new on my personal Gmail?"), always run `zele whoami` first to list the connected accounts and find the exact email address to pass to `--account`. Never guess the email — use the output of `zele whoami` to pick the right one. The output also shows account type (`google` or `imap_smtp`) and capabilities.
|
|
62
73
|
|
|
63
74
|
```bash
|
|
64
75
|
# list connected accounts
|
|
@@ -68,6 +79,24 @@ zele whoami
|
|
|
68
79
|
zele mail list --account user@work.com
|
|
69
80
|
```
|
|
70
81
|
|
|
82
|
+
## Google-only features
|
|
83
|
+
|
|
84
|
+
These commands only work with Google accounts. IMAP/SMTP accounts show a helpful error:
|
|
85
|
+
|
|
86
|
+
- `zele label list/counts/create/delete` — IMAP uses folders, not labels
|
|
87
|
+
- `zele mail label` — adding/removing labels
|
|
88
|
+
- `zele mail filter list` — server-side Gmail filters
|
|
89
|
+
- `zele cal *` — calendar requires Google OAuth
|
|
90
|
+
- `zele profile` — shows limited info for IMAP (email only, no message counts)
|
|
91
|
+
|
|
92
|
+
## IMAP search support
|
|
93
|
+
|
|
94
|
+
IMAP accounts support a subset of Gmail query syntax, translated to IMAP SEARCH:
|
|
95
|
+
|
|
96
|
+
`from:`, `to:`, `subject:`, `is:unread`, `is:starred`, `has:attachment`, `newer_than:Nd`, `older_than:Nm`, `after:YYYY/MM/DD`, `before:YYYY/MM/DD`
|
|
97
|
+
|
|
98
|
+
Unsupported on IMAP: `cc:`, `-` (negate), `label:`, `in:`, `filename:`, `size:`/`larger:`/`smaller:`, `OR`, `{ }`.
|
|
99
|
+
|
|
71
100
|
## Examples
|
|
72
101
|
|
|
73
102
|
```bash
|
|
@@ -77,8 +106,8 @@ zele mail list
|
|
|
77
106
|
# list only unread emails
|
|
78
107
|
zele mail list --filter "is:unread"
|
|
79
108
|
|
|
80
|
-
# list
|
|
81
|
-
zele mail list --filter "
|
|
109
|
+
# list emails from last 7 days (works for both Google and IMAP)
|
|
110
|
+
zele mail list --filter "newer_than:7d"
|
|
82
111
|
|
|
83
112
|
# combine filter with folder
|
|
84
113
|
zele mail list --filter "from:github" --folder sent
|
|
@@ -98,15 +127,15 @@ zele mail reply <threadId> --body "Thanks!" --all
|
|
|
98
127
|
# watch inbox for new mail (polls every 15s)
|
|
99
128
|
zele mail watch
|
|
100
129
|
|
|
101
|
-
#
|
|
130
|
+
# add an IMAP account (non-interactive, for agents)
|
|
131
|
+
zele login imap --email you@fastmail.com --imap-host imap.fastmail.com --smtp-host smtp.fastmail.com --password "app-pass"
|
|
132
|
+
|
|
133
|
+
# today's calendar events (Google only)
|
|
102
134
|
zele cal events --today --all
|
|
103
135
|
|
|
104
|
-
# create a meeting with Google Meet
|
|
136
|
+
# create a meeting with Google Meet (Google only)
|
|
105
137
|
zele cal create --summary "Standup" --from tomorrow --to +30m --meet --attendees bob@example.com
|
|
106
138
|
|
|
107
|
-
#
|
|
108
|
-
zele cal freebusy --from today --to +8h
|
|
109
|
-
|
|
110
|
-
# list Gmail filters
|
|
139
|
+
# list Gmail filters (Google only)
|
|
111
140
|
zele mail filter list
|
|
112
141
|
```
|
|
@@ -958,6 +958,7 @@ describe('agent model resolution', () => {
|
|
|
958
958
|
⬥ ok
|
|
959
959
|
*project ⋅ main ⋅ Ns ⋅ N% ⋅ agent-model-v2 ⋅ **test-agent***
|
|
960
960
|
Switched to **plan** agent for this session (was **test-agent**)
|
|
961
|
+
Model: *deterministic-provider/plan-model-v2*
|
|
961
962
|
The agent will change on the next message.
|
|
962
963
|
--- from: user (agent-model-tester)
|
|
963
964
|
Reply with exactly: after-switch-msg
|
|
@@ -592,6 +592,20 @@ function toClaudeCodeToolName(name: string) {
|
|
|
592
592
|
return OPENCODE_TO_CLAUDE_CODE_TOOL_NAME[name.toLowerCase()] ?? name;
|
|
593
593
|
}
|
|
594
594
|
|
|
595
|
+
/**
|
|
596
|
+
* Strips the OpenCode identity block (from "You are OpenCode…" up to the
|
|
597
|
+
* Anthropic prompt marker "Skills provide specialized instructions") and
|
|
598
|
+
* re-injects essential environment context as a small XML tag.
|
|
599
|
+
*
|
|
600
|
+
* The original OpenCode prompt between those markers contains the current
|
|
601
|
+
* working directory and other runtime context. Stripping it wholesale loses
|
|
602
|
+
* that info, so we add back what the model needs (cwd) in a compact form.
|
|
603
|
+
*
|
|
604
|
+
* Original OpenCode Anthropic prompt structure (for reference):
|
|
605
|
+
* "You are OpenCode, the best coding agent on the planet."
|
|
606
|
+
* + environment block (cwd, OS, shell, date, etc.)
|
|
607
|
+
* + "Skills provide specialized instructions …"
|
|
608
|
+
*/
|
|
595
609
|
function sanitizeAnthropicSystemText(
|
|
596
610
|
text: string,
|
|
597
611
|
onError?: (msg: string) => void,
|
|
@@ -608,10 +622,16 @@ function sanitizeAnthropicSystemText(
|
|
|
608
622
|
return text;
|
|
609
623
|
}
|
|
610
624
|
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
625
|
+
// Re-inject the process working directory that was inside the stripped block.
|
|
626
|
+
const envContext = `\n<environment>\n<cwd>${process.cwd()}</cwd>\n</environment>\n`;
|
|
627
|
+
|
|
628
|
+
// Replace all case-insensitive whole-word occurrences of "opencode" with "openc0de"
|
|
629
|
+
const result =
|
|
630
|
+
text.slice(0, startIdx) +
|
|
631
|
+
envContext +
|
|
632
|
+
text.slice(endIdx);
|
|
633
|
+
// Use a regex with global, case-insensitive, and word boundary flags
|
|
634
|
+
return result.replace(/\bopencode\b/gi, "openc0de");
|
|
615
635
|
}
|
|
616
636
|
|
|
617
637
|
function mapSystemTextPart(
|