@drafthq/draft 3.2.1 → 3.3.1

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 (31) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.cursor-plugin/plugin.json +28 -0
  4. package/README.md +20 -2
  5. package/cli/src/hosts/cursor.js +35 -5
  6. package/cli/src/installer.js +12 -0
  7. package/cli/src/lib/cursor-registry.js +122 -0
  8. package/cli/src/lib/plugin-manifest.js +20 -0
  9. package/core/methodology.md +1 -1
  10. package/core/templates/okf/ai-context-index.md +48 -0
  11. package/core/templates/okf/concept.md +54 -0
  12. package/core/templates/okf/index.md +40 -0
  13. package/core/templates/okf/section-index.md +25 -0
  14. package/integrations/agents/AGENTS.md +452 -2
  15. package/integrations/copilot/.github/copilot-instructions.md +452 -2
  16. package/package.json +3 -2
  17. package/scripts/lib.sh +9 -0
  18. package/scripts/tools/graph-preflight.sh +259 -0
  19. package/scripts/tools/okf-render-views.sh +373 -0
  20. package/scripts/tools/okf-validate.sh +204 -0
  21. package/skills/init/SKILL.md +24 -1
  22. package/skills/init/references/okf-emitter.md +223 -0
  23. package/integrations/copilot/.github/copilot-instructions.md.7iDz8X +0 -91
  24. package/integrations/copilot/.github/copilot-instructions.md.DoBdtd +0 -91
  25. package/integrations/copilot/.github/copilot-instructions.md.McGoBW +0 -122
  26. package/integrations/copilot/.github/copilot-instructions.md.VsPyLB +0 -91
  27. package/integrations/copilot/.github/copilot-instructions.md.XAVr7D +0 -91
  28. package/integrations/copilot/.github/copilot-instructions.md.YoFVFa +0 -91
  29. package/integrations/copilot/.github/copilot-instructions.md.a9DeW0 +0 -91
  30. package/integrations/copilot/.github/copilot-instructions.md.oxQs3B +0 -91
  31. package/integrations/copilot/.github/copilot-instructions.md.ww33Ly +0 -91
@@ -12,7 +12,7 @@
12
12
  "name": "draft",
13
13
  "source": "./",
14
14
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
15
- "version": "3.2.1",
15
+ "version": "3.3.1",
16
16
  "author": {
17
17
  "name": "mayurpise"
18
18
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "draft",
3
3
  "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
4
- "version": "3.2.1",
4
+ "version": "3.3.1",
5
5
  "author": {
6
6
  "name": "mayurpise"
7
7
  },
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "draft",
3
+ "displayName": "Draft",
4
+ "description": "Context-Driven Development: draft specs and plans before implementation. Structured workflows for features and fixes.",
5
+ "version": "3.3.1",
6
+ "skills": "./skills/",
7
+ "agents": "./core/agents/",
8
+ "author": {
9
+ "name": "mayurpise"
10
+ },
11
+ "homepage": "https://github.com/drafthq/draft",
12
+ "license": "MIT",
13
+ "keywords": [
14
+ "context-driven-development",
15
+ "ai-assisted",
16
+ "planning",
17
+ "specifications",
18
+ "architecture",
19
+ "code-review",
20
+ "monorepo",
21
+ "tdd",
22
+ "validation",
23
+ "enterprise",
24
+ "incremental-refresh",
25
+ "signal-detection",
26
+ "state-management"
27
+ ]
28
+ }
package/README.md CHANGED
@@ -67,7 +67,7 @@ Each host installs the way that host actually loads extensions — no manual ste
67
67
  | Host | `draft install …` | What it does |
68
68
  |------|-------------------|--------------|
69
69
  | **Claude Code** | `claude-code` | Registers the plugin via `claude plugin marketplace add` + `claude plugin install` (user scope). Restart Claude Code. |
70
- | **Cursor** | `cursor` | Copies the plugin into `~/.cursor/plugins/local/draft/` (auto-loaded). Restart Cursor. |
70
+ | **Cursor** | `cursor` | Copies the plugin into `~/.cursor/plugins/local/draft/`, writes `.cursor-plugin/plugin.json`, registers `draft@draft-plugins` in Cursor's plugin registry, and enables it. Restart Cursor (or Developer: Reload Window). Existing installs upgrade with `draft install cursor --force`. |
71
71
  | **Codex** | `codex` | Writes `./AGENTS.md`, which Codex reads automatically. |
