@owlmeans/agent-skills 0.1.9 → 0.1.11

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.
@@ -0,0 +1,43 @@
1
+ ---
2
+ description: "How project memory works in an OwlMeans app — where to store facts/decisions/gotchas for Copilot (.github/memory/) and Claude Code (.claude/memory/), the MEMORY.md index, and when to read/write memory. Apply when remembering something about the project or deciding where a note belongs."
3
+ applyTo: "**/.github/memory/**, **/.claude/memory/**"
4
+ scope: general
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # Project memory
9
+
10
+ Memory is how durable, project-specific knowledge survives across sessions. Keep it **inside the
11
+ project**, never outside the repository.
12
+
13
+ | Tool | Memory dir | Index |
14
+ |---|---|---|
15
+ | GitHub Copilot | `.github/memory/` | `.github/memory/MEMORY.md` |
16
+ | Claude Code | `.claude/memory/` | `.claude/memory/MEMORY.md` |
17
+
18
+ Each memory is one Markdown file holding one fact, decision, or gotcha. The `MEMORY.md` index keeps a
19
+ one-line pointer per file and is loaded every session, so keep it current.
20
+
21
+ ## When to read
22
+
23
+ - **Start of every session** — read `MEMORY.md`, then open any relevant file.
24
+ - **Before a non-trivial task** — check for and read a memory file on that topic.
25
+
26
+ ## When to write
27
+
28
+ After a task produces knowledge not obvious from the code or git history: a decision and its
29
+ rationale, a gotcha, or project state/goals. Do not record what the repo or git history already says,
30
+ or what only matters to the current conversation.
31
+
32
+ ## How to write one
33
+
34
+ 1. Create `.github/memory/<topic>.md` (and the Claude copy under `.claude/memory/`) with a short slug
35
+ and a one-line description.
36
+ 2. Write the single fact; for decisions/gotchas add why it matters and how to apply it.
37
+ 3. Add a one-line pointer to `MEMORY.md`.
38
+ 4. Update an existing file instead of duplicating; delete memories that prove wrong.
39
+
40
+ ## Memory vs skill
41
+
42
+ A repeatable procedure is a skill (see the `skill-authoring` instruction). Memory is for facts about
43
+ this project.
@@ -0,0 +1,62 @@
1
+ ---
2
+ description: "How to build a fullstack OwlMeans Common app — the common/api/web three-workspace pattern, server and web context bootstrap, shared entrypoints + elevate(), and a session-scoped in-memory resource via @owlmeans/static-resource. Apply when starting a new OwlMeans project or wiring web↔api."
3
+ applyTo: "**/modules.ts, **/context.ts, **/config.ts, **/render.tsx, **/index.tsx, **/sources/**"
4
+ scope: general
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # Building a fullstack OwlMeans Common app
9
+
10
+ A minimal app is a **bun-workspace monorepo with three packages**:
11
+
12
+ - `sources/common` — shared **entrypoints** (routes), AJV schemas, types and config. The single
13
+ source of truth.
14
+ - `sources/api` — `@owlmeans/server-app` backend; handlers attached to the shared entrypoints.
15
+ - `sources/web` — `@owlmeans/web-panel` + shadcn UI; screens attached to the same entrypoints.
16
+
17
+ Full walkthrough: `docs/getting-started.md`. To generate it: `npm create @owlmeans/app`.
18
+
19
+ ## One contract, two sides
20
+
21
+ Declare each route once in `common`, then `elevate()` it on both sides:
22
+
23
+ ```ts
24
+ // common/modules.ts
25
+ export const sessionModules = [
26
+ entrypoint(route(session.list, '/:sid/items', { parent: session.base, method: RouteMethod.GET }),
27
+ filter(params(SessionParamsSchema))),
28
+ ]
29
+ // api/modules.ts → elevate(sessionModules, session.list, handlers.list)
30
+ // web/modules.ts → elevate(modules, session.list) // call-only; screens via handler(Component)
31
+ ```
32
+
33
+ ## Config
34
+
35
+ `common/config.ts` registers both services; `base: 'api'` prefixes API routes with `/api`:
36
+
37
+ ```ts
38
+ const cfg = service({ type: AppType.Frontend, service: APP_WEB, host: 'localhost', port: 3001 })
39
+ service({ type: AppType.Backend, service: APP_API, host: 'localhost', port: 3000, base: 'api' }, cfg)
40
+ ```
41
+
42
+ ## Backend + in-memory data
43
+
44
+ ```ts
45
+ const context = makeContext(cfg, true)
46
+ appendStaticResource(context, SESSION_ITEMS) // @owlmeans/static-resource — no DB
47
+ ```
48
+
49
+ Handlers (`handleRequest`/`handleBody`/`handleParams`) use `ctx.getStaticResource<T>(alias)`.
50
+ `static-resource.list()` accepts no criteria — list all and filter in JS. `main(context, appModules)`
51
+ starts the server. Swap for `@owlmeans/mongo-resource` / `redis-resource` when you need persistence.
52
+
53
+ ## Web (shadcn)
54
+
55
+ `web-panel`'s `PanelApp` is shadcn/Tailwind v4. The app **provides** `@/lib/utils` and
56
+ `@/components/ui/{alert,button,card,input,label,progress}` at the `@` alias (→ `src`). Render with
57
+ `provide` from `@owlmeans/web-client`: `basicRender(<PanelApp context={context} provide={provide} />)`.
58
+ A parent `BASE` route renders the layout via `handler(Layout)`; `HOME` is its default child. Screens
59
+ call the API with `context.entrypoint(alias).call({ params, body })`.
60
+
61
+ This shape is auth-free by design; add `@owlmeans/server-auth` + `@owlmeans/client-auth` and
62
+ `guard(...)` when needed.
@@ -0,0 +1,47 @@
1
+ ---
2
+ description: "How to scaffold a new OwlMeans Common project — the @owlmeans/create-app CLI (npm/bun/yarn create), its flags and output, plus the manual alternative. Apply when creating or bootstrapping a fresh OwlMeans app."
3
+ applyTo: "**/package.json, **/README.md"
4
+ scope: general
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # Scaffolding a new OwlMeans Common project
9
+
10
+ Two paths produce the same minimal fullstack project (`common` + `api` + `web`, shadcn UI
11
+ navigation/layout, **no auth**, a **session-scoped in-memory resource**). Reference:
12
+ `docs/getting-started.md`.
13
+
14
+ ## Path 1 — `@owlmeans/create-app`
15
+
16
+ ```sh
17
+ npm create @owlmeans/app@latest my-app # or: bun create @owlmeans/app my-app
18
+ npx @owlmeans/create-app my-app
19
+ ```
20
+
21
+ By default it copies the template, runs `git init`, installs dependencies, and deploys agent
22
+ guidance via `@owlmeans/agent-skills` (`.claude/skills/` + `.github/instructions/`).
23
+
24
+ Flags: `--name`, `--pm <bun|npm|yarn>` (default `bun`), `--no-install`, `--no-skills`, `--no-git`,
25
+ `--yes`. Run with `cd my-app && bun run dev` (API :3000, web :3001).
26
+
27
+ Prefer this over hand-writing boilerplate when the user wants to start a new OwlMeans app.
28
+
29
+ ## Path 2 — manual
30
+
31
+ Follow Option B in `docs/getting-started.md`: bun workspace → `common` (entrypoints/schemas/config)
32
+ → `api` (`@owlmeans/server-app` + `appendStaticResource` handlers + `main`) → `web`
33
+ (`@owlmeans/web-panel` + shadcn `@`-provided primitives + layout/nav/screens). Finish with
34
+ `npx @owlmeans/agent-skills`.
35
+
36
+ ## Generated layout
37
+
38
+ ```
39
+ my-app/
40
+ ├── package.json # bun workspaces: sources/*
41
+ ├── sources/common/ # consts, types, schemas, config, modules
42
+ ├── sources/api/ # context (appendStaticResource), app/session/*, modules, index
43
+ └── sources/web/ # vite + tailwind v4, components/ui/*, layout, nav, screens, render
44
+ ```
45
+
46
+ Session items live in `@owlmeans/static-resource`, namespaced by a client-generated `sid` in
47
+ `localStorage` — no database, no authentication.
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  description: How to update Tailwind CSS, shadcn UI primitives, and related UI lib versions across shadcn-based OwlMeans packages. Distinct from the @owlmeans/* version sync (see versions.instructions.md).
3
3
  applyTo: "**/components.json"
