@albinocrabs/feynman 0.2.2 → 0.2.4

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.
package/bin/feynman.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  // bin/feynman.js — feynman unified CLI
3
- // Subcommands: install, uninstall, doctor, lint, version, help
3
+ // Subcommands: install, uninstall, doctor, lint, examples, bootstrap, version, help
4
4
  // Zero runtime deps. CJS only. Node >= 18.
5
5
  'use strict';
6
6
 
@@ -22,6 +22,7 @@ const c = {
22
22
 
23
23
  const PKG = require('../package.json');
24
24
  const VERSION = PKG.version;
25
+ const ROOT_DIR = path.resolve(__dirname, '..');
25
26
 
26
27
  // Resolve paths using os.homedir() — never tilde literal (bug #8810)
27
28
  const HOME = os.homedir();
@@ -31,7 +32,11 @@ const HOOK_PATH = path.resolve(__dirname, '..', 'hooks', 'feynman-activate.js');
31
32
  const RULES_PATH = path.resolve(__dirname, '..', 'rules', 'feynman-activate.md');
32
33
 
33
34
  const DEFAULT_STATE = { enabled: true, intensity: 'full', injections: 0 };
34
- const VALID_TARGETS = ['claude', 'codex', 'both'];
35
+ const TARGET_ALIASES = {
36
+ all: 'both',
37
+ '*': 'both',
38
+ };
39
+ const VALID_TARGETS = ['claude', 'codex', 'both', 'all', '*'];
35
40
 
36
41
  function targetConfig(name) {
37
42
  const dirName = name === 'codex' ? '.codex' : '.claude';
@@ -52,7 +57,7 @@ function targetNames(target) {
52
57
  return target === 'both' ? ['claude', 'codex'] : [target];
53
58
  }
54
59
 
55
- function parseTarget(args, fallback = 'claude') {
60
+ function parseTarget(args, fallback = 'codex') {
56
61
  let target = fallback;
57
62
  const keep = [];
58
63
  for (let i = 0; i < args.length; i++) {
@@ -66,9 +71,10 @@ function parseTarget(args, fallback = 'claude') {
66
71
  }
67
72
  }
68
73
  if (!VALID_TARGETS.includes(target)) {
69
- console.error(`feynman: invalid --target '${target}' (expected claude, codex, or both)`);
74
+ console.error(`feynman: invalid --target '${target}' (expected claude, codex, both, all, or *)`);
70
75
  process.exit(2);
71
76
  }
77
+ target = TARGET_ALIASES[target] || target;
72
78
  return { target, args: keep };
73
79
  }
74
80
 
@@ -89,30 +95,329 @@ ${c.bold('Commands:')}
89
95
  uninstall Remove feynman hook (state preserved)
90
96
  doctor Check installation health
91
97
  lint <file> Lint a markdown file for diagram rule violations
98
+ examples List and display example prompts from the repository
99
+ bootstrap Export shared Feynman assets to a local folder
92
100
  version Print version number
93
101
  help Show this help
94
102
 
95
103
  ${c.bold('Options:')}
96
104
  --help, -h Show help for a command
97
- --target claude | codex | both (default: claude)
105
+ --target claude | codex | both | all | *
98
106
  --force (install) Re-register even if already installed
99
107
 
100
108
  ${c.bold('Examples:')}
101
109
  npx @albinocrabs/feynman install
102
110
  npx @albinocrabs/feynman install --target codex
103
111
  npx @albinocrabs/feynman install --target both
112
+ npx @albinocrabs/feynman install --target all
104
113
  npx @albinocrabs/feynman doctor
105
114
  feynman lint response.md
115
+ feynman bootstrap --out ./feynman-package
116
+ feynman examples
106
117
  feynman uninstall
107
118
  `;
108
119
 
120
+ const EXAMPLES_HELP = `${c.bold('feynman examples')} — print built-in demonstration prompts
121
+
122
+ ${c.bold('Usage:')}
123
+ feynman examples # list available examples
124
+ feynman examples --name <fileBase> # print a specific example
125
+ feynman examples --random # print a random example
126
+
127
+ ${c.bold('Options:')}
128
+ --name Example filename without .md extension (examples/feature-planning)
129
+ --random Show one random example in full
130
+ --help Show this help
131
+
132
+ Example filenames:
133
+ - architecture-review
134
+ - api-flow
135
+ - c4-platform-diagramming
136
+ - db-schema
137
+ - algorithm-explain
138
+ - deploy-pipeline
139
+ - code-review
140
+ - incident-response
141
+ - feature-planning
142
+ `;
143
+
144
+ const EXAMPLES_DIR = path.resolve(__dirname, '..', 'examples');
145
+ const SKILL_SRC = path.resolve(ROOT_DIR, 'skills', 'feynman', 'SKILL.md');
146
+ const CLAUDE_PLUGIN = path.resolve(ROOT_DIR, '.claude-plugin', 'plugin.json');
147
+ const CODEX_PLUGIN = path.resolve(ROOT_DIR, '.codex-plugin', 'plugin.json');
148
+ const PACKAGE_HOOKS = path.resolve(ROOT_DIR, 'hooks', 'hooks.json');
149
+ const DEFAULT_BOOTSTRAP_DIR = 'feynman-package';
150
+ const ACTIVATOR_JS = path.resolve(ROOT_DIR, 'hooks', 'feynman-activate.js');
151
+ const CLI_JS = path.resolve(ROOT_DIR, 'bin', 'feynman.js');
152
+ const PACKAGE_JSON = path.resolve(ROOT_DIR, 'package.json');
153
+
154
+ const BOOTSTRAP_HELP = `${c.bold('feynman bootstrap')} — export Feynman assets into local folder
155
+
156
+ ${c.bold('Usage:')}
157
+ feynman bootstrap
158
+ feynman bootstrap --out <directory>
159
+
160
+ ${c.bold('Options:')}
161
+ --out Output folder (default: ./feynman-package)
162
+ --force Recreate output folder if it exists
163
+ --help Show this help
164
+ `;
165
+
166
+ function ensureDir(dir) {
167
+ fs.mkdirSync(dir, { recursive: true });
168
+ }
169
+
170
+ function copyFileIfExists(src, dest) {
171
+ if (!fs.existsSync(src)) return false;
172
+ ensureDir(path.dirname(dest));
173
+ fs.copyFileSync(src, dest);
174
+ return true;
175
+ }
176
+
177
+ function copyMarkdownDir(src, dest) {
178
+ if (!fs.existsSync(src)) return 0;
179
+ let copied = 0;
180
+ for (const entry of fs.readdirSync(src, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name))) {
181
+ const sourcePath = path.join(src, entry.name);
182
+ const destPath = path.join(dest, entry.name);
183
+
184
+ if (entry.isDirectory()) {
185
+ copied += copyMarkdownDir(sourcePath, destPath);
186
+ continue;
187
+ }
188
+
189
+ if (!entry.isFile() || !entry.name.endsWith('.md')) {
190
+ continue;
191
+ }
192
+
193
+ ensureDir(path.dirname(destPath));
194
+ fs.copyFileSync(sourcePath, destPath);
195
+ copied += 1;
196
+ }
197
+ return copied;
198
+ }
199
+
200
+ function examplesIndex() {
201
+ if (!fs.existsSync(EXAMPLES_DIR)) return [];
202
+
203
+ return fs.readdirSync(EXAMPLES_DIR)
204
+ .filter((name) => name.endsWith('.md'))
205
+ .sort()
206
+ .map((name) => {
207
+ const file = path.join(EXAMPLES_DIR, name);
208
+ const content = fs.readFileSync(file, 'utf8');
209
+ const title = (content.match(/^#\s*(.+)$/m) || [null, name])[1].trim();
210
+ const question = (content.match(/^> (.*)$/m) || [null, ''])[1].trim();
211
+ return {
212
+ name: name.replace(/\.md$/, ''),
213
+ title,
214
+ question,
215
+ path: file,
216
+ };
217
+ });
218
+ }
219
+
220
+ function cmdExamples(args) {
221
+ if (args.includes('--help') || args.includes('-h')) {
222
+ console.log(EXAMPLES_HELP);
223
+ process.exit(0);
224
+ }
225
+
226
+ const entries = examplesIndex();
227
+ if (!entries.length) {
228
+ console.log('No examples found under examples/.');
229
+ process.exit(0);
230
+ }
231
+
232
+ let random = false;
233
+ let wantsName = null;
234
+ const unknown = [];
235
+
236
+ for (let i = 0; i < args.length; i += 1) {
237
+ const arg = args[i];
238
+ if (arg === '--name' || arg === '-n') {
239
+ const value = args[i + 1];
240
+ if (!value || value.startsWith('-')) {
241
+ console.error('feynman examples: --name requires a value');
242
+ process.exit(2);
243
+ }
244
+ if (wantsName !== null) {
245
+ console.error('feynman examples: duplicate --name');
246
+ process.exit(2);
247
+ }
248
+ wantsName = value;
249
+ i += 1;
250
+ continue;
251
+ }
252
+
253
+ if (arg === '--random' || arg === '-r') {
254
+ random = true;
255
+ continue;
256
+ }
257
+
258
+ if (arg.startsWith('-')) {
259
+ unknown.push(arg);
260
+ continue;
261
+ }
262
+
263
+ unknown.push(arg);
264
+ }
265
+
266
+ if (random && wantsName) {
267
+ console.error('feynman examples: use either --random or --name');
268
+ process.exit(2);
269
+ }
270
+
271
+ if (unknown.length > 0) {
272
+ console.error(`feynman examples: unexpected arguments "${unknown.join(' ')}"`);
273
+ console.error('Run `feynman examples --help` for usage.');
274
+ process.exit(2);
275
+ }
276
+
277
+ if (random) {
278
+ const entry = entries[Math.floor(Math.random() * entries.length)];
279
+ const content = fs.readFileSync(entry.path, 'utf8');
280
+ console.log(`\n[${entry.name}] ${entry.title}\n`);
281
+ console.log('Question:');
282
+ console.log(entry.question ? `> ${entry.question}` : '(no question marker found)');
283
+ console.log('\nPreview:\n');
284
+ const lines = content.split('\n').slice(0, 26);
285
+ console.log(lines.join('\n'));
286
+ process.exit(0);
287
+ }
288
+
289
+ if (wantsName) {
290
+ const entry = entries.find((item) => item.name === wantsName);
291
+ if (!entry) {
292
+ console.error(`feynman examples: unknown example '${wantsName}'`);
293
+ process.exit(2);
294
+ }
295
+ const content = fs.readFileSync(entry.path, 'utf8');
296
+ console.log(`\n[${entry.name}] ${entry.title}\n`);
297
+ console.log(content);
298
+ process.exit(0);
299
+ }
300
+
301
+ if (args.length === 0) {
302
+ console.log('Available examples:\n');
303
+ for (const entry of entries) {
304
+ const q = entry.question ? ` — ${entry.question}` : '';
305
+ console.log(`- ${entry.name}`);
306
+ console.log(` ${entry.title}${q ? ` — ${q}` : ''}`);
307
+ }
308
+ process.exit(0);
309
+ }
310
+ }
311
+
312
+ function cmdBootstrap(args) {
313
+ if (args.includes('--help') || args.includes('-h')) {
314
+ console.log(BOOTSTRAP_HELP);
315
+ process.exit(0);
316
+ }
317
+
318
+ let out = path.resolve(process.cwd(), DEFAULT_BOOTSTRAP_DIR);
319
+ let force = false;
320
+ const unknown = [];
321
+
322
+ for (let i = 0; i < args.length; i += 1) {
323
+ const arg = args[i];
324
+
325
+ if (arg === '--force') {
326
+ force = true;
327
+ continue;
328
+ }
329
+
330
+ if (arg === '--out') {
331
+ const value = args[i + 1];
332
+ if (!value || value.startsWith('-')) {
333
+ console.error('feynman bootstrap: --out requires a value');
334
+ process.exit(2);
335
+ }
336
+ out = path.resolve(process.cwd(), value);
337
+ i += 1;
338
+ continue;
339
+ }
340
+
341
+ if (arg.startsWith('--out=')) {
342
+ const value = arg.slice('--out='.length);
343
+ if (!value) {
344
+ console.error('feynman bootstrap: invalid --out argument');
345
+ process.exit(2);
346
+ }
347
+ out = path.resolve(process.cwd(), value);
348
+ continue;
349
+ }
350
+
351
+ if (arg.startsWith('-')) {
352
+ unknown.push(arg);
353
+ continue;
354
+ }
355
+
356
+ unknown.push(arg);
357
+ }
358
+
359
+ if (unknown.length > 0) {
360
+ console.error(`feynman bootstrap: unexpected arguments "${unknown.join(' ')}"`);
361
+ console.error('Run `feynman bootstrap --help` for usage.');
362
+ process.exit(2);
363
+ }
364
+
365
+ if (fs.existsSync(out) && !force) {
366
+ console.log(`feynman bootstrap: output already exists at ${out}`);
367
+ console.log('Use `--force` to recreate it.');
368
+ process.exit(0);
369
+ }
370
+
371
+ if (fs.existsSync(out)) {
372
+ fs.rmSync(out, { recursive: true, force: true });
373
+ }
374
+
375
+ const counts = {
376
+ examples: copyMarkdownDir(EXAMPLES_DIR, path.join(out, 'examples')),
377
+ rules: copyFileIfExists(RULES_PATH, path.join(out, 'rules', 'feynman-activate.md')) ? 1 : 0,
378
+ hooks: copyFileIfExists(PACKAGE_HOOKS, path.join(out, 'hooks', 'hooks.json')) ? 1 : 0,
379
+ hookRuntime: copyFileIfExists(ACTIVATOR_JS, path.join(out, 'hooks', 'feynman-activate.js')) ? 1 : 0,
380
+ cliRuntime: copyFileIfExists(CLI_JS, path.join(out, 'bin', 'feynman.js')) ? 1 : 0,
381
+ packageManifest: copyFileIfExists(PACKAGE_JSON, path.join(out, 'package.json')) ? 1 : 0,
382
+ plugins:
383
+ (copyFileIfExists(CLAUDE_PLUGIN, path.join(out, '.claude-plugin', 'plugin.json')) ? 1 : 0) +
384
+ (copyFileIfExists(CODEX_PLUGIN, path.join(out, '.codex-plugin', 'plugin.json')) ? 1 : 0),
385
+ skill: copyFileIfExists(SKILL_SRC, path.join(out, 'skills', 'feynman', 'SKILL.md')) ? 1 : 0,
386
+ };
387
+
388
+ ensureDir(out);
389
+ fs.writeFileSync(
390
+ path.join(out, 'feynman-bootstrap.json'),
391
+ JSON.stringify({
392
+ version: VERSION,
393
+ createdAt: new Date().toISOString(),
394
+ outputDir: out,
395
+ counts,
396
+ }, null, 2) + '\n'
397
+ );
398
+
399
+ const total = Object.values(counts).reduce((sum, count) => sum + (count || 0), 1);
400
+ console.log('');
401
+ console.log('┌─ feynman bootstrap ────────────────────────────────────────┐');
402
+ console.log(`│ output: ${out}`);
403
+ console.log(`│ examples: ${counts.examples}`);
404
+ console.log(`│ rules: ${counts.rules}`);
405
+ console.log(`│ hooks: ${counts.hooks}`);
406
+ console.log(`│ runtime: ${counts.hookRuntime + counts.cliRuntime + counts.packageManifest}`);
407
+ console.log(`│ plugins: ${counts.plugins}`);
408
+ console.log(`│ skill: ${counts.skill}`);
409
+ console.log(`│ files: ${total}`);
410
+ console.log('└───────────────────────────────────────────────────────────┘');
411
+ process.exit(0);
412
+ }
413
+
109
414
  const INSTALL_HELP = `${c.bold('feynman install')} — register feynman hook
110
415
 
111
416
  ${c.bold('Usage:')}
112
- feynman install [--target claude|codex|both] [--force]
417
+ feynman install [--target claude|codex|both|all|*] [--force]
113
418
 
114
419
  ${c.bold('Options:')}
115
- --target Install into Claude Code, Codex, or both (default: claude)
420
+ --target Install into Claude Code, Codex, both, all, or * (default: codex)
116
421
  --force Re-register hook even if already installed
117
422
 
118
423
  Claude creates:
@@ -130,7 +435,7 @@ Idempotent by default: skips if feynman-activate.js entry already exists.
130
435
  const UNINSTALL_HELP = `${c.bold('feynman uninstall')} — remove feynman hook
131
436
 
132
437
  ${c.bold('Usage:')}
133
- feynman uninstall [--target claude|codex|both]
438
+ feynman uninstall [--target claude|codex|both|all|*]
134
439
 
135
440
  Removes feynman hook entries from target config.
136
441
  Preserves .feynman/state.json (user data).
@@ -142,7 +447,7 @@ Idempotent: safe to run multiple times.
142
447
  const DOCTOR_HELP = `${c.bold('feynman doctor')} — check feynman installation health
143
448
 
144
449
  ${c.bold('Usage:')}
145
- feynman doctor [--target claude|codex]
450
+ feynman doctor [--target claude|codex|both|all|*]
146
451
 
147
452
  Checks:
148
453
  1. target hook config present
@@ -547,6 +852,14 @@ switch (sub) {
547
852
  cmdLint(rest);
548
853
  break;
549
854
  }
855
+ case 'examples': {
856
+ cmdExamples(rest);
857
+ break;
858
+ }
859
+ case 'bootstrap': {
860
+ cmdBootstrap(rest);
861
+ break;
862
+ }
550
863
  case 'version': {
551
864
  cmdVersion(rest);
552
865
  break;
@@ -153,7 +153,7 @@ All runtime state lives in two files under the selected client root:
153
153
  state.intensity = <value>
154
154
  .feynman-active content updated
155
155
 
156
- [npx @albinocrabs/feynman uninstall --target claude|codex|both]
156
+ [npx @albinocrabs/feynman uninstall --target claude|codex|both|all|*]
157
157
  hook removed from target hook config
158
158
  .feynman-active deleted
159
159
  state.json preserved (user data)
@@ -171,20 +171,27 @@ managed by skill commands in `skills/feynman/SKILL.md`.
171
171
  ## CLI Subcommand Map
172
172
 
173
173
  ```
174
- bin/feynman.js
175
- ├── install → writes target hook config + state.json + flag
176
- ├── uninstall → removes target hook entries + flag (keeps state)
177
- ├── doctor → checks target health criteria, prints frame
178
- ├── lint → delegates to bin/feynman-lint.js
179
- └── version prints package.json version
174
+ bin/feynman.js
175
+ ├── install → writes target hook config + state.json + flag
176
+ ├── uninstall → removes target hook entries + flag (keeps state)
177
+ ├── doctor → checks target health criteria, prints frame
178
+ ├── lint → delegates to bin/feynman-lint.js
179
+ ├── examples list and render built-in ASCII examples
180
+ ├── help → this help/usage block
181
+ ├── bootstrap → exports examples + manifests + skill into local package folder
182
+ └── version → prints package.json version
180
183
  ```
181
184
 
185
+ `/feynman on|off|start|stop|lite|full|ultra` are handled by the skill contract
186
+ in `skills/feynman/SKILL.md` and share aliases:
187
+ `start` == `on`, `stop` == `off`.
188
+
182
189
  Targets:
183
190
 
184
191
  ```
185
192
  claude → ~/.claude/settings.json + ~/.claude/.feynman/
186
193
  codex → ~/.codex/hooks.json + ~/.codex/.feynman/
187
- both → runs claude and codex installers idempotently
194
+ both, all, * → runs claude and codex installers/uninstallers idempotently
188
195
  ```
189
196
 
190
197
  **File:** `bin/feynman.js`
package/docs/launch.md CHANGED
@@ -9,7 +9,7 @@ become columns, priorities become scales, and status summaries become frames.
9
9
  ## One-liner
10
10
 
11
11
  ```bash
12
- npx -y @albinocrabs/feynman@latest install --target both
12
+ npx -y @albinocrabs/feynman@latest install --target all
13
13
  ```
14
14
 
15
15
  ## Short Description
@@ -30,9 +30,10 @@ without asking every time.
30
30
  ## Demo Script
31
31
 
32
32
  ```bash
33
- npx -y @albinocrabs/feynman@latest install --target both
33
+ npx -y @albinocrabs/feynman@latest install --target '*'
34
34
  npx -y @albinocrabs/feynman@latest doctor --target claude
35
35
  npx -y @albinocrabs/feynman@latest doctor --target codex
36
+ feynman bootstrap --out ./feynman-package
36
37
  ```
37
38
 
38
39
  Prompt:
@@ -65,3 +66,10 @@ limited writes | production-ready | persistence opt
65
66
  ```bash
66
67
  npx -y @albinocrabs/feynman@latest version
67
68
  ```
69
+
70
+ For the full release playbook, see: [docs/release.md](release.md)
71
+
72
+ ## Detailed Release Docs
73
+
74
+ - [docs/release.md](release.md): full end-to-end release procedure, release notes
75
+ contract, workflow behavior, and post-release verification.
@@ -0,0 +1,91 @@
1
+ # Feynman Product Passport
2
+
3
+ ## 1) Миссия и область применения
4
+
5
+ `feynman` — plugin для Claude Code и Codex, который injects ASCII-правила диаграмм в каждый prompt на слое `UserPromptSubmit`.
6
+
7
+ Главная задача: ускорить расшифровку структуры и снизить когнитивную нагрузку в ответах с потоками, иерархией, сравнением и статусами.
8
+
9
+ Не делает:
10
+ - бизнес-логику приложения,
11
+ - редизайн ответа API/фронтенда,
12
+ - сетевые вызовы или фоновое хранение текстовых логов.
13
+
14
+ ## 2) Артефакты проекта
15
+
16
+ | Слой | Артефакт | Назначение |
17
+ |---|---|---|
18
+ | Hook | `hooks/feynman-activate.js` | Чтение состояния и инъекция `additionalContext` |
19
+ | Rules | `rules/feynman-activate.md` | Набор правил `lite/full/ultra` |
20
+ | CLI | `bin/feynman.js` | `install/uninstall/doctor/lint/examples/bootstrap/version/help` |
21
+ | Lint | `bin/feynman-lint.js`, `lib/lint/*` | Проверка корректности ASCII-диаграмм |
22
+ | Skill | `skills/feynman/SKILL.md` | Slash-команды управления режимом |
23
+ | Package | `.claude-plugin/`, `.codex-plugin/`, `hooks.json` | Доставка плагина |
24
+
25
+ ## 3) Runtime state
26
+
27
+ `~/.claude/.feynman/` или `~/.codex/.feynman/`:
28
+
29
+ - `state.json` — `enabled`, `intensity`, `injections`.
30
+ - `.feynman-active` — флаг включения.
31
+
32
+ Семантика:
33
+ - флаг есть: инъекция может выполняться (если `enabled: true`);
34
+ - флага нет + state есть: отключено пользователем;
35
+ - оба файла отсутствуют: bootstrap первого запуска.
36
+
37
+ ## 4) Управление состоянием (slash)
38
+
39
+ - `/feynman on` / `/feynman start` — включить инъекцию;
40
+ - `/feynman off` / `/feynman stop` — выключить инъекцию;
41
+ - `/feynman lite|full|ultra` — переключить интенсивность;
42
+ - `/feynman` / `/feynman status` — статус без изменений.
43
+
44
+ ## 5) CLI контракт
45
+
46
+ | Команда | Что делает |
47
+ |---|---|
48
+ | `feynman install` | Регистрирует hook в `settings.json`/`hooks.json` |
49
+ | `feynman uninstall` | Удаляет hook, оставляя state |
50
+ | `feynman doctor` | Диагностический health-check |
51
+ | `feynman lint` | Проверяет Markdown на правила L01-L08 |
52
+ | `feynman examples` | Печатает примеры ответов |
53
+ | `feynman bootstrap` | Экспортирует артефакты в локальную папку |
54
+ | `feynman version` | Версия пакета |
55
+ | `feynman help` | Текущая справка CLI |
56
+
57
+ ## 6) Качество диаграмм
58
+
59
+ Источник правил:
60
+ - `rules/feynman-activate.md` (источник истины для `lite/full/ultra`)
61
+
62
+ Обновление правил требует синхронизации:
63
+ - `hooks/feynman-activate.js` + `bin/feynman.js` + `skills/feynman/SKILL.md`
64
+ - плюс smoke/pattern-тесты `tests/cli.test.js`.
65
+
66
+ ## 7) Технико-операционные ограничения
67
+
68
+ - Zero deps, CommonJS.
69
+ - Node >= 18.
70
+ - No network at runtime для hook execution (внешние пути не используются).
71
+ - Bootstrap включает runtime files для дисконнекта: `hooks/feynman-activate.js`, `bin/feynman.js`, `package.json` и ассеты.
72
+
73
+ ## 8) Проверки и сопровождение
74
+
75
+ Обязательные для релиза:
76
+ - `feynman bootstrap --out ... --force`
77
+ - `feynman lint --json README.md` (рекурентно по docs),
78
+ - CI matrix (`Node 18/20` на `ubuntu`/`macos`).
79
+
80
+ ## 9) Известный рынок и позиционирование
81
+
82
+ В рынке есть аналоги по prompt-управлению (кнопки в IDE/CLI/prompt-команды), но у `feynman` уникально сочетание:
83
+ - автоматическая ASCII-семантика на hooks,
84
+ - отдельный lint для качества диаграмм,
85
+ - zero-dep runtime.
86
+
87
+ ## 10) Связь
88
+
89
+ - [Architecture](./architecture.md)
90
+ - [Launch](./launch.md)
91
+ - [RTK Playbook](../RTK.md)