toga-ai 1.0.803 → 1.0.804

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.
@@ -70,6 +70,16 @@
70
70
  "timeout": 3000
71
71
  }
72
72
  ]
73
+ },
74
+ {
75
+ "matcher": "Edit|Write|MultiEdit",
76
+ "hooks": [
77
+ {
78
+ "type": "command",
79
+ "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/toga/design-fidelity-reminder.js\"",
80
+ "timeout": 3000
81
+ }
82
+ ]
73
83
  }
74
84
  ],
75
85
  "PostToolUse": [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toga-ai",
3
- "version": "1.0.803",
3
+ "version": "1.0.804",
4
4
  "description": "TOGA Technology Team Claude Knowledge System — shared AI coding harness with skills, knowledge base CLI, and project installer for Claude Code.",
5
5
  "keywords": [
6
6
  "claude",
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /*
5
+ * design-fidelity-reminder.js — PRE-EDIT enforcement of the Design Contract discipline.
6
+ *
7
+ * Why this exists: on Desk (and the other 2.0 React apps) the model keeps improvising
8
+ * UI — inventing button text, helper lines, field order and labels instead of matching
9
+ * the designer's on-disk design. That drift ships UIs that "function" but do not match
10
+ * what Matt designed, and the developer has to catch it every time. A rule in a file is
11
+ * passive; over a long session the model slides back to improvising. This is the
12
+ * mechanical backstop, modeled on plain-talk.js.
13
+ *
14
+ * How it works: registered on PreToolUse for Edit|Write|MultiEdit. Just before the model
15
+ * edits a UI source file — a path under a `src/` tree ending .tsx / .jsx / .css — it prints
16
+ * a short <system-reminder> to stdout with the Design Contract rules, so the discipline is
17
+ * in context at the moment the UI is written. On any non-UI file it stays silent.
18
+ *
19
+ * It never blocks. It always exits 0 — a reminder must never stop or deny an edit. It
20
+ * fails open on any error: a crashing nudge must never brick a session. Per team hook
21
+ * convention DENY/error text goes to STDERR — this hook never denies, so it never uses it.
22
+ *
23
+ * Escape hatch: set TOGA_DESIGN_FIDELITY_DISABLED=1 to silence the reminder.
24
+ */
25
+
26
+ const fs = require('fs');
27
+
28
+ /* Read the CC hook payload. PreToolUse delivers the tool input on stdin as
29
+ * { tool_name, tool_input: { file_path, ... } }; fall back to the CLAUDE_TOOL_INPUT env
30
+ * (what the sibling PostToolUse reminders read) and to a top-level file_path. */
31
+ function readFilePath() {
32
+ let raw = '';
33
+ try { raw = fs.readFileSync(0, 'utf8'); } catch (e) { /* no stdin */ }
34
+
35
+ let data = {};
36
+ if (raw && raw.trim()) {
37
+ try { data = JSON.parse(raw); } catch (e) { data = {}; }
38
+ }
39
+
40
+ let fp =
41
+ (data.tool_input && typeof data.tool_input.file_path === 'string' && data.tool_input.file_path) ||
42
+ (typeof data.file_path === 'string' && data.file_path) ||
43
+ '';
44
+
45
+ if (!fp && process.env.CLAUDE_TOOL_INPUT) {
46
+ try {
47
+ const env = JSON.parse(process.env.CLAUDE_TOOL_INPUT);
48
+ if (env && typeof env.file_path === 'string') fp = env.file_path;
49
+ } catch (e) { /* ignore */ }
50
+ }
51
+ return fp;
52
+ }
53
+
54
+ /* A Desk / React-app UI source file: under a `src/` tree AND ending .tsx / .jsx / .css. */
55
+ function isUiSourceFile(filePath) {
56
+ if (!filePath) return false;
57
+ const p = filePath.replace(/\\/g, '/').toLowerCase();
58
+ return /\/src\//.test(p) && /\.(tsx|jsx|css)$/.test(p);
59
+ }
60
+
61
+ function reminder() {
62
+ return [
63
+ '<system-reminder>',
64
+ 'DESIGN CONTRACT — this edit touches a UI source file. Match the designer\'s design,',
65
+ 'do not improvise UI. Reusing a shell or making it "function" is NOT fidelity.',
66
+ '',
67
+ ' 1. The on-disk design is the authority. Open the exact design source file FIRST and',
68
+ ' build from it — match its LAYOUT/placement, not just pixels.',
69
+ ' 2. Build to a written Design Contract: every field + its order, each label/placeholder,',
70
+ ' required flags, button text, modal size/layout, and the header/footer.',
71
+ ' 3. Invent NOTHING user-visible — button text, helper/info lines, labels, required flags.',
72
+ ' Copy comes from the design + Surface/settings. If it is not in the design, ASK.',
73
+ ' 4. Design-vs-data conflict → STOP and ask. Do not decide it yourself.',
74
+ ' 5. No self-sign-off. Hand the developer a labeled diff (mine vs the design). "It',
75
+ ' functions" is not fidelity.',
76
+ '</system-reminder>',
77
+ ].join('\n');
78
+ }
79
+
80
+ function main() {
81
+ if (process.env.TOGA_DESIGN_FIDELITY_DISABLED === '1') process.exit(0);
82
+
83
+ const filePath = readFilePath();
84
+
85
+ if (isUiSourceFile(filePath)) {
86
+ // Inject the reminder into the model's context via the PreToolUse additionalContext
87
+ // channel (raw stdout on PreToolUse only shows in transcript, not context). Non-blocking:
88
+ // additionalContext adds context, it does not deny the edit.
89
+ console.log(JSON.stringify({
90
+ hookSpecificOutput: {
91
+ hookEventName: 'PreToolUse',
92
+ additionalContext: reminder(),
93
+ },
94
+ }));
95
+ }
96
+ process.exit(0);
97
+ }
98
+
99
+ try { main(); } catch (e) { process.exit(0); } // fail-open: never block or brick an edit