@albinocrabs/feynman 0.2.1 → 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/.codex-plugin/plugin.json +1 -1
- package/CHANGELOG.md +50 -3
- package/CONTRIBUTING.md +1 -0
- package/README.md +249 -13
- package/SECURITY.md +11 -0
- package/bin/feynman.js +322 -9
- package/docs/architecture.md +15 -8
- package/docs/launch.md +10 -2
- package/docs/object-passport.md +91 -0
- package/docs/release.md +162 -0
- package/examples/activity-sequence.md +105 -0
- package/examples/api-flow.md +32 -3
- package/examples/bug-isolation.md +89 -0
- package/examples/c4-platform-diagramming.md +112 -0
- package/examples/context-splitting.md +77 -0
- package/examples/feature-planning.md +107 -0
- package/examples/incident-response.md +77 -0
- package/examples/release-readiness.md +73 -0
- package/examples/service-migration.md +72 -0
- package/package.json +5 -3
- package/rules/feynman-activate.md +5 -7
- package/skills/feynman/SKILL.md +9 -7
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
|
|
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 = '
|
|
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
|
|
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
|
|
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
|
|
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;
|
package/docs/architecture.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
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
|
|
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
|
|
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)
|