72
72
  | **opencode** | `opencode` | Writes `./AGENTS.md` + `~/.agents/skills/draft/`, both auto-discovered. |
73
73
 
@@ -92,7 +92,7 @@ Run `/draft` for the full command map.
92
92
  ```
93
93
 
94
94
  ### Cursor — from GitHub
95
- Cursor natively supports the `.claude-plugin/` structure. Add via *Settings > Rules, Skills, Subagents > Rules > New > Add from Github*:
95
+ Cursor requires `.cursor-plugin/plugin.json`; the `draft install cursor` command also registers the plugin via the shared Claude plugin registry that Cursor reads on many builds. To add from source instead, use *Settings > Rules, Skills, Subagents > Rules > New > Add from Github*:
96
96
  ```
97
97
  https://github.com/drafthq/draft.git
98
98
  ```
@@ -223,6 +223,24 @@ Skills also call into **shell helpers** under `scripts/tools/` for mechanical wo
223
223
  files with changed hashes
224
224
  ```
225
225
 
226
+ ### Context output modes (`/draft:init`)
227
+
228
+ `/draft:init` packages your architecture context in one of two modes, selected
229
+ automatically by repo size (override with `DRAFT_INIT_MODE`):
230
+
231
+ - **`monolith`** (default for small repos, tiers 1–2) — a single
232
+ graph-primary `architecture.md` is the source of truth; `.ai-context.md` is
233
+ the token-optimized AI view derived from it.
234
+ - **`okf`** (default for larger repos, tiers 3+) — an **OKF concept taxonomy**
235
+ under `draft/wiki/` is the source of truth (one concept per file, cross-links
236
+ form the graph), `.ai-context.md` becomes the navigable index root
237
+ (Synopsis + Concept Map), and `architecture.md` is demoted to a generated
238
+ rendered view. An optional self-contained offline HTML viewer ships under
239
+ `draft/wiki/web/`.
240
+
241
+ Both modes produce the same `product.md`, `tech-stack.md`, `workflow.md`,
242
+ `guardrails.md`, tracks, and `.state/` — only the architecture packaging differs.
243
+
226
244
  [Full workflow →](core/methodology.md#core-workflow)
227
245
 
228
246
  ---
@@ -2,11 +2,15 @@
2
2
 
3
3
  const path = require('path');
4
4
  const { asset } = require('../lib/paths');
5
+ const { readPluginVersion } = require('../lib/plugin-manifest');
6
+ const { applyCursorRegistration } = require('../lib/cursor-registry');
5
7
 
6
- // Cursor natively understands the .claude-plugin structure. Install the same
7
- // native plugin tree, by default to the user-level plugin directory so it is
8
- // available across all projects.
8
+ // Cursor discovers Draft through two layers, both shipped here: the native
9
+ // .cursor-plugin/plugin.json manifest, and the shared Claude plugin registry
10
+ // under ~/.claude/ (written in postInstall). A file copy alone is not enough —
11
+ // current Cursor builds never surface /draft:* commands without registration.
9
12
  const ITEMS = [
13
+ { p: '.cursor-plugin', kind: 'copyTree' },
10
14
  { p: '.claude-plugin', kind: 'copyTree' },
11
15
  { p: 'skills', kind: 'copyTree' },
12
16
  { p: 'core', kind: 'copyTree' },
@@ -37,14 +41,40 @@ module.exports = {
37
41
  dest: path.join(base, it.p),
38
42
  label: it.p,
39
43
  }));
40
- // Guard the whole install dir on the manifest's presence.
44
+ // Guard the whole install dir on the .cursor-plugin manifest's presence.
41
45
  actions[0].guard = true;
42
46
 
