@albinocrabs/feynman 0.2.5 → 0.2.6
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 +30 -0
- package/README.md +114 -10
- package/SECURITY.md +1 -1
- package/bin/feynman.js +42 -27
- package/hooks/feynman-session-start.js +9 -10
- package/package.json +1 -1
- package/rules/feynman-activate.md +224 -1
- package/skills/feynman/SKILL.md +29 -8
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.2.6 - 2026-05-08
|
|
6
|
+
|
|
7
|
+
Changes since v0.2.5.
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- harden feynman runtime checks
|
|
12
|
+
- add roadmap phase uat risk patterns
|
|
13
|
+
- add sdlc output patterns to feynman
|
|
14
|
+
- add terminal-safe feynman rendering rules
|
|
15
|
+
|
|
16
|
+
### Fixes
|
|
17
|
+
|
|
18
|
+
- clarify terminal table rendering rules
|
|
19
|
+
- harden feynman hook lifecycle
|
|
20
|
+
|
|
21
|
+
### Documentation
|
|
22
|
+
|
|
23
|
+
- restore GSD validation coverage
|
|
24
|
+
|
|
25
|
+
### Tests
|
|
26
|
+
|
|
27
|
+
- cover codex app-server hook visibility
|
|
28
|
+
- cover ci threshold branches
|
|
29
|
+
|
|
30
|
+
### Maintenance
|
|
31
|
+
|
|
32
|
+
- use global gsd defaults
|
|
33
|
+
- remove gsd model bindings from repo config
|
|
34
|
+
|
|
5
35
|
## 0.2.5 - 2026-05-08
|
|
6
36
|
|
|
7
37
|
### Features
|
package/README.md
CHANGED
|
@@ -22,7 +22,10 @@
|
|
|
22
22
|
<a href="docs/object-passport.md">Passport</a> •
|
|
23
23
|
<a href="#before--after">Before/After</a> •
|
|
24
24
|
<a href="#install">Install</a> •
|
|
25
|
+
<a href="#verify-the-install">Verify</a> •
|
|
25
26
|
<a href="#intensity-levels">Levels</a> •
|
|
27
|
+
<a href="#security-notes">Security</a> •
|
|
28
|
+
<a href="#token-budget-and-output-size">Tokens</a> •
|
|
26
29
|
<a href="#lint">Lint</a> •
|
|
27
30
|
<a href="#examples">Examples</a> •
|
|
28
31
|
<a href="docs/launch.md">Launch</a> •
|
|
@@ -32,8 +35,8 @@
|
|
|
32
35
|
---
|
|
33
36
|
|
|
34
37
|
A [Claude Code](https://docs.anthropic.com/en/docs/claude-code) and Codex
|
|
35
|
-
plugin that automatically injects ASCII diagram rules
|
|
36
|
-
`UserPromptSubmit`
|
|
38
|
+
plugin that automatically injects ASCII diagram rules through `SessionStart`
|
|
39
|
+
and `UserPromptSubmit` hooks.
|
|
37
40
|
|
|
38
41
|
```bash
|
|
39
42
|
npx -y @albinocrabs/feynman@latest install --target all
|
|
@@ -173,16 +176,15 @@ feynman hook instead of adding duplicates.
|
|
|
173
176
|
git clone https://github.com/apolenkov/feynman && bash feynman/install.sh
|
|
174
177
|
```
|
|
175
178
|
|
|
176
|
-
Restart Claude Code or Codex
|
|
177
|
-
|
|
178
|
-
**Verify:** `npx @albinocrabs/feynman doctor --target claude` or `npx @albinocrabs/feynman doctor --target codex`
|
|
179
|
+
Restart Claude Code or Codex after install so a fresh `SessionStart` hook can
|
|
180
|
+
prime the session.
|
|
179
181
|
|
|
180
182
|
**Uninstall:** `npx @albinocrabs/feynman uninstall --target claude|codex|both|all|*`
|
|
181
183
|
|
|
182
184
|
**Plugin manifests:** this repo also ships `.claude-plugin/plugin.json`,
|
|
183
|
-
`hooks/hooks.json`, `.codex-plugin/plugin.json`, and `hooks.json`
|
|
184
|
-
|
|
185
|
-
|
|
185
|
+
`hooks/hooks.json`, `.codex-plugin/plugin.json`, and `hooks.json` for direct
|
|
186
|
+
client integrations. The npx installer remains the production fallback because
|
|
187
|
+
both clients still support direct user hook registration.
|
|
186
188
|
|
|
187
189
|
<details>
|
|
188
190
|
<summary>Manual install</summary>
|
|
@@ -193,6 +195,18 @@ Add to `~/.claude/settings.json` — use the absolute path, not `~/`
|
|
|
193
195
|
```json
|
|
194
196
|
{
|
|
195
197
|
"hooks": {
|
|
198
|
+
"SessionStart": [
|
|
199
|
+
{
|
|
200
|
+
"matcher": "startup|resume",
|
|
201
|
+
"hooks": [
|
|
202
|
+
{
|
|
203
|
+
"type": "command",
|
|
204
|
+
"command": "node \"/absolute/path/to/feynman/hooks/feynman-session-start.js\"",
|
|
205
|
+
"timeout": 5
|
|
206
|
+
}
|
|
207
|
+
]
|
|
208
|
+
}
|
|
209
|
+
],
|
|
196
210
|
"UserPromptSubmit": [
|
|
197
211
|
{
|
|
198
212
|
"hooks": [
|
|
@@ -214,6 +228,18 @@ For Codex, add the same shape to `~/.codex/hooks.json` and set
|
|
|
214
228
|
```json
|
|
215
229
|
{
|
|
216
230
|
"hooks": {
|
|
231
|
+
"SessionStart": [
|
|
232
|
+
{
|
|
233
|
+
"matcher": "startup|resume",
|
|
234
|
+
"hooks": [
|
|
235
|
+
{
|
|
236
|
+
"type": "command",
|
|
237
|
+
"command": "FEYNMAN_HOME=\"$HOME/.codex\" node \"/absolute/path/to/feynman/hooks/feynman-session-start.js\"",
|
|
238
|
+
"timeout": 5
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
}
|
|
242
|
+
],
|
|
217
243
|
"UserPromptSubmit": [
|
|
218
244
|
{
|
|
219
245
|
"hooks": [
|
|
@@ -234,6 +260,37 @@ After install, feynman starts in `full` mode by default. Disable or change it
|
|
|
234
260
|
explicitly with `/feynman off`, `/feynman lite`, `/feynman full`, or
|
|
235
261
|
`/feynman ultra`.
|
|
236
262
|
|
|
263
|
+
## Verify the install
|
|
264
|
+
|
|
265
|
+
Run `doctor` after installing or after manually editing hooks:
|
|
266
|
+
|
|
267
|
+
```bash
|
|
268
|
+
npx @albinocrabs/feynman doctor --target codex
|
|
269
|
+
npx @albinocrabs/feynman doctor --target claude
|
|
270
|
+
npx @albinocrabs/feynman doctor --target all
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
A healthy target reports both hook events and both hook script files as `OK`:
|
|
274
|
+
|
|
275
|
+
```text
|
|
276
|
+
[OK] hook registered (feynman-session-start.js in SessionStart)
|
|
277
|
+
[OK] hook registered (feynman-activate.js in UserPromptSubmit)
|
|
278
|
+
[OK] session hook script file exists and is readable
|
|
279
|
+
[OK] prompt hook script file exists and is readable
|
|
280
|
+
Status: OK
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
For Codex, runtime state should live under `~/.codex`:
|
|
284
|
+
|
|
285
|
+
```bash
|
|
286
|
+
cat ~/.codex/.feynman/state.json
|
|
287
|
+
test -f ~/.codex/.feynman-active
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
For Claude Code, use `~/.claude` instead of `~/.codex`. `doctor` fails if a
|
|
291
|
+
registered hook command cannot be parsed to a real hook script, which catches
|
|
292
|
+
stale or broken manual installs.
|
|
293
|
+
|
|
237
294
|
## Intensity Levels
|
|
238
295
|
|
|
239
296
|
| Level | What draws | Use when |
|
|
@@ -278,6 +335,26 @@ This bypasses `~/.codex/hooks.json` hook execution entirely.
|
|
|
278
335
|
Regular `/feynman off` and `/feynman on` continue to use normal profile state
|
|
279
336
|
files (`~/.codex/.feynman-active`, `~/.codex/.feynman/state.json`).
|
|
280
337
|
|
|
338
|
+
## Security notes
|
|
339
|
+
|
|
340
|
+
feynman hooks are local prompt-context hooks. They do not require network
|
|
341
|
+
access, do not read repository files, and do not read credentials. The active
|
|
342
|
+
mode is stored only in the client-local state path:
|
|
343
|
+
|
|
344
|
+
```text
|
|
345
|
+
~/.claude/.feynman/state.json
|
|
346
|
+
~/.codex/.feynman/state.json
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
The hook runtime treats invalid state as disabled for that turn, removes the
|
|
350
|
+
activation flag, and stays silent. That prevents a corrupted state file from
|
|
351
|
+
silently forcing diagram rules into future prompts.
|
|
352
|
+
|
|
353
|
+
`uninstall` removes only feynman hook commands and preserves unrelated hooks in
|
|
354
|
+
the same hook group. `doctor` validates that registered commands point to real
|
|
355
|
+
hook scripts, so broken manual commands are visible before a session depends on
|
|
356
|
+
them.
|
|
357
|
+
|
|
281
358
|
## CLI examples
|
|
282
359
|
|
|
283
360
|
Quickly discover and view repository prompt templates:
|
|
@@ -492,15 +569,42 @@ the rules for the active intensity level, and inject them into model context.
|
|
|
492
569
|
```
|
|
493
570
|
[your prompt]
|
|
494
571
|
+
|
|
495
|
-
[feynman rules]
|
|
572
|
+
[feynman rules] injected by hook
|
|
496
573
|
│
|
|
497
574
|
▼
|
|
498
|
-
|
|
575
|
+
[Claude Code]
|
|
576
|
+
or
|
|
577
|
+
[Codex]
|
|
499
578
|
│
|
|
500
579
|
▼
|
|
501
580
|
[structured response with ASCII diagrams]
|
|
502
581
|
```
|
|
503
582
|
|
|
583
|
+
## Token budget and output size
|
|
584
|
+
|
|
585
|
+
feynman always adds some input context when it is enabled, because the active
|
|
586
|
+
diagram rules are injected into the client prompt context. It does not add
|
|
587
|
+
visible output by itself; output changes only when the assistant uses the
|
|
588
|
+
rules.
|
|
589
|
+
|
|
590
|
+
Current rule payload sizes are approximately:
|
|
591
|
+
|
|
592
|
+
| Mode | Bytes | Approx tokens | Use when |
|
|
593
|
+
| ---- | ----- | ------------- | -------- |
|
|
594
|
+
| `lite` | 1307 | 317 | minimal flows and trees |
|
|
595
|
+
| `full` | 2180 | 532 | default diagram coverage |
|
|
596
|
+
| `ultra` | 1390 | 337 | force diagrams more often |
|
|
597
|
+
|
|
598
|
+
The token count is a rough `chars / 4` estimate, not a billing counter. The
|
|
599
|
+
actual number depends on the runtime tokenizer and surrounding hook envelope.
|
|
600
|
+
|
|
601
|
+
The plugin can reduce output size when a diagram replaces repeated prose,
|
|
602
|
+
especially for flows, status summaries, hierarchies, and comparisons. It can
|
|
603
|
+
increase output for short answers where a diagram would be unnecessary, so the
|
|
604
|
+
rules explicitly suppress diagrams for one- or two-sentence direct answers.
|
|
605
|
+
Use `/feynman lite` for lower overhead or `/feynman off` when token budget is
|
|
606
|
+
more important than visual structure.
|
|
607
|
+
|
|
504
608
|
## Release process
|
|
505
609
|
|
|
506
610
|
Every push runs tests on Node 18 and 20 across Ubuntu and macOS. The release
|
package/SECURITY.md
CHANGED
|
@@ -39,4 +39,4 @@ Before publishing a new npm version:
|
|
|
39
39
|
- GitHub release tag must match `package.json` version with a `v` prefix.
|
|
40
40
|
- GitHub Actions secret `NPM_TOKEN` must be present for first publish of a new version.
|
|
41
41
|
- npm provenance is enabled in the release workflow.
|
|
42
|
-
- Registry smoke verification must pass after publish (`npm view`, install from npm, `feynman doctor --target
|
|
42
|
+
- Registry smoke verification must pass after publish (`npm view`, install from npm, `feynman doctor --target all`).
|
package/bin/feynman.js
CHANGED
|
@@ -109,7 +109,7 @@ ${c.bold('Options:')}
|
|
|
109
109
|
${c.bold('Examples:')}
|
|
110
110
|
npx @albinocrabs/feynman install
|
|
111
111
|
npx @albinocrabs/feynman install --target codex
|
|
112
|
-
npx @albinocrabs/feynman install --target
|
|
112
|
+
npx @albinocrabs/feynman install --target all
|
|
113
113
|
npx @albinocrabs/feynman install --target all
|
|
114
114
|
npx @albinocrabs/feynman doctor
|
|
115
115
|
feynman lint response.md
|
|
@@ -507,6 +507,14 @@ function writeSettings(target, settings) {
|
|
|
507
507
|
fs.writeFileSync(cfg.settingsPath, JSON.stringify(settings, null, 2) + '\n');
|
|
508
508
|
}
|
|
509
509
|
|
|
510
|
+
function isFeynmanHookCommand(command) {
|
|
511
|
+
return typeof command === 'string' && (
|
|
512
|
+
command.includes('feynman-session-start.js') ||
|
|
513
|
+
command.includes('feynman-activate.js') ||
|
|
514
|
+
command.includes('feynman-lint.js')
|
|
515
|
+
);
|
|
516
|
+
}
|
|
517
|
+
|
|
510
518
|
function hasFeynmanHook(settings) {
|
|
511
519
|
const promptHook = ((settings.hooks && settings.hooks.UserPromptSubmit) || []).some(g =>
|
|
512
520
|
g.hooks && g.hooks.some(h => h.command && h.command.includes('feynman-activate.js'))
|
|
@@ -517,19 +525,40 @@ function hasFeynmanHook(settings) {
|
|
|
517
525
|
return promptHook && sessionHook;
|
|
518
526
|
}
|
|
519
527
|
|
|
528
|
+
function hasAnyFeynmanHook(settings) {
|
|
529
|
+
if (!settings.hooks) return false;
|
|
530
|
+
return ['SessionStart', 'UserPromptSubmit', 'Stop'].some(eventName =>
|
|
531
|
+
((settings.hooks && settings.hooks[eventName]) || []).some(g =>
|
|
532
|
+
g.hooks && g.hooks.some(h => isFeynmanHookCommand(h.command))
|
|
533
|
+
)
|
|
534
|
+
);
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
function extractHookScriptPath(command, scriptName) {
|
|
538
|
+
if (typeof command !== 'string') return null;
|
|
539
|
+
const escaped = scriptName.replace(/\./g, '\\.');
|
|
540
|
+
const quotedPattern = new RegExp("[\"']([^\"']*" + escaped + ")[\"']");
|
|
541
|
+
const quoted = command.match(quotedPattern);
|
|
542
|
+
if (quoted) return quoted[1];
|
|
543
|
+
|
|
544
|
+
const unquotedPattern = new RegExp("(?:^|\\s)(/[^\\s\"';&|<>]*" + escaped + ")(?=$|\\s)");
|
|
545
|
+
const unquoted = command.match(unquotedPattern);
|
|
546
|
+
return unquoted ? unquoted[1] : null;
|
|
547
|
+
}
|
|
548
|
+
|
|
520
549
|
function removeFeynmanHooks(settings) {
|
|
521
550
|
if (!settings.hooks) return settings;
|
|
522
551
|
for (const eventName of ['SessionStart', 'UserPromptSubmit', 'Stop']) {
|
|
523
552
|
if (!Array.isArray(settings.hooks[eventName])) continue;
|
|
524
|
-
settings.hooks[eventName] = settings.hooks[eventName]
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
)
|
|
532
|
-
|
|
553
|
+
settings.hooks[eventName] = settings.hooks[eventName]
|
|
554
|
+
.map(g => {
|
|
555
|
+
if (!Array.isArray(g.hooks)) return g;
|
|
556
|
+
return {
|
|
557
|
+
...g,
|
|
558
|
+
hooks: g.hooks.filter(h => !isFeynmanHookCommand(h.command)),
|
|
559
|
+
};
|
|
560
|
+
})
|
|
561
|
+
.filter(g => !Array.isArray(g.hooks) || g.hooks.length > 0);
|
|
533
562
|
if (settings.hooks[eventName].length === 0) {
|
|
534
563
|
delete settings.hooks[eventName];
|
|
535
564
|
}
|
|
@@ -675,7 +704,7 @@ function uninstallOne(target) {
|
|
|
675
704
|
}
|
|
676
705
|
|
|
677
706
|
const cfg = readSettings(target);
|
|
678
|
-
const hadHook =
|
|
707
|
+
const hadHook = hasAnyFeynmanHook(cfg);
|
|
679
708
|
removeFeynmanHooks(cfg);
|
|
680
709
|
writeSettings(target, cfg);
|
|
681
710
|
|
|
@@ -735,8 +764,7 @@ function cmdDoctor(opts = {}) {
|
|
|
735
764
|
sessionHookRegistered = !!feynmanSessionEntry;
|
|
736
765
|
if (feynmanSessionEntry) {
|
|
737
766
|
const hookCmd = feynmanSessionEntry.hooks.find(h => h.command && h.command.includes('feynman-session-start.js')).command;
|
|
738
|
-
|
|
739
|
-
if (match) sessionHookAbsPath = match[1];
|
|
767
|
+
sessionHookAbsPath = extractHookScriptPath(hookCmd, 'feynman-session-start.js');
|
|
740
768
|
}
|
|
741
769
|
|
|
742
770
|
const entries = (cfg.hooks && cfg.hooks.UserPromptSubmit) || [];
|
|
@@ -745,10 +773,8 @@ function cmdDoctor(opts = {}) {
|
|
|
745
773
|
);
|
|
746
774
|
hookRegistered = !!feynmanEntry;
|
|
747
775
|
if (feynmanEntry) {
|
|
748
|
-
// Extract the path from command: node "/abs/path/to/feynman-activate.js"
|
|
749
776
|
const hookCmd = feynmanEntry.hooks.find(h => h.command && h.command.includes('feynman-activate.js')).command;
|
|
750
|
-
|
|
751
|
-
if (match) hookAbsPath = match[1];
|
|
777
|
+
hookAbsPath = extractHookScriptPath(hookCmd, 'feynman-activate.js');
|
|
752
778
|
}
|
|
753
779
|
}
|
|
754
780
|
check('hook registered (feynman-session-start.js in SessionStart)', sessionHookRegistered);
|
|
@@ -761,11 +787,6 @@ function cmdDoctor(opts = {}) {
|
|
|
761
787
|
fs.accessSync(sessionHookAbsPath, fs.constants.R_OK);
|
|
762
788
|
sessionHookFileOk = true;
|
|
763
789
|
} catch (_) {}
|
|
764
|
-
} else if (sessionHookRegistered) {
|
|
765
|
-
try {
|
|
766
|
-
fs.accessSync(SESSION_HOOK_PATH, fs.constants.R_OK);
|
|
767
|
-
sessionHookFileOk = true;
|
|
768
|
-
} catch (_) {}
|
|
769
790
|
}
|
|
770
791
|
check('session hook script file exists and is readable', sessionHookFileOk);
|
|
771
792
|
|
|
@@ -775,12 +796,6 @@ function cmdDoctor(opts = {}) {
|
|
|
775
796
|
fs.accessSync(hookAbsPath, fs.constants.R_OK);
|
|
776
797
|
hookFileOk = true;
|
|
777
798
|
} catch (_) {}
|
|
778
|
-
} else if (hookRegistered) {
|
|
779
|
-
// Hook registered but path extraction failed — check default location
|
|
780
|
-
try {
|
|
781
|
-
fs.accessSync(HOOK_PATH, fs.constants.R_OK);
|
|
782
|
-
hookFileOk = true;
|
|
783
|
-
} catch (_) {}
|
|
784
799
|
}
|
|
785
800
|
check('prompt hook script file exists and is readable', hookFileOk);
|
|
786
801
|
|
|
@@ -17,14 +17,6 @@ const RULES_PATH = path.join(__dirname, '..', 'rules', 'feynman-activate.md');
|
|
|
17
17
|
const DEFAULT_STATE = { enabled: true, intensity: 'full', injections: 0 };
|
|
18
18
|
const VALID_INTENSITIES = ['lite', 'full', 'ultra'];
|
|
19
19
|
|
|
20
|
-
function readState() {
|
|
21
|
-
try {
|
|
22
|
-
return { ...DEFAULT_STATE, ...JSON.parse(fs.readFileSync(STATE_PATH, 'utf8')) };
|
|
23
|
-
} catch (_) {
|
|
24
|
-
return { ...DEFAULT_STATE };
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
20
|
function writeState(state) {
|
|
29
21
|
fs.mkdirSync(FEYNMAN_DIR, { recursive: true });
|
|
30
22
|
fs.writeFileSync(STATE_PATH, JSON.stringify(state, null, 2));
|
|
@@ -53,9 +45,16 @@ process.stdin.on('end', () => {
|
|
|
53
45
|
|
|
54
46
|
const stateExists = fs.existsSync(STATE_PATH);
|
|
55
47
|
const flagExists = fs.existsSync(FLAG_PATH);
|
|
56
|
-
|
|
48
|
+
let state = { ...DEFAULT_STATE };
|
|
57
49
|
|
|
58
|
-
if (
|
|
50
|
+
if (stateExists) {
|
|
51
|
+
try {
|
|
52
|
+
state = { ...DEFAULT_STATE, ...JSON.parse(fs.readFileSync(STATE_PATH, 'utf8')) };
|
|
53
|
+
} catch (_) {
|
|
54
|
+
try { fs.unlinkSync(FLAG_PATH); } catch (_) {}
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
59
58
|
writeState(state);
|
|
60
59
|
}
|
|
61
60
|
|
package/package.json
CHANGED
|
@@ -111,6 +111,168 @@ Syntax:
|
|
|
111
111
|
item-3
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
+
### Terminal-safe rendering
|
|
115
|
+
|
|
116
|
+
Tables are allowed when they stay readable in terminal chat. Wide Markdown
|
|
117
|
+
tables are a rendering defect: convert them into a cleaner terminal layout
|
|
118
|
+
instead of letting columns wrap unpredictably.
|
|
119
|
+
|
|
120
|
+
Choose the layout by readability:
|
|
121
|
+
|
|
122
|
+
```text
|
|
123
|
+
short matrix -> Markdown table is OK
|
|
124
|
+
status maps -> ASCII frame blocks
|
|
125
|
+
long rows -> key-value bullets
|
|
126
|
+
hierarchy -> ASCII tree
|
|
127
|
+
sequence -> arrow flow
|
|
128
|
+
comparison -> max 3 columns, max 10 words per cell
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
Frame discipline:
|
|
132
|
+
|
|
133
|
+
```text
|
|
134
|
+
+---- Status ----+
|
|
135
|
+
| item-a | done |
|
|
136
|
+
| item-b | risk |
|
|
137
|
+
+----------------+
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
- Keep frame rows visually aligned.
|
|
141
|
+
- Keep diagram lines under about 88 columns when possible.
|
|
142
|
+
- If a row becomes long, split it into bullets or grouped frames instead of
|
|
143
|
+
widening the table.
|
|
144
|
+
- Optimize for human scanning: aligned labels, short cells, stable columns,
|
|
145
|
+
and no wrapped table rows.
|
|
146
|
+
- Use plain ASCII frames with `+`, `-`, and `|` when terminal compatibility
|
|
147
|
+
matters.
|
|
148
|
+
|
|
149
|
+
### SDLC output patterns
|
|
150
|
+
|
|
151
|
+
For engineering status, retrospectives, handoffs, reviews, and release notes,
|
|
152
|
+
choose a human-scannable shape before writing prose. Prefer compact blocks with
|
|
153
|
+
explicit evidence over long narrative.
|
|
154
|
+
|
|
155
|
+
Use these shapes:
|
|
156
|
+
|
|
157
|
+
```text
|
|
158
|
+
status -> frame with state, branch, commit, checks, blocker
|
|
159
|
+
retro -> DONE / WORKED / FRAGILE / LEFT
|
|
160
|
+
handoff -> NOW / NEXT / FILES / COMMANDS / RISK
|
|
161
|
+
review -> FINDINGS first, then QUESTIONS, then SUMMARY
|
|
162
|
+
incident -> IMPACT / CAUSE / FIX / PREVENTION
|
|
163
|
+
release -> CHANGED / VERIFIED / RISK / ROLLBACK
|
|
164
|
+
decision -> CONTEXT / OPTIONS / CHOICE / CONSEQUENCE
|
|
165
|
+
verification -> command -> result -> evidence -> gap
|
|
166
|
+
roadmap -> NOW / NEXT / LATER / BLOCKED
|
|
167
|
+
phase -> GOAL / SCOPE / PLAN / VERIFY / EXIT
|
|
168
|
+
UAT -> SCENARIO / EXPECTED / ACTUAL / RESULT
|
|
169
|
+
risk register -> RISK / IMPACT / MITIGATION / OWNER
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Status block pattern:
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
+---- Status ----+
|
|
176
|
+
| repo | name |
|
|
177
|
+
| branch | main |
|
|
178
|
+
| commit | abc12 |
|
|
179
|
+
| checks | PASS |
|
|
180
|
+
| blocker | none |
|
|
181
|
+
+----------------+
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
Retro pattern:
|
|
185
|
+
|
|
186
|
+
```text
|
|
187
|
+
DONE:
|
|
188
|
+
- landed change with evidence
|
|
189
|
+
|
|
190
|
+
WORKED:
|
|
191
|
+
- useful command or decision
|
|
192
|
+
|
|
193
|
+
FRAGILE:
|
|
194
|
+
- risk or assumption
|
|
195
|
+
|
|
196
|
+
LEFT:
|
|
197
|
+
- next executable action
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
Roadmap pattern:
|
|
201
|
+
|
|
202
|
+
```text
|
|
203
|
+
NOW:
|
|
204
|
+
- current milestone or active phase
|
|
205
|
+
|
|
206
|
+
NEXT:
|
|
207
|
+
- next executable phase
|
|
208
|
+
|
|
209
|
+
LATER:
|
|
210
|
+
- deferred work
|
|
211
|
+
|
|
212
|
+
BLOCKED:
|
|
213
|
+
- dependency or decision needed
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
Phase pattern:
|
|
217
|
+
|
|
218
|
+
```text
|
|
219
|
+
GOAL:
|
|
220
|
+
- promised outcome
|
|
221
|
+
|
|
222
|
+
SCOPE:
|
|
223
|
+
- included / excluded boundaries
|
|
224
|
+
|
|
225
|
+
PLAN:
|
|
226
|
+
- implementation path
|
|
227
|
+
|
|
228
|
+
VERIFY:
|
|
229
|
+
- command, test, or review evidence
|
|
230
|
+
|
|
231
|
+
EXIT:
|
|
232
|
+
- condition for done
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
UAT pattern:
|
|
236
|
+
|
|
237
|
+
```text
|
|
238
|
+
SCENARIO:
|
|
239
|
+
- user action or workflow
|
|
240
|
+
|
|
241
|
+
EXPECTED:
|
|
242
|
+
- expected behavior
|
|
243
|
+
|
|
244
|
+
ACTUAL:
|
|
245
|
+
- observed behavior
|
|
246
|
+
|
|
247
|
+
RESULT:
|
|
248
|
+
- PASS / FAIL / BLOCKED
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
Risk register pattern:
|
|
252
|
+
|
|
253
|
+
```text
|
|
254
|
+
▲ high
|
|
255
|
+
RISK:
|
|
256
|
+
- what can go wrong
|
|
257
|
+
IMPACT:
|
|
258
|
+
- why it matters
|
|
259
|
+
MITIGATION:
|
|
260
|
+
- concrete control
|
|
261
|
+
OWNER:
|
|
262
|
+
- agent / human / system
|
|
263
|
+
▼ low
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
Rules:
|
|
267
|
+
|
|
268
|
+
- Put the answer first, evidence second, next action last.
|
|
269
|
+
- Never bury blockers in prose; give them their own label.
|
|
270
|
+
- For status and retro, avoid wide tables even when Markdown would be valid.
|
|
271
|
+
- Prefer `PASS`, `FAIL`, `BLOCKED`, `not run`, and exact command names.
|
|
272
|
+
- If verification was not run, say `not run` and name the command.
|
|
273
|
+
- For Russian chat, keep prose Russian and keep commands, paths, config keys,
|
|
274
|
+
commits, and status labels in English.
|
|
275
|
+
|
|
114
276
|
### When no diagram appears
|
|
115
277
|
|
|
116
278
|
Responses that are any of the following contain no diagram:
|
|
@@ -154,13 +316,74 @@ detail | detail
|
|
|
154
316
|
|
|
155
317
|
**Comparisons** — Same as full mode. Includes side-by-side ASCII columns.
|
|
156
318
|
|
|
157
|
-
**Status summaries** — Same as full mode. Uses
|
|
319
|
+
**Status summaries** — Same as full mode. Uses ASCII `+---` frame blocks.
|
|
158
320
|
|
|
159
321
|
**Priority orderings** — Same as full mode. Uses ▲▼ scale.
|
|
160
322
|
|
|
323
|
+
### Terminal-safe rendering
|
|
324
|
+
|
|
325
|
+
Tables are allowed when they are compact and readable. Wide Markdown tables are
|
|
326
|
+
a rendering defect in terminal chat. Prefer ASCII frames, key-value bullets,
|
|
327
|
+
trees, and short columns for long content. Keep diagram lines under about 88
|
|
328
|
+
columns when possible. If content is long, split it into bullets or grouped
|
|
329
|
+
frames instead of widening the visual block.
|
|
330
|
+
|
|
331
|
+
### SDLC output patterns
|
|
332
|
+
|
|
333
|
+
All full-mode SDLC patterns apply. In ultra mode, use them aggressively for any
|
|
334
|
+
engineering status, retrospective, review, release, handoff, decision, or
|
|
335
|
+
verification answer. The default shape is:
|
|
336
|
+
|
|
337
|
+
```text
|
|
338
|
+
[state] --> [evidence] --> [risk] --> [next]
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
For retrospectives, prefer:
|
|
342
|
+
|
|
343
|
+
```text
|
|
344
|
+
DONE / WORKED / FRAGILE / LEFT
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
For status answers, prefer:
|
|
348
|
+
|
|
349
|
+
```text
|
|
350
|
+
+---- Status ----+
|
|
351
|
+
| item | state |
|
|
352
|
+
| checks | PASS |
|
|
353
|
+
| blocker | none |
|
|
354
|
+
+----------------+
|
|
355
|
+
```
|
|
356
|
+
|
|
161
357
|
### When no diagram appears
|
|
162
358
|
|
|
163
359
|
The only response that contains no diagram is a single sentence of pure prose with no enumerable items, no steps, no comparisons, and no structure of any kind.
|
|
164
360
|
|
|
165
361
|
All other responses — including those with two or more items, any named concept with sub-parts, any sequence of actions, any set of options — include an ASCII diagram.
|
|
166
362
|
<!-- /ultra -->
|
|
363
|
+
|
|
364
|
+
### Russian terminal chat visual guardrail
|
|
365
|
+
|
|
366
|
+
For Russian terminal chat, avoid using ASCII frames as two-column tables when
|
|
367
|
+
values contain long Cyrillic or mixed Cyrillic/Latin text. Visual alignment can
|
|
368
|
+
look broken even when character counts are correct.
|
|
369
|
+
|
|
370
|
+
Prefer bullets for long Russian status facts:
|
|
371
|
+
|
|
372
|
+
Итог:
|
|
373
|
+
- Что сделали: dtp-retro стал русским по умолчанию.
|
|
374
|
+
- История: текущая session ищется автоматически.
|
|
375
|
+
- Форма: сначала смысл, потом proof/details.
|
|
376
|
+
- Статус: правки есть, commit ещё не сделан.
|
|
377
|
+
|
|
378
|
+
Avoid frame rows like this for long Russian text:
|
|
379
|
+
|
|
380
|
+
| что сделали | длинная русская строка ... |
|
|
381
|
+
| история | длинная смешанная строка ... |
|
|
382
|
+
|
|
383
|
+
Rule:
|
|
384
|
+
|
|
385
|
+
- frame with short values -> OK
|
|
386
|
+
- long Russian value -> bullets
|
|
387
|
+
- sequence -> arrows
|
|
388
|
+
- comparison -> max 3 short columns
|
|
389
|
+
- status with long text -> label bullets, not frame rows
|
package/skills/feynman/SKILL.md
CHANGED
|
@@ -26,11 +26,21 @@ Parse `$ARGUMENTS`:
|
|
|
26
26
|
```bash
|
|
27
27
|
node -e "
|
|
28
28
|
const fs = require('fs'), os = require('os'), path = require('path');
|
|
29
|
-
|
|
30
|
-
|
|
29
|
+
function clientHome() {
|
|
30
|
+
if (process.env.FEYNMAN_HOME) return process.env.FEYNMAN_HOME;
|
|
31
|
+
if (process.env.FEYNMAN_TARGET === 'codex') return path.join(os.homedir(), '.codex');
|
|
32
|
+
if (process.env.FEYNMAN_TARGET === 'claude') return path.join(os.homedir(), '.claude');
|
|
33
|
+
if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
|
|
34
|
+
if (process.env.CODEX_THREAD_ID || process.env.CODEX_SANDBOX) return path.join(os.homedir(), '.codex');
|
|
35
|
+
if (process.env.CLAUDE_CONFIG_DIR) return process.env.CLAUDE_CONFIG_DIR;
|
|
36
|
+
return path.join(os.homedir(), '.claude');
|
|
37
|
+
}
|
|
38
|
+
const root = clientHome();
|
|
39
|
+
const stateFile = path.join(root, '.feynman', 'state.json');
|
|
40
|
+
const flagFile = path.join(root, '.feynman-active');
|
|
31
41
|
let st = {enabled: true, intensity: 'full', injections: 0};
|
|
32
42
|
try { st = JSON.parse(fs.readFileSync(stateFile, 'utf8')); } catch(e) {}
|
|
33
|
-
console.log('enabled:', st.enabled, '| intensity:', st.intensity, '| injections:', (st.injections ?? st.count ?? 0), '| flag:', fs.existsSync(flagFile));
|
|
43
|
+
console.log('target:', path.basename(root), '| enabled:', st.enabled, '| intensity:', st.intensity, '| injections:', (st.injections ?? st.count ?? 0), '| flag:', fs.existsSync(flagFile));
|
|
34
44
|
"
|
|
35
45
|
```
|
|
36
46
|
|
|
@@ -39,18 +49,29 @@ console.log('enabled:', st.enabled, '| intensity:', st.intensity, '| injections:
|
|
|
39
49
|
```bash
|
|
40
50
|
node -e "
|
|
41
51
|
const fs = require('fs'), os = require('os'), path = require('path');
|
|
42
|
-
|
|
43
|
-
|
|
52
|
+
function clientHome() {
|
|
53
|
+
if (process.env.FEYNMAN_HOME) return process.env.FEYNMAN_HOME;
|
|
54
|
+
if (process.env.FEYNMAN_TARGET === 'codex') return path.join(os.homedir(), '.codex');
|
|
55
|
+
if (process.env.FEYNMAN_TARGET === 'claude') return path.join(os.homedir(), '.claude');
|
|
56
|
+
if (process.env.CODEX_HOME) return process.env.CODEX_HOME;
|
|
57
|
+
if (process.env.CODEX_THREAD_ID || process.env.CODEX_SANDBOX) return path.join(os.homedir(), '.codex');
|
|
58
|
+
if (process.env.CLAUDE_CONFIG_DIR) return process.env.CLAUDE_CONFIG_DIR;
|
|
59
|
+
return path.join(os.homedir(), '.claude');
|
|
60
|
+
}
|
|
61
|
+
const root = clientHome();
|
|
62
|
+
const stateFile = path.join(root, '.feynman', 'state.json');
|
|
63
|
+
const flagFile = path.join(root, '.feynman-active');
|
|
44
64
|
const arg = (process.argv[1] || '').trim().toLowerCase();
|
|
45
65
|
const normalized = arg === 'start' ? 'on' : arg === 'stop' ? 'off' : arg;
|
|
46
66
|
let st = {enabled: false, intensity: 'full', injections: 0};
|
|
47
67
|
try { st = JSON.parse(fs.readFileSync(stateFile, 'utf8')); } catch(e) {}
|
|
48
|
-
|
|
68
|
+
function writeFlag(value) { fs.mkdirSync(root, {recursive: true}); fs.writeFileSync(flagFile, value || 'full'); }
|
|
69
|
+
if (normalized === 'on') { st.enabled = true; writeFlag(st.intensity); }
|
|
49
70
|
if (normalized === 'off') { st.enabled = false; try { fs.unlinkSync(flagFile); } catch(e) {} }
|
|
50
|
-
if (['lite','full','ultra'].includes(normalized)) { st.intensity = normalized; st.enabled = true;
|
|
71
|
+
if (['lite','full','ultra'].includes(normalized)) { st.intensity = normalized; st.enabled = true; writeFlag(normalized); }
|
|
51
72
|
fs.mkdirSync(path.dirname(stateFile), {recursive: true});
|
|
52
73
|
fs.writeFileSync(stateFile, JSON.stringify(st, null, 2));
|
|
53
|
-
console.log(JSON.stringify(st));
|
|
74
|
+
console.log(JSON.stringify({target: path.basename(root), ...st}));
|
|
54
75
|
" "$ARGUMENTS"
|
|
55
76
|
```
|
|
56
77
|
|