eidosmd 0.1.0 → 0.2.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 (100) hide show
  1. package/README.md +50 -29
  2. package/browser/dist/assets/index-C2NMN_D4.css +1 -0
  3. package/browser/dist/assets/index-C65k1ihb.js +46 -0
  4. package/browser/dist/favicon.svg +5 -0
  5. package/browser/dist/index.html +15 -0
  6. package/browser/dist/mark.svg +4 -0
  7. package/dist/src/cli.js +7 -0
  8. package/dist/src/commands/agents.js +1 -1
  9. package/dist/src/commands/canvas.js +77 -0
  10. package/dist/src/commands/check.js +1 -1
  11. package/dist/src/commands/framework.js +24 -5
  12. package/dist/src/commands/index.js +4 -4
  13. package/dist/src/commands/init.js +1 -0
  14. package/dist/src/commands/instructions.js +1 -1
  15. package/dist/src/commands/list.js +8 -8
  16. package/dist/src/commands/migrate.js +32 -0
  17. package/dist/src/commands/new.js +3 -3
  18. package/dist/src/commands/seeds.js +5 -5
  19. package/dist/src/commands/setup.js +119 -0
  20. package/dist/src/commands/version.js +44 -0
  21. package/dist/src/commands/whoami.js +4 -4
  22. package/dist/src/context.js +5 -5
  23. package/dist/src/core/blueprint.js +16 -11
  24. package/dist/src/core/canvas-schema.js +148 -0
  25. package/dist/src/core/canvas.js +722 -0
  26. package/dist/src/core/check.js +127 -51
  27. package/dist/src/core/convert.js +7 -6
  28. package/dist/src/core/framework-markdown.js +113 -34
  29. package/dist/src/core/framework-model.js +27 -10
  30. package/dist/src/core/framework-structured.js +132 -33
  31. package/dist/src/core/framework.js +13 -13
  32. package/dist/src/core/git.js +59 -0
  33. package/dist/src/core/index-leaf.js +2 -2
  34. package/dist/src/core/me.js +16 -8
  35. package/dist/src/core/migrate.js +243 -0
  36. package/dist/src/core/naming.js +1 -1
  37. package/dist/src/core/root.js +2 -2
  38. package/dist/src/core/scaffold.js +19 -19
  39. package/dist/src/core/seed.js +151 -60
  40. package/dist/src/core/server.js +1236 -52
  41. package/dist/src/core/settings.js +181 -0
  42. package/dist/src/core/store.js +259 -0
  43. package/dist/src/core/template.js +32 -0
  44. package/dist/src/core/versions.js +79 -0
  45. package/dist/src/output.js +4 -1
  46. package/dist/src/program.js +134 -39
  47. package/instructions/authoring.md +13 -12
  48. package/instructions/configuring.md +65 -34
  49. package/instructions/init-required.md +4 -4
  50. package/instructions/overview.md +17 -7
  51. package/instructions/validating.md +5 -4
  52. package/package.json +21 -12
  53. package/standard/EIDOS.md +129 -188
  54. package/standard/seeds/README.md +6 -6
  55. package/standard/seeds/book/Framework.yaml +81 -0
  56. package/standard/seeds/book/README.md +9 -5
  57. package/standard/seeds/book/_gitignore +3 -3
  58. package/standard/seeds/book/me.md +1 -1
  59. package/standard/seeds/book/roles/README.md +3 -3
  60. package/standard/seeds/book/roles/framework-owner.md +2 -2
  61. package/standard/seeds/book/{shapes → templates}/chapter.full.md +0 -8
  62. package/standard/seeds/book/{shapes → templates}/chapter.sketch.md +0 -7
  63. package/standard/seeds/book/{shapes → templates}/frame.market.md +0 -6
  64. package/standard/seeds/book/templates/frame.premise.md +17 -0
  65. package/standard/seeds/book/{shapes → templates}/frame.reader.md +0 -6
  66. package/standard/seeds/book/{shapes → templates}/frame.voice.md +0 -7
  67. package/standard/seeds/research/Framework.yaml +81 -0
  68. package/standard/seeds/research/README.md +9 -5
  69. package/standard/seeds/research/_gitignore +3 -3
  70. package/standard/seeds/research/me.md +1 -1
  71. package/standard/seeds/research/roles/README.md +3 -3
  72. package/standard/seeds/research/roles/framework-owner.md +2 -2
  73. package/standard/seeds/research/{shapes → templates}/frame.ethics.md +0 -6
  74. package/standard/seeds/research/{shapes → templates}/frame.method.md +0 -7
  75. package/standard/seeds/research/{shapes → templates}/frame.prior-work.md +0 -6
  76. package/standard/seeds/research/{shapes → templates}/frame.question.md +0 -7
  77. package/standard/seeds/research/{shapes → templates}/investigation.full.md +0 -8
  78. package/standard/seeds/research/{shapes → templates}/investigation.note.md +0 -7
  79. package/standard/seeds/software/Framework.yaml +82 -0
  80. package/standard/seeds/software/README.md +5 -5
  81. package/standard/seeds/software/_gitignore +3 -3
  82. package/standard/seeds/software/me.md +1 -1
  83. package/standard/seeds/software/roles/README.md +3 -3
  84. package/standard/seeds/software/roles/framework-owner.md +2 -2
  85. package/standard/seeds/software/roles/project-manager.md +2 -2
  86. package/standard/seeds/software/roles/stakeholder.md +1 -1
  87. package/standard/seeds/software/{shapes → templates}/frame.architecture.md +0 -7
  88. package/standard/seeds/software/{shapes → templates}/frame.audience.md +1 -8
  89. package/standard/seeds/software/{shapes → templates}/frame.criteria.md +0 -8
  90. package/standard/seeds/software/{shapes → templates}/frame.market.md +0 -8
  91. package/standard/seeds/software/{shapes → templates}/spec.full.md +0 -8
  92. package/standard/seeds/software/{shapes → templates}/spec.micro.md +0 -9
  93. package/browser/index.html +0 -268
  94. package/dist/src/commands/convert.js +0 -30
  95. package/dist/src/core/shape.js +0 -26
  96. package/standard/seeds/book/Framework.md +0 -87
  97. package/standard/seeds/book/shapes/frame.premise.md +0 -24
  98. package/standard/seeds/research/Framework.md +0 -88
  99. package/standard/seeds/software/Framework.md +0 -88
  100. /package/standard/seeds/software/{shapes → templates}/.gitkeep +0 -0