43
47
  return {
44
48
  targetSummary: `${base} (${ctx.scope})`,
45
49
  actions,
46
50
  graph: true,
47
- done: `Draft installed to ${base}. Restart Cursor to detect the plugin.`,
51
+ // Runs after the file copies: register + enable the plugin in the shared
52
+ // Claude registry. On a dry run it computes the merges and writes nothing.
53
+ postInstall(c) {
54
+ const installedManifest = path.join(base, '.cursor-plugin', 'plugin.json');
55
+ const version = readPluginVersion(
56
+ c.dryRun ? asset('.cursor-plugin', 'plugin.json') : installedManifest
57
+ );
58
+ return applyCursorRegistration({
59
+ home: c.home,
60
+ installPath: base,
61
+ version,
62
+ scope: c.scope,
63
+ dryRun: c.dryRun,
64
+ });
65
+ },
66
+ done: [
67
+ `Draft installed to ${base}.`,
68
+ 'Plugin registered and enabled in ~/.claude/plugins/.',
69
+ 'Restart Cursor (Developer: Reload Window) to load /draft:* commands.',
70
+ ].join(' '),
71
+ fallbackTitle: 'If /draft commands do not appear after restart:',
72
+ fallback: [
73
+ `Confirm ${base}/.cursor-plugin/plugin.json exists`,
74
+ 'Confirm ~/.claude/plugins/installed_plugins.json contains "draft@draft-plugins"',
75
+ 'Confirm ~/.claude/settings.json has "draft@draft-plugins": true',
76
+ 'Run Developer: Reload Window in Cursor',
77
+ ],
48
78
  };
49
79
  },
50
80
  };
@@ -108,6 +108,18 @@ function install(host, ctx) {
108
108
  fetchGraph(ctx);
109
109
  }
110
110
 
111
+ // Host-specific registration (e.g. Cursor's plugin registry). On a real
112
+ // install the hook performs the writes (logging each path); on a dry run it
113
+ // returns the planned paths so we can surface them without touching disk.
114
+ if (plan.postInstall) {
115
+ const reg = plan.postInstall(ctx);
116
+ if (reg && ctx.dryRun) {
117
+ log.plan(`would update registry: ${reg.kmPath}`);
118
+ log.plan(`would update registry: ${reg.ipPath}`);
119
+ log.plan(`would update registry: ${reg.settingsPath}`);
120
+ }
121
+ }
122
+
111
123
  // Record the install path so skills can locate scripts/tools/ from the user's
112
124
  // project cwd (best-effort; graph skills glob-fallback if the marker is absent).
