@freetesh/zeno 0.0.0-stage → 0.7.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 (61) hide show
  1. package/.claude-plugin/marketplace.json +14 -0
  2. package/.claude-plugin/plugin.json +10 -0
  3. package/LICENSE +21 -0
  4. package/README.md +175 -2
  5. package/cli/dist/checks/architecture.js +126 -0
  6. package/cli/dist/checks/canonical-values.js +46 -0
  7. package/cli/dist/checks/context-layout.js +41 -0
  8. package/cli/dist/checks/copies.js +73 -0
  9. package/cli/dist/checks/external.js +30 -0
  10. package/cli/dist/checks/index.js +15 -0
  11. package/cli/dist/checks/types.js +1 -0
  12. package/cli/dist/checks/wiki-frontmatter.js +71 -0
  13. package/cli/dist/commands/add.js +24 -0
  14. package/cli/dist/commands/check.js +19 -0
  15. package/cli/dist/commands/doctor.js +16 -0
  16. package/cli/dist/commands/env.js +6 -0
  17. package/cli/dist/commands/init.js +78 -0
  18. package/cli/dist/commands/learn.js +35 -0
  19. package/cli/dist/commands/standards.js +26 -0
  20. package/cli/dist/commands/status.js +11 -0
  21. package/cli/dist/commands/sync.js +23 -0
  22. package/cli/dist/commands/ui.js +64 -0
  23. package/cli/dist/commands/weigh.js +16 -0
  24. package/cli/dist/config/load.js +93 -0
  25. package/cli/dist/config/schema.js +43 -0
  26. package/cli/dist/doctor.js +31 -0
  27. package/cli/dist/format.js +37 -0
  28. package/cli/dist/index.js +36 -0
  29. package/cli/dist/init.js +120 -0
  30. package/cli/dist/learn.js +69 -0
  31. package/cli/dist/lib/files.js +23 -0
  32. package/cli/dist/lib/frontmatter.js +16 -0
  33. package/cli/dist/lib/system.js +19 -0
  34. package/cli/dist/packs.js +85 -0
  35. package/cli/dist/run.js +45 -0
  36. package/cli/dist/standards.js +42 -0
  37. package/cli/dist/status.js +27 -0
  38. package/cli/dist/sync.js +30 -0
  39. package/cli/dist/ui.js +69 -0
  40. package/package.json +50 -4
  41. package/packs/expo/pack.yaml +5 -0
  42. package/packs/firebase/pack.yaml +5 -0
  43. package/packs/nextjs/pack.yaml +5 -0
  44. package/packs/react/pack.yaml +5 -0
  45. package/packs/revenuecat/pack.yaml +5 -0
  46. package/packs/uniwind/pack.yaml +5 -0
  47. package/skills/ask/SKILL.md +42 -0
  48. package/skills/decision/SKILL.md +33 -0
  49. package/skills/ingest/SKILL.md +44 -0
  50. package/skills/standards/SKILL.md +16 -0
  51. package/skills/weigh/SKILL.md +102 -0
  52. package/skills/wiki-lint/SKILL.md +69 -0
  53. package/standards/README.md +13 -0
  54. package/standards/agent-contract.md +40 -0
  55. package/standards/constitution.md +89 -0
  56. package/standards/context-layout.md +112 -0
  57. package/standards/wiki-schema.md +249 -0
  58. package/templates/dependency-cruiser/app.cjs +23 -0
  59. package/templates/dependency-cruiser/base.cjs +52 -0
  60. package/templates/dependency-cruiser/package.cjs +31 -0
  61. package/templates/dependency-cruiser/root.cjs +15 -0