@@ -1,8 +1,8 @@
1
1
  # Roles
2
2
 
3
- Default **roles** for this framework — who is in the seat, and how the agent should respond to them. A role is a **response contract**: it sets the vocabulary, the level of technical depth, what to surface vs. fold away, and who holds which decisions. The agent reads it **before acting** (see the Eidos standard's `EIDOS.md`, "The actor").
3
+ Default **roles** for this framework — who is in the seat, and how the agent should respond to them. A role is a **response contract**: it sets the vocabulary, the level of technical depth, what to surface vs. fold away, and who holds which decisions. The agent reads it **before acting** (see the Eidos standard's `EIDOS.md`, "Roles").
4
4
 
5
- These are the opinionated baseline, browsable here and installed into a root's `_eidos/roles/` by `install` (committed, so a team can tune how a role is treated for their product). Each person who works on the folder picks one in their personal, gitignored `_eidos/me.md` and **calibrates** it — what they own on this folder, their experience with the scope, and their technical capacity — with the `whoami` skill. Role sets the baseline; calibration tunes it per person.
5
+ These are the opinionated baseline, browsable here and installed into a root's `.eidos/roles/` by `eidos init` (committed, so a team can tune how a role is treated for their product). Each person who works on the folder picks one in their personal, gitignored `.eidos/me.md` and **calibrates** it — what they own on this folder, their experience with the scope, and their technical capacity — with `eidos whoami`. Role sets the baseline; calibration tunes it per person.
6
6
 
7
7
  - [Framework Owner](framework-owner.md) — holds intent, scope, and decisions.
8
8
  - [Developer](developer.md) — builds from the blueprints.
@@ -10,4 +10,4 @@ These are the opinionated baseline, browsable here and installed into a root's `
10
10
  - [Designer](designer.md) — shapes the experience.
11
11
  - [Project Manager](project-manager.md) — tracks scope and progress.
12
12
 
13
- A role is a baseline, not a cage: an actor can write a custom role in their `me.md`, and a framework can add or reshape role files here. The human-first principle holds for every role — the human authors and decides; the role only changes _how_ the agent helps.
13
+ A role is a baseline, not a cage: anyone can write a custom role in their `me.md`, and a framework can add or reshape role files here. The human-first principle holds for every role — the human authors and decides; the role only changes _how_ the agent helps.
@@ -2,11 +2,11 @@
2
2
 
3
3
  ## Who they are
4
4
 
5
- Holds the **intent, scope, and decisions** — true ownership of whatever it defines, be it a product, a body of research, a methodology, or any other form of thought or effort. The person Eidos is built for — they think through what the thing is, and they own the calls. Everything else serves their clarity.
5
+ Holds the **intent, scope, and decisions** — true ownership of the product, whatever kind it is: an app, a body of research, a methodology, any other form of thought or effort. The person Eidos is built for — they think through what the product is, and they own the calls. Everything else serves their clarity.
6
6
 
7
7
  ## How to respond
8
8
 
9
- - **Vocabulary & depth:** lead with the terms of the thing itself and the decision at hand. But many Framework Owners are also technical — don't assume otherwise; follow their **technical capacity** calibration and go as deep as they want, rather than withholding mechanism by default.
9
+ - **Vocabulary & depth:** lead with the product's own terms and the decision at hand. But many Framework Owners are also technical — don't assume otherwise; follow their **technical capacity** calibration and go as deep as they want, rather than withholding mechanism by default.
10
10
  - **Decisions:** theirs. Bring choices and trade-offs for them to decide; never decide direction or resolve an Open Question on their behalf. Press hardest on **Out of Scope**.
11
11
  - **Surface / hide:** surface intent, scope, audience, criteria, and the consequences of a choice; fold mechanism into a link they can follow.
12
12
  - **Focus:** Intent, Out of Scope, Audience, Criteria, and whether each blueprint still says what they mean.
@@ -11,8 +11,8 @@ Tracks **scope and progress**, not product direction or implementation. Wants to
11
11
  - **Surface / hide:** surface **Out of Scope** (the in/out line), each unit's `status` (its lifecycle stage), `depends_on` and other dependencies, and the Decisions log with the `created`/`modified` dates that show movement. Fold away mechanism and prose rationale.
12
12
  - **Focus:** what's in vs. out, what stage each unit is at, what blocks what, and where scope is drifting from Criteria.
13
13
 
14
- Remember Eidos captures **state and intent, not work** — there are no sprint, estimate, or assignee fields, on purpose. So for this actor: read **progress** from `status` and git history (the Decisions log, `created`/`modified`), not a burn-down; infer **level of effort** from a unit's shape — its acceptance criteria, dependencies, and open questions — not a stored estimate; and for sprint-level tracking, point to the tracker a unit links to rather than adding work fields to a blueprint.
14
+ Remember Eidos captures **state and intent, not work** — there are no sprint, estimate, or assignee fields, on purpose. So for this role: read **progress** from `status` and git history (the Decisions log, `created`/`modified`), not a burn-down; infer **level of effort** from a unit's shape — its acceptance criteria, dependencies, and open questions — not a stored estimate; and for sprint-level tracking, point to the tracker a unit links to rather than adding work fields to a blueprint.
15
15
 
16
16
  ## Calibration
17
17
 
18
- Usually moderate **technical capacity**, and broad-but-shallow **experience with the scope** — they span the whole thing rather than living in one unit. Lean on `status`, dependencies, and the in/out line.
18
+ Usually moderate **technical capacity**, and broad-but-shallow **experience with the scope** — they span the whole product rather than living in one unit. Lean on `status`, dependencies, and the in/out line.
@@ -9,7 +9,7 @@ Reviews direction and outcomes — a sponsor, a partner, a lead from another tea
9
9
  - **Vocabulary & depth:** plain, outcome-oriented language. No implementation detail unless asked; translate everything into impact, scope, and risk.
10
10
  - **Decisions:** advisory. They weigh in on direction; the Framework Owner holds the call. Don't ask them to make build decisions.
11
11
  - **Surface / hide:** surface summaries, scope, trade-offs, and risk; hide mechanism entirely.
12
- - **Focus:** Market, Criteria, Roadmap, and the shape of scope — what's in, what's out, what it costs.
12
+ - **Focus:** Market, Criteria, and the shape of scope — what's in, what's out, what it costs.
13
13
 
14
14
  ## Calibration
15
15
 
@@ -1,10 +1,3 @@
1
- <!--
2
- The Frame shape — architecture flavor. The overarching shape of the product as a built system, one
3
- per product. Suggestive, not prescriptive: keep the sections that serve the product, drop what doesn't
4
- yet apply, add what's missing. A Frame's frontmatter is generated from the framework's Schema (in Framework.md), so it is not
5
- written here. Keep the order and headings; delete the italic prompts as you fill each section in.
6
- -->
7
-
8
1
  # {{title}}
9
2
 
10
3
  _The overarching shape of the product as a built system. When the detail outgrows a single file, expand into an optional `Arch/` folder and keep this doc as the map that points into it._
@@ -1,10 +1,3 @@
1
- <!--
2
- The Frame shape — audience flavor. Who the product is for, and how each kind of person interacts with
3
- it differently. This frame and criteria are the two that decide scope. Keep it to simple blocks of
4
- prose — no tables, no headshot-and-demographics cards. Suggestive, not prescriptive. A Frame's frontmatter is
5
- generated from the framework's Schema (in Framework.md), so it is not written here. Delete the italic prompts as you fill in.
6
- -->
7
-
8
1
  # {{title}}
9
2
 
10
3
  _Who the product is for, and how each kind of person interacts with it differently. This frame and Criteria are the two that decide scope: a spec that serves no one here, or serves a persona that doesn't exist here, is a spec to question._
@@ -15,7 +8,7 @@ _Who uses this product, in a paragraph or two. The whole population it is built
15
8
 
16
9
  ## Personas
17
10
 
18
- _How each kind of person interacts differently. One short block of prose per persona. The point is the difference: what this persona comes for, how they reach for the product, what they ignore. A persona earns its place here only when it interacts in a way the others do not._
11
+ _How each kind of person interacts differently. One short block of prose per persona, never a table or a headshot-and-demographics card. The point is the difference: what this persona comes for, how they reach for the product, what they ignore. A persona earns its place here only when it interacts in a way the others do not._
19
12
 
20
13
  ### Persona name
21
14
 
@@ -1,11 +1,3 @@
1
- <!--
2
- The Frame shape — criteria flavor. The frame that decides and audits scope: what the product can
3
- afford, what it is trying to be, and by when. Paired with Audience, this is what a spec is measured
4
- against. A point-in-time snapshot — record what's true now, revise when it changes. Suggestive, not
5
- prescriptive. A Frame's frontmatter is generated from the framework's Schema (in Framework.md), so it is not written here.
6
- Delete the italic prompts as you fill each section in.
7
- -->
8
-
9
1
  # {{title}}
10
2
 
11
3
  _The frame that decides and audits scope: what the product can afford, what it is trying to be, and by when. When scope creeps, it creeps past this doc._
@@ -1,11 +1,3 @@
1
- <!--
2
- The Frame shape — market flavor. Where the product sits in its marketplace, why it is not just another
3
- one of many, and how it is intended to make money. Answers the two questions a product must survive:
4
- why would anyone pick this, and how does it earn. A point-in-time snapshot — markets move, revise when
5
- they do. Suggestive, not prescriptive. A Frame's frontmatter is generated from the framework's Schema (in Framework.md), so it
6
- is not written here. Delete the italic prompts as you fill each section in.
7
- -->
8
-
9
1
  # {{title}}
10
2
 
11
3
  _Where the product sits in its marketplace, why it is not just another one of many, and how it is intended to make money._
@@ -1,11 +1,3 @@
1
- <!--
2
- The Spec shape — the body of a spec, and the documentation of each section. A spec's
3
- frontmatter is generated from the framework's Schema (in Framework.md), so it is not written here. Keep the sections
4
- that apply and delete the rest, but leave the order and headings as they are — a reader should
5
- know what to expect from any spec in this folder. The italic prompts are guidance; delete
6
- them as you fill each section in.
7
- -->
8
-
9
1
  # {{title}}
10
2
 
11
3
  ## Intent
@@ -1,12 +1,3 @@
1
- <!--
2
- The Spec shape — micro flavor. The smallest spec worth writing: why it exists, what you're getting,
3
- and what it will not do. A starting point that grows into the full flavor (spec.full.md) as the unit
4
- firms up — add the full flavor's sections (Implementation Notes, Dependencies, Testing, Constraints &
5
- Decisions, …) when they earn their place, and set `flavor: full` (or drop `flavor`) once it has.
6
- A spec's frontmatter is generated from the framework's Schema (in Framework.md), so it is not written here. Keep the order and
7
- headings; the italic prompts are guidance — delete them as you fill each section in.
8
- -->
9
-
10
1
  # {{title}}
11
2
 
12
3
  ## Intent
@@ -1,268 +0,0 @@
1
- <!doctype html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="utf-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1">
6
- <title>Eidos</title>
7
- <style>
8
- :root {
9
- color-scheme: light dark;
10
- --bg: #fbfaf7; --panel: #f3f1ec; --line: #ddd8cf; --text: #1f1d1a; --muted: #6b665e;
11
- --accent: #7a4a1f; --accent-soft: #f1e4d6; --error: #b23a2a; --warn: #9a6a12; --ok: #2f6b3a;
12
- --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
13
- --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
14
- }
15
- @media (prefers-color-scheme: dark) {
16
- :root { --bg: #1b1916; --panel: #232019; --line: #3a352c; --text: #ebe6dc; --muted: #a39d92; --accent: #d9a56b; --accent-soft: #3a2d1f; --error: #e2705f; --warn: #d8a437; --ok: #7bc48a; }
17
- }
18
- * { box-sizing: border-box; }
19
- body { margin: 0; font: 15px/1.5 var(--sans); color: var(--text); background: var(--bg); }
20
- a { color: var(--accent); }
21
- button, select, input, textarea { font: inherit; color: inherit; }
22
- button { background: var(--panel); border: 1px solid var(--line); border-radius: 6px; padding: 4px 10px; cursor: pointer; }
23
- button:hover { border-color: var(--accent); }
24
- button.primary { background: var(--accent); color: var(--bg); border-color: var(--accent); }
25
- input, select, textarea { background: var(--bg); border: 1px solid var(--line); border-radius: 6px; padding: 5px 8px; width: 100%; }
26
- code, pre { font-family: var(--mono); font-size: 0.92em; }
27
- pre { background: var(--panel); padding: 10px 12px; border-radius: 6px; overflow-x: auto; }
28
- .layout { display: grid; grid-template-columns: 290px 1fr; min-height: 100vh; }
29
- aside { background: var(--panel); border-right: 1px solid var(--line); padding: 14px; overflow-y: auto; position: sticky; top: 0; height: 100vh; }
30
- main { padding: 20px 32px 60px; max-width: 980px; }
31
- .brand { display: flex; align-items: baseline; gap: 8px; margin-bottom: 10px; }
32
- .brand strong { font-size: 18px; }
33
- .brand small { color: var(--muted); }
34
- .nav a { display: block; padding: 3px 6px; border-radius: 4px; color: inherit; text-decoration: none; }
35
- .nav a.active, .nav a:hover { background: var(--accent-soft); }
36
- .nav h3 { margin: 14px 0 4px; font-size: 12px; text-transform: uppercase; letter-spacing: .06em; color: var(--muted); display: flex; justify-content: space-between; align-items: center; }
37
- .nav h3 button { font-size: 12px; padding: 1px 7px; }
38
- .nav h4 { margin: 8px 0 2px 6px; font-size: 12px; color: var(--muted); font-weight: 600; }
39
- .nav .bp { font-size: 14px; padding-left: 12px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
40
- .nav .bp.flagged::after { content: " •"; color: var(--warn); }
41
- .search { margin: 6px 0 4px; }
42
- .toolbar { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 18px; align-items: center; }
43
- .toolbar .spacer { flex: 1; }
44
- .meta { color: var(--muted); font-size: 13px; margin: 0 0 6px; }
45
- .meta code { color: var(--text); }
46
- h1.title { margin: 0 0 6px; font-size: 26px; }
47
- table.props { border-collapse: collapse; margin: 14px 0 22px; font-size: 14px; }
48
- table.props td { padding: 3px 12px 3px 0; vertical-align: top; }
49
- table.props td:first-child { color: var(--muted); font-family: var(--mono); font-size: 13px; white-space: nowrap; }
50
- .body { border-top: 1px solid var(--line); padding-top: 12px; }
51
- .body h1 { font-size: 24px; } .body h2 { font-size: 20px; margin-top: 28px; border-bottom: 1px solid var(--line); padding-bottom: 4px; } .body h3 { font-size: 16px; }
52
- .body table { border-collapse: collapse; } .body th, .body td { border: 1px solid var(--line); padding: 4px 8px; }
53
- .body em:only-child { color: var(--muted); }
54
- .findings { margin: 10px 0 16px; padding: 0; list-style: none; font-size: 14px; }
55
- .findings li { padding: 4px 8px; border-left: 3px solid var(--warn); background: var(--panel); margin-bottom: 4px; border-radius: 0 4px 4px 0; }
56
- .findings li.error { border-left-color: var(--error); }
57
- .findings code { color: var(--muted); }
58
- .badge { display: inline-block; font-size: 12px; padding: 1px 8px; border-radius: 10px; background: var(--panel); border: 1px solid var(--line); color: var(--muted); margin-left: 6px; }
59
- .badge.error { color: var(--error); border-color: var(--error); } .badge.warn { color: var(--warn); border-color: var(--warn); } .badge.ok { color: var(--ok); border-color: var(--ok); }
60
- textarea.editor { min-height: 60vh; font-family: var(--mono); font-size: 13px; line-height: 1.45; white-space: pre; }
61
- .form { display: grid; grid-template-columns: 140px 1fr; gap: 10px 14px; max-width: 640px; align-items: center; margin: 16px 0; }
62
- .form label { color: var(--muted); font-size: 14px; }
63
- .toast { position: fixed; right: 20px; bottom: 20px; background: var(--text); color: var(--bg); padding: 10px 16px; border-radius: 8px; max-width: 420px; font-size: 14px; white-space: pre-wrap; box-shadow: 0 6px 24px rgba(0,0,0,.25); }
64
- .file-group { margin: 18px 0 6px; font-family: var(--mono); font-size: 14px; }
65
- .file-group a { text-decoration: none; }
66
- .hint { color: var(--muted); font-size: 13px; }
67
- .empty { color: var(--muted); padding: 40px 0; }
68
- .fw h2 { margin-top: 26px; }
69
- .fw dl { display: grid; grid-template-columns: max-content 1fr; gap: 4px 14px; }
70
- .fw dt { color: var(--muted); }
71
- @media (max-width: 760px) { .layout { grid-template-columns: 1fr; } aside { position: static; height: auto; } main { padding: 16px; } }
72
- </style>
73
- </head>
74
- <body>
75
- <div class="layout">
76
- <aside>
77
- <div class="brand"><strong id="rootName">Eidos</strong><small id="rootVersion"></small></div>
78
- <input class="search" id="search" type="search" placeholder="Find a blueprint…" autocomplete="off">
79
- <nav class="nav" id="nav"></nav>
80
- </aside>
81
- <main id="main"><div class="empty">Loading…</div></main>
82
- </div>
83
- <script>
84
- (() => {
85
- const $ = (sel, el = document) => el.querySelector(sel);
86
- const esc = (s) => String(s ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
87
- const api = async (method, url, body) => {
88
- const res = await fetch(url, { method, headers: body ? { 'content-type': 'application/json' } : {}, body: body ? JSON.stringify(body) : undefined });
89
- const data = await res.json().catch(() => ({}));
90
- if (!res.ok) throw new Error(data.error || res.statusText);
91
- return data;
92
- };
93
- let state = { root: null, filter: '', flagged: new Set() };
94
- let toastTimer;
95
- const toast = (msg, ms = 4200) => {
96
- let el = $('.toast'); if (!el) { el = document.createElement('div'); el.className = 'toast'; document.body.appendChild(el); }
97
- el.textContent = msg; clearTimeout(toastTimer); toastTimer = setTimeout(() => el.remove(), ms);
98
- };
99
- const route = () => { const h = location.hash.replace(/^#\/?/, ''); const [view, ...rest] = h.split('/'); return { view: view || 'framework', arg: rest.join('/') }; };
100
- const go = (path) => { location.hash = '#/' + path; };
101
- const bpHref = (p) => '#/blueprint/' + p.split('/').map(encodeURIComponent).join('/');
102
-
103
- async function loadRoot() {
104
- state.root = await api('GET', '/api/root');
105
- $('#rootName').textContent = state.root.name;
106
- $('#rootVersion').textContent = `Eidos ${state.root.framework.eidos_version ?? '?'} · ${state.root.framework.file}`;
107
- document.title = `${state.root.name} · Eidos`;
108
- renderNav();
109
- }
110
-
111
- function renderNav() {
112
- const { framework, blueprints } = state.root;
113
- const q = state.filter.toLowerCase();
114
- const cur = route();
115
- const active = (h) => location.hash === h ? ' active' : '';
116
- let html = `<a href="#/framework" class="${active('#/framework') || (cur.view === 'framework' ? 'active' : '')}">Framework</a>
117
- <a href="#/check" class="${cur.view === 'check' ? 'active' : ''}">Check</a>
118
- <a href="#/standard" class="${cur.view === 'standard' ? 'active' : ''}">The standard</a>
119
- <a href="#/instructions/overview" class="${cur.view === 'instructions' ? 'active' : ''}">Agent instructions</a>`;
120
- for (const c of framework.collections) {
121
- const own = blueprints.filter((b) => b.collection === c.name && (!q || [b.title, b.id, b.summary, b.path].some((v) => (v || '').toLowerCase().includes(q))));
122
- html += `<h3><span>${esc(c.name)}</span><button data-new="${esc(c.name)}" title="New blueprint in ${esc(c.name)}">+ new</button></h3>`;
123
- const groups = [...new Set(own.map((b) => b.group).filter(Boolean))].sort();
124
- const item = (b) => `<a class="bp${state.flagged.has(b.path) ? ' flagged' : ''}${cur.view === 'blueprint' && decodeURIComponent(cur.arg) === b.path ? ' active' : ''}" href="${bpHref(b.path)}" title="${esc(b.summary || b.path)}">${esc(b.title)}</a>`;
125
- html += own.filter((b) => !b.group).map(item).join('');
126
- for (const g of groups) html += `<h4>${esc(g)}</h4>` + own.filter((b) => b.group === g).map(item).join('');
127
- if (own.length === 0) html += `<div class="hint" style="padding-left:12px">${q ? 'no match' : 'empty'}</div>`;
128
- }
129
- $('#nav').innerHTML = html;
130
- }
131
-
132
- const findingsHtml = (findings) => findings.length ? `<ul class="findings">${findings.map((f) => `<li class="${f.level}"><code>${esc(f.code)}</code> ${esc(f.message)}</li>`).join('')}</ul>` : '';
133
-
134
- async function viewBlueprint(pathArg) {
135
- const p = decodeURIComponent(pathArg);
136
- const main = $('#main');
137
- let b;
138
- try { b = await api('GET', '/api/blueprints/' + p.split('/').map(encodeURIComponent).join('/')); }
139
- catch (e) { main.innerHTML = `<div class="empty">${esc(e.message)}</div>`; return; }
140
- const errors = b.findings.filter((f) => f.level === 'error').length, warns = b.findings.length - errors;
141
- const badge = errors ? `<span class="badge error">${errors} error${errors > 1 ? 's' : ''}</span>` : '';
142
- const wbadge = warns ? `<span class="badge warn">${warns} warning${warns > 1 ? 's' : ''}</span>` : '';
143
- // a property value that is a markdown link opens the blueprint it points at
144
- const val = (x) => { const m = typeof x === 'string' && /^\s*\[([^\]]*)\]\(([^)]+)\)\s*$/.exec(x); if (!m) return `<code>${esc(x)}</code>`; const hit = state.root.blueprints.find((y) => y.path === resolveRel(p, m[2].split('#')[0])); return hit ? `<a href="${bpHref(hit.path)}">${esc(m[1])}</a>` : `<span title="not a blueprint in this root: ${esc(m[2])}">${esc(m[1])}</span> <span class="badge error">broken</span>`; };
145
- const props = Object.entries(b.properties).map(([k, v]) => `<tr><td>${esc(k)}</td><td>${Array.isArray(v) ? (v.length ? v.map(val).join(', ') : '<span class="hint">[]</span>') : (v === null || v === '' ? '<span class="hint">—</span>' : esc(v))}</td></tr>`).join('');
146
- const missing = b.shape_sections.filter((s) => !b.sections.map((x) => x.toLowerCase()).includes(s.toLowerCase()));
147
- main.innerHTML = `
148
- <div class="toolbar"><span class="meta">${esc(b.collection)}${b.group ? ' / ' + esc(b.group) : ''} · <code>${esc(b.path)}</code> · ${esc(b.flavor || '')}</span><span class="spacer"></span>
149
- <button id="edit">Edit</button></div>
150
- <h1 class="title">${esc(b.title)}${badge}${wbadge}</h1>
151
- <p class="meta">id <code>${esc(b.id || '(none)')}</code>${b.summary ? ' · ' + esc(b.summary) : ' · <span class="hint">no summary yet</span>'}</p>
152
- ${findingsHtml(b.findings)}
153
- ${missing.length ? `<p class="hint">Sections the ${esc(b.flavor)} shape declares and this blueprint lacks: ${missing.map((s) => `<code>${esc(s)}</code>`).join(', ')}</p>` : ''}
154
- <table class="props">${props}</table>
155
- <div class="body" id="body">${b.html}</div>
156
- <div id="editor" hidden><textarea class="editor" id="text"></textarea>
157
- <div class="toolbar" style="margin-top:10px"><button class="primary" id="save">Save</button><button id="cancel">Cancel</button><span class="hint">Saving runs the check on this file; the result shows above.</span></div></div>`;
158
- if (b.findings.length) state.flagged.add(p); else state.flagged.delete(p);
159
- renderNav();
160
- // relative .md links open the blueprint they point at
161
- for (const a of main.querySelectorAll('#body a[href]')) {
162
- const href = a.getAttribute('href');
163
- if (/^[a-z]+:/i.test(href) || href.startsWith('#')) continue;
164
- const target = resolveRel(p, href.split('#')[0]);
165
- const hit = state.root.blueprints.find((x) => x.path === target);
166
- if (hit) { a.href = bpHref(hit.path); } else { a.title = 'not a blueprint in this root: ' + target; a.addEventListener('click', (e) => { e.preventDefault(); toast('Not a blueprint in this root: ' + target); }); }
167
- }
168
- $('#edit').onclick = () => { $('#body').hidden = true; $('#editor').hidden = false; $('#text').value = b.raw; $('#edit').hidden = true; $('#text').focus(); };
169
- $('#cancel').onclick = () => viewBlueprint(pathArg);
170
- $('#save').onclick = async () => {
171
- try { const r = await api('PUT', '/api/blueprints/' + p.split('/').map(encodeURIComponent).join('/'), { text: $('#text').value }); toast(r.findings.length ? `Saved. ${r.findings.length} finding${r.findings.length > 1 ? 's' : ''} on this file.` : 'Saved. No findings.'); await loadRoot(); viewBlueprint(pathArg); }
172
- catch (e) { toast('Save failed: ' + e.message); }
173
- };
174
- }
175
- function resolveRel(from, href) {
176
- const parts = from.split('/').slice(0, -1);
177
- for (const seg of decodeURIComponent(href).split('/')) { if (seg === '..') parts.pop(); else if (seg !== '.' && seg !== '') parts.push(seg); }
178
- return parts.join('/');
179
- }
180
-
181
- function viewFramework() {
182
- const f = state.root.framework, a = state.root.actor;
183
- const flav = (c) => c.flavors.map((x) => `<code>${esc(x.name)}</code>${x.default ? ' (default)' : ''}${x.description ? ' — ' + esc(x.description) : ''}`).join('<br>');
184
- const canvas = (c) => !c.canvas ? '' : typeof c.canvas === 'string' ? c.canvas : `card from ## ${esc(c.canvas.section)}`;
185
- $('#main').innerHTML = `<div class="fw">
186
- <h1 class="title">${esc(state.root.name)}</h1>
187
- <p class="meta">Eidos ${esc(f.eidos_version ?? 'unversioned')} · naming <code>${esc(f.naming)}</code> · <code>${esc(f.file)}</code> · CLI ${esc(state.root.cli_version)} (standard ${esc(state.root.standard_version)})</p>
188
- ${f.problems.length ? findingsHtml(f.problems.map((m) => ({ level: 'error', code: 'framework-invalid', message: m }))) : ''}
189
- <div class="toolbar"><button id="reindex">Rebuild index</button><button id="runcheck">Run check</button><span class="hint">Both run exactly what <code>eidos index</code> and <code>eidos check</code> run.</span></div>
190
- <h2>Actor</h2><p>${a.exists && a.role ? `<strong>${esc(a.role)}</strong> · ownership: ${esc(a.ownership || '—')} · experience: ${esc(a.experience || '—')} · capacity: ${esc(a.capacity || '—')}` : `<span class="hint">unset (full facilitation) — set it with <code>eidos whoami --role &lt;name&gt;</code></span>`}</p>
191
- <p class="hint">Roles installed: ${state.root.roles.map((r) => `<code>${esc(r.name)}</code>`).join(', ') || 'none'}</p>
192
- <h2>Collections</h2>
193
- ${f.collections.map((c, i) => `<h3>${esc(c.name)}${i === 0 ? ' <span class="badge">framing</span>' : ''}${c.grouping ? ` <span class="badge">grouped by ${esc(c.grouping.label)}${c.grouping.property ? ' (' + esc(c.grouping.property) + ')' : ''}</span>` : ''}</h3>
194
- ${c.description ? `<p>${esc(c.description)}</p>` : ''}
195
- <dl><dt>flavors</dt><dd>${flav(c)}</dd>${c.canvas ? `<dt>canvas</dt><dd>${canvas(c)}</dd>` : ''}${c.grouping && c.grouping.groups ? `<dt>groups</dt><dd>${c.grouping.groups.map((g) => `<code>${esc(g.name)}</code>${g.description ? ' — ' + esc(g.description) : ''}`).join('<br>')}</dd>` : ''}</dl>`).join('')}
196
- <h2>Schema</h2>
197
- <table class="props"><tr><td>core</td><td>${f.schema.core.map((p) => `<code>${esc(p.name)}</code>`).join(' ')}</td></tr>
198
- ${f.schema.custom.map((p) => `<tr><td>${esc(p.name)}</td><td><span class="badge">${esc(p.type)}</span> <span class="badge">${Array.isArray(p.applies_to) ? esc(p.applies_to.join(', ')) : 'all'}</span> ${esc(p.meaning)}</td></tr>`).join('')}</table>
199
- ${f.top_level && f.top_level.length ? `<h2>Top-level docs</h2><ul>${f.top_level.map((d) => `<li><strong>${esc(d.title)}</strong> <code>${esc(d.path)}</code>${d.description ? ' — ' + esc(d.description) : ''}</li>`).join('')}</ul>` : ''}
200
- </div>`;
201
- $('#reindex').onclick = async () => { try { const r = await api('POST', '/api/index'); toast(r.indexes.map((i) => `${i.collection}: ${i.status} (${i.blueprints})`).join('\n') + (r.missing_summary.length ? `\n${r.missing_summary.length} blueprint(s) still need a summary` : '')); } catch (e) { toast(e.message); } };
202
- $('#runcheck').onclick = () => go('check');
203
- }
204
-
205
- async function viewCheck() {
206
- const main = $('#main'); main.innerHTML = '<div class="empty">Checking…</div>';
207
- const r = await api('GET', '/api/check');
208
- state.flagged = new Set(r.findings.map((f) => f.path).filter(Boolean));
209
- renderNav();
210
- const byFile = new Map();
211
- for (const f of r.findings) { const k = f.path || '(root)'; if (!byFile.has(k)) byFile.set(k, []); byFile.get(k).push(f); }
212
- const isBp = (k) => state.root.blueprints.some((b) => b.path === k);
213
- main.innerHTML = `<h1 class="title">Check <span class="badge ${r.errors ? 'error' : r.warnings ? 'warn' : 'ok'}">${r.errors} error${r.errors === 1 ? '' : 's'}, ${r.warnings} warning${r.warnings === 1 ? '' : 's'}</span></h1>
214
- <p class="meta">${r.blueprints} blueprints in ${r.collections} collections · Eidos ${esc(r.eidos_version ?? '?')} · ${esc(r.naming)} · the same report as <code>eidos check</code></p>
215
- ${r.findings.length === 0 ? '<p class="empty">No findings.</p>' : ''}
216
- ${[...byFile.entries()].map(([k, fs]) => `<div class="file-group">${isBp(k) ? `<a href="${bpHref(k)}">${esc(k)}</a>` : esc(k)}</div>${findingsHtml(fs)}`).join('')}`;
217
- }
218
-
219
- async function viewDoc(url, title) {
220
- const main = $('#main'); main.innerHTML = '<div class="empty">Loading…</div>';
221
- const r = await api('GET', url);
222
- main.innerHTML = `<div class="body" style="border:0">${r.html}</div>`;
223
- }
224
-
225
- function viewNew(collectionName) {
226
- const f = state.root.framework;
227
- const c = f.collections.find((x) => x.name === collectionName) || f.collections[0];
228
- const groups = (c.grouping && c.grouping.groups) ? c.grouping.groups.map((g) => g.name) : [];
229
- $('#main').innerHTML = `<h1 class="title">New blueprint</h1>
230
- <p class="hint">Runs <code>eidos new</code>: frontmatter from the Schema, body from the flavor's shape, filename in the naming convention. You fill the prose afterwards.</p>
231
- <div class="form">
232
- <label>Collection</label><select id="n-col">${f.collections.map((x) => `<option ${x.name === c.name ? 'selected' : ''}>${esc(x.name)}</option>`).join('')}</select>
233
- <label>Title</label><input id="n-title" placeholder="Session Management" autofocus>
234
- <label>Flavor</label><select id="n-flavor">${c.flavors.map((x) => `<option ${x.default ? 'selected' : ''}>${esc(x.name)}</option>`).join('')}</select>
235
- <label>Group</label><input id="n-group" list="n-groups" placeholder="${groups.length ? 'one of the declared groups, or a new one' : c.grouping ? c.grouping.label : 'optional sub-folder'}"><datalist id="n-groups">${groups.map((g) => `<option value="${esc(g)}">`).join('')}</datalist>
236
- <label>Summary</label><input id="n-summary" placeholder="One plain line: what this blueprint is.">
237
- <span></span><div class="toolbar"><button class="primary" id="n-create">Create</button><button id="n-cancel">Cancel</button></div>
238
- </div>`;
239
- $('#n-col').onchange = () => viewNew($('#n-col').value);
240
- $('#n-cancel').onclick = () => history.back();
241
- $('#n-create').onclick = async () => {
242
- try {
243
- const r = await api('POST', '/api/blueprints', { collection: $('#n-col').value, title: $('#n-title').value, flavor: $('#n-flavor').value, group: $('#n-group').value, summary: $('#n-summary').value });
244
- toast(`Created ${r.path}` + (r.notes.length ? '\n' + r.notes.join('\n') : ''), 6000);
245
- await loadRoot(); go('blueprint/' + r.path.split('/').map(encodeURIComponent).join('/'));
246
- } catch (e) { toast(e.message, 6000); }
247
- };
248
- }
249
-
250
- async function render() {
251
- const { view, arg } = route();
252
- renderNav();
253
- if (view === 'blueprint') return viewBlueprint(arg);
254
- if (view === 'check') return viewCheck();
255
- if (view === 'standard') return viewDoc('/api/standard');
256
- if (view === 'instructions') return viewDoc('/api/instructions/' + (arg || 'overview'));
257
- if (view === 'new') return viewNew(decodeURIComponent(arg));
258
- return viewFramework();
259
- }
260
-
261
- $('#search').addEventListener('input', (e) => { state.filter = e.target.value; renderNav(); });
262
- $('#nav').addEventListener('click', (e) => { const b = e.target.closest('button[data-new]'); if (b) go('new/' + encodeURIComponent(b.dataset.new)); });
263
- window.addEventListener('hashchange', render);
264
- loadRoot().then(render).catch((e) => { $('#main').innerHTML = `<div class="empty">${esc(e.message)}</div>`; });
265
- })();
266
- </script>
267
- </body>
268
- </html>
@@ -1,30 +0,0 @@
1
- // `eidos convert`: move a markdown root to Framework.yaml, the form the CLI works in.
2
- import path from 'node:path';
3
- import { loadRoot } from '../context.js';
4
- import { convertToYaml } from '../core/convert.js';
5
- import { EXIT_OK, print, printJson } from '../output.js';
6
- export function runConvert(globals, options, cwd = process.cwd()) {
7
- const context = loadRoot(globals, cwd, { allowMarkdown: true });
8
- const display = (file) => path.relative(cwd, file) || file;
9
- if (context.framework.format === 'yaml') {
10
- if (options.json) {
11
- printJson({ converted: false, framework_file: display(context.framework.file), reason: 'already YAML' });
12
- }
13
- else {
14
- print(`${display(context.framework.file)} is already the YAML form; nothing to convert`);
15
- }
16
- return EXIT_OK;
17
- }
18
- const result = convertToYaml(context.framework, options.dryRun);
19
- if (options.json) {
20
- printJson({ converted: !options.dryRun, dry_run: options.dryRun, framework_file: display(result.written), removed: result.removed.map(display), actions: result.actions.map(display) });
21
- return EXIT_OK;
22
- }
23
- print(...result.actions.map(display));
24
- print('', `${options.dryRun ? 'would convert' : 'converted'}: ${display(context.framework.file)} → ${display(result.written)}, index inside it`);
25
- print(" left behind: the markdown form's prose (section intros, comments); each collection's description travelled. Keep anything that matters in the root's README.");
26
- if (!options.dryRun) {
27
- print(' next: eidos check');
28
- }
29
- return EXIT_OK;
30
- }
@@ -1,26 +0,0 @@
1
- // A shape file: one body template in `_eidos/shapes/`, read for the sections
2
- // it declares (to validate a blueprint against) and as the template a new
3
- // blueprint's body is rendered from.
4
- import { existsSync, readFileSync } from 'node:fs';
5
- import { headings } from './markdown.js';
6
- import { shapePath } from './framework.js';
7
- export function loadShape(framework, flavor) {
8
- const file = shapePath(framework, flavor);
9
- if (!existsSync(file)) {
10
- return null;
11
- }
12
- const template = readFileSync(file, 'utf8');
13
- const all = headings(template);
14
- return {
15
- path: file,
16
- flavor,
17
- template,
18
- sections: all.filter((heading) => heading.level === 2).map((heading) => heading.text),
19
- headings: all,
20
- };
21
- }
22
- // The body a scaffolded blueprint starts from: the template with its title
23
- // filled in and any leading blank lines dropped.
24
- export function renderShape(template, title) {
25
- return template.replace(/\r\n?/g, '\n').replace(/\{\{title\}\}/g, title).replace(/^\n+/, '');
26
- }
@@ -1,87 +0,0 @@
1
- ---
2
- # The Eidos version this framework targets; migrate reads and bumps it.
3
- eidos_version: 4.5.0
4
- # How files, folders, and links are named: kebab-case | TitleCase | Title Case. Absent = kebab-case.
5
- naming: kebab-case
6
- ---
7
-
8
- # Framework
9
-
10
- The framework's index and config, in one place: the version and naming convention above, and below the
11
- Top-Level documents, the Collections (with their flavors and grouping), and the property Schema. The
12
- visible `README.md` at the root is the friendly door to it; keep it current with the
13
- `configure` skill.
14
-
15
- ## Top-Level
16
-
17
- <!-- configure: top-level index (regenerated) -->
18
- - [README](../README.md) — the root's front door: what this book is, and pointers in.
19
- <!-- One bullet per top-level document. README is the door and comes first; add your own below (an
20
- Outline, a Synopsis, the generated Blueprint Map canvas), each a link and a one-line description.
21
- The framing docs live in the Frames collection, not here. configure refreshes this list. -->
22
-
23
- ## Collections
24
-
25
- A collection is a top-level folder of repeated blueprints that share a body shape. `Frames` holds the
26
- framing docs — the most primary thing the folder says about itself — and `Chapters` the book's
27
- units. Add more with `configure`. Each lists its flavors (the default marked), how it draws on
28
- the canvas, and its grouping, and points at its generated `index.md` leaf.
29
-
30
- ### Frames
31
-
32
- The framing docs that set what every chapter is judged against — what the book argues, who it is for,
33
- how it sounds, and where it sits. This framework's framing collection; each frame follows the flavor
34
- of its kind, and one left unwritten is a gap to surface, not a failure.
35
-
36
- - **Leaf:** [Frames/index.md](../Frames/index.md)
37
- - **Flavors:**
38
- - [premise](shapes/frame.premise.md) — what the book says, and why it has to exist (default).
39
- - [reader](shapes/frame.reader.md) — who it is for, and what changes for them.
40
- - [voice](shapes/frame.voice.md) — person, tense, register, and the rules the prose keeps.
41
- - [market](shapes/frame.market.md) — shelf, comparables, and how it reaches readers.
42
- - **Canvas:** file
43
-
44
- ### Chapters
45
-
46
- The book's units, one per chapter, grouped by part.
47
-
48
- - **Leaf:** [Chapters/index.md](../Chapters/index.md)
49
- - **Flavors:**
50
- - [full](shapes/chapter.full.md) — the complete chapter shape (default).
51
- - [sketch](shapes/chapter.sketch.md) — Intent, Open Questions, What Happens, Out of Scope; grow into full.
52
- - **Canvas:** card from `## Intent`
53
- - **Parts:** _(add one bullet per part — a name and a short description — as parts accrue)_
54
-
55
- ## Schema
56
-
57
- The property contract — what a blueprint's frontmatter may carry, across every collection. Two parts: the
58
- **core** properties Eidos's own machinery uses, and the **custom** ones you (or the seed) add. Every
59
- custom property declares which collections it **applies to** — `all`, or a list — so a property never
60
- lands where it makes no sense (`part` is Chapters-only). A property's type comes from the Obsidian set
61
- (Text, List, Number, Checkbox, Date, Date & time), so frontmatter renders natively in an Obsidian
62
- vault. The `configure` skill edits this section.
63
-
64
- ### Eidos Core
65
-
66
- _Present on every blueprint. Managed by the standard (Eidos 4.5.0); `migrate` rewrites this block on a version change — don't hand-edit it. (`flavor` absent = the collection's default; `connects_to` absent = no canvas edges; a missing `summary` is flagged by the index.)_
67
-
68
- | Name | Type | Meaning |
69
- | ----------- | ---- | ---------------------------------------------------------------------------------------------- |
70
- | id | Text | Stable, unique, kebab-case identity. Assigned once, never renamed. References point at it. |
71
- | title | Text | Human-readable name. |
72
- | summary | Text | One plain line — what this blueprint is, in a sentence, distilled from Intent. Source for the collection index.md listing; absent, the index flags it. |
73
- | flavor | Text | Which body flavor this blueprint follows, from its collection's declared flavors. Absent = the collection's default flavor. |
74
- | connects_to | List | Blueprints this one connects to on the canvas, each a markdown link; drawn as a directed edge (this → target). The intentional map, distinct from depends_on. |
75
-
76
- ### Custom Properties
77
-
78
- _Yours to shape with the `configure` skill. The seed ships a few useful defaults below — keep, scope, or drop any of them; Eidos doesn't depend on them. Absence where a property applies is a soft gap the validator notes, never refuses._
79
-
80
- | Name | Type | Applies To | Meaning |
81
- | ------------- | ---- | ---------- | ------------------------------------------------------------------------------------------ |
82
- | status | Text | all | Lifecycle value: Draft / Outlined / Drafted / Revised / Final / Cut. An off-list value warns. |
83
- | date_created | Date | all | YYYY-MM-DD. The day the blueprint was first written. Set once. |
84
- | date_modified | Date | all | YYYY-MM-DD. The day the blueprint was last changed. |
85
- | tags | List | all | Free tags. |
86
- | part | Text | Chapters | The grouping, matching the blueprint's sub-folder under its collection in the naming convention. An unknown value warns, never fails. |
87
- | depends_on | List | Chapters | Chapters a reader must have read first, each a markdown link. A reading dependency, not a canvas edge. |
@@ -1,24 +0,0 @@
1
- <!--
2
- The Premise frame — what the book says and why it has to exist. Loose, point-in-time prose: record
3
- what is true now and revise when it changes. Suggestive, not prescriptive. A Frame's frontmatter is
4
- generated from the framework's Schema (in Framework.md), so it is not written here. Delete the italic
5
- prompts as you fill in.
6
- -->
7
-
8
- # Premise
9
-
10
- ## The Argument
11
-
12
- _In a few sentences: what this book claims, or what story it tells. If you can't say it without a list, it isn't a premise yet._
13
-
14
- ## Why It Has To Exist
15
-
16
- _What is wrong, missing, or misunderstood in the world without this book. The problem the reader has._
17
-
18
- ## The Shape of It
19
-
20
- _How the whole thing is built — parts, arc, the movement from the first page to the last. Not a table of contents; the logic behind one._
21
-
22
- ## What It Is Not
23
-
24
- _The adjacent book you are deliberately not writing. This is where a book's scope is held._