@owlmeans/create-app 0.1.18-rc.8 → 0.1.19-rc.0

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.
Files changed (116) hide show
  1. package/README.md +49 -5
  2. package/THIRD-PARTY-NOTICES.txt +24 -0
  3. package/build/args/types.d.ts +9 -0
  4. package/build/args/types.d.ts.map +1 -0
  5. package/build/args/types.js +2 -0
  6. package/build/args/types.js.map +1 -0
  7. package/build/args.d.ts +3 -20
  8. package/build/args.d.ts.map +1 -1
  9. package/build/args.js +121 -88
  10. package/build/args.js.map +1 -1
  11. package/build/bin.js +3 -3
  12. package/build/bin.js.map +1 -1
  13. package/build/consts.d.ts +6 -0
  14. package/build/consts.d.ts.map +1 -0
  15. package/build/consts.js +6 -0
  16. package/build/consts.js.map +1 -0
  17. package/build/consts.local.d.ts +12 -0
  18. package/build/consts.local.d.ts.map +1 -0
  19. package/build/consts.local.js +49 -0
  20. package/build/consts.local.js.map +1 -0
  21. package/build/index.d.ts +11 -4
  22. package/build/index.d.ts.map +1 -1
  23. package/build/index.js +5 -2
  24. package/build/index.js.map +1 -1
  25. package/build/naming/types.d.ts +14 -0
  26. package/build/naming/types.d.ts.map +1 -0
  27. package/build/naming/types.js +2 -0
  28. package/build/naming/types.js.map +1 -0
  29. package/build/naming.d.ts +4 -0
  30. package/build/naming.d.ts.map +1 -0
  31. package/build/naming.js +18 -0
  32. package/build/naming.js.map +1 -0
  33. package/build/run.d.ts +1 -1
  34. package/build/run.d.ts.map +1 -1
  35. package/build/run.js +23 -17
  36. package/build/run.js.map +1 -1
  37. package/build/scaffold.d.ts +8 -0
  38. package/build/scaffold.d.ts.map +1 -0
  39. package/build/scaffold.js +19 -0
  40. package/build/scaffold.js.map +1 -0
  41. package/build/template/types.d.ts +16 -0
  42. package/build/template/types.d.ts.map +1 -0
  43. package/build/template/types.js +2 -0
  44. package/build/template/types.js.map +1 -0
  45. package/build/template.d.ts +3 -16
  46. package/build/template.d.ts.map +1 -1
  47. package/build/template.js +69 -52
  48. package/build/template.js.map +1 -1
  49. package/build/types.d.ts +61 -0
  50. package/build/types.d.ts.map +1 -0
  51. package/build/types.js +2 -0
  52. package/build/types.js.map +1 -0
  53. package/package.json +8 -6
  54. package/template/AGENTS.md +61 -2
  55. package/template/README.bare.md +70 -0
  56. package/template/README.md +9 -60
  57. package/template/_agents/scripts/link-skills.sh +327 -15
  58. package/template/_agents/skills/agent-memory/SKILL.md +12 -11
  59. package/template/_agents/skills/getting-started/SKILL.md +38 -79
  60. package/template/_agents/skills/logging/SKILL.md +41 -0
  61. package/template/_agents/skills/memory-promotion/SKILL.md +10 -4
  62. package/template/_agents/skills/memory-recompact/SKILL.md +15 -14
  63. package/template/_agents/skills/owlmeans-code-structure/SKILL.md +188 -0
  64. package/template/_agents/skills/owlmeans-code-structure/parts/code-structure-reference.md +125 -0
  65. package/template/_agents/skills/reuse-code/SKILL.md +56 -10
  66. package/template/_agents/skills/self-education/SKILL.md +25 -5
  67. package/template/_agents/skills/skill-authoring/SKILL.md +132 -14
  68. package/template/_bare.json +24 -0
  69. package/template/_gitignore +8 -0
  70. package/template/bunfig.toml +2 -0
  71. package/template/package.json +3 -2
  72. package/template/sources/api/package.json +5 -4
  73. package/template/sources/api/src/app/session/add.ts +6 -17
  74. package/template/sources/api/src/app/session/list.ts +6 -12
  75. package/template/sources/api/src/app/session/remove.ts +6 -15
  76. package/template/sources/api/src/config.ts +7 -0
  77. package/template/sources/api/src/context.bare.ts +11 -0
  78. package/template/sources/api/src/context.ts +0 -4
  79. package/template/sources/api/src/entrypoints.bare.ts +4 -0
  80. package/template/sources/api/src/entrypoints.ts +13 -0
  81. package/template/sources/api/src/index.ts +8 -2
  82. package/template/sources/api/src/models/session/types.ts +14 -0
  83. package/template/sources/api/src/models/session.ts +43 -0
  84. package/template/sources/api/src/types.bare.ts +5 -0
  85. package/template/sources/common/package.json +8 -8
  86. package/template/sources/common/src/config.ts +2 -1
  87. package/template/sources/common/src/consts.bare.ts +8 -0
  88. package/template/sources/common/src/consts.ts +0 -14
  89. package/template/sources/common/src/entrypoints.bare.ts +14 -0
  90. package/template/sources/common/src/entrypoints.ts +60 -0
  91. package/template/sources/common/src/index.bare.ts +3 -0
  92. package/template/sources/common/src/index.ts +1 -1
  93. package/template/sources/common/src/schemas.ts +7 -7
  94. package/template/sources/web/THIRD-PARTY-NOTICES.txt +24 -0
  95. package/template/sources/web/index.html +3 -1
  96. package/template/sources/web/package.json +33 -32
  97. package/template/sources/web/src/components/ui/dropdown-menu.tsx +257 -0
  98. package/template/sources/web/src/config.ts +8 -0
  99. package/template/sources/web/src/consts.ts +15 -0
  100. package/template/sources/web/src/context.bare.ts +13 -0
  101. package/template/sources/web/src/context.ts +1 -7
  102. package/template/sources/web/src/entrypoints.bare.ts +13 -0
  103. package/template/sources/web/src/entrypoints.ts +16 -0
  104. package/template/sources/web/src/index.css +1 -1
  105. package/template/sources/web/src/index.tsx +2 -2
  106. package/template/sources/web/src/layout/main.tsx +7 -2
  107. package/template/sources/web/src/nav.bare.ts +24 -0
  108. package/template/sources/web/src/nav.ts +3 -8
  109. package/template/sources/web/src/screens/home.bare.tsx +17 -0
  110. package/template/sources/web/src/screens/session.tsx +14 -16
  111. package/template/sources/web/src/vite-env.d.ts +8 -0
  112. package/template/sources/web/vite.config.ts +4 -2
  113. package/template/CLAUDE.md +0 -13
  114. package/template/sources/api/src/modules.ts +0 -11
  115. package/template/sources/common/src/modules.ts +0 -26
  116. package/template/sources/web/src/modules.ts +0 -22
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: reuse-code
3
- description: Discovery-first, reuse-first workflow for OwlMeans projects. Use BEFORE planning or building any feature, before proposing a third-party library or a custom solution, and after writing code. Find an existing @owlmeans/* package or existing code first; extend before writing new; simplify what you write.
3
+ description: Discovery-first, reuse-first workflow for OwlMeans projects. Use BEFORE planning or building any feature, before proposing a third-party library or a custom solution, and after writing code. Find an existing @owlmeans/* package or existing code first (membership, invitations, organizations and access come from the IAM); extend before writing new; simplify what you write.
4
4
  user-invocable: true
5
5
  metadata:
6
6
  scope: general
@@ -18,9 +18,17 @@ order — during planning **and** during implementation.
18
18
  Before suggesting any external library or writing custom code, look for an `@owlmeans/*` package that
19
19
  already solves the problem.
20
20
 
21
- - **Consult the deployed skills.** Each installed `@owlmeans/*` package ships a skill at
22
- `.agents/skills/<pkg>/SKILL.md` describing what it
23
- does. Read those first — they are your local catalogue of installed capabilities.
21
+ - **Consult the deployed skills.** `.agents/skills/` is the local catalogue of installed
22
+ capabilities: one directory per skill, each holding a `SKILL.md` that describes what a package
23
+ does and how it is consumed. A directory is named after the **skill**, not the package —
24
+ `@owlmeans/test-ui` deploys `testing-ui`, `@owlmeans/server-auth` deploys `server-auth` **and**
25
+ `supervisor-auth`, `@owlmeans/test` deploys `testing-unit` and `testing-overview` — so list the
26
+ directory instead of guessing a path from a package name.
27
+ - **Read `.agents/linked-skills/` too when it is there.** `.agents/scripts/link-skills.sh` links
28
+ the skills that ship inside the installed `@owlmeans/*` packages into it (and mirrors them into
29
+ `.claude/skills/` for Claude Code), with a skill / origin / description table in its
30
+ `INDEX.md`. It is generated and git-ignored, and a skill of the same name in `.agents/skills/`
31
+ always wins.
24
32
  - **Scan installed packages.** Look in `node_modules/@owlmeans/*` **and**, in a workspace monorepo,
25
33
  the nested `sources/*/node_modules/@owlmeans/*` (bun nests workspace deps).
26
34
  - **Discover packages that aren't installed yet** by researching the **owlmeans/common** repository —
@@ -39,20 +47,56 @@ How you research the repo depends on whether `@owlmeans/*` is linked locally:
39
47
  **https://github.com/owlmeans/common** — `tree.md` and package READMEs — to find the right package.
40
48
 
41
49
  This is the same dev-linked detection `@owlmeans/agent-skills` uses (see its `detectLinked`). After
42
- adding an `@owlmeans/*` dependency, run `npx @owlmeans/agent-skills` to deploy its skill. Prefer an
43
- `@owlmeans/*` package over a third-party library or bespoke code whenever one fits.
50
+ adding an `@owlmeans/*` dependency, run `npx @owlmeans/agent-skills@^0.1.19-rc.0` to deploy its
51
+ skill. Prefer an `@owlmeans/*` package over a third-party library or bespoke code whenever one fits.
52
+
53
+ ### Never add an OwlMeans dependency without an explicit range
54
+
55
+ Write the range yourself, as a caret at the version the rest of this project already uses for its
56
+ other `@owlmeans/*` packages (or the one the package's own skill names on its **Install:** line):
57
+
58
+ ```json
59
+ "dependencies": {
60
+ "@owlmeans/queue": "^<version>"
61
+ }
62
+ ```
63
+
64
+ A `bun add` that names no version — and a hand-written `"latest"`, `"next"`, `"*"` or empty range
65
+ — resolves through a dist-tag instead. OwlMeans publishes prereleases under `next`, so the tag
66
+ named `latest` points at an OLDER version than the one every other package here is pinned to. The
67
+ install succeeds, nothing warns, and the code you wrote against the current API is compiled
68
+ against the previous one. Put the version in the same breath as the package name, or copy the
69
+ `**Install:**` line from that package's own skill, which always carries a current range.
44
70
 
45
71
  ## 2. Reuse or extend before writing custom
46
72
 
47
73
  If an installed package nearly fits, **configure or extend it** rather than writing something new — use
48
- its resources, services, modules, and helpers. A small extension of a framework package beats a new
74
+ its resources, services, entrypoints, and helpers. A small extension of a framework package beats a new
49
75
  parallel implementation.
50
76
 
77
+ ### People, organizations and access come from the IAM
78
+
79
+ An application that signs its people in through an OwlMeans IAM provider already has accounts,
80
+ organizations, memberships, invitations and grants — the provider keeps them. Before writing a
81
+ members table, an invitation flow, a role column or an organization picker:
82
+
83
+ - **Server** (`@owlmeans/server-iam`): `makeIamRuntimeClient(context, request)` lists the person's
84
+ organizations, creates one, lists / adds (find-or-create by e-mail) / updates / removes members,
85
+ and lists, assigns and revokes grants — as the signed-in person, with owner and member rights
86
+ decided by the provider. `makeOrganizationScope(context, request).organizationOf` /
87
+ `.organizationsOf` read the session's organizations; gates and `hasPermission` enforce access;
88
+ `makeEntityScope(req).requireEntityKey()` is the tenant key to store records by.
89
+ - **Browser** (`@owlmeans/client-iam`): `organizationSwitchOf(ctx).listOrganizations` /
90
+ `.switchOrganization` — the acting organization is session state, never a per-request parameter.
91
+ - If the project's own scaffold already ships an organization switcher or a people / access screen,
92
+ extend it rather than adding a second one.
93
+
51
94
  ## 3. No package? Reuse code and extract an abstraction
52
95
 
53
96
  When no package solves it, search the codebase for code that already solves a **similar** problem.
54
97
  Prefer factoring out a shared helper, base, or generic function — extract an abstraction — over
55
- duplicating logic or writing from scratch. Only write genuinely new code when nothing reusable exists.
98
+ duplicating logic or writing from scratch. A shared helper is ONE factory-built object whose interface is declared
99
+ first; it lives in its own `<name>.ts`, with a same-named folder for its types and parts (`/owlmeans-code-structure`). Only write genuinely new code when nothing reusable exists.
56
100
 
57
101
  ## 4. Simplify after writing
58
102
 
@@ -60,5 +104,7 @@ Once code is written, review it: can it be **shorter, clearer, or expressed with
60
104
  Lean on framework utilities, remove dead branches, collapse needless indirection. Less code that reuses
61
105
  the framework is better than more bespoke code.
62
106
 
63
- See `[[dependency-tree]]` for the package map, `[[scaffolding]]` for how a project is assembled, and
64
- `[[bun]]` for adding dependencies.
107
+ See `[[scaffolding]]` for how a project is assembled, and `[[agent-skills]]` for keeping the
108
+ deployed skill catalogue current. The package map itself is `tree.md` at the root of the
109
+ [owlmeans/common](https://github.com/owlmeans/common) repository — layer by layer, every package
110
+ and what it depends on.
@@ -27,10 +27,14 @@ Also recommended after any unplanned change that made an existing skill inaccura
27
27
 
28
28
  For each area the work touched:
29
29
 
30
- 1. Which existing skill covers it? (Check `.agents/skills/`.)
30
+ 1. Which existing skill covers it? Check `.agents/skills/`, and — where the project has one —
31
+ `.agents/linked-skills/`, for ground a skill this project does not own already covers.
31
32
  2. Do its commands, paths, APIs, and behavior claims still hold after the change?
32
33
  3. Fix in place — rewrite the affected lines so they describe current behavior; never append a
33
34
  note about what this change did.
35
+ 4. Never record what the work taught in `AGENTS.md`: the rule goes to the governing skill, the
36
+ incident to `.agents/memory/`. `AGENTS.md` gains at most a new skill's one index line or a new
37
+ repo-wide mandatory rule in one line (`create-skill` → What may enter AGENTS.md).
34
38
 
35
39
  ## Shape of an update
36
40
 
@@ -46,11 +50,24 @@ rewrite recipe is `memory-promotion` → Distillation.
46
50
 
47
51
  Test: a finished skill reads as though the feature was always this way.
48
52
 
49
- ## Non-project skills
53
+ ## Skills this project does not own
50
54
 
51
- If a general or imported skill gained an important usage pattern during the work, add the pattern
52
- to the **deployed copy** in this repo and note it in the report as an upstream candidate —
53
- canonical archive copies change only on explicit operator request.
55
+ A skill that came from somewhere else is not edited here, and the two kinds fail differently:
56
+
57
+ - An entry under `.agents/linked-skills/<name>` is a symlink into the repo or installed package
58
+ that owns the skill. Writing through it edits the owner's own file — an unrequested change in
59
+ another project, which nothing here undoes: the link script only creates and prunes symlinks.
60
+ - A skill placed by the `@owlmeans/agent-skills` installer is a real file at
61
+ `.agents/skills/<name>/SKILL.md` carrying
62
+ `<!-- AUTO-GENERATED — do not edit. Regenerate via sync-agent-meta. -->`. The next install
63
+ overwrites every file still carrying that banner, so an edit made under it is lost; strip the
64
+ banner and the file instead becomes a conflict the installer skips and reports.
65
+
66
+ When such a skill gained an important usage pattern during the work, capture the pattern in a
67
+ local skill under `.agents/skills/`, named after the pattern — a local skill named after the
68
+ upstream one shadows it for the whole project — and list the upstream skill in the report as an
69
+ upstream candidate. Changing the skill at its source is a separate change in the owning project,
70
+ made only on explicit operator request.
54
71
 
55
72
  ## External docs
56
73
 
@@ -73,6 +90,9 @@ memory-vs-skill boundary (`agent-memory` / `memory-promotion`).
73
90
 
74
91
  ## Completion gate
75
92
 
93
+ Before reporting, run `sh .agents/scripts/agents-size.sh`; if `AGENTS.md` is over budget, move
94
+ lines out to their skills first.
95
+
76
96
  The completion report must contain a Self-education table:
77
97
 
78
98
  | Item | Action | Path |
@@ -9,13 +9,26 @@ metadata:
9
9
 
10
10
  # Authoring agent guidance (skills)
11
11
 
12
- OwlMeans projects carry agent guidance in two places, and only two:
12
+ An OwlMeans project carries agent guidance in up to three places:
13
13
 
14
14
  | What | Where | Loaded |
15
15
  |---|---|---|
16
16
  | Always-on project context | `AGENTS.md` at the repo root | every session |
17
+ | Always-on policy, kept out of `AGENTS.md` | `.agents/rules/<topic>.md`, pulled in from `AGENTS.md` by an `@.agents/rules/<topic>.md` import line | every session |
17
18
  | Topic guidance | `.agents/skills/<name>/SKILL.md` | on demand, by topic or `/<name>` |
18
19
 
20
+ The middle tier is optional and belongs to a repo that has grown standing policy of its own. A
21
+ project scaffolded by `@owlmeans/create-app` starts without a `.agents/rules/` directory: its
22
+ generated `AGENTS.md` states the git policy inline and points at the seeded `git` skill for the
23
+ rest. Follow the layout the project already has — add a rules file only where `AGENTS.md` already
24
+ imports one.
25
+
26
+ **Rule or skill?** A rule is policy that has to hold whether or not anyone thought to load
27
+ anything — the git workflow is the standing example, whether a repo keeps it as
28
+ `.agents/rules/git.md` or inline in `AGENTS.md`. A skill is guidance for a task, loaded when that
29
+ task comes up. If it only matters while you are doing X, write a skill; if breaking it is wrong at any moment, state it in a line or two where `AGENTS.md` loads it every
30
+ session, and keep its detail in a skill (§ What may enter AGENTS.md).
31
+
19
32
  `.agents/skills/` is the [Agent Skills](https://agentskills.io) standard location: GitHub Copilot
20
33
  and Codex discover it natively. Claude Code reads skills only from `.claude/skills/`, so each skill
21
34
  is bridged there by a generated symlink — see "Refresh the Claude Code links" below. **Write a skill
@@ -26,8 +39,8 @@ once; never author a per-agent copy** (`.github/instructions/*.instructions.md`,
26
39
 
27
40
  ```
28
41
  .agents/skills/<name>/
29
- ├── SKILL.md # required — frontmatter + body
30
- ├── reference.md # optional — deeper detail loaded on demand
42
+ ├── SKILL.md # required — frontmatter + body (a package skill's core)
43
+ ├── parts/<part>.md # optional — a package skill's purpose parts, loaded on demand
31
44
  └── scripts/ # optional — shell scripts the skill runs
32
45
  ```
33
46
 
@@ -38,20 +51,95 @@ once; never author a per-agent copy** (`.github/instructions/*.instructions.md`,
38
51
  name: my-skill # REQUIRED, must equal the directory name (lowercase, hyphens, ≤64 chars)
39
52
  description: What it does and WHEN to use it. # REQUIRED, ≤1024 chars — the auto-invocation signal
40
53
  user-invocable: true # false = background knowledge only, hidden from the / menu
41
- allowed-tools: Bash(bun *) Read # optional — space-separated; tools usable without per-call approval
54
+ allowed-tools: Bash(bun *), Read # optional — COMMA-separated; tools usable without per-call approval
42
55
  metadata: # optional — anything non-standard goes here
43
56
  scope: general
44
57
  ---
45
58
  ```
46
59
 
60
+ The `name` is not free: it is the key every store is keyed by, and a LOCAL skill always wins over
61
+ one that arrives from a dependency. Naming a skill after a package you depend on therefore hides
62
+ that package's own guidance completely, and the installer reports the file as a conflict on every
63
+ run. Give a skill about your own use of `@owlmeans/payment` a name of its own — `billing`, or your
64
+ product's name with a suffix — never `payment`.
65
+
66
+ **Never name a skill after a Claude Code built-in slash command.** Claude Code resolves `/<name>` to
67
+ a skill before the built-in command of the same name, so a skill called `context`, `config`,
68
+ `memory` or `review` makes that command unreachable wherever the skill is installed or linked, and
69
+ nothing reports it. The same holds for a command's aliases (`/settings` is `/config`, `/cost` is
70
+ `/usage`) and for the skills Claude Code bundles (`/loop`, `/simplify`); type `/` in a Claude Code
71
+ session to see every taken name. OwlMeans package skills follow this too: the guidance for
72
+ `@owlmeans/context` and `@owlmeans/config` ships as `owlmeans-context` and `owlmeans-config`.
73
+
47
74
  The `description` is the most important field: every agent uses it to decide when to load the
48
75
  skill, so state both the topic and the trigger ("Use when …"). Keep it under 1024 characters —
49
- Copilot rejects longer ones.
50
-
51
- Only the fields above (plus `license` and `compatibility`) are portable. Anything else — including
52
- this monorepo's `scope: general` routing marker — belongs under `metadata:`, so a skill stays valid
53
- in every agent that reads it. Claude Code additionally understands `disable-model-invocation`,
54
- `argument-hint` and `context: fork`; use them only when the skill genuinely needs them.
76
+ Copilot rejects longer ones. It is YAML, so a value containing `: ` (colon-space) has to be quoted
77
+ or the file stops parsing and the skill silently disappears from every agent.
78
+
79
+ `allowed-tools` is parsed as a list split on commas, newlines and YAML `-` bullets — never on
80
+ plain spaces. `Bash(bun *) Read` is read as one tool named `Bash(bun *) Read`, which matches
81
+ nothing; write `Bash(bun *), Read`.
82
+
83
+ Six keys are what the Agent Skills standard defines: `name`, `description`, `license`,
84
+ `compatibility`, `allowed-tools` and nested `metadata`. `@owlmeans/agent-skills` parses no
85
+ frontmatter; the generator that embeds package guidance reads it. Project-specific keys —
86
+ including the OwlMeans `scope: general` routing marker, which sends a skill to the installer bundle
87
+ rather than to one package — belong under `metadata:`, so a skill stays valid in every agent that
88
+ reads it. The invocation switches sit outside the standard and are written at the top level
89
+ alongside it: `user-invocable` (`false` hides a skill from the `/`
90
+ menu, marking it background knowledge), plus `disable-model-invocation` and `argument-hint`, which
91
+ Claude Code understands. Set any of them only when the skill needs it.
92
+
93
+ ## Chunked package skills (parts)
94
+
95
+ A package skill that grows past what one call should carry is split. `SKILL.md` stays the
96
+ **core**, loaded whenever the package is chosen; each `parts/<part>.md` holds ONE purpose, loaded
97
+ only for a call that needs it (selection: the `agent-skills` skill).
98
+
99
+ - **The core** keeps the frontmatter, the `**Install:**` line, the exports, the wiring, any
100
+ `## Target wiring` section (an adoption reads it from the core alone) and the rules every use
101
+ needs. Target ≤ 12 000 chars. It ENDS with a Parts table linking every part:
102
+
103
+ ```markdown
104
+ ## Parts
105
+
106
+ | Part | Load it when |
107
+ |---|---|
108
+ | [navigation](parts/navigation.md) | building the application shell or its menus |
109
+ ```
110
+
111
+ - **A part** opens with exactly this frontmatter, then `# @owlmeans/<package> — <part title>` and
112
+ the moved sections:
113
+
114
+ ```yaml
115
+ ---
116
+ name: navigation # = the file stem, [a-z0-9-], unique within the package
117
+ description: The application shell — NavLayout, TopNav/SideNav/MobileNav/Footer, the nav config.
118
+ metadata:
119
+ purposes: navigation, layout, screen # closed vocabulary, comma-separated
120
+ mentions: NavLayout, TopNav, SideNav # symbols and import specifiers that call for it
121
+ ---
122
+ ```
123
+
124
+ - **Size:** the generator warns above 12 000 chars per part and fails above 16 000; it also warns
125
+ about a package-specific skill over 16 000 chars that has no parts.
126
+ - **Purposes** come from a closed vocabulary (`PACKAGE_PURPOSES`, `@owlmeans/llm-common`):
127
+ `view`, `form`, `list`, `detail`, `dialog`, `picker`, `navigation`, `layout`, `screen`,
128
+ `theme`, `feedback`, `state`, `client-api`, `server-api`, `data`, `migration`, `query`, `auth`,
129
+ `consent`, `payment`, `planning`, `job`, `agent`, `config`, `test`, `ops`.
130
+ A value outside it fails the generator. Purposes may stay empty only in a general skill.
131
+ - **Mentions** are distinctive symbols or import specifiers, never common words: a call without a
132
+ focus loads every part whose mention appears in its messages.
133
+ - **Names are unique per package** — a part is addressed as `@owlmeans/<package>#<part>`. Every
134
+ general skill routes to the installer package, so a general skill's part carries the skill's own
135
+ prefix (`code-structure-reference`, never `reference`).
136
+ - **YAML:** a value that starts with `@`, a backtick, `[`, `{`, `*` or `&`, or that contains `: `
137
+ or ` #`, stops parsing — start the mentions with a symbol, not an import specifier.
138
+ - **Splitting moves text, never rewrites it.** Every section lands in the core or in exactly one
139
+ part, each part stands alone for its purpose, and a reference to a moved section names its part
140
+ (see "Organizations" in the `sessions` part).
141
+ - Only `SKILL.md` and `parts/*.md` are embedded into a package; any other file beside them stays in
142
+ the repository, and the generator warns about a root `reference.md`.
55
143
 
56
144
  ## Refresh the Claude Code links
57
145
 
@@ -61,9 +149,23 @@ After creating, renaming, or deleting a skill, run:
61
149
  sh .agents/scripts/link-skills.sh
62
150
  ```
63
151
 
64
- It creates `.claude/skills/<name>` → `../../.agents/skills/<name>` for every skill and prunes links
65
- whose skill is gone. The links are gitignored and are recreated at session start, but a skill added
66
- mid-session is invisible to Claude Code until the script runs.
152
+ It creates `.claude/skills/<name>` → `../../.agents/skills/<name>` for every local skill and prunes
153
+ links whose skill is gone. The links are gitignored and are recreated at session start (a committed
154
+ `SessionStart` hook, and on every install where the project declares a root `prepare` script that
155
+ calls it), but a skill added mid-session is invisible to Claude Code until the script runs.
156
+
157
+ The same script also brings in the skills of everything this project depends on, from whichever of
158
+ two sources applies. In a linked checkout — a repo whose root `package.json` lists an upstream
159
+ repo's packages as workspace entries — it resolves those upstream repos first, recursing into each
160
+ one's own manifest up to four levels, and links the skills from the upstream's own
161
+ `.agents/skills/`. A project that declares no such linked upstream falls back to what a plain npm
162
+ install gives it: the read-only `agent-meta/skills/` copies shipped inside each installed
163
+ `@owlmeans/*` package. Either way the links land in `.agents/linked-skills/<name>` (Copilot, Codex)
164
+ and `.claude/skills/<name>` (Claude Code), with a generated `.agents/linked-skills/INDEX.md` listing
165
+ skill, origin and description. Load one by name exactly like a local skill. A local skill of the
166
+ same name always wins, and a nearer dependency wins over a farther one. The whole
167
+ `.agents/linked-skills/` directory is generated and git-ignored — never edit or commit it, and never
168
+ edit an `agent-meta/` copy: fix the skill in the package that ships it.
67
169
 
68
170
  ## Skill vs memory
69
171
 
@@ -80,10 +182,26 @@ Never paste memory text into a skill. Memory content enters guidance only as a r
80
182
  rule — trigger, step, and the failure it prevents, with dates, phase/status markers, versions and
81
183
  incident narrative stripped (`memory-promotion` → Distillation).
82
184
 
185
+ ## What may enter AGENTS.md
186
+
187
+ `AGENTS.md` — with every file it `@`-imports and `.agents/memory/MEMORY.md` — is loaded in full in
188
+ every session, so it has a budget: **≤ 40 000 chars**, checked by `sh .agents/scripts/agents-size.sh`
189
+ from the repo root where the repo carries it. A line enters it only as:
190
+
191
+ 1. a mandatory rule that must hold before any skill is loaded (git, reporting, environments,
192
+ naming, code structure) — stated in one or two lines, with the detail in a skill;
193
+ 2. one line of the package map or the command list;
194
+ 3. one line of the skills index: `` - `/name` — when to load it ``, ≤ 160 chars.
195
+
196
+ Everything else is loaded on demand: a subsystem's rules, contracts and failure fingerprints go to
197
+ the skill that governs it, and the outage that taught them to `.agents/memory/`. Never paraphrase a
198
+ skill's `description:` into the index — skills self-describe. A change that would push the file over
199
+ budget shortens or moves an existing line in the same change.
200
+
83
201
  ## After adding a skill
84
202
 
85
203
  1. If it replaces an ad-hoc `.agents/<topic>.md`, remove that file.
86
204
  2. If it distilled memory content into rules, shrink the source `.agents/memory/` node to a
87
205
  pointer line (`memory-promotion`) — the memory index does not list skills.
88
- 3. Reference it from `AGENTS.md` if it should be discoverable every session.
206
+ 3. Add one line to the skills index in `AGENTS.md` (`/name` — when to load it, ≤ 160 chars) — never a paragraph (§ What may enter AGENTS.md).
89
207
  4. Run `sh .agents/scripts/link-skills.sh`.
@@ -0,0 +1,24 @@
1
+ {
2
+ "remove": [
3
+ "sources/common/src/types.ts",
4
+ "sources/common/src/schemas.ts",
5
+ "sources/api/src/consts.ts",
6
+ "sources/api/src/app",
7
+ "sources/api/src/models",
8
+ "sources/web/src/screens/about.tsx",
9
+ "sources/web/src/screens/session.tsx"
10
+ ],
11
+ "overrides": {
12
+ "README.md": "README.bare.md",
13
+ "sources/common/src/consts.ts": "sources/common/src/consts.bare.ts",
14
+ "sources/common/src/entrypoints.ts": "sources/common/src/entrypoints.bare.ts",
15
+ "sources/common/src/index.ts": "sources/common/src/index.bare.ts",
16
+ "sources/api/src/context.ts": "sources/api/src/context.bare.ts",
17
+ "sources/api/src/entrypoints.ts": "sources/api/src/entrypoints.bare.ts",
18
+ "sources/api/src/types.ts": "sources/api/src/types.bare.ts",
19
+ "sources/web/src/context.ts": "sources/web/src/context.bare.ts",
20
+ "sources/web/src/entrypoints.ts": "sources/web/src/entrypoints.bare.ts",
21
+ "sources/web/src/nav.ts": "sources/web/src/nav.bare.ts",
22
+ "sources/web/src/screens/home.tsx": "sources/web/src/screens/home.bare.tsx"
23
+ }
24
+ }
@@ -6,6 +6,14 @@ dist
6
6
  bun.lock
7
7
  bun.lockb
8
8
 
9
+ # Developer-local environment (LOG_LEVEL, VITE_LOG_LEVEL, …)
10
+ .env
11
+ .env.local
12
+ .env.*.local
13
+
9
14
  # Generated Claude Code skill symlinks (canonical skills live in .agents/skills/)
10
15
  .claude/skills/*
11
16
  !.claude/skills/.gitkeep
17
+
18
+ # Skills linked from upstream repos (generated by .agents/scripts/link-skills.sh)
19
+ .agents/linked-skills/
@@ -0,0 +1,2 @@
1
+ [install]
2
+ linker = "hoisted"
@@ -3,14 +3,15 @@
3
3
  "version": "0.1.0",
4
4
  "private": true,
5
5
  "type": "module",
6
- "packageManager": "bun@1.3.14",
6
+ "packageManager": "bun@1.4.2",
7
7
  "workspaces": [
8
8
  "sources/*"
9
9
  ],
10
10
  "scripts": {
11
11
  "dev": "bun run --filter './sources/common' build && bun run --filter './sources/*' --parallel dev",
12
12
  "build": "bun run --filter './sources/*' build",
13
- "typecheck": "bun run --filter './sources/*' typecheck"
13
+ "typecheck": "bun run --filter './sources/*' typecheck",
14
+ "prepare": "sh -c 'test -f .agents/scripts/link-skills.sh && sh .agents/scripts/link-skills.sh || true'"
14
15
  },
15
16
  "devDependencies": {
16
17
  "nodemon": "^3.1.14"
@@ -11,13 +11,14 @@
11
11
  "typecheck": "tsc -b"
12
12
  },
13
13
  "dependencies": {
14
- "@owlmeans/server-app": "^0.1.18-rc.0",
15
- "@owlmeans/static-resource": "^0.1.18-rc.0",
14
+ "@owlmeans/log": "^0.1.19-rc.0",
15
+ "@owlmeans/server-app": "^0.1.19-rc.0",
16
+ "@owlmeans/static-resource": "^0.1.19-rc.0",
16
17
  "__APP_SLUG__-common": "workspace:^"
17
18
  },
18
19
  "devDependencies": {
19
- "@owlmeans/dep-config": "^0.1.18-rc.0",
20
- "@types/node": "^24.10.1",
20
+ "@owlmeans/dep-config": "^0.1.18",
21
+ "@types/node": "^26.6.5",
21
22
  "typescript": "^7.0.2"
22
23
  }
23
24
  }
@@ -1,20 +1,9 @@
1
- import { randomUUID } from 'node:crypto'
2
- import { handleBody } from '@owlmeans/server-app'
3
- import type { AddItemPayload, SessionItem, SessionParams } from '__APP_SLUG__-common'
4
- import { SESSION_ITEMS } from '../../consts.js'
1
+ import { handlers } from '@owlmeans/server-app'
2
+ import { session } from '__APP_SLUG__-common'
3
+ import { makeSessionModel } from '../../models/session.js'
5
4
  import type { Context } from '../../types.js'
6
5
 
7
- export const add = handleBody<AddItemPayload>(async (payload, context, req) => {
8
- const ctx = context as Context
9
- const { sid } = req.params as SessionParams
10
- const resource = ctx.getStaticResource<SessionItem>(SESSION_ITEMS)
6
+ const handle = handlers<Context>()
11
7
 
12
- const item: SessionItem = {
13
- id: randomUUID(),
14
- sessionId: sid,
15
- text: payload.text,
16
- createdAt: new Date().toISOString(),
17
- }
18
-
19
- return await resource.create(item)
20
- })
8
+ export const add = handle.body(session.add, async (payload, context, request) =>
9
+ await makeSessionModel(context, request.params.sid).add(payload.text))
@@ -1,15 +1,9 @@
1
- import { handleParams } from '@owlmeans/server-app'
2
- import type { SessionItem, SessionParams } from '__APP_SLUG__-common'
3
- import { SESSION_ITEMS } from '../../consts.js'
1
+ import { handlers } from '@owlmeans/server-app'
2
+ import { session } from '__APP_SLUG__-common'
3
+ import { makeSessionModel } from '../../models/session.js'
4
4
  import type { Context } from '../../types.js'
5
5
 
6
- export const list = handleParams<SessionParams>(async (params, context) => {
7
- const ctx = context as Context
8
- const resource = ctx.getStaticResource<SessionItem>(SESSION_ITEMS)
6
+ const handle = handlers<Context>()
9
7
 
10
- // The static resource lists every record; filter to this session and sort newest first.
11
- const { items } = await resource.list<SessionItem>()
12
- return items
13
- .filter(item => item.sessionId === params.sid)
14
- .sort((a, b) => (a.createdAt < b.createdAt ? 1 : -1))
15
- })
8
+ export const list = handle.params(session.list, async (params, context) =>
9
+ await makeSessionModel(context, params.sid).list())
@@ -1,18 +1,9 @@
1
- import { handleParams } from '@owlmeans/server-app'
2
- import type { ItemParams, SessionItem } from '__APP_SLUG__-common'
3
- import { SESSION_ITEMS } from '../../consts.js'
1
+ import { handlers } from '@owlmeans/server-app'
2
+ import { session } from '__APP_SLUG__-common'
3
+ import { makeSessionModel } from '../../models/session.js'
4
4
  import type { Context } from '../../types.js'
5
5
 
6
- export const remove = handleParams<ItemParams>(async (params, context) => {
7
- const ctx = context as Context
8
- const resource = ctx.getStaticResource<SessionItem>(SESSION_ITEMS)
6
+ const handle = handlers<Context>()
9
7
 
10
- const existing = await resource.load<SessionItem>(params.id)
11
- // Only remove the item if it belongs to the requesting session.
12
- if (existing == null || existing.sessionId !== params.sid) {
13
- return { removed: false }
14
- }
15
-
16
- await resource.delete(params.id)
17
- return { removed: true }
18
- })
8
+ export const remove = handle.params(session.remove, async (params, context) =>
9
+ await makeSessionModel(context, params.sid).remove(params.id))
@@ -1,8 +1,15 @@
1
1
  import { config } from '@owlmeans/server-app'
2
2
  import { APP_API, commonConfig } from '__APP_SLUG__-common'
3
3
  import type { Config } from './types.js'
4
+ // The module augmentation that gives the config its `log` field — a side-effect import only.
5
+ import type {} from '@owlmeans/log'
4
6
 
5
7
  // The server listens on the APP_API service route's port (3000), declared in common/config.ts.
6
8
  const cfg = config<Config>(APP_API, commonConfig as Config)
7
9
 
10
+ // The log level comes from the runtime environment, never from code: `info` unless LOG_LEVEL
11
+ // says otherwise; LOG_DEBUG lists scopes forced to debug (`*` for all). Bun reads
12
+ // `sources/api/.env` on its own.
13
+ cfg.log = { level: process.env.LOG_LEVEL || 'info', debug: process.env.LOG_DEBUG ?? '' }
14
+
8
15
  export default cfg
@@ -0,0 +1,11 @@
1
+ import { makeContext as makeBasicContext } from '@owlmeans/server-app'
2
+ import type { Config, Context } from './types.js'
3
+
4
+ /**
5
+ * Where the app's resources are registered. `@owlmeans/static-resource` is already a dependency,
6
+ * so `appendStaticResource<C, T>(context, ALIAS)` gives you an in-memory store with no database
7
+ * behind it; a mongo/postgres resource takes its place once the data has to outlive the process.
8
+ * Whatever you append here must also widen `Context` in `types.ts`, or the getter will not exist.
9
+ */
10
+ export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T =>
11
+ makeBasicContext<C, T>(cfg, true)
@@ -10,9 +10,5 @@ export const makeContext = <C extends Config, T extends Context<C>>(cfg: C): T =
10
10
  // data lives in process memory and is cleared when the api restarts.