@@ -0,0 +1,249 @@
1
+ # Wiki — schema and rules
2
+
3
+ This folder is a **Claude-maintained knowledge base** for the product. The model is the LLM-Wiki pattern: `.context/raw/` holds immutable sources, `.context/wiki/` holds synthesised pages with `[[wikilinks]]`, this file defines the schema.
4
+
5
+ **Read this file before creating or editing any page under `.context/wiki/`.**
6
+
7
+ ---
8
+
9
+ ## Three layers
10
+
11
+ | Layer | Path | Mutability | Who writes |
12
+ |---|---|---|---|
13
+ | Raw | `.context/raw/`, `.context/designs/` | **Immutable** — never edit | Humans (or external systems) |
14
+ | Wiki | `.context/wiki/` | Mutable, schema-bound | Claude (via `/zeno:ingest`, `/zeno:ask`, `/zeno:wiki-lint`) |
15
+ | Index | `.context/wiki/index.md` | Auto-maintained | `/zeno:ingest` and `/zeno:wiki-lint` |
16
+
17
+ The wiki is the **only** layer Claude reads for `/zeno:ask`. Never answer from `.context/raw/` directly — the synthesis must be written down or it doesn't exist.
18
+
19
+ ---
20
+
21
+ ## Page types
22
+
23
+ Every page in `.context/wiki/` belongs to exactly one type. The folder name is the type.
24
+
25
+ | Type | Folder | What it owns | Example |
26
+ |---|---|---|---|
27
+ | `concept` | `concepts/` | An idea, principle, or invariant. Prose-shaped. | `offline-first`, `money-in-minor-units` |
28
+ | `entity` | `entities/` | A thing with canonical values (price, name, enum). Table-shaped. | `plan-pro`, `plan-free` |
29
+ | `surface` | `surfaces/` | One screen / route / email / callable. Links to a raw wireframe. | `onboarding-welcome` |
30
+ | `flow` | `flows/` | A user journey. Ordered list of `[[surface]]` steps. | `new-user-to-first-save` |
31
+ | `decision` | `decisions/` | One ADR. Filename = `YYYY-MM-DD-slug.md`. Append-only. | `2026-06-24-annual-plan-only` |
32
+ | `synthesis` | `synthesis/` | Cross-cutting page built from many entities/concepts. Refreshable. | `pricing`, `launch-checklist` |
33
+
34
+ A page cannot be two types. If it feels like it is, it's two pages — split it.
35
+
36
+ ---
37
+
38
+ ## Frontmatter (mandatory)
39
+
40
+ Every page begins with YAML frontmatter:
41
+
42
+ ```yaml
43
+ ---
44
+ id: PLAN.PRO # Stable, uppercase, dot-separated. Never changes. Referenced from code.
45
+ type: entity # concept | entity | surface | flow | decision | synthesis
46
+ status: canonical # canonical | draft | superseded
47
+ last-verified: 2026-06-24 # ISO date. /zeno:wiki-lint flags pages > 90 days.
48
+ supersedes: [] # IDs this page replaces (decisions only)
49
+ superseded-by: null # If non-null, this page is no longer canonical
50
+ ---
51
+ ```
52
+
53
+ ### ID conventions
54
+
55
+ | Type | ID shape | Example |
56
+ |---|---|---|
57
+ | concept | `CONCEPT.<SLUG>` | `CONCEPT.OFFLINE-FIRST` |
58
+ | entity | `<DOMAIN>.<NAME>` | `PLAN.PRO`, `PLAN.FREE` |
59
+ | surface | `SURFACE.<APP>.<PATH>` | `SURFACE.MOBILE.ONBOARDING.WELCOME` |
60
+ | flow | `FLOW.<SLUG>` | `FLOW.NEW-USER-TO-FIRST-SAVE` |
61
+ | decision | `DECISION.<YYYY-MM-DD>.<SLUG>` | `DECISION.2026-06-24.ANNUAL-PLAN-ONLY` |
62
+ | synthesis | `SYNTHESIS.<SLUG>` | `SYNTHESIS.PRICING` |
63
+
64
+ Once assigned, an ID never changes. If a page is renamed, the ID stays. If the thing it describes is killed, mark `status: superseded` and set `superseded-by`; do not delete.
65
+
66
+ ---
67
+
68
+ ## Page body — required sections
69
+
70
+ Each type has a required section list. `/zeno:wiki-lint` fails the page if any are missing.
71
+
72
+ ### `concept`
73
+ ```markdown
74
+ ## Summary
75
+ One sentence — what the concept is.
76
+
77
+ ## Why it matters
78
+ Why this is a load-bearing idea. Tie it to a constraint, an incident, or a principle.
79
+
80
+ ## Implications
81
+ Bulleted: what this forces / forbids in practice.
82
+
83
+ ## Related
84
+ - [[other-concept]] — relationship
85
+ - [[entity-x]] — where this applies
86
+
87
+ ## Sources
88
+ - .context/raw/<source> or .context/designs/<surface>/<flow>/<screen>.html
89
+ ```
90
+
91
+ ### `entity`
92
+ ```markdown
93
+ ## Summary
94
+ One sentence — what this entity is.
95
+
96
+ ## Canonical values
97
+ | Key | Value | Last changed |
98
+ |---|---|---|
99
+ | PRICE.PRO.YEARLY | $49 | 2026-04-12 |
100
+ | STORAGE.PRO | 5 GB | 2026-04-12 |
101
+
102
+ ## Related
103
+ - [[concept-x]] — concept this entity instantiates
104
+ - [[entity-y]] — sibling / upgrade-from
105
+
106
+ ## Sources
107
+ - .context/raw/<source>
108
+ ```
109
+
110
+ The `Canonical values` table is the single source of truth. Any value here that disagrees with the codebase is a bug — fix the code or update this page.
111
+
112
+ ### `surface`
113
+ ```markdown
114
+ ## Summary
115
+ One sentence — what this screen does.
116
+
117
+ ## Raw wireframe
118
+ - .context/designs/<surface>/<flow>/<screen>.html
119
+
120
+ ## Copy
121
+ Verbatim user-visible strings. Each must pass the [[copy-voice]] rules.
122
+
123
+ ## Flows that touch this surface
124
+ - [[new-user-to-first-save]] — step 4
125
+
126
+ ## Entities referenced
127
+ - [[plan-free]]
128
+ - [[privacy-consent]]
129
+
130
+ ## Sources
131
+ - .context/raw/<source>
132
+ ```
133
+
134
+ ### `flow`
135
+ ```markdown
136
+ ## Summary
137
+ One sentence — what journey this is.
138
+
139
+ ## Audience
140
+ end-user | admin
141
+
142
+ ## Entry
143
+ Where the user arrives from.
144
+
145
+ ## Steps
146
+ | # | Step | Surface | Notes |
147
+ |---|---|---|---|
148
+ | 1 | Welcome | [[welcome]] | First launch |
149
+ | 2 | Sign up | [[sign-up]] | Email |
150
+ | ... |
151
+
152
+ ## Exit
153
+ The success state.
154
+
155
+ ## Escapes
156
+ Back-button, skips, abandonment outcomes.
157
+
158
+ ## Sources
159
+ - .context/raw/<source>
160
+ ```
161
+
162
+ ### `decision`
163
+ ```markdown
164
+ ## Decision
165
+ One-line statement.
166
+
167
+ ## Why
168
+ The motivation. Cite the trigger — incident, user feedback, regulator, scope cut.
169
+
170
+ ## Impact
171
+ What changes downstream. Which [[entities]] / [[surfaces]] / [[concepts]] update.
172
+
173
+ ## Status
174
+ Active | Superseded by [[decision-id]]
175
+
176
+ ## Sources
177
+ - .context/raw/<source>
178
+ ```
179
+
180
+ Decisions are append-only. To reverse a decision, write a new one with `supersedes: [DECISION.<old>]` and mark the old one's `superseded-by`.
181
+
182
+ ### `synthesis`
183
+ ```markdown
184
+ ## Summary
185
+ What this synthesis combines.
186
+
187
+ ## Built from
188
+ - [[entity-x]]
189
+ - [[entity-y]]
190
+ - [[concept-z]]
191
+
192
+ ## Body
193
+ The cross-cutting answer. Every claim links back to a source page.
194
+
195
+ ## Last refresh
196
+ 2026-06-24 — refresh when any "Built from" page changes.
197
+
198
+ ## Sources
199
+ - (synthesis pages cite wiki pages, not raw files)
200
+ ```
201
+
202
+ Synthesis pages are **derived**. If a source page changes, `/zeno:wiki-lint` marks the synthesis stale.
203
+
204
+ ---
205
+
206
+ ## Linking
207
+
208
+ - Use `[[page-slug]]` — the filename without `.md`. Match the file exactly (case-sensitive on case-sensitive filesystems).
209
+ - Cross-folder links use the slug, not the path: `[[tier-self]]`, not `[[entities/tier-self]]`.
210
+ - An `[[unknown-link]]` is a TODO marker — `/zeno:wiki-lint` reports it. Never silently delete a broken link; either create the target page or rename the link.
211
+ - The `Related` section is what makes the graph navigable. Every page must have at least two outbound links (or be flagged as orphan-source).
212
+
213
+ ---
214
+
215
+ ## Lint rules (what `/zeno:wiki-lint` enforces)
216
+
217
+ | Rule | Failure |
218
+ |---|---|
219
+ | Missing required section | Block — must be fixed |
220
+ | `[[wikilink]]` to a non-existent page | Block — create target or rename link |
221
+ | Orphan page (zero inbound links) | Warn |
222
+ | Two entities claim the same `id` | Block |
223
+ | Two entities have contradictory canonical values for the same key | Block |
224
+ | `last-verified` > 90 days | Warn |
225
+ | `status: superseded` without `superseded-by` | Block |
226
+ | Source file in `.context/raw/` not referenced by any wiki page | Warn |
227
+ | Synthesis page where a `Built from` page changed after `Last refresh` | Warn |
228
+ | Filename does not match the `id`'s slug portion | Block |
229
+
230
+ `/zeno:wiki-lint --fix` auto-fixes: filename ↔ id mismatches, missing `last-verified` (sets to today if the page is touched), index regeneration.
231
+
232
+ ---
233
+
234
+ ## What the wiki is NOT for
235
+
236
+ - **Not for code documentation.** Docstrings stay in code. The wiki is product-shaped, not API-shaped.
237
+ - **Not for transient task tracking.** Use `TaskCreate` / PR descriptions / commit messages for ephemeral work.
238
+ - **Not for chat logs.** If a chat produces a decision, write a `decision` page; throw the chat away.
239
+ - **Not for drafts of code.** If it would be a comment, it's a comment.
240
+
241
+ ---
242
+
243
+ ## Source of truth
244
+
245
+ `.context/wiki/` is the canonical knowledge base, and every page in it follows
246
+ the schema above — there are no plan-shaped exceptions. What is *next* lives in
247
+ `.context/features/`, and the ordered queue lives in `.context/tasks.md`; the
248
+ wiki describes what is, not what is planned. The repo root holds no
249
+ product-knowledge `.md` files.
@@ -0,0 +1,23 @@
1
+ /* global module */
2
+ // This app's architecture rules, enforced by `zeno check` (architecture module).
3
+ // zeno wrote this starter once; it is yours now. Add the layers this app has
4
+ // and keep the why in a wiki concept page.
5
+ // Rule reference: https://github.com/sverweij/dependency-cruiser/blob/main/doc/rules-reference.md
6
+
7
+ /** @type {import('dependency-cruiser').IConfiguration} */
8
+ module.exports = {
9
+ extends: "../../.dependency-cruiser.base.cjs",
10
+ forbidden: [
11
+ {
12
+ name: "no-app-to-app",
13
+ comment: "An app never imports another app; share code through packages/.",
14
+ severity: "error",
15
+ from: {},
16
+ to: { path: "^\\.\\./[^./]" },
17
+ },
18
+ ],
19
+ options: {
20
+ // The level `zeno ui` draws the architecture graph at: one box per match.
21
+ reporterOptions: { archi: { collapsePattern: "^src/[^/]+/[^/]+|^src/[^/]+|^[^/.][^/]*|^\\.\\./\\.\\./packages/[^/]+" } },
22
+ },
23
+ };
@@ -0,0 +1,52 @@
1
+ /* global module, require */
2
+ // Managed by zeno — `zeno sync` rewrites this file from zeno's template, so edits here are lost.
3
+ // Put this repo's own rules in the .dependency-cruiser.cjs files that extend it.
4
+ // Each workspace runs on its own because apps map `@/` to their own src, and
5
+ // dependency-cruiser reads one tsconfig per run.
6
+
7
+ const { existsSync } = require("node:fs");
8
+
9
+ // recommended's no-orphans exceptions (dot files, .d.ts, tsconfig, babel/webpack configs),
10
+ // restated because the package does not export its rule files…
11
+ const KNOWN_CONFIG_FILES = [
12
+ "(^|/)\\.[^/]+\\.(js|cjs|mjs|ts|json)$",
13
+ "\\.d\\.(c|m)?ts$",
14
+ "(^|/)tsconfig\\.json$",
15
+ "(^|/)(?:babel|webpack)\\.config\\.(?:js|cjs|mjs|ts|json)$",
16
+ ];
17
+ // …plus files a framework or tool loads by convention, so nothing imports them.
18
+ const ENTRY_POINTS = [
19
+ "(^|/)src/app/[^.]*(page|layout|route|loading|error|not-found|template|default)\\.tsx?$", // Next.js app router
20
+ "(^|/)src/pages/[^.]+\\.tsx?$", // Next.js pages router
21
+ "^src/app/[^.]+\\.tsx?$", // Expo Router: every file under app/ is a route
22
+ "^src/(instrumentation|middleware)\\.ts$", // Next.js hooks
23
+ "^(scripts|plugins|eslint|e2e|public)/", // run by a tool, not imported
24
+ "(^|/)__mocks__/", // jest mocks
25
+ "^(src/)?index\\.ts$", // a package's entry
26
+ "(^|/)codegen\\.ts$", // GraphQL codegen script
27
+ ];
28
+
29
+ /** @type {import('dependency-cruiser').IConfiguration} */
30
+ module.exports = {
31
+ extends: "dependency-cruiser/configs/recommended",
32
+ forbidden: [
33
+ {
34
+ name: "no-orphans",
35
+ comment: "Nothing imports this file: use it or delete it. Add a real entry point to ENTRY_POINTS instead.",
36
+ severity: "warn",
37
+ from: { orphan: true, pathNot: [...KNOWN_CONFIG_FILES, ...ENTRY_POINTS] },
38
+ to: {},
39
+ },
40
+ ],
41
+ options: {
42
+ // Record imports into a sibling workspace, but leave cruising it to that workspace's own run.
43
+ doNotFollow: { path: ["node_modules", "^\\.\\./"] },
44
+ // Specs and tooling configs are not app code.
45
+ exclude: { path: "^(dist|build|out|generated|coverage|\\.next|\\.expo|\\.turbo)/|^tests/|\\.(spec|test)\\.tsx?$|(^|/)[^/]+\\.config\\.(c|m)?(j|t)s$|(^|/)\\.dependency-cruiser" },
46
+ tsPreCompilationDeps: true,
47
+ // Each run starts in its workspace, so this is that workspace's tsconfig (if it has one).
48
+ ...(existsSync("tsconfig.json") ? { tsConfig: { fileName: "tsconfig.json" } } : {}),
49
+ // Packages like uuid 13 export only `node`/`default` conditions.
50
+ enhancedResolveOptions: { exportsFields: ["exports"], conditionNames: ["import", "require", "node", "default", "types"] },
51
+ },
52
+ };
@@ -0,0 +1,31 @@
1
+ /* global module */
2
+ // This package's architecture rules, enforced by `zeno check` (architecture module).
3
+ // zeno wrote this starter once; it is yours now.
4
+
5
+ /** @type {import('dependency-cruiser').IConfiguration} */
6
+ module.exports = {
7
+ extends: "../../.dependency-cruiser.base.cjs",
8
+ forbidden: [
9
+ {
10
+ name: "no-package-to-app",
11
+ comment: "packages/ are shared; they never depend on an app.",
12
+ severity: "error",
13
+ from: {},
14
+ to: { path: "^\\.\\./\\.\\./apps/" },
15
+ },
16
+ {
17
+ name: "no-orphans",
18
+ comment: "Off here: apps import a package's files through subpath exports, which a run inside the package cannot see.",
19
+ severity: "ignore",
20
+ from: { orphan: true },
21
+ to: {},
22
+ },
23
+ {
24
+ name: "no-duplicate-dep-types",
25
+ comment: "Off here: a shared package lists react and friends as both peer and dev dependency on purpose.",
26
+ severity: "ignore",
27
+ from: {},
28
+ to: {},
29
+ },
30
+ ],
31
+ };
@@ -0,0 +1,15 @@
1
+ /* global module */
2
+ // This repo's architecture rules, enforced by `zeno check` (architecture module).
3
+ // zeno wrote this starter once; it is yours now. Add the layers this product has
4
+ // (see the rule reference below) and keep the why in a wiki concept page.
5
+ // Rule reference: https://github.com/sverweij/dependency-cruiser/blob/main/doc/rules-reference.md
6
+
7
+ /** @type {import('dependency-cruiser').IConfiguration} */
8
+ module.exports = {
9
+ extends: "./.dependency-cruiser.base.cjs",
10
+ forbidden: [],
11
+ options: {
12
+ // The level `zeno ui` draws the architecture graph at: one box per match.
13
+ reporterOptions: { archi: { collapsePattern: "^src/[^/]+|^[^/]+" } },
14
+ },
15
+ };