ctx-cc 3.5.0 → 4.0.0
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/README.md +34 -289
- package/agents/ctx-arch-mapper.md +5 -3
- package/agents/ctx-auditor.md +5 -3
- package/agents/ctx-concerns-mapper.md +5 -3
- package/agents/ctx-criteria-suggester.md +6 -4
- package/agents/ctx-debugger.md +5 -3
- package/agents/ctx-designer.md +488 -114
- package/agents/ctx-discusser.md +5 -3
- package/agents/ctx-executor.md +5 -3
- package/agents/ctx-handoff.md +6 -4
- package/agents/ctx-learner.md +5 -3
- package/agents/ctx-mapper.md +4 -3
- package/agents/ctx-ml-analyst.md +600 -0
- package/agents/ctx-ml-engineer.md +933 -0
- package/agents/ctx-ml-reviewer.md +485 -0
- package/agents/ctx-ml-scientist.md +626 -0
- package/agents/ctx-parallelizer.md +4 -3
- package/agents/ctx-planner.md +5 -3
- package/agents/ctx-predictor.md +4 -3
- package/agents/ctx-qa.md +5 -3
- package/agents/ctx-quality-mapper.md +5 -3
- package/agents/ctx-researcher.md +5 -3
- package/agents/ctx-reviewer.md +6 -4
- package/agents/ctx-team-coordinator.md +5 -3
- package/agents/ctx-tech-mapper.md +5 -3
- package/agents/ctx-verifier.md +5 -3
- package/bin/ctx.js +168 -27
- package/commands/brand.md +309 -0
- package/commands/design.md +304 -0
- package/commands/experiment.md +251 -0
- package/commands/help.md +57 -7
- package/commands/metrics.md +1 -1
- package/commands/milestone.md +1 -1
- package/commands/ml-status.md +197 -0
- package/commands/monitor.md +1 -1
- package/commands/train.md +266 -0
- package/commands/visual-qa.md +559 -0
- package/commands/voice.md +1 -1
- package/hooks/post-tool-use.js +39 -0
- package/hooks/pre-tool-use.js +93 -0
- package/hooks/subagent-stop.js +32 -0
- package/package.json +9 -3
- package/plugin.json +45 -0
- package/skills/ctx-design-system/SKILL.md +572 -0
- package/skills/ctx-ml-experiment/SKILL.md +334 -0
- package/skills/ctx-ml-pipeline/SKILL.md +437 -0
- package/skills/ctx-orchestrator/SKILL.md +91 -0
- package/skills/ctx-review-gate/SKILL.md +111 -0
- package/skills/ctx-state/SKILL.md +100 -0
- package/skills/ctx-visual-qa/SKILL.md +587 -0
- package/src/agents.js +109 -0
- package/src/auto.js +287 -0
- package/src/capabilities.js +171 -0
- package/src/commits.js +94 -0
- package/src/config.js +112 -0
- package/src/context.js +241 -0
- package/src/handoff.js +156 -0
- package/src/hooks.js +218 -0
- package/src/install.js +119 -51
- package/src/lifecycle.js +194 -0
- package/src/metrics.js +198 -0
- package/src/pipeline.js +269 -0
- package/src/review-gate.js +244 -0
- package/src/runner.js +120 -0
- package/src/skills.js +143 -0
- package/src/state.js +267 -0
- package/src/worktree.js +244 -0
- package/templates/PRD.json +1 -1
- package/templates/config.json +1 -237
- package/workflows/ctx-router.md +0 -485
- package/workflows/map-codebase.md +0 -329
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* CTX SubagentStop Hook
|
|
5
|
+
* Records agent completion in .ctx/STATE.json when a subagent finishes.
|
|
6
|
+
* Installed to .claude/hooks/ctx-subagent-stop.js
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import fs from 'fs';
|
|
10
|
+
import path from 'path';
|
|
11
|
+
|
|
12
|
+
const stateFile = path.join(process.cwd(), '.ctx', 'STATE.json');
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
if (!fs.existsSync(stateFile)) process.exit(0);
|
|
16
|
+
|
|
17
|
+
const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
|
|
18
|
+
const history = state.agentHistory || [];
|
|
19
|
+
|
|
20
|
+
// Mark last incomplete invocation as completed
|
|
21
|
+
for (let i = history.length - 1; i >= 0; i--) {
|
|
22
|
+
if (!history[i].completedAt) {
|
|
23
|
+
history[i].completedAt = new Date().toISOString();
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
state.session = { ...state.session, lastActivity: new Date().toISOString() };
|
|
29
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2) + '\n');
|
|
30
|
+
} catch {
|
|
31
|
+
// Non-fatal — don't block on state errors
|
|
32
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ctx-cc",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "CTX
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "CTX 4.0 — Intelligent workflow orchestration for Claude Code. 21 subagents, 3 skills, deterministic hooks. Phase-based lifecycle with autonomous execution.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude",
|
|
7
7
|
"claude-code",
|
|
@@ -29,10 +29,16 @@
|
|
|
29
29
|
"src/",
|
|
30
30
|
"commands/",
|
|
31
31
|
"agents/",
|
|
32
|
+
"skills/",
|
|
33
|
+
"hooks/",
|
|
32
34
|
"references/",
|
|
33
35
|
"templates/",
|
|
34
|
-
"
|
|
36
|
+
"plugin.json"
|
|
35
37
|
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"test": "node --test test/*.test.js",
|
|
40
|
+
"setup": "node bin/ctx.js"
|
|
41
|
+
},
|
|
36
42
|
"engines": {
|
|
37
43
|
"node": ">=18.0.0"
|
|
38
44
|
},
|
package/plugin.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ctx",
|
|
3
|
+
"version": "4.0.0",
|
|
4
|
+
"description": "CTX — Intelligent workflow orchestration for Claude Code. 21 specialized agents, phase-based lifecycle, two-stage review gate, autonomous execution.",
|
|
5
|
+
"author": "jufjuf",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://github.com/jufjuf/CTX",
|
|
8
|
+
"repository": "https://github.com/jufjuf/CTX",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"workflow",
|
|
11
|
+
"orchestration",
|
|
12
|
+
"multi-agent",
|
|
13
|
+
"development",
|
|
14
|
+
"productivity"
|
|
15
|
+
],
|
|
16
|
+
"components": {
|
|
17
|
+
"agents": "agents/",
|
|
18
|
+
"commands": "commands/",
|
|
19
|
+
"skills": "skills/",
|
|
20
|
+
"hooks": "hooks/"
|
|
21
|
+
},
|
|
22
|
+
"hooks": {
|
|
23
|
+
"SubagentStop": [
|
|
24
|
+
{
|
|
25
|
+
"command": "node .claude/hooks/ctx-subagent-stop.js"
|
|
26
|
+
}
|
|
27
|
+
],
|
|
28
|
+
"PreToolUse": [
|
|
29
|
+
{
|
|
30
|
+
"command": "node .claude/hooks/ctx-pre-tool-use.js"
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
"PostToolUse": [
|
|
34
|
+
{
|
|
35
|
+
"command": "node .claude/hooks/ctx-post-tool-use.js"
|
|
36
|
+
}
|
|
37
|
+
]
|
|
38
|
+
},
|
|
39
|
+
"settings": {
|
|
40
|
+
"reviewGate": true,
|
|
41
|
+
"tddMode": "off",
|
|
42
|
+
"maxReviewCycles": 3,
|
|
43
|
+
"maxAutoIterations": 5
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ctx-design-system
|
|
3
|
+
description: |
|
|
4
|
+
WHEN: Design system creation, design token management, brand kit updates, token export (CSS/SCSS/JS/Tailwind), theme switching, design system audit for unused/missing tokens.
|
|
5
|
+
WHEN NOT: Individual component design, general coding tasks, non-visual work, functional bug fixes, API work.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# CTX Design System — Token Management & Living Design System
|
|
9
|
+
|
|
10
|
+
You manage the design system as a living, versioned artifact. Tokens are the single source of truth for all visual decisions. Every color, dimension, duration, and typography value flows from here.
|
|
11
|
+
|
|
12
|
+
## Token Format: W3C DTCG 2025.10
|
|
13
|
+
|
|
14
|
+
All tokens follow the W3C Design Token Community Group specification. Every token entry uses:
|
|
15
|
+
|
|
16
|
+
```json
|
|
17
|
+
{
|
|
18
|
+
"token-name": {
|
|
19
|
+
"$type": "color",
|
|
20
|
+
"$value": "#1a1a2e",
|
|
21
|
+
"$description": "Purpose of this token in the system",
|
|
22
|
+
"$extensions": {
|
|
23
|
+
"ctx-design-system": {
|
|
24
|
+
"tier": "primitive",
|
|
25
|
+
"category": "brand",
|
|
26
|
+
"deprecated": false,
|
|
27
|
+
"figma-variable-id": "VariableID:123:456"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Valid $type Values
|
|
35
|
+
|
|
36
|
+
| Type | $value format | Example |
|
|
37
|
+
|------|--------------|---------|
|
|
38
|
+
| `color` | hex, oklch(), p3() | `"oklch(0.45 0.18 264)"` |
|
|
39
|
+
| `dimension` | px, rem, em | `"16px"` |
|
|
40
|
+
| `duration` | ms, s | `"200ms"` |
|
|
41
|
+
| `fontFamily` | array of strings | `["Inter", "system-ui", "sans-serif"]` |
|
|
42
|
+
| `fontWeight` | number | `500` |
|
|
43
|
+
| `fontSize` | px or rem | `"1rem"` |
|
|
44
|
+
| `lineHeight` | unitless or px | `"1.5"` |
|
|
45
|
+
| `letterSpacing` | em | `"0.02em"` |
|
|
46
|
+
| `shadow` | composite object | see Shadow section |
|
|
47
|
+
| `border` | composite object | see Border section |
|
|
48
|
+
| `typography` | composite object | see Typography section |
|
|
49
|
+
|
|
50
|
+
### Composite Token Types
|
|
51
|
+
|
|
52
|
+
**Typography composite:**
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"heading-xl": {
|
|
56
|
+
"$type": "typography",
|
|
57
|
+
"$value": {
|
|
58
|
+
"fontFamily": "{font.family.display}",
|
|
59
|
+
"fontSize": "{font.size.5xl}",
|
|
60
|
+
"fontWeight": "{font.weight.bold}",
|
|
61
|
+
"lineHeight": "{font.line-height.tight}",
|
|
62
|
+
"letterSpacing": "{font.tracking.tight}"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
**Shadow composite:**
|
|
69
|
+
```json
|
|
70
|
+
{
|
|
71
|
+
"elevation-md": {
|
|
72
|
+
"$type": "shadow",
|
|
73
|
+
"$value": [
|
|
74
|
+
{ "offsetX": "0", "offsetY": "4px", "blur": "6px", "spread": "-1px", "color": "oklch(0 0 0 / 0.10)" },
|
|
75
|
+
{ "offsetX": "0", "offsetY": "2px", "blur": "4px", "spread": "-2px", "color": "oklch(0 0 0 / 0.10)" }
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Border composite:**
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"subtle": {
|
|
85
|
+
"$type": "border",
|
|
86
|
+
"$value": {
|
|
87
|
+
"width": "{border.width.1}",
|
|
88
|
+
"style": "solid",
|
|
89
|
+
"color": "{color.border.default}"
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## Three-Tier Token Architecture
|
|
98
|
+
|
|
99
|
+
Tokens flow in one direction: primitive → semantic → component. Never reference a component token from a semantic token. Never reference semantic from primitive.
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
PRIMITIVE Raw values, no semantic meaning
|
|
103
|
+
blue-500, space-4, ms-200, weight-600
|
|
104
|
+
|
|
105
|
+
↓ aliases reference primitives
|
|
106
|
+
|
|
107
|
+
SEMANTIC Purposeful names, mode-aware
|
|
108
|
+
color-text-primary, space-content-gap, duration-transition
|
|
109
|
+
|
|
110
|
+
↓ aliases reference semantic tokens
|
|
111
|
+
|
|
112
|
+
COMPONENT Component-scoped tokens
|
|
113
|
+
button-background, input-border-focus, card-padding
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### When to Add a Token
|
|
117
|
+
|
|
118
|
+
- Add to **primitive** when you need a new raw value not yet in the scale.
|
|
119
|
+
- Add to **semantic** when a primitive is being used for a specific purpose across 2+ components.
|
|
120
|
+
- Add to **component** when a value is unique to a single component and unlikely to be shared.
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## File Organization
|
|
125
|
+
|
|
126
|
+
```
|
|
127
|
+
tokens/
|
|
128
|
+
├── primitive.tokens.json # Raw scale values — no aliases
|
|
129
|
+
├── semantic.tokens.json # Purpose aliases — references primitives
|
|
130
|
+
├── component.tokens.json # Component-scoped — references semantic
|
|
131
|
+
└── themes/
|
|
132
|
+
├── light.tokens.json # Light mode overrides (semantic values only)
|
|
133
|
+
└── dark.tokens.json # Dark mode overrides (semantic values only)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### tokens/primitive.tokens.json
|
|
137
|
+
|
|
138
|
+
Complete primitive scale. Groups: `color`, `dimension`, `font`, `duration`, `easing`.
|
|
139
|
+
|
|
140
|
+
```json
|
|
141
|
+
{
|
|
142
|
+
"$schema": "https://design-tokens.org/schema.json",
|
|
143
|
+
"color": {
|
|
144
|
+
"gray": {
|
|
145
|
+
"$type": "color",
|
|
146
|
+
"50": { "$value": "oklch(0.985 0.002 247)", "$description": "Near white" },
|
|
147
|
+
"100": { "$value": "oklch(0.965 0.004 247)" },
|
|
148
|
+
"200": { "$value": "oklch(0.922 0.008 247)" },
|
|
149
|
+
"300": { "$value": "oklch(0.865 0.014 247)" },
|
|
150
|
+
"400": { "$value": "oklch(0.780 0.022 247)" },
|
|
151
|
+
"500": { "$value": "oklch(0.680 0.028 247)" },
|
|
152
|
+
"600": { "$value": "oklch(0.560 0.030 247)" },
|
|
153
|
+
"700": { "$value": "oklch(0.440 0.028 247)" },
|
|
154
|
+
"800": { "$value": "oklch(0.320 0.022 247)" },
|
|
155
|
+
"900": { "$value": "oklch(0.200 0.014 247)" },
|
|
156
|
+
"950": { "$value": "oklch(0.130 0.010 247)", "$description": "Near black" }
|
|
157
|
+
},
|
|
158
|
+
"brand": {
|
|
159
|
+
"$type": "color",
|
|
160
|
+
"50": { "$value": "oklch(0.970 0.025 264)" },
|
|
161
|
+
"500": { "$value": "oklch(0.520 0.180 264)", "$description": "Primary brand hue" },
|
|
162
|
+
"600": { "$value": "oklch(0.450 0.185 264)" },
|
|
163
|
+
"700": { "$value": "oklch(0.380 0.175 264)" }
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"dimension": {
|
|
167
|
+
"space": {
|
|
168
|
+
"$type": "dimension",
|
|
169
|
+
"0": { "$value": "0px" },
|
|
170
|
+
"1": { "$value": "4px" },
|
|
171
|
+
"2": { "$value": "8px" },
|
|
172
|
+
"3": { "$value": "12px" },
|
|
173
|
+
"4": { "$value": "16px" },
|
|
174
|
+
"5": { "$value": "20px" },
|
|
175
|
+
"6": { "$value": "24px" },
|
|
176
|
+
"8": { "$value": "32px" },
|
|
177
|
+
"10": { "$value": "40px" },
|
|
178
|
+
"12": { "$value": "48px" },
|
|
179
|
+
"16": { "$value": "64px" },
|
|
180
|
+
"20": { "$value": "80px" },
|
|
181
|
+
"24": { "$value": "96px" }
|
|
182
|
+
},
|
|
183
|
+
"radius": {
|
|
184
|
+
"$type": "dimension",
|
|
185
|
+
"none": { "$value": "0px" },
|
|
186
|
+
"sm": { "$value": "4px" },
|
|
187
|
+
"md": { "$value": "8px" },
|
|
188
|
+
"lg": { "$value": "12px" },
|
|
189
|
+
"xl": { "$value": "16px" },
|
|
190
|
+
"full": { "$value": "9999px" }
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
"font": {
|
|
194
|
+
"size": {
|
|
195
|
+
"$type": "fontSize",
|
|
196
|
+
"xs": { "$value": "0.75rem" },
|
|
197
|
+
"sm": { "$value": "0.875rem" },
|
|
198
|
+
"base": { "$value": "1rem" },
|
|
199
|
+
"lg": { "$value": "1.125rem" },
|
|
200
|
+
"xl": { "$value": "1.25rem" },
|
|
201
|
+
"2xl": { "$value": "1.5rem" },
|
|
202
|
+
"3xl": { "$value": "1.875rem" },
|
|
203
|
+
"4xl": { "$value": "2.25rem" },
|
|
204
|
+
"5xl": { "$value": "3rem" }
|
|
205
|
+
},
|
|
206
|
+
"weight": {
|
|
207
|
+
"$type": "fontWeight",
|
|
208
|
+
"regular": { "$value": 400 },
|
|
209
|
+
"medium": { "$value": 500 },
|
|
210
|
+
"semibold":{ "$value": 600 },
|
|
211
|
+
"bold": { "$value": 700 }
|
|
212
|
+
},
|
|
213
|
+
"family": {
|
|
214
|
+
"sans": { "$type": "fontFamily", "$value": ["Inter", "system-ui", "sans-serif"] },
|
|
215
|
+
"display": { "$type": "fontFamily", "$value": ["Cal Sans", "Inter", "sans-serif"] },
|
|
216
|
+
"mono": { "$type": "fontFamily", "$value": ["JetBrains Mono", "Menlo", "monospace"] }
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
"duration": {
|
|
220
|
+
"$type": "duration",
|
|
221
|
+
"instant": { "$value": "0ms" },
|
|
222
|
+
"fast": { "$value": "100ms" },
|
|
223
|
+
"normal": { "$value": "200ms" },
|
|
224
|
+
"slow": { "$value": "300ms" },
|
|
225
|
+
"slower": { "$value": "500ms" }
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### tokens/semantic.tokens.json
|
|
231
|
+
|
|
232
|
+
Semantic tokens reference primitives using `{path.to.token}` syntax. Values are objects with `light` and `dark` keys when mode-aware.
|
|
233
|
+
|
|
234
|
+
```json
|
|
235
|
+
{
|
|
236
|
+
"$schema": "https://design-tokens.org/schema.json",
|
|
237
|
+
"color": {
|
|
238
|
+
"background": {
|
|
239
|
+
"page": { "$type": "color", "$value": { "light": "{color.gray.50}", "dark": "{color.gray.950}" }, "$description": "Root page background" },
|
|
240
|
+
"surface": { "$type": "color", "$value": { "light": "{color.gray.100}", "dark": "{color.gray.900}" }, "$description": "Card, panel surfaces" },
|
|
241
|
+
"overlay": { "$type": "color", "$value": { "light": "{color.gray.200}", "dark": "{color.gray.800}" }, "$description": "Overlay, popover background" }
|
|
242
|
+
},
|
|
243
|
+
"text": {
|
|
244
|
+
"primary": { "$type": "color", "$value": { "light": "{color.gray.900}", "dark": "{color.gray.50}" } },
|
|
245
|
+
"secondary": { "$type": "color", "$value": { "light": "{color.gray.600}", "dark": "{color.gray.400}" } },
|
|
246
|
+
"disabled": { "$type": "color", "$value": { "light": "{color.gray.400}", "dark": "{color.gray.600}" } },
|
|
247
|
+
"on-accent": { "$type": "color", "$value": { "light": "#ffffff", "dark": "#ffffff" }, "$description": "Text on accent backgrounds" }
|
|
248
|
+
},
|
|
249
|
+
"border": {
|
|
250
|
+
"default": { "$type": "color", "$value": { "light": "{color.gray.200}", "dark": "{color.gray.700}" } },
|
|
251
|
+
"strong": { "$type": "color", "$value": { "light": "{color.gray.400}", "dark": "{color.gray.500}" } }
|
|
252
|
+
},
|
|
253
|
+
"interactive": {
|
|
254
|
+
"default": { "$type": "color", "$value": { "light": "{color.brand.500}", "dark": "{color.brand.600}" }, "$description": "Primary interactive (buttons, links)" },
|
|
255
|
+
"hover": { "$type": "color", "$value": { "light": "{color.brand.600}", "dark": "{color.brand.500}" } },
|
|
256
|
+
"focus": { "$type": "color", "$value": { "light": "{color.brand.500}", "dark": "{color.brand.500}" }, "$description": "Focus ring color — must achieve 3:1 against background" }
|
|
257
|
+
}
|
|
258
|
+
},
|
|
259
|
+
"space": {
|
|
260
|
+
"component-gap": { "$type": "dimension", "$value": "{dimension.space.4}", "$description": "Gap between components within a section" },
|
|
261
|
+
"section-gap": { "$type": "dimension", "$value": "{dimension.space.12}", "$description": "Gap between sections" },
|
|
262
|
+
"content-inset": { "$type": "dimension", "$value": "{dimension.space.6}", "$description": "Inner padding for cards and panels" }
|
|
263
|
+
},
|
|
264
|
+
"transition": {
|
|
265
|
+
"interactive": {
|
|
266
|
+
"$type": "duration",
|
|
267
|
+
"$value": "{duration.normal}",
|
|
268
|
+
"$description": "Standard duration for hover/focus transitions"
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### tokens/component.tokens.json
|
|
275
|
+
|
|
276
|
+
Component tokens reference semantic tokens. Kept minimal — only add when a component deviates from semantic defaults.
|
|
277
|
+
|
|
278
|
+
```json
|
|
279
|
+
{
|
|
280
|
+
"$schema": "https://design-tokens.org/schema.json",
|
|
281
|
+
"button": {
|
|
282
|
+
"background": { "$type": "color", "$value": "{color.interactive.default}" },
|
|
283
|
+
"background-hover":{ "$type": "color", "$value": "{color.interactive.hover}" },
|
|
284
|
+
"text": { "$type": "color", "$value": "{color.text.on-accent}" },
|
|
285
|
+
"min-height": { "$type": "dimension", "$value": "44px", "$description": "WCAG 2.2 AA touch target — do not reduce" },
|
|
286
|
+
"min-width": { "$type": "dimension", "$value": "44px", "$description": "WCAG 2.2 AA touch target — do not reduce" },
|
|
287
|
+
"padding-x": { "$type": "dimension", "$value": "{dimension.space.4}" },
|
|
288
|
+
"padding-y": { "$type": "dimension", "$value": "{dimension.space.3}" },
|
|
289
|
+
"radius": { "$type": "dimension", "$value": "{dimension.radius.md}" }
|
|
290
|
+
},
|
|
291
|
+
"input": {
|
|
292
|
+
"background": { "$type": "color", "$value": "{color.background.surface}" },
|
|
293
|
+
"border": { "$type": "color", "$value": "{color.border.default}" },
|
|
294
|
+
"border-focus": { "$type": "color", "$value": "{color.interactive.focus}" },
|
|
295
|
+
"text": { "$type": "color", "$value": "{color.text.primary}" },
|
|
296
|
+
"min-height": { "$type": "dimension", "$value": "44px" },
|
|
297
|
+
"padding-x": { "$type": "dimension", "$value": "{dimension.space.3}" },
|
|
298
|
+
"radius": { "$type": "dimension", "$value": "{dimension.radius.md}" }
|
|
299
|
+
},
|
|
300
|
+
"card": {
|
|
301
|
+
"background": { "$type": "color", "$value": "{color.background.surface}" },
|
|
302
|
+
"border": { "$type": "color", "$value": "{color.border.default}" },
|
|
303
|
+
"padding": { "$type": "dimension", "$value": "{space.content-inset}" },
|
|
304
|
+
"radius": { "$type": "dimension", "$value": "{dimension.radius.lg}" }
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### tokens/themes/dark.tokens.json
|
|
310
|
+
|
|
311
|
+
Theme files override semantic token values for a given mode. Only include tokens that differ from the default (light) mode.
|
|
312
|
+
|
|
313
|
+
```json
|
|
314
|
+
{
|
|
315
|
+
"$schema": "https://design-tokens.org/schema.json",
|
|
316
|
+
"color": {
|
|
317
|
+
"background": {
|
|
318
|
+
"page": { "$type": "color", "$value": "{color.gray.950}" },
|
|
319
|
+
"surface": { "$type": "color", "$value": "{color.gray.900}" }
|
|
320
|
+
},
|
|
321
|
+
"text": {
|
|
322
|
+
"primary": { "$type": "color", "$value": "{color.gray.50}" },
|
|
323
|
+
"secondary": { "$type": "color", "$value": "{color.gray.400}" }
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Color Spaces
|
|
332
|
+
|
|
333
|
+
Use modern color spaces for superior gamut coverage and perceptual uniformity:
|
|
334
|
+
|
|
335
|
+
**Oklch** — preferred for scales and accessible colors:
|
|
336
|
+
```
|
|
337
|
+
oklch(lightness chroma hue / alpha)
|
|
338
|
+
oklch(0.520 0.180 264) /* brand blue */
|
|
339
|
+
```
|
|
340
|
+
- Lightness 0–1 (perceptually uniform, use for contrast calculation)
|
|
341
|
+
- Chroma 0–0.4 (0 = gray, higher = more saturated)
|
|
342
|
+
- Hue 0–360 (same as HSL hue)
|
|
343
|
+
|
|
344
|
+
**Display P3** — for wide-gamut displays (Safari, Chrome on modern hardware):
|
|
345
|
+
```
|
|
346
|
+
color(display-p3 0.18 0.47 0.89)
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
**CSS fallback pattern:**
|
|
350
|
+
```css
|
|
351
|
+
/* Always provide sRGB fallback first */
|
|
352
|
+
color: rgb(46 120 227);
|
|
353
|
+
color: oklch(0.52 0.18 264); /* wider gamut where supported */
|
|
354
|
+
color: color(display-p3 0.18 0.47 0.89); /* widest gamut */
|
|
355
|
+
```
|
|
356
|
+
|
|
357
|
+
---
|
|
358
|
+
|
|
359
|
+
## Export Targets
|
|
360
|
+
|
|
361
|
+
Run the following exports after any token change. Use Style Dictionary or token-transformer.
|
|
362
|
+
|
|
363
|
+
### CSS Custom Properties
|
|
364
|
+
|
|
365
|
+
```css
|
|
366
|
+
/* brand-assets/tokens.css */
|
|
367
|
+
:root {
|
|
368
|
+
/* Primitive */
|
|
369
|
+
--color-gray-50: oklch(0.985 0.002 247);
|
|
370
|
+
--color-brand-500: oklch(0.52 0.18 264);
|
|
371
|
+
--space-4: 16px;
|
|
372
|
+
--font-size-base: 1rem;
|
|
373
|
+
|
|
374
|
+
/* Semantic — light mode defaults */
|
|
375
|
+
--color-background-page: var(--color-gray-50);
|
|
376
|
+
--color-text-primary: var(--color-gray-900);
|
|
377
|
+
--color-interactive-default: var(--color-brand-500);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
[data-theme="dark"] {
|
|
381
|
+
--color-background-page: var(--color-gray-950);
|
|
382
|
+
--color-text-primary: var(--color-gray-50);
|
|
383
|
+
}
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### SCSS Variables
|
|
387
|
+
|
|
388
|
+
```scss
|
|
389
|
+
/* brand-assets/tokens.scss */
|
|
390
|
+
$color-gray-50: oklch(0.985 0.002 247);
|
|
391
|
+
$color-brand-500: oklch(0.52 0.18 264);
|
|
392
|
+
$color-background-page: var(--color-background-page);
|
|
393
|
+
$space-4: 16px;
|
|
394
|
+
$button-min-height: 44px;
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
### JS Constants
|
|
398
|
+
|
|
399
|
+
```js
|
|
400
|
+
/* brand-assets/tokens.js */
|
|
401
|
+
export const tokens = {
|
|
402
|
+
color: {
|
|
403
|
+
gray: { 50: 'oklch(0.985 0.002 247)', 900: 'oklch(0.20 0.014 247)' },
|
|
404
|
+
brand: { 500: 'oklch(0.52 0.18 264)' },
|
|
405
|
+
background: { page: 'var(--color-background-page)' },
|
|
406
|
+
text: { primary: 'var(--color-text-primary)' }
|
|
407
|
+
},
|
|
408
|
+
space: { 4: '16px', 6: '24px', 8: '32px' },
|
|
409
|
+
button: { minHeight: '44px' }
|
|
410
|
+
};
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
### Tailwind Config
|
|
414
|
+
|
|
415
|
+
```js
|
|
416
|
+
/* brand-assets/tailwind.config.js */
|
|
417
|
+
module.exports = {
|
|
418
|
+
theme: {
|
|
419
|
+
extend: {
|
|
420
|
+
colors: {
|
|
421
|
+
brand: { 500: 'oklch(0.52 0.18 264)', 600: 'oklch(0.45 0.185 264)' },
|
|
422
|
+
background: { page: 'var(--color-background-page)', surface: 'var(--color-background-surface)' },
|
|
423
|
+
text: { primary: 'var(--color-text-primary)', secondary: 'var(--color-text-secondary)' },
|
|
424
|
+
border: { default: 'var(--color-border-default)' },
|
|
425
|
+
interactive:{ default: 'var(--color-interactive-default)', hover: 'var(--color-interactive-hover)' }
|
|
426
|
+
},
|
|
427
|
+
spacing: {
|
|
428
|
+
'component-gap': 'var(--space-component-gap)',
|
|
429
|
+
'content-inset': 'var(--space-content-inset)'
|
|
430
|
+
},
|
|
431
|
+
borderRadius: {
|
|
432
|
+
sm: '4px', md: '8px', lg: '12px', xl: '16px'
|
|
433
|
+
},
|
|
434
|
+
fontFamily: {
|
|
435
|
+
sans: ['Inter', 'system-ui', 'sans-serif'],
|
|
436
|
+
display: ['Cal Sans', 'Inter', 'sans-serif'],
|
|
437
|
+
mono: ['JetBrains Mono', 'Menlo', 'monospace']
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
};
|
|
442
|
+
```
|
|
443
|
+
|
|
444
|
+
---
|
|
445
|
+
|
|
446
|
+
## Figma Sync
|
|
447
|
+
|
|
448
|
+
### Read Tokens from Figma Variables
|
|
449
|
+
|
|
450
|
+
Pull current Figma variable values and reconcile with token files:
|
|
451
|
+
|
|
452
|
+
```javascript
|
|
453
|
+
// 1. Fetch all Figma variable collections
|
|
454
|
+
const collections = await mcp__figma__get_variable_defs({ fileKey: figmaFileKey });
|
|
455
|
+
|
|
456
|
+
// 2. For each collection, extract token values
|
|
457
|
+
collections.forEach(collection => {
|
|
458
|
+
collection.variables.forEach(variable => {
|
|
459
|
+
const tokenPath = figmaNameToTokenPath(variable.name);
|
|
460
|
+
const value = variable.resolvedValuesByMode[collection.defaultModeId];
|
|
461
|
+
// Map to DTCG format and write to appropriate tier file
|
|
462
|
+
});
|
|
463
|
+
});
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### Write Tokens to Figma
|
|
467
|
+
|
|
468
|
+
After token changes, push updated values back to Figma variables to keep design and code in sync:
|
|
469
|
+
|
|
470
|
+
```javascript
|
|
471
|
+
// Update a Figma variable value
|
|
472
|
+
await mcp__figma__update_variable({
|
|
473
|
+
fileKey: figmaFileKey,
|
|
474
|
+
variableId: token.$extensions['ctx-design-system']['figma-variable-id'],
|
|
475
|
+
value: resolveTokenValue(token.$value)
|
|
476
|
+
});
|
|
477
|
+
```
|
|
478
|
+
|
|
479
|
+
### Figma Name → Token Path Mapping
|
|
480
|
+
|
|
481
|
+
| Figma Variable Name | Token Path |
|
|
482
|
+
|--------------------|-----------|
|
|
483
|
+
| `Color/Gray/500` | `color.gray.500` |
|
|
484
|
+
| `Semantic/Background/Page` | `color.background.page` |
|
|
485
|
+
| `Component/Button/Min Height` | `button.min-height` |
|
|
486
|
+
|
|
487
|
+
---
|
|
488
|
+
|
|
489
|
+
## Design System Audit
|
|
490
|
+
|
|
491
|
+
Run these checks before any token release.
|
|
492
|
+
|
|
493
|
+
### 1. Unused Token Detection
|
|
494
|
+
|
|
495
|
+
```bash
|
|
496
|
+
# Find all CSS custom property references in source
|
|
497
|
+
grep -r "var(--" src/ --include="*.tsx" --include="*.css" | \
|
|
498
|
+
grep -oP 'var\(--[\w-]+\)' | sort | uniq > .ctx/audit/used-tokens.txt
|
|
499
|
+
|
|
500
|
+
# Compare against token export
|
|
501
|
+
diff .ctx/audit/used-tokens.txt brand-assets/all-tokens.txt
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
### 2. Missing Semantic Mappings
|
|
505
|
+
|
|
506
|
+
Verify every primitive token used in components has a semantic alias:
|
|
507
|
+
- If `color.gray.500` appears in `component.tokens.json`, that is a violation — reference `color.text.secondary` instead.
|
|
508
|
+
|
|
509
|
+
### 3. Accessibility Violations
|
|
510
|
+
|
|
511
|
+
Check contrast ratios for all text/background token pairs:
|
|
512
|
+
|
|
513
|
+
```javascript
|
|
514
|
+
function relativeLuminance(oklch) {
|
|
515
|
+
// Convert oklch → linear sRGB → luminance
|
|
516
|
+
const rgb = oklchToLinearRGB(oklch);
|
|
517
|
+
return 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
function contrastRatio(l1, l2) {
|
|
521
|
+
const lighter = Math.max(l1, l2);
|
|
522
|
+
const darker = Math.min(l1, l2);
|
|
523
|
+
return (lighter + 0.05) / (darker + 0.05);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
// Required: text 4.5:1, UI components 3:1
|
|
527
|
+
const pairs = [
|
|
528
|
+
{ fg: 'color.text.primary', bg: 'color.background.page', required: 4.5 },
|
|
529
|
+
{ fg: 'color.text.secondary', bg: 'color.background.page', required: 4.5 },
|
|
530
|
+
{ fg: 'color.text.on-accent', bg: 'color.interactive.default', required: 4.5 },
|
|
531
|
+
{ fg: 'color.border.default', bg: 'color.background.page', required: 3.0 }
|
|
532
|
+
];
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
### 4. Audit Report Format
|
|
536
|
+
|
|
537
|
+
Write to `.ctx/audit/TOKEN_AUDIT.md`:
|
|
538
|
+
|
|
539
|
+
```markdown
|
|
540
|
+
# Token Audit — [ISO-8601]
|
|
541
|
+
|
|
542
|
+
## Summary
|
|
543
|
+
- Total tokens: {n}
|
|
544
|
+
- Unused tokens: {n}
|
|
545
|
+
- Missing semantic mappings: {n}
|
|
546
|
+
- Contrast violations: {n}
|
|
547
|
+
|
|
548
|
+
## Unused Tokens
|
|
549
|
+
| Token | Last Used | Action |
|
|
550
|
+
|-------|-----------|--------|
|
|
551
|
+
| color.gray.150 | never | Remove |
|
|
552
|
+
|
|
553
|
+
## Contrast Violations
|
|
554
|
+
| Pair | Ratio | Required | Status |
|
|
555
|
+
|------|-------|----------|--------|
|
|
556
|
+
| text.disabled on bg.page | 2.8:1 | 4.5:1 | FAIL |
|
|
557
|
+
|
|
558
|
+
## Missing Semantic Mappings
|
|
559
|
+
| Primitive | Used In | Should Become |
|
|
560
|
+
|-----------|---------|---------------|
|
|
561
|
+
| color.gray.500 | button.tokens | color.border.strong |
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
---
|
|
565
|
+
|
|
566
|
+
## Operational Rules
|
|
567
|
+
|
|
568
|
+
- Never delete a token that is currently exported and deployed without a deprecation period.
|
|
569
|
+
- Mark deprecated tokens with `"deprecated": true` in `$extensions` before removal.
|
|
570
|
+
- Increment version in BRAND_KIT.md header on every token release.
|
|
571
|
+
- All token changes must pass the audit before export.
|
|
572
|
+
- Theme files may only reference semantic-tier tokens, never primitives.
|