11
11
  appendStaticResource<C, T>(context, SESSION_ITEMS)
12
12
 
13
- // A child context has to inherit THIS factory, not the layer's — otherwise a derived context
14
- // is built without the resource registered above and every lookup on it throws.
15
- context.makeContext = makeContext as typeof context.makeContext
16
-
17
13
  return context
18
14
  }
@@ -0,0 +1,4 @@
1
+ import { entrypoints } from '@owlmeans/server-app'
2
+
3
+ // The bare shell has no API declarations yet. Bind new protocols here as they are added.
4
+ export const appBindings = [...entrypoints]
@@ -0,0 +1,13 @@
1
+ import { entrypoints } from '@owlmeans/server-app'
2
+ import { bind } from '@owlmeans/server-entrypoint'
3
+ import { appProtocols } from '__APP_SLUG__-common'
4
+ import * as handlers from './app/session/index.js'
5
+
6
+ /** Local server bindings for the shared session protocol tree. */
7
+ export const appBindings = [
8
+ ...entrypoints,
9
+ bind(appProtocols.api.session.base),
10
+ bind(appProtocols.api.session.list, handlers.list),
11
+ bind(appProtocols.api.session.add, handlers.add),
12
+ bind(appProtocols.api.session.remove, handlers.remove),
13
+ ]
@@ -1,9 +1,15 @@
1
+ import { logger } from '@owlmeans/log'
1
2
  import { main } from '@owlmeans/server-app'
2
3
  import config from './config.js'
3
4
  import { makeContext } from './context.js'
4
- import { appModules } from './modules.js'
5
+ import { appBindings } from './entrypoints.js'
5
6
  import type { Config, Context } from './types.js'
6
7
 
8
+ const log = logger('api')
9
+
7
10
  const context = makeContext<Config, Context>(config)
8
11
 
9
- main<{}, Config, Context>(context, appModules)
12
+ main<{}, Config, Context>(context, appBindings).catch(error => {
13
+ log.error('Start failed', error)
14
+ process.exit(1)
15
+ })
@@ -0,0 +1,14 @@
1
+ import type { SessionItem } from '__APP_SLUG__-common'
2
+
3
+ /**
4
+ * The items of ONE browser session. The session id is bound when the model is built, so no
5
+ * member can reach another session's items; the member names are the session endpoints' keys.
6
+ */
7
+ export interface SessionModel {
8
+ /** This session's items, newest first. */
9
+ list: () => Promise<SessionItem[]>
10
+ /** Store a new item under this session. */
11
+ add: (text: string) => Promise<SessionItem>
12
+ /** Remove an item — only when it belongs to this session. */
13
+ remove: (id: string) => Promise<{ removed: boolean }>
14
+ }