4
+ scope: general
4
5
  ---
5
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
7
 
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  description: How to build and maintain shadcn UI + Tailwind v4 web packages in OwlMeans Common. Load when editing components.json, tailwind.config.*, globals.css, or files under components/ui/.
3
3
  applyTo: "**/components.json, **/tailwind.config.{ts,js,mjs}, **/globals.css, **/components/ui/**, **/@/lib/utils.ts"
4
+ scope: general
4
5
  ---
5
6
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
6
7
 
@@ -0,0 +1,60 @@
1
+ ---
2
+ description: "How to add agent guidance to an OwlMeans project — author a Claude Code skill (.claude/skills/<name>/SKILL.md) and the matching Copilot instruction (.github/instructions/<name>.instructions.md), keep them in sync, and choose skill vs memory. Apply when capturing knowledge as a skill or instruction."
3
+ applyTo: "**/.claude/skills/**, **/.github/instructions/**"
4
+ scope: general
5
+ ---
6
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
+
8
+ # Authoring agent guidance (skills + instructions)
9
+
10
+ OwlMeans projects carry agent guidance in two parallel places so both Claude Code and GitHub
11
+ Copilot can use it:
12
+
13
+ | Tool | Location | Shape |
14
+ |---|---|---|
15
+ | Claude Code | `.claude/skills/<name>/SKILL.md` | one directory per skill, `SKILL.md` is the entrypoint |
16
+ | GitHub Copilot | `.github/instructions/<name>.instructions.md` | one file per topic |
17
+
18
+ Author **both** for any guidance you want available to either tool, using the same `<name>` on both
19
+ sides so they read as one topic.
20
+
21
+ ## SKILL.md frontmatter (Claude Code)
22
+
23
+ ```yaml
24
+ ---
25
+ name: my-skill # the /slash-command (lowercase, hyphens)
26
+ description: What it does and WHEN to use it. # primary auto-invocation signal
27
+ user-invocable: true # false = background knowledge only
28
+ allowed-tools: Bash(bun *) Read # optional
29
+ ---
30
+ ```
31
+
32
+ The `description` decides when Claude auto-invokes the skill — describe the topic and the trigger.
33
+
34
+ ## Instruction frontmatter (Copilot)
35
+
36
+ ```yaml
37
+ ---
38
+ description: "Short summary — apply when …"
39
+ applyTo: "**/*.ts, **/relevant/**" # globs that auto-attach this instruction
40
+ ---
41
+ ```
42
+
43
+ `applyTo` controls when Copilot attaches the instruction. Use the patterns the guidance is about;
44
+ fall back to `**/*.ts, **/*.tsx` if broadly relevant.
45
+
46
+ ## Keep the pair in sync
47
+
48
+ Change one side, change the other. They need not be identical prose but must not contradict.
49
+ Cross-reference related guidance by name.
50
+
51
+ ## Skill vs memory
52
+
53
+ - **Skill / instruction** — a reusable procedure or reference worth auto-invoking.
54
+ - **Memory** (`.claude/memory/`, `.github/memory/`) — a fact, decision, or gotcha specific to this
55
+ project's history. See the `agent-memory` instruction.
56
+
57
+ ## After adding a skill
58
+
59
+ List it in `.github/memory/MEMORY.md` (and `.claude/memory/MEMORY.md`), and reference it from
60
+ `.github/copilot-instructions.md` / `CLAUDE.md` if it should be discoverable every session.
@@ -1,35 +1,91 @@
1
1
  {
2
2
  "schemaVersion": 1,
3
3
  "package": "@owlmeans/agent-skills",
4
- "version": "0.1.9",
5
- "generatedAt": "2026-06-14T13:34:06.135Z",
4
+ "version": "0.1.11",
5
+ "generatedAt": "2026-06-17T10:37:52.346Z",
6
6
  "canonicalRepo": "https://github.com/owlmeans/common",
7
7
  "entries": [
8
+ {
9
+ "kind": "skill",
10
+ "name": "getting-started",
11
+ "category": "general",
12
+ "file": "skills/getting-started/SKILL.md",
13
+ "canonicalPath": ".claude/skills/getting-started/SKILL.md"
14
+ },
8
15
  {
9
16
  "kind": "skill",
10
17
  "name": "shadcn-web",
11
- "category": "multi-package",
18
+ "category": "general",
12
19
  "file": "skills/shadcn-web/SKILL.md",
13
20
  "canonicalPath": ".claude/skills/shadcn-web/SKILL.md"
14
21
  },
22
+ {
23
+ "kind": "skill",
24
+ "name": "agent-memory",
25
+ "category": "general",
26
+ "file": "skills/agent-memory/SKILL.md",
27
+ "canonicalPath": ".claude/skills/agent-memory/SKILL.md"
28
+ },
29
+ {
30
+ "kind": "skill",
31
+ "name": "scaffolding",
32
+ "category": "general",
33
+ "file": "skills/scaffolding/SKILL.md",
34
+ "canonicalPath": ".claude/skills/scaffolding/SKILL.md"
35
+ },
36
+ {
37
+ "kind": "skill",
38
+ "name": "skill-authoring",
39
+ "category": "general",
40
+ "file": "skills/skill-authoring/SKILL.md",
41
+ "canonicalPath": ".claude/skills/skill-authoring/SKILL.md"
42
+ },
15
43
  {
16
44
  "kind": "skill",
17
45
  "name": "shadcn-versions",
18
- "category": "multi-package",
46
+ "category": "general",
19
47
  "file": "skills/shadcn-versions/SKILL.md",
20
48
  "canonicalPath": ".claude/skills/shadcn-versions/SKILL.md"
21
49
  },
50
+ {
51
+ "kind": "instruction",
52
+ "name": "getting-started",
53
+ "category": "general",
54
+ "file": "instructions/getting-started.instructions.md",
55
+ "canonicalPath": ".github/instructions/getting-started.instructions.md"
56
+ },
57
+ {
58
+ "kind": "instruction",
59
+ "name": "scaffolding",
60
+ "category": "general",
61
+ "file": "instructions/scaffolding.instructions.md",
62
+ "canonicalPath": ".github/instructions/scaffolding.instructions.md"
63
+ },
64
+ {
65
+ "kind": "instruction",
66
+ "name": "skill-authoring",
67
+ "category": "general",
68
+ "file": "instructions/skill-authoring.instructions.md",
69
+ "canonicalPath": ".github/instructions/skill-authoring.instructions.md"
70
+ },
22
71
  {
23
72
  "kind": "instruction",
24
73
  "name": "shadcn-versions",
25
- "category": "multi-package",
74
+ "category": "general",
26
75
  "file": "instructions/shadcn-versions.instructions.md",
27
76
  "canonicalPath": ".github/instructions/shadcn-versions.instructions.md"
28
77
  },
78
+ {
79
+ "kind": "instruction",
80
+ "name": "agent-memory",
81
+ "category": "general",
82
+ "file": "instructions/agent-memory.instructions.md",
83
+ "canonicalPath": ".github/instructions/agent-memory.instructions.md"
84
+ },
29
85
  {
30
86
  "kind": "instruction",
31
87
  "name": "shadcn-web",
32
- "category": "multi-package",
88
+ "category": "general",
33
89
  "file": "instructions/shadcn-web.instructions.md",
34
90
  "canonicalPath": ".github/instructions/shadcn-web.instructions.md"
35
91
  }
@@ -0,0 +1,52 @@
1
+ ---
2
+ name: agent-memory
3
+ description: How project memory works in an OwlMeans app — where to store facts/decisions/gotchas for Claude Code (.claude/memory/) and Copilot (.github/memory/), the MEMORY.md index, and when to read and write memory. Use when asked to remember something about the project, or to decide where a note belongs.
4
+ user-invocable: true
5
+ scope: general
6
+ ---
7
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
8
+
9
+ # Project memory
10
+
11
+ Memory is how durable, project-specific knowledge survives across sessions. Keep it **inside the
12
+ project**, never in `~/.claude/` or `~/.copilot/`.
13
+
14
+ | Tool | Memory dir | Index |
15
+ |---|---|---|
16
+ | Claude Code | `.claude/memory/` | `.claude/memory/MEMORY.md` |
17
+ | GitHub Copilot | `.github/memory/` | `.github/memory/MEMORY.md` |
18
+
19
+ Each memory is one Markdown file holding one fact, decision, or gotcha. The `MEMORY.md` index has a
20
+ one-line pointer per file (`- [Title](file.md) — hook`) and is loaded every session, so it must stay
21
+ current.
22
+
23
+ ## When to read memory
24
+
25
+ - **At the start of every session** — read `MEMORY.md`, then open any file relevant to the task.
26
+ - **Before a non-trivial task** — check for a memory file on that topic and read it first.
27
+ - Don't rely on assumptions when a memory file exists; read it.
28
+
29
+ ## When to write memory
30
+
31
+ After a task produces knowledge that isn't obvious from the code or git history:
32
+
33
+ - A **decision** and why it was made (and what was rejected).
34
+ - A **gotcha** or sharp edge discovered the hard way.
35
+ - **Project state / goals** not derivable from the repo.
36
+
37
+ Do **not** record what the code or git history already says, or what only matters to the current
38
+ conversation.
39
+
40
+ ## How to write one
41
+
42
+ 1. Create `.claude/memory/<topic>.md` (and the Copilot copy under `.github/memory/`) with a short
43
+ slug name and a one-line description.
44
+ 2. Write the single fact; for decisions/gotchas, add why it matters and how to apply it.
45
+ 3. Add a one-line pointer to the matching `MEMORY.md` index.
46
+ 4. Before saving, check for an existing file that already covers it — update that instead of
47
+ duplicating. Delete memories that turn out to be wrong.
48
+
49
+ ## Memory vs skill
50
+
51
+ If you are writing a **repeatable procedure** ("to do X, do Y"), that is a skill — see the
52
+ `skill-authoring` skill. Memory is for **facts about this project** ("we chose X because Y").
@@ -0,0 +1,104 @@
1
+ ---
2
+ name: getting-started
3
+ description: How to build a fullstack OwlMeans Common app from scratch — the common/api/web three-workspace pattern, context bootstrap on server and web, shared entrypoints + elevate(), and a session-scoped in-memory resource with @owlmeans/static-resource. Use when starting a new OwlMeans project, wiring web↔api, or asked how the pieces fit together.
4
+ user-invocable: true
5
+ scope: general
6
+ ---
7
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
8
+
9
+ # Getting started with OwlMeans Common — fullstack app shape
10
+
11
+ A minimal OwlMeans app is a **bun-workspace monorepo with three packages**:
12
+
13
+ ```
14
+ sources/
15
+ ├── common/ # shared entrypoints (routes), AJV schemas, types, config — the single source of truth
16
+ ├── api/ # @owlmeans/server-app backend; handlers attached to the shared entrypoints
17
+ └── web/ # @owlmeans/web-panel + shadcn UI; screens attached to the same entrypoints
18
+ ```
19
+
20
+ The full, runnable walkthrough (scaffolded **and** manual) lives in
21
+ [`docs/getting-started.md`](../../../docs/getting-started.md). To generate this exact project, use
22
+ [[scaffolding]] (`npm create @owlmeans/app`). This skill is the mental model.
23
+
24
+ ## The core idea: one contract, two sides
25
+
26
+ Declare each route once in `common` as an **entrypoint**, then `elevate()` it on each side:
27
+
28
+ ```ts
29
+ // common/modules.ts — declaration + validation, no implementation
30
+ export const sessionModules = [
31
+ entrypoint(route(session.base, '/session')),
32
+ entrypoint(route(session.list, '/:sid/items', { parent: session.base, method: RouteMethod.GET }),
33
+ filter(params<SessionParams>(SessionParamsSchema))),
34
+ entrypoint(route(session.add, '/:sid/items', { parent: session.base, method: RouteMethod.POST }),
35
+ filter(params<SessionParams>(SessionParamsSchema, body<AddItemPayload>(AddItemSchema)))),
36
+ ]
37
+ ```
38
+
39
+ ```ts
40
+ // api/modules.ts — attach handlers
41
+ elevate(sessionModules, session.list, handlers.list)
42
+ export const appModules = [...modules, ...sessionModules] // `modules` = framework defaults
43
+ ```
44
+
45
+ ```ts
46
+ // web/modules.ts — attach screen components, plus call-only elevation for backend routes
47
+ elevate(modules, session.list) // callable from the client
48
+ modules.push(entrypoint(route(web.session, '/session', frontend({ parent: BASE })), handler(SessionScreen)))
49
+ ```
50
+
51
+ Change a route or schema in `common` and both sides stay in sync. See [[entrypoint]], [[route]],
52
+ [[server-app]], [[web-client]], [[web-panel]].
53
+
54
+ ## Shared config (where services live)
55
+
56
+ `common/config.ts` registers both services so the web knows where the API is. `base: 'api'`
57
+ prefixes API routes with `/api`:
58
+
59
+ ```ts
60
+ const cfg = service({ type: AppType.Frontend, service: APP_WEB, host: 'localhost', port: 3001 })
61
+ service({ type: AppType.Backend, service: APP_API, host: 'localhost', port: 3000, base: 'api' }, cfg)
62
+ cfg.debug = { all: true }
63
+ export const commonConfig = cfg
64
+ ```
65
+
66
+ api: `config(APP_API, commonConfig)` (+ `cfg.port`). web: `config(APP_WEB, commonConfig)`. See [[config]].
67
+
68
+ ## Backend bootstrap + in-memory data
69
+
70
+ ```ts
71
+ // api/context.ts
72
+ const context = makeContext(cfg, true) // from @owlmeans/server-app
73
+ appendStaticResource(context, SESSION_ITEMS) // @owlmeans/static-resource — in-memory, no DB
74
+ ```
75
+
76
+ Handlers use `handleRequest` / `handleBody` / `handleParams` (validated payload, then context, then
77
+ req). Read/write `ctx.getStaticResource<T>(alias)` — full CRUD (`get/load/list/create/save/delete`).
78
+ **`static-resource.list()` takes no criteria** — list all and filter in JS (e.g. by `sessionId`).
79
+ `main(context, appModules)` starts the server. See [[static-resource]], [[server-app]], [[resource]].
80
+
81
+ Swap `@owlmeans/static-resource` for [[mongo-resource]] / [[redis-resource]] when you need
82
+ persistence — the handler shape is identical.
83
+
84
+ ## Web bootstrap (shadcn)
85
+
86
+ `@owlmeans/web-panel`'s `PanelApp` is shadcn/Tailwind v4 (no MUI). The **app provides** the shadcn
87
+ primitives at the `@` alias — `web-panel` references `@/lib/utils` and
88
+ `@/components/ui/{alert,button,card,input,label,progress}`; copy those into `src/`. Render with
89
+ `provide` from `@owlmeans/web-client`:
90
+
91
+ ```tsx
92
+ basicRender(<PanelApp context={context} provide={provide} />)
93
+ ```
94
+
95
+ `vite.config.ts` sets `@`→`src`, `@tailwindcss/vite`, and dedupes the owlmeans/react singletons.
96
+ `index.css` is `@import "tailwindcss";` + a shadcn `@theme` token block (replaces `@owlmeans/owl-theme`).
97
+ A parent `BASE` route renders the layout via `handler(LayoutComponent)`; `HOME` is its default child.
98
+ Screens call the backend with `context.entrypoint(alias).call({ params, body })` → `[data, outcome]`.
99
+ See [[web-panel]], [[web-client]], [[shadcn-web]], [[client-entrypoint]].
100
+
101
+ ## Authentication
102
+
103
+ This shape is intentionally **auth-free**. To add it: `@owlmeans/server-auth` + `@owlmeans/client-auth`
104
+ and `guard(...)` on entrypoints. See [[auth-protocol]], [[server-auth]], [[client-auth]].
@@ -0,0 +1,76 @@
1
+ ---
2
+ name: scaffolding
3
+ description: How to scaffold a new OwlMeans Common project — the @owlmeans/create-app CLI (npm/bun/yarn create), its flags, what it generates, and the manual alternative. Use when asked to create/bootstrap/start a new OwlMeans app or set up a fresh project.
4
+ user-invocable: true
5
+ scope: general
6
+ ---
7
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
8
+
9
+ # Scaffolding a new OwlMeans Common project
10
+
11
+ Two paths produce the same minimal fullstack project (`common` + `api` + `web`, shadcn UI
12
+ navigation/layout, **no auth**, a **session-scoped in-memory resource**). The full reference is
13
+ [`docs/getting-started.md`](../../../docs/getting-started.md); the framework shape is [[getting-started]].
14
+
15
+ ## Path 1 — `@owlmeans/create-app` (one command)
16
+
17
+ ```sh
18
+ npm create @owlmeans/app@latest my-app
19
+ bun create @owlmeans/app my-app
20
+ yarn create @owlmeans/app my-app
21
+ npx @owlmeans/create-app my-app
22
+ ```
23
+
24
+ By default it copies the template, runs `git init`, installs dependencies, and **deploys agent
25
+ guidance** into the project via [[bun]]-installed `@owlmeans/agent-skills` (`.claude/skills/` +
26
+ `.github/instructions/`).
27
+
28
+ Flags: `--name <name>`, `--pm <bun|npm|yarn>` (default `bun`), `--no-install`, `--no-skills`,
29
+ `--no-git`, `--yes`/`-y`, `--help`.
30
+
31
+ Then run:
32
+
33
+ ```sh
34
+ cd my-app && bun run dev # API :3000, web :3001
35
+ ```
36
+
37
+ **When an agent should use this:** the user asks to start/bootstrap/create a new OwlMeans app from
38
+ nothing. Prefer it over hand-writing boilerplate. After it runs, point the user at the **Session**
39
+ screen and `docs/getting-started.md`.
40
+
41
+ ## Path 2 — manual
42
+
43
+ Follow **Option B** in [`docs/getting-started.md`](../../../docs/getting-started.md): create the bun
44
+ workspace, then `common` (shared entrypoints/schemas/config), `api` (`@owlmeans/server-app` +
45
+ `appendStaticResource` handlers + `main`), and `web` (`@owlmeans/web-panel` + shadcn `@`-provided
46
+ primitives + layout/nav/screens). Finish with `npx @owlmeans/agent-skills` to add agent guidance.
47
+
48
+ ## What gets generated
49
+
50
+ ```
51
+ my-app/
52
+ ├── package.json # bun workspaces: sources/*
53
+ ├── CLAUDE.md # agent context: memory + skill directives + project-purpose placeholder
54
+ ├── .github/
55
+ │ ├── copilot-instructions.md # same directives for Copilot
56
+ │ └── memory/MEMORY.md # starter memory index
57
+ ├── .claude/memory/MEMORY.md # starter memory index
58
+ ├── sources/common/ # consts, types, schemas, config, modules (entrypoints)
59
+ ├── sources/api/ # context.ts (appendStaticResource), app/session/*, modules.ts, index.ts
60
+ └── sources/web/ # vite + tailwind v4, components/ui/*, layout, nav, screens, render.tsx
61
+ ```
62
+
63
+ The session demo stores items in `@owlmeans/static-resource`, namespaced by a client-generated
64
+ `sid` kept in `localStorage` — no database, no authentication. See [[static-resource]],
65
+ [[server-app]], [[web-panel]].
66
+
67
+ ## Generated agent guidance
68
+
69
+ `CLAUDE.md` and `.github/copilot-instructions.md` carry the same memory and skill directives as a
70
+ real OwlMeans project, plus a **project-purpose placeholder** (`<!-- OWLMEANS:PROJECT-PURPOSE -->`).
71
+ On the first agent session that block instructs the agent to ask the user what the project is for and
72
+ replace it in both files. The deployed skills include the general management skills
73
+ [[skill-authoring]] and [[agent-memory]] so the project can grow its own guidance. After adding any
74
+ `@owlmeans/*` dependency, re-run `npx @owlmeans/agent-skills` — discovery scans **every**
75
+ `node_modules/@owlmeans` in the workspace (root and nested under `sources/*`), so package-specific
76
+ skills are picked up even though bun nests them.
@@ -2,6 +2,7 @@
2
2
  name: shadcn-versions
3
3
  description: How to update versions of shadcn UI primitives, Tailwind CSS v4, and related UI libs across all shadcn-based OwlMeans web packages. Distinct from the @owlmeans/* package version sync (see [[versions]] skill). Use when bumping tailwind, shadcn components, or utility libs.
4
4
  allowed-tools: Bash(grep *), Bash(sed *), Bash(bun install)
5
+ scope: general
5
6
  ---
6
7
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
8
 
@@ -2,6 +2,7 @@
2
2
  name: shadcn-web
3
3
  description: How to build and maintain shadcn UI + Tailwind v4 web packages in the OwlMeans framework. Auto-invoked when editing components.json, tailwind.config.*, globals.css, or files under components/ui/. Use when creating a new shadcn-based web package or adding shadcn components.
4
4
  user-invocable: false
5
+ scope: general
5
6
  ---
6
7
  <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
7
8
 
@@ -0,0 +1,79 @@
1
+ ---
2
+ name: skill-authoring
3
+ description: How to add agent guidance to an OwlMeans project — author a Claude Code skill (.claude/skills/<name>/SKILL.md) and its matching GitHub Copilot instruction (.github/instructions/<name>.instructions.md), keep the two in sync, choose frontmatter, and decide skill vs memory. Use when asked to capture knowledge as a skill, add a slash command, or document a repeatable procedure.
4
+ user-invocable: true
5
+ scope: general
6
+ ---
7
+ <!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->
8
+
9
+ # Authoring agent guidance (skills + instructions)
10
+
11
+ OwlMeans projects carry agent guidance in two parallel places so both Claude Code and GitHub
12
+ Copilot can use it:
13
+
14
+ | Tool | Location | Shape |
15
+ |---|---|---|
16
+ | Claude Code | `.claude/skills/<name>/SKILL.md` | one directory per skill, `SKILL.md` is the entrypoint |
17
+ | GitHub Copilot | `.github/instructions/<name>.instructions.md` | one file per topic |
18
+
19
+ Author **both** for any guidance you want available to either tool. Keep the same `<name>` on both
20
+ sides so they read as one topic.
21
+
22
+ ## SKILL.md (Claude Code)
23
+
24
+ ```
25
+ .claude/skills/<name>/
26
+ ├── SKILL.md # required — frontmatter + body
27
+ ├── reference.md # optional — deeper detail loaded on demand
28
+ └── scripts/ # optional — shell scripts the skill runs
29
+ ```
30
+
31
+ Frontmatter:
32
+
33
+ ```yaml
34
+ ---
35
+ name: my-skill # the /slash-command (lowercase, hyphens)
36
+ description: What it does and WHEN to use it. # primary auto-invocation signal
37
+ user-invocable: true # false = background knowledge only, hidden from the / menu
38
+ allowed-tools: Bash(bun *) Read # optional — tools usable without per-call approval
39
+ ---
40
+ ```
41
+
42
+ The `description` is the most important field: Claude uses it to decide when to auto-invoke the
43
+ skill, so describe both the topic and the trigger ("Use when …").
44
+
45
+ ## <name>.instructions.md (Copilot)
46
+
47
+ ```yaml
48
+ ---
49
+ description: "Short summary — apply when …"
50
+ applyTo: "**/*.ts, **/relevant/**" # globs that auto-attach this instruction
51
+ ---
52
+ ```
53
+
54
+ `applyTo` controls when Copilot pulls the instruction in. Use the file patterns the guidance is
55
+ actually about; fall back to `**/*.ts, **/*.tsx` if it is broadly relevant.
56
+
57
+ ## Keep the pair in sync
58
+
59
+ When you change one side, change the other. They do not have to be identical prose, but they must
60
+ not contradict each other. Cross-reference related guidance by name (e.g. "see the `getting-started`
61
+ skill").
62
+
63
+ ## Skill vs memory
64
+
65
+ - **Skill / instruction** — a reusable procedure or reference you (or the tools) will want again,
66
+ worth auto-invoking. Lives in `.claude/skills/` + `.github/instructions/`.
67
+ - **Memory** — a fact, decision, or gotcha specific to this project's history/state. Lives in
68
+ `.claude/memory/` + `.github/memory/`. See the `agent-memory` skill.
69
+
70
+ If you find yourself writing "last time we…", that is memory. If you are writing "to do X, do Y",
71
+ that is a skill.
72
+
73
+ ## After adding a skill
74
+
75
+ 1. If it replaces an ad-hoc `.claude/<topic>.md`, remove that file.
76
+ 2. Mention it in `.claude/memory/MEMORY.md` (and `.github/memory/MEMORY.md`) so future sessions know
77
+ it exists.
78
+ 3. Reference it from `CLAUDE.md` / `.github/copilot-instructions.md` if it should be discoverable
79
+ every session.
@@ -1,7 +1,7 @@
1
1
  export interface ManifestEntry {
2
2
  kind: 'skill' | 'instruction';
3
3
  name: string;
4
- category: 'package-specific' | 'multi-package';
4
+ category: 'package-specific' | 'multi-package' | 'general';
5
5
  file: string;
6
6
  canonicalPath: string;
7
7
  }
@@ -16,7 +16,7 @@ export interface Manifest {
16
16
  export interface DiscoveredEntry {
17
17
  kind: 'skill' | 'instruction';
18
18
  name: string;
19
- category: 'package-specific' | 'multi-package';
19
+ category: 'package-specific' | 'multi-package' | 'general';
20
20
  /** Absolute path to the embedded source file. */
21
21
  sourcePath: string;
22
22
  canonicalPath: string;
@@ -1 +1 @@
1
- {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,GAAG,aAAa,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,kBAAkB,GAAG,eAAe,CAAA;IAC9C,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,aAAa,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,aAAa,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,kBAAkB,GAAG,eAAe,CAAA;IAC9C,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAA;CACjB;AA4FD,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,2BAA2B;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,mEAAmE;AACnE,eAAO,MAAM,QAAQ,GAAI,WAAW,MAAM,EAAE,OAAM,eAAoB,KAAG,eAAe,EA4CvF,CAAA"}
1
+ {"version":3,"file":"discover.d.ts","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,OAAO,GAAG,aAAa,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,kBAAkB,GAAG,eAAe,GAAG,SAAS,CAAA;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,aAAa,EAAE,MAAM,CAAA;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,MAAM,CAAA;IACf,WAAW,EAAE,MAAM,CAAA;IACnB,aAAa,EAAE,MAAM,CAAA;IACrB,OAAO,EAAE,aAAa,EAAE,CAAA;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,aAAa,CAAA;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,QAAQ,EAAE,kBAAkB,GAAG,eAAe,GAAG,SAAS,CAAA;IAC1D,iDAAiD;IACjD,UAAU,EAAE,MAAM,CAAA;IAClB,aAAa,EAAE,MAAM,CAAA;IACrB,8BAA8B;IAC9B,WAAW,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,uDAAuD;IACvD,OAAO,EAAE,OAAO,CAAA;CACjB;AA+KD,MAAM,WAAW,eAAe;IAC9B,wDAAwD;IACxD,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;IACf,2BAA2B;IAC3B,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAA;CACtB;AAED,mEAAmE;AACnE,eAAO,MAAM,QAAQ,GAAI,WAAW,MAAM,EAAE,OAAM,eAAoB,KAAG,eAAe,EA4CvF,CAAA"}
package/build/discover.js CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs';
1
+ import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  /** Parse a manifest.json, return null if invalid/unparseable. */
@@ -14,27 +14,85 @@ const parseManifest = (manifestPath) => {
14
14
  return null;
15
15
  }
16
16
  };
17
- // Scan node_modules/@owlmeans/*/agent-meta/ for manifests.
18
- const scanNodeModules = (targetDir) => {
19
- const nmDir = join(targetDir, 'node_modules', '@owlmeans');
20
- if (!existsSync(nmDir))
21
- return [];
22
- const entries = [];
17
+ const OWLMEANS_SCOPE = '@owlmeans';
18
+ /**
19
+ * Collect every existing `<...>/node_modules/@owlmeans` directory in the project
20
+ * tree. A scaffolded app is a bun workspace whose `@owlmeans/*` deps belong to its
21
+ * `sources/*` packages (the root has none), so bun may keep them under
22
+ * `sources/<pkg>/node_modules/@owlmeans` rather than at the root. Scanning only the
23
+ * root would miss them — so we walk the whole tree.
24
+ *
25
+ * The walk is bounded: it never descends into a `node_modules` directory (it just
26
+ * records its `@owlmeans` scope), skips hidden dirs (`.git`, `.github`, …), and
27
+ * tracks visited realpaths to avoid symlink cycles.
28
+ */
29
+ const collectScopeDirs = (targetDir) => {
30
+ const found = [];
31
+ const seen = new Set();
32
+ const walk = (dir) => {
33
+ let real;
34
+ try {
35
+ real = realpathSync(dir);
36
+ }
37
+ catch {
38
+ return;
39
+ }
40
+ if (seen.has(real))
41
+ return;
42
+ seen.add(real);
43
+ let dirents;
44
+ try {
45
+ dirents = readdirSync(dir, { withFileTypes: true });
46
+ }
47
+ catch {
48
+ return;
49
+ }
50
+ for (const dirent of dirents) {
51
+ const name = dirent.name;
52
+ if (name === 'node_modules') {
53
+ const scope = join(dir, 'node_modules', OWLMEANS_SCOPE);
54
+ if (existsSync(scope))
55
+ found.push(scope);
56
+ // never descend into node_modules
57
+ continue;
58
+ }
59
+ if (name.startsWith('.'))
60
+ continue;
61
+ if (!dirent.isDirectory())
62
+ continue;
63
+ walk(join(dir, name));
64
+ }
65
+ };
66
+ walk(targetDir);
67
+ return found;
68
+ };
69
+ /** Read all @owlmeans packages under a single `.../node_modules/@owlmeans` dir. */
70
+ const scanScopeDir = (scopeDir) => {
23
71
  let pkgDirs;
24
72
  try {
25
- pkgDirs = readdirSync(nmDir);
73
+ pkgDirs = readdirSync(scopeDir);
26
74
  }
27
75
  catch {
28
76
  return [];
29
77
  }
78
+ const packages = [];
30
79
  for (const pkgDir of pkgDirs) {
31
- const manifestPath = join(nmDir, pkgDir, 'agent-meta', 'manifest.json');
80
+ const pkgPath = join(scopeDir, pkgDir);
81
+ const agentMetaDir = join(pkgPath, 'agent-meta');
82
+ const manifestPath = join(agentMetaDir, 'manifest.json');
32
83
  if (!existsSync(manifestPath))
33
84
  continue;
34
85
  const manifest = parseManifest(manifestPath);
35
86
  if (manifest == null)
36
87
  continue;
37
- const agentMetaDir = join(nmDir, pkgDir, 'agent-meta');
88
+ let realDir;
89
+ try {
90
+ realDir = realpathSync(pkgPath);
91
+ }
92
+ catch {
93
+ realDir = pkgPath;
94
+ }
95
+ const entries = [];
38
96
  for (const e of manifest.entries) {
39
97
  const sourcePath = join(agentMetaDir, e.file);
40
98
  if (!existsSync(sourcePath))
@@ -50,6 +108,23 @@ const scanNodeModules = (targetDir) => {
50
108
  isExtra: false,
51
109
  });
52
110
  }
111
+ if (entries.length > 0)
112
+ packages.push({ realDir, entries });
113
+ }
114
+ return packages;
115
+ };
116
+ // Scan every node_modules/@owlmeans/*/agent-meta/ in the project tree for manifests.
117
+ const scanNodeModules = (targetDir) => {
118
+ const entries = [];
119
+ const seenPkgDirs = new Set();
120
+ for (const scopeDir of collectScopeDirs(targetDir)) {
121
+ for (const pkg of scanScopeDir(scopeDir)) {
122
+ // Skip a package already read via another (symlinked) location.
123
+ if (seenPkgDirs.has(pkg.realDir))
124
+ continue;
125
+ seenPkgDirs.add(pkg.realDir);
126
+ entries.push(...pkg.entries);
127
+ }
53
128
  }
54
129
  return entries;
55
130
  };
@@ -1 +1 @@
1
- {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"discover.js","sourceRoot":"","sources":["../src/discover.ts"],"names":[],"mappings":"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"}
package/build/linked.d.ts CHANGED
@@ -10,6 +10,15 @@ export interface LinkedResult {
10
10
  *
11
11
  * A package is considered linked when its node_modules/@owlmeans/<pkg>
12
12
  * entry is a symlink whose real path escapes the node_modules tree.
13
+ *
14
+ * This intentionally scans only the ROOT node_modules, even though discovery
15
+ * (see discover.ts) now walks the whole tree. Dev-linked monorepos (e.g. a project
16
+ * that symlinks the common source under libraries/*) hoist @owlmeans/* to the root
17
+ * node_modules as escaping symlinks, so the root scan catches them. A standalone
18
+ * scaffolded app has no root @owlmeans scope at all (its deps nest under
19
+ * sources/*\/node_modules), so this correctly returns { linked: false } and the
20
+ * install proceeds. Scanning nested node_modules here would risk false positives on
21
+ * bun's in-tree store symlinks, so we deliberately do not.
13
22
  */
14
23
  export declare const detectLinked: (targetDir: string) => LinkedResult;
15
24
  //# sourceMappingURL=linked.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"linked.d.ts","sourceRoot":"","sources":["../src/linked.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAA;IACf,qDAAqD;IACrD,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,KAAG,YAiChD,CAAA"}
1
+ {"version":3,"file":"linked.d.ts","sourceRoot":"","sources":["../src/linked.ts"],"names":[],"mappings":"AAGA,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,OAAO,CAAA;IACf,qDAAqD;IACrD,QAAQ,EAAE,MAAM,EAAE,CAAA;CACnB;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,eAAO,MAAM,YAAY,GAAI,WAAW,MAAM,KAAG,YAiChD,CAAA"}
package/build/linked.js CHANGED
@@ -7,6 +7,15 @@ import { join, resolve } from 'node:path';
7
7
  *
8
8
  * A package is considered linked when its node_modules/@owlmeans/<pkg>
9
9
  * entry is a symlink whose real path escapes the node_modules tree.
10
+ *
11
+ * This intentionally scans only the ROOT node_modules, even though discovery
12
+ * (see discover.ts) now walks the whole tree. Dev-linked monorepos (e.g. a project
13
+ * that symlinks the common source under libraries/*) hoist @owlmeans/* to the root
14
+ * node_modules as escaping symlinks, so the root scan catches them. A standalone
15
+ * scaffolded app has no root @owlmeans scope at all (its deps nest under
16
+ * sources/*\/node_modules), so this correctly returns { linked: false } and the
17
+ * install proceeds. Scanning nested node_modules here would risk false positives on
18
+ * bun's in-tree store symlinks, so we deliberately do not.
10
19
  */
11
20
  export const detectLinked = (targetDir) => {
12
21
  const nmScope = join(targetDir, 'node_modules', '@owlmeans');
@@ -1 +1 @@
1
- {"version":3,"file":"linked.js","sourceRoot":"","sources":["../src/linked.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC1E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQzC;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAgB,EAAE;IAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,WAAW,CAAC,CAAA;IAC5D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IAEhE,IAAI,QAAkB,CAAA;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IACxC,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAA;IAEtD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;YAC/B,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,CAAA;gBACjC,SAAQ;YACV,CAAC;YACD,qEAAqE;YACrE,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;YAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC;gBACpE,QAAQ,CAAC,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAA;AAClD,CAAC,CAAA"}
1
+ {"version":3,"file":"linked.js","sourceRoot":"","sources":["../src/linked.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC1E,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAQzC;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAiB,EAAgB,EAAE;IAC9D,MAAM,OAAO,GAAG,IAAI,CAAC,SAAS,EAAE,cAAc,EAAE,WAAW,CAAC,CAAA;IAC5D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IAEhE,IAAI,QAAkB,CAAA;IACtB,IAAI,CAAC;QACH,QAAQ,GAAG,WAAW,CAAC,OAAO,CAAC,CAAA;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAA;IACxC,CAAC;IAED,MAAM,QAAQ,GAAa,EAAE,CAAA;IAC7B,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,SAAS,EAAE,cAAc,CAAC,CAAC,CAAA;IAEtD,KAAK,MAAM,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,EAAE,GAAG,CAAC,CAAA;QAClC,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,SAAS,CAAC,OAAO,CAAC,CAAA;YAC/B,IAAI,IAAI,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC1B,QAAQ,CAAC,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,CAAA;gBACjC,SAAQ;YACV,CAAC;YACD,qEAAqE;YACrE,MAAM,IAAI,GAAG,YAAY,CAAC,OAAO,CAAC,CAAA;YAClC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC,KAAK,GAAG,IAAI,CAAC,EAAE,CAAC;gBACpE,QAAQ,CAAC,IAAI,CAAC,aAAa,GAAG,EAAE,CAAC,CAAA;YACnC,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,qBAAqB;QACvB,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAA;AAClD,CAAC,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@owlmeans/agent-skills",
3
- "version": "0.1.9",
3
+ "version": "0.1.11",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "scripts": {
package/src/discover.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { existsSync, readdirSync, readFileSync } from 'node:fs'
1
+ import { existsSync, readdirSync, readFileSync, realpathSync } from 'node:fs'
2
2
  import { dirname, join } from 'node:path'
3
3
  import { fileURLToPath } from 'node:url'
4
4
 
5
5
  export interface ManifestEntry {
6
6
  kind: 'skill' | 'instruction'
7
7
  name: string
8
- category: 'package-specific' | 'multi-package'
8
+ category: 'package-specific' | 'multi-package' | 'general'
9
9
  file: string
10
10
  canonicalPath: string
11
11
  }
@@ -22,7 +22,7 @@ export interface Manifest {
22
22
  export interface DiscoveredEntry {
23
23
  kind: 'skill' | 'instruction'
24
24
  name: string
25
- category: 'package-specific' | 'multi-package'
25
+ category: 'package-specific' | 'multi-package' | 'general'
26
26
  /** Absolute path to the embedded source file. */
27
27
  sourcePath: string
28
28
  canonicalPath: string
@@ -45,26 +45,91 @@ const parseManifest = (manifestPath: string): Manifest | null => {
45
45
  }
46
46
  }
47
47
 
48
- // Scan node_modules/@owlmeans/*/agent-meta/ for manifests.
49
- const scanNodeModules = (targetDir: string): DiscoveredEntry[] => {
50
- const nmDir = join(targetDir, 'node_modules', '@owlmeans')
51
- if (!existsSync(nmDir)) return []
48
+ const OWLMEANS_SCOPE = '@owlmeans'
52
49
 
53
- const entries: DiscoveredEntry[] = []
50
+ /**
51
+ * Collect every existing `<...>/node_modules/@owlmeans` directory in the project
52
+ * tree. A scaffolded app is a bun workspace whose `@owlmeans/*` deps belong to its
53
+ * `sources/*` packages (the root has none), so bun may keep them under
54
+ * `sources/<pkg>/node_modules/@owlmeans` rather than at the root. Scanning only the
55
+ * root would miss them — so we walk the whole tree.
56
+ *
57
+ * The walk is bounded: it never descends into a `node_modules` directory (it just
58
+ * records its `@owlmeans` scope), skips hidden dirs (`.git`, `.github`, …), and
59
+ * tracks visited realpaths to avoid symlink cycles.
60
+ */
61
+ const collectScopeDirs = (targetDir: string): string[] => {
62
+ const found: string[] = []
63
+ const seen = new Set<string>()
64
+
65
+ const walk = (dir: string): void => {
66
+ let real: string
67
+ try {
68
+ real = realpathSync(dir)
69
+ } catch {
70
+ return
71
+ }
72
+ if (seen.has(real)) return
73
+ seen.add(real)
74
+
75
+ let dirents: import('node:fs').Dirent[]
76
+ try {
77
+ dirents = readdirSync(dir, { withFileTypes: true })
78
+ } catch {
79
+ return
80
+ }
81
+
82
+ for (const dirent of dirents) {
83
+ const name = dirent.name
84
+ if (name === 'node_modules') {
85
+ const scope = join(dir, 'node_modules', OWLMEANS_SCOPE)
86
+ if (existsSync(scope)) found.push(scope)
87
+ // never descend into node_modules
88
+ continue
89
+ }
90
+ if (name.startsWith('.')) continue
91
+ if (!dirent.isDirectory()) continue
92
+ walk(join(dir, name))
93
+ }
94
+ }
95
+
96
+ walk(targetDir)
97
+ return found
98
+ }
99
+
100
+ interface ScopePackage {
101
+ /** Realpath of the package dir, used to dedup the same physical package read via
102
+ * multiple (symlinked) locations. */
103
+ realDir: string
104
+ entries: DiscoveredEntry[]
105
+ }
106
+
107
+ /** Read all @owlmeans packages under a single `.../node_modules/@owlmeans` dir. */
108
+ const scanScopeDir = (scopeDir: string): ScopePackage[] => {
54
109
  let pkgDirs: string[]
55
110
  try {
56
- pkgDirs = readdirSync(nmDir)
111
+ pkgDirs = readdirSync(scopeDir)
57
112
  } catch {
58
113
  return []
59
114
  }
60
115
 
116
+ const packages: ScopePackage[] = []
61
117
  for (const pkgDir of pkgDirs) {
62
- const manifestPath = join(nmDir, pkgDir, 'agent-meta', 'manifest.json')
118
+ const pkgPath = join(scopeDir, pkgDir)
119
+ const agentMetaDir = join(pkgPath, 'agent-meta')
120
+ const manifestPath = join(agentMetaDir, 'manifest.json')
63
121
  if (!existsSync(manifestPath)) continue
64
122
  const manifest = parseManifest(manifestPath)
65
123
  if (manifest == null) continue
66
124
 
67
- const agentMetaDir = join(nmDir, pkgDir, 'agent-meta')
125
+ let realDir: string
126
+ try {
127
+ realDir = realpathSync(pkgPath)
128
+ } catch {
129
+ realDir = pkgPath
130
+ }
131
+
132
+ const entries: DiscoveredEntry[] = []
68
133
  for (const e of manifest.entries) {
69
134
  const sourcePath = join(agentMetaDir, e.file)
70
135
  if (!existsSync(sourcePath)) continue
@@ -79,6 +144,24 @@ const scanNodeModules = (targetDir: string): DiscoveredEntry[] => {
79
144
  isExtra: false,
80
145
  })
81
146
  }
147
+ if (entries.length > 0) packages.push({ realDir, entries })
148
+ }
149
+
150
+ return packages
151
+ }
152
+
153
+ // Scan every node_modules/@owlmeans/*/agent-meta/ in the project tree for manifests.
154
+ const scanNodeModules = (targetDir: string): DiscoveredEntry[] => {
155
+ const entries: DiscoveredEntry[] = []
156
+ const seenPkgDirs = new Set<string>()
157
+
158
+ for (const scopeDir of collectScopeDirs(targetDir)) {
159
+ for (const pkg of scanScopeDir(scopeDir)) {
160
+ // Skip a package already read via another (symlinked) location.
161
+ if (seenPkgDirs.has(pkg.realDir)) continue
162
+ seenPkgDirs.add(pkg.realDir)
163
+ entries.push(...pkg.entries)
164
+ }
82
165
  }
83
166
 
84
167
  return entries
package/src/linked.ts CHANGED
@@ -14,6 +14,15 @@ export interface LinkedResult {
14
14
  *
15
15
  * A package is considered linked when its node_modules/@owlmeans/<pkg>
16
16
  * entry is a symlink whose real path escapes the node_modules tree.
17
+ *
18
+ * This intentionally scans only the ROOT node_modules, even though discovery
19
+ * (see discover.ts) now walks the whole tree. Dev-linked monorepos (e.g. a project
20
+ * that symlinks the common source under libraries/*) hoist @owlmeans/* to the root
21
+ * node_modules as escaping symlinks, so the root scan catches them. A standalone
22
+ * scaffolded app has no root @owlmeans scope at all (its deps nest under
23
+ * sources/*\/node_modules), so this correctly returns { linked: false } and the
24
+ * install proceeds. Scanning nested node_modules here would risk false positives on
25
+ * bun's in-tree store symlinks, so we deliberately do not.
17
26
  */
18
27
  export const detectLinked = (targetDir: string): LinkedResult => {
19
28
  const nmScope = join(targetDir, 'node_modules', '@owlmeans')
@@ -142,6 +142,57 @@ describe('discover()', () => {
142
142
  const entries = discover(tmpDir, { extras: false, copilotOnly: true })
143
143
  expect(entries.every(e => e.kind === 'instruction')).toBe(true)
144
144
  })
145
+
146
+ it('discovers entries nested in workspace package node_modules (bun monorepo)', () => {
147
+ // Scaffolded bun workspace: root has no @owlmeans; deps live under sources/*.
148
+ writeFixturePackage(join(tmpDir, 'sources', 'web', 'node_modules'), '@owlmeans/web-panel', '0.1.9', [
149
+ { kind: 'skill', name: 'web-panel' },
150
+ { kind: 'instruction', name: 'web-panel' },
151
+ ])
152
+ writeFixturePackage(join(tmpDir, 'sources', 'api', 'node_modules'), '@owlmeans/server-app', '0.1.9', [
153
+ { kind: 'skill', name: 'server-app' },
154
+ ])
155
+ const entries = discover(tmpDir, { extras: false })
156
+ expect(entries.some(e => e.kind === 'skill' && e.name === 'web-panel')).toBe(true)
157
+ expect(entries.some(e => e.kind === 'instruction' && e.name === 'web-panel')).toBe(true)
158
+ expect(entries.some(e => e.kind === 'skill' && e.name === 'server-app')).toBe(true)
159
+ })
160
+
161
+ it('combines root and nested node_modules', () => {
162
+ writeFixturePackage(join(tmpDir, 'node_modules'), '@owlmeans/context', '0.1.9', [
163
+ { kind: 'skill', name: 'context' },
164
+ ])
165
+ writeFixturePackage(join(tmpDir, 'sources', 'web', 'node_modules'), '@owlmeans/web-panel', '0.1.9', [
166
+ { kind: 'skill', name: 'web-panel' },
167
+ ])
168
+ const entries = discover(tmpDir, { extras: false })
169
+ expect(entries.some(e => e.name === 'context')).toBe(true)
170
+ expect(entries.some(e => e.name === 'web-panel')).toBe(true)
171
+ })
172
+
173
+ it('does not double-count a package symlinked into multiple workspaces', () => {
174
+ // Physical package under the root store…
175
+ writeFixturePackage(join(tmpDir, 'node_modules'), '@owlmeans/context', '0.1.9', [
176
+ { kind: 'skill', name: 'context' },
177
+ ])
178
+ const realPkg = join(tmpDir, 'node_modules', '@owlmeans', 'context')
179
+ // …symlinked into a workspace's node_modules (as bun's store linker may do).
180
+ const nestedScope = join(tmpDir, 'sources', 'web', 'node_modules', '@owlmeans')
181
+ mkdirSync(nestedScope, { recursive: true })
182
+ symlinkSync(realPkg, join(nestedScope, 'context'))
183
+
184
+ const entries = discover(tmpDir, { extras: false })
185
+ expect(entries.filter(e => e.name === 'context')).toHaveLength(1)
186
+ })
187
+
188
+ it('ignores hidden directories while walking', () => {
189
+ // A stray manifest under a hidden dir must not be picked up.
190
+ writeFixturePackage(join(tmpDir, '.git', 'node_modules'), '@owlmeans/ghost', '0.1.9', [
191
+ { kind: 'skill', name: 'ghost' },
192
+ ])
193
+ const entries = discover(tmpDir, { extras: false })
194
+ expect(entries.some(e => e.name === 'ghost')).toBe(false)
195
+ })
145
196
  })
146
197
 
147
198
  // ---------------------------------------------------------------------------