113
125
  if (!ctx.dryRun) {
@@ -0,0 +1,122 @@
1
+ 'use strict';
2
+
3
+ // Cursor reads plugins from the shared Claude plugin registry under ~/.claude/
4
+ // on many builds, so a file copy alone never surfaces /draft:* commands. This
5
+ // module merges Draft into the three registry files non-destructively:
6
+ // - ~/.claude/plugins/known_marketplaces.json (marketplace entry)
7
+ // - ~/.claude/plugins/installed_plugins.json (install record)
8
+ // - ~/.claude/settings.json (enabledPlugins flag)
9
+ // Every write preserves all other plugins, hooks, and unknown keys. Reads of a
10
+ // corrupt JSON file fail loud rather than silently clobbering user data.
11
+ const fs = require('fs');
12
+ const path = require('path');
13
+ const log = require('./log');
14
+
15
+ const MARKETPLACE_KEY = 'draft-plugins';
16
+ const PLUGIN_KEY = `draft@${MARKETPLACE_KEY}`; // name@<marketplace name>
17
+
18
+ function readJson(filePath, fallback) {
19
+ try {
20
+ return JSON.parse(fs.readFileSync(filePath, 'utf8'));
21
+ } catch (err) {
22
+ if (err.code === 'ENOENT') return fallback;
23
+ throw new Error(`Cannot parse ${filePath}: ${err.message}`);
24
+ }
25
+ }
26
+
27
+ function writeJsonAtomic(filePath, data) {
28
+ const dir = path.dirname(filePath);
29
+ fs.mkdirSync(dir, { recursive: true });
30
+ const tmp = `${filePath}.tmp.${process.pid}`;
31
+ fs.writeFileSync(tmp, JSON.stringify(data, null, 2) + '\n', 'utf8');
32
+ fs.renameSync(tmp, filePath);
33
+ }
34
+
35
+ function claudeHome(home) {
36
+ return path.join(home, '.claude');
37
+ }
38
+
39
+ function registryPaths(home) {
40
+ const base = path.join(claudeHome(home), 'plugins');
41
+ return {
42
+ kmPath: path.join(base, 'known_marketplaces.json'),
43
+ ipPath: path.join(base, 'installed_plugins.json'),
44
+ settingsPath: path.join(claudeHome(home), 'settings.json'),
45
+ };
46
+ }
47
+
48
+ function registryScope(scope) {
49
+ return scope === 'project' ? 'project' : 'user';
50
+ }
51
+
52
+ // Pure: compute the merged registry objects without touching disk. Callers that
53
+ // want to persist them use applyCursorRegistration (which delegates here first).
54
+ function registerCursorPlugin(opts) {
55
+ const { home, version, scope } = opts;
56
+ const installPath = path.resolve(opts.installPath);
57
+ const now = new Date().toISOString();
58
+ const paths = registryPaths(home);
59
+
60
+ // --- known_marketplaces.json: overwrite only our key. ---
61
+ const km = readJson(paths.kmPath, {});
62
+ km[MARKETPLACE_KEY] = {
63
+ source: { source: 'directory', path: installPath },
64
+ installLocation: installPath,
65
+ lastUpdated: now,
66
+ };
67
+
68
+ // --- installed_plugins.json: merge our key, preserve installedAt on upgrade. ---
69
+ const ip = readJson(paths.ipPath, { version: 2, plugins: {} });
70
+ if (typeof ip.version !== 'number') ip.version = 2;
71
+ if (!ip.plugins || typeof ip.plugins !== 'object') ip.plugins = {};
72
+ const existing = Array.isArray(ip.plugins[PLUGIN_KEY]) ? ip.plugins[PLUGIN_KEY][0] : null;
73
+ const installedAt = existing && existing.installedAt ? existing.installedAt : now;
74
+ ip.plugins[PLUGIN_KEY] = [
75
+ {
76
+ scope: registryScope(scope),
77
+ installPath,
78
+ version,
79
+ installedAt,
80
+ lastUpdated: now,
81
+ },
82
+ ];
83
+
84
+ // --- settings.json: flip our enabledPlugins flag, preserve everything else. ---
85
+ const settings = readJson(paths.settingsPath, {});
86
+ if (!settings.enabledPlugins || typeof settings.enabledPlugins !== 'object') {
87
+ settings.enabledPlugins = {};
88
+ }
89
+ settings.enabledPlugins[PLUGIN_KEY] = true;
90
+
91
+ return {
92
+ kmPath: paths.kmPath,
93
+ km,
94
+ ipPath: paths.ipPath,
95
+ ip,
96
+ settingsPath: paths.settingsPath,
97
+ settings,
98
+ };
99
+ }
100
+
101
+ // Compute the merges and, unless dryRun, persist them atomically. Returns the
102
+ // same shape as registerCursorPlugin so the installer can log the paths.
103
+ function applyCursorRegistration(opts) {
104
+ const result = registerCursorPlugin(opts);
105
+ if (opts.dryRun) return result;
106
+
107
+ log.plan(`writing: ${result.kmPath}`);
108
+ writeJsonAtomic(result.kmPath, result.km);
109
+ log.plan(`writing: ${result.ipPath}`);
110
+ writeJsonAtomic(result.ipPath, result.ip);
111
+ log.plan(`writing: ${result.settingsPath}`);
112
+ writeJsonAtomic(result.settingsPath, result.settings);
113
+
114
+ return result;
115
+ }
116
+
117
+ module.exports = {
118
+ PLUGIN_KEY,
119
+ MARKETPLACE_KEY,
120
+ registerCursorPlugin,
121
+ applyCursorRegistration,
122
+ };
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ // Read name/version from a plugin manifest JSON (.cursor-plugin/plugin.json or
4
+ // .claude-plugin/plugin.json). Fails loud if either required field is missing —
5
+ // a manifest without a version would corrupt the registry's install record.
6
+ const fs = require('fs');
7
+
8
+ function readPluginManifest(manifestPath) {
9
+ const raw = fs.readFileSync(manifestPath, 'utf8');
10
+ const data = JSON.parse(raw);
11
+ if (!data.name) throw new Error(`Missing name in ${manifestPath}`);
12
+ if (!data.version) throw new Error(`Missing version in ${manifestPath}`);
13
+ return data;
14
+ }
15
+
16
+ function readPluginVersion(manifestPath) {
17
+ return readPluginManifest(manifestPath).version;
18
+ }
19
+
20
+ module.exports = { readPluginManifest, readPluginVersion };
@@ -300,7 +300,7 @@ You should see the list of available Draft commands. If not, check that the plug
300
300
 
301
301
  ### Supported Platforms
302
302
 
303
- Draft works with **Claude Code** (native `.claude-plugin/` support) and **Cursor** (supports `.claude/` plugin structure natively). No build pipeline required.
303
+ Draft works with **Claude Code** (native `.claude-plugin/` support) and **Cursor** (requires `.cursor-plugin/plugin.json` plus registration in the shared Claude plugin registry, both handled by `draft install cursor`). No build pipeline required.
304
304
 
305
305
  ---
306
306
 
@@ -0,0 +1,48 @@
1
+ ---
2
+ project: "{PROJECT_NAME}"
3
+ module: "{MODULE_NAME or 'root'}"
4
+ generated_by: "draft:init"
5
+ generated_at: "{ISO_TIMESTAMP}"
6
+ draft_init_mode: okf
7
+ ---
8
+
9
+ # {PROJECT_NAME} — AI Context Index
10
+
11
+ > Index root for the OKF taxonomy bundle (`wiki/`). Read **Synopsis** for broad
12
+ > tasks (they usually terminate here). For focused tasks, route through the
13
+ > **Concept Map** to ≤N concept pages — each lists `x-grounded-paths`. This is
14
+ > both the cheap broad-context path AND the progressive-disclosure entry point.
15
+
16
+ ## Synopsis
17
+
18
+ <!-- 150–250 lines: the cheap broad-context path (prior .ai-context.md value
19
+ preserved). Architecture in brief, key invariants, where to start, top
20
+ hotspots. A broad task should be answerable from this section alone. -->
21
+
22
+ - **Architecture in brief:** {2–4 sentences}
23
+ - **Key invariants:** {bullet list, provenance-tagged}
24
+ - **Where to start:** {entrypoints + core subsystems}
25
+ - **Top hotspots:** {from hotspot-rank.sh — symbol, fan-in}
26
+
27
+ ## Concept Map
28
+
29
+ <!-- Routing table built from each concept's frontmatter `description`.
30
+ Open a section index for the full per-concept list. -->
31
+
32
+ | Section | Routing |
33
+ |---------|---------|
34
+ | `wiki/systems/` | {one-line per subsystem — what it owns, when to open} |
35
+ | `wiki/features/` | {one-line per feature} |
36
+ | `wiki/reference/` | config, schemas, APIs, ADRs, runbooks |
37
+ | `wiki/entrypoints/` | binaries / CLIs / handler roots |
38
+
39
+ Full taxonomy: [wiki/index.md](wiki/index.md).
40
+
41
+ ## How to navigate
42
+
43
+ 1. **Broad task** (summarize, "what owns X", topology) → answer from **Synopsis**.
44
+ 2. **Focused task** ("what breaks if I change Y", "add a field to Z") → open the
45
+ matching concept via the **Concept Map**; follow its `x-grounded-paths` and
46
+ `Used by` cross-links. Do not read the whole bundle.
47
+ 3. Every concept page is verified against the live call graph; trust its
48
+ `Blast radius` section over re-deriving by hand.
@@ -0,0 +1,54 @@
1
+ ---
2
+ type: Subsystem # required (OKF) — one of the frozen vocab below
3
+ title: "{CONCEPT_TITLE}" # OKF
4
+ description: > # OKF — LOAD-BEARING: the agent's routing key.
5
+ Write this as a ROUTING DECISION, not a summary. It must answer
6
+ "should the agent open this file for the task at hand?" from the index
7
+ alone. Name the responsibilities and the words a task would use.
8
+ resource: "{CANONICAL_SOURCE_PATH}" # OKF — canonical source path(s)
9
+ tags: [tag1, tag2] # OKF
10
+ timestamp: "{ISO_TIMESTAMP}" # OKF — last regeneration
11
+ # Draft extensions (ignored by generic OKF consumers; namespaced x-):
12
+ x-grounded-paths: ["{path/a}", "{path/b}"] # exact source files this page grounds
13
+ x-hotspot-score: 0.0 # from hotspot-rank.sh (0..1)
14
+ x-callers: ["{module/a}", "{module/b}"] # from graph-callers.sh
15
+ ---
16
+
17
+ # {CONCEPT_TITLE}
18
+
19
+ <!--
20
+ Frozen `type` vocabulary (changing it churns every file — versioned via
21
+ index.md: okf_types_version):
22
+ Subsystem — major graph cluster / package boundary → systems/
23
+ Module — single package/dir with cohesive responsibility → systems/
24
+ Feature — user-facing capability spanning modules → features/
25
+ Entrypoint — binary / main / CLI / handler root → entrypoints/
26
+ API — public interface, route group, RPC surface → reference/
27
+ DataModel — schema, table, core struct/type → reference/
28
+ Dependency — notable external dep + how it's used → reference/
29
+ ADR — architecture decision record → reference/
30
+ Runbook — operational procedure → reference/
31
+ -->
32
+
33
+ ## What it is
34
+
35
+ One paragraph: the concept's responsibility and boundary. Graph-grounded.
36
+
37
+ ## How it works
38
+
39
+ Primary control/data flow. At least one Mermaid diagram for a significant
40
+ concept (workflow, state, or sequence). Grounded in the call graph.
41
+
42
+ ## Used by
43
+
44
+ Cross-links to callers (from `x-callers`). Each link is a relative path to
45
+ another concept page so `okf-validate.sh` can resolve it.
46
+
47
+ ## Blast radius
48
+
49
+ What breaks if this changes (from `graph-impact.sh`). Lists `x-grounded-paths`
50
+ so a focused task knows exactly which source files to open.
51
+
52
+ ## See also
53
+
54
+ - [Related concept](../systems/other.md)
@@ -0,0 +1,40 @@
1
+ ---
2
+ type: Subsystem
3
+ title: "{PROJECT_NAME} — Knowledge Bundle"
4
+ description: >
5
+ Root index of the OKF taxonomy bundle. Start here, then route into
6
+ overview/, systems/, features/, reference/, or entrypoints/ via the
7
+ Concept Map. Open a concept only when its description matches the task.
8
+ resource: .
9
+ tags: [index]
10
+ timestamp: "{ISO_TIMESTAMP}"
11
+ okf_version: "0.1"
12
+ okf_types_version: "0.1"
13
+ ---
14
+
15
+ # {PROJECT_NAME} — Knowledge Bundle
16
+
17
+ > OKF v0.1 bundle. One concept per file; cross-links form the graph. The
18
+ > live call graph (`codebase-memory-mcp`) is the grounding source; this bundle
19
+ > is the navigable serialization. `../ai-context.md` is the consumption entry point.
20
+
21
+ ## Sections
22
+
23
+ | Section | Holds | Index |
24
+ |---------|-------|-------|
25
+ | `overview/` | System map, getting-started, glossary | [overview/index.md](overview/index.md) |
26
+ | `systems/` | Subsystems & modules (graph clusters) | [systems/index.md](systems/index.md) |
27
+ | `features/` | User-facing capabilities spanning modules | [features/index.md](features/index.md) |
28
+ | `reference/` | APIs, data models, dependencies, ADRs, runbooks | [reference/index.md](reference/index.md) |
29
+ | `entrypoints/` | Binaries / mains / CLIs / handler roots | see pages below |
30
+
31
+ ## Concept Map
32
+
33
+ <!-- Built from each concept's frontmatter `description` (the routing key).
34
+ One line per concept. Regenerated on every init/refresh. -->
35
+ <!-- CONCEPT-MAP:START -->
36
+ <!-- CONCEPT-MAP:END -->
37
+
38
+ ## Change log
39
+
40
+ See [log.md](log.md) for chronological regeneration history.
@@ -0,0 +1,25 @@
1
+ ---
2
+ type: Subsystem
3
+ title: "{SECTION_TITLE}"
4
+ description: >
5
+ Section index. Lists every concept in this section with its one-line
6
+ routing description so an agent can pick the right page without opening
7
+ each one. {SECTION_PURPOSE}
8
+ resource: .
9
+ tags: [index]
10
+ timestamp: "{ISO_TIMESTAMP}"
11
+ ---
12
+
13
+ # {SECTION_TITLE}
14
+
15
+ > Section of the OKF bundle. Back to [bundle root](../index.md).
16
+
17
+ ## Concepts
18
+
19
+ <!-- One row per concept page in this section. `description` is the routing key
20
+ copied from each page's frontmatter. Regenerated on every init/refresh. -->
21
+
22
+ | Concept | Type | Routing description |
23
+ |---------|------|---------------------|
24
+ | [{concept-a}]({concept-a}.md) | Module | {one-line routing desc} |
25
+ | [{concept-b}]({concept-b}.md) | Feature | {one-line routing desc} |