@theproductguy/create-mission-control 1.0.4 → 1.0.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.
@@ -136,6 +136,53 @@ app.get('/api/status', async (req, res) => {
136
136
  const hasTokens = fs.existsSync(path.join(designDir, 'product/design-system/colors.json'));
137
137
  const hasShell = fs.existsSync(path.join(designDir, 'product/shell/spec.md'));
138
138
 
139
+ // Implementation tracking
140
+ const appDir = path.join(PROJECT_ROOT, 'app');
141
+ const hasScaffold = fs.existsSync(path.join(APP_DIR, 'src/lib/utils.ts'));
142
+
143
+ // Check for test files
144
+ const testFiles = glob.sync('**/*.{test,spec}.{ts,tsx,js,jsx}', { cwd: appDir, ignore: ['node_modules/**'] });
145
+ const hasTests = testFiles.length > 0;
146
+
147
+ // Check for coverage report
148
+ const coverageDir = path.join(PROJECT_ROOT, 'coverage');
149
+ const coverageSummary = path.join(coverageDir, 'coverage-summary.json');
150
+ let coveragePercent = null;
151
+ if (fs.existsSync(coverageSummary)) {
152
+ try {
153
+ const coverage = JSON.parse(fs.readFileSync(coverageSummary, 'utf-8'));
154
+ if (coverage.total && coverage.total.lines) {
155
+ coveragePercent = Math.round(coverage.total.lines.pct);
156
+ }
157
+ } catch (e) {
158
+ // Ignore parsing errors
159
+ }
160
+ }
161
+
162
+ // Check git status
163
+ let gitInfo = { initialized: false, branch: null, uncommitted: 0, lastCommit: null };
164
+ const gitDir = path.join(PROJECT_ROOT, '.git');
165
+ if (fs.existsSync(gitDir)) {
166
+ gitInfo.initialized = true;
167
+ try {
168
+ const { execSync } = require('child_process');
169
+ gitInfo.branch = execSync('git rev-parse --abbrev-ref HEAD', { cwd: PROJECT_ROOT, encoding: 'utf-8' }).trim();
170
+ const status = execSync('git status --porcelain', { cwd: PROJECT_ROOT, encoding: 'utf-8' });
171
+ gitInfo.uncommitted = status.split('\n').filter(l => l.trim()).length;
172
+ try {
173
+ gitInfo.lastCommit = execSync('git log -1 --pretty=format:"%s"', { cwd: PROJECT_ROOT, encoding: 'utf-8' }).trim();
174
+ } catch (e) {
175
+ // No commits yet
176
+ }
177
+ } catch (e) {
178
+ // Git commands failed
179
+ }
180
+ }
181
+
182
+ // Check for linked specs
183
+ const specsWithTasks = specs.filter(s => s.tasks.exists && s.tasks.total > 0);
184
+ const specsCompleted = specs.filter(s => s.tasks.exists && s.tasks.completed === s.tasks.total && s.tasks.total > 0);
185
+
139
186
  res.json({
140
187
  product: productFiles,
141
188
  services,
@@ -155,7 +202,17 @@ app.get('/api/status', async (req, res) => {
155
202
  }
156
203
  },
157
204
  implementation: {
158
- scaffolded: fs.existsSync(path.join(APP_DIR, 'src/lib/utils.ts'))
205
+ scaffolded: hasScaffold,
206
+ tests: {
207
+ count: testFiles.length,
208
+ hasTests
209
+ },
210
+ coverage: coveragePercent,
211
+ specs: {
212
+ total: specsWithTasks.length,
213
+ completed: specsCompleted.length
214
+ },
215
+ git: gitInfo
159
216
  },
160
217
  specs,
161
218
  projectRoot: PROJECT_ROOT
@@ -1,6 +1,6 @@
1
1
  import { useEffect, useState } from 'react';
2
2
  import axios from 'axios';
3
- import { Layout, Layers, FileText, Code, CheckSquare, RefreshCw, ArrowRight, X, Plus, Trash2, WalletCards, LayoutDashboard, Copy, Package } from 'lucide-react';
3
+ import { Layout, Layers, FileText, Code, CheckSquare, RefreshCw, ArrowRight, X, Plus, Trash2, WalletCards, LayoutDashboard, Copy, Package, Play } from 'lucide-react';
4
4
  import { useToast } from './components/ui/ToastContext';
5
5
  import { ThemeToggle } from '@theproductguy/agent-os-ui';
6
6
  import '@theproductguy/agent-os-ui/style.css';
@@ -46,6 +46,21 @@ interface ProjectState {
46
46
  };
47
47
  implementation: {
48
48
  scaffolded: boolean;
49
+ tests?: {
50
+ count: number;
51
+ hasTests: boolean;
52
+ };
53
+ coverage?: number | null;
54
+ specs?: {
55
+ total: number;
56
+ completed: number;
57
+ };
58
+ git?: {
59
+ initialized: boolean;
60
+ branch: string | null;
61
+ uncommitted: number;
62
+ lastCommit: string | null;
63
+ };
49
64
  };
50
65
  specs: Spec[];
51
66
  services?: {
@@ -130,19 +145,33 @@ function StatusItem({ label, status, icon, small, onClick }: any) {
130
145
  }
131
146
 
132
147
  function PromptButton({ label, prompt, onClick, small, primary }: any) {
148
+ const deepLink = `vscode://vscode.executeCommand/workbench.action.chat.open?query=${encodeURIComponent(prompt)}`;
149
+
133
150
  return (
134
- <button
135
- onClick={() => onClick(prompt)}
136
- className={`flex items-center justify-center gap-2 rounded-lg font-medium transition cursor-pointer
137
- ${small ? 'px-3 py-1.5 text-xs flex-1' : 'px-4 py-2 text-sm w-full'}
138
- ${primary
139
- ? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-sm'
140
- : 'bg-background hover:bg-secondary border border-border text-foreground hover:text-foreground'}
141
- `}
142
- >
143
- <Copy size={small ? 12 : 14} />
144
- {label}
145
- </button>
151
+ <div className={`flex gap-1 items-center ${!small ? 'w-full' : 'flex-1'}`}>
152
+ <button
153
+ onClick={() => onClick(prompt)}
154
+ className={`flex items-center justify-center gap-2 rounded-lg font-medium transition cursor-pointer flex-1
155
+ ${small ? 'px-3 py-1.5 text-xs' : 'px-4 py-2 text-sm'}
156
+ ${primary
157
+ ? 'bg-primary hover:bg-primary/90 text-primary-foreground shadow-sm'
158
+ : 'bg-background hover:bg-secondary border border-border text-foreground hover:text-foreground'}
159
+ `}
160
+ >
161
+ <Copy size={small ? 12 : 14} />
162
+ {label}
163
+ </button>
164
+
165
+ <a
166
+ href={deepLink}
167
+ className={`flex items-center justify-center rounded-lg border border-border bg-secondary hover:bg-secondary/80 text-foreground transition
168
+ ${small ? 'w-8 h-[30px]' : 'w-10 h-[38px]'}
169
+ `}
170
+ title="Run in IDE (Deep Link)"
171
+ >
172
+ <Play size={small ? 12 : 14} fill="currentColor" className="opacity-70" />
173
+ </a>
174
+ </div>
146
175
  )
147
176
  }
148
177
 
@@ -701,77 +730,14 @@ function App() {
701
730
  </div>
702
731
  </section>
703
732
 
704
- {/* Phase 3: Implementation */}
705
- <section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
706
- <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
707
- <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
708
- <Package size={20} />
709
- </div>
710
- <h2 className="text-lg font-semibold">3. Implementation</h2>
711
- </div>
712
-
713
- <div className="space-y-4 flex-1">
714
- <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
715
- <span className="text-foreground font-medium text-sm flex items-center gap-2"><Layout size={16} className="text-muted-foreground" /> Scaffold</span>
716
- {state?.implementation?.scaffolded ? (
717
- <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Done</span>
718
- ) : (
719
- <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
720
- )}
721
- </div>
722
- </div>
723
-
724
- <div className="mt-6">
725
- {!state?.implementation?.scaffolded ? (
726
- <PromptButton
727
- label="Scaffold App"
728
- prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'."
729
- onClick={copyToClipboard}
730
- />
731
- ) : (
732
- <div className="p-3 bg-secondary/30 rounded-lg border border-border/50 text-sm text-muted-foreground text-center italic mb-4">
733
- App scaffolded. Ready for implementation.
734
- </div>
735
- )}
736
-
737
- {state?.implementation?.scaffolded && state?.design?.exportPrompts?.oneShot && (
738
- <div className="pt-3 border-t border-border/50">
739
- <p className="text-xs text-muted-foreground mb-2 font-medium">Implementation Options:</p>
740
- <div className="space-y-2">
741
- <PromptButton
742
- label="Option A: One-Shot (All)"
743
- prompt={`Antigravity, implement the app. Read 'product-plan/prompts/one-shot-prompt.md'.`}
744
- onClick={copyToClipboard}
745
- small
746
- primary
747
- />
748
- {state?.design?.exportPrompts?.section && (
749
- <div className="relative">
750
- <PromptButton
751
- label="Option B: Incremental (Section)"
752
- prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'.`}
753
- onClick={copyToClipboard}
754
- small
755
- />
756
- <p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">
757
- Build feature-by-feature using the <strong>Feature Specs</strong> list &rarr;
758
- </p>
759
- </div>
760
- )}
761
- </div>
762
- </div>
763
- )}
764
- </div>
765
- </section>
766
-
767
- {/* Phase 4: Specs & Implementation */}
733
+ {/* Phase 3: Feature Specs */}
768
734
  <section className="bg-card border border-border rounded-xl p-5 shadow-sm col-span-1 md:col-span-2 lg:col-span-1 h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
769
735
  <div className="flex items-center justify-between mb-5 pb-4 border-b border-border/50">
770
736
  <div className="flex items-center gap-3">
771
737
  <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
772
738
  <Code size={20} />
773
739
  </div>
774
- <h2 className="text-lg font-semibold">4. Feature Specs</h2>
740
+ <h2 className="text-lg font-semibold">3. Feature Specs</h2>
775
741
  </div>
776
742
  <button className="text-xs bg-secondary hover:bg-secondary/80 border border-border px-3 py-2 rounded-md flex items-center gap-1.5 font-medium transition cursor-pointer"
777
743
  onClick={() => setCreatingSpec(true)}>
@@ -780,6 +746,24 @@ function App() {
780
746
  </div>
781
747
 
782
748
  <div className="space-y-3 flex-1 overflow-y-auto max-h-[300px] pr-1">
749
+ {/* Auto-generate from Roadmap after Design Export */}
750
+ {(state?.product?.roadmap?.items?.filter(item =>
751
+ !item.completed &&
752
+ !state?.product?.roadmap?.isBoilerplate
753
+ ) ?? []).length > 0 && !state?.specs?.length && (
754
+ <div className="bg-blue-50 dark:bg-blue-900/20 border border-blue-200 dark:border-blue-800 p-4 rounded-lg mb-3">
755
+ <p className="text-sm text-blue-700 dark:text-blue-300 mb-2">
756
+ Ready to generate specs from roadmap items?
757
+ </p>
758
+ <PromptButton
759
+ label="Generate All Specs"
760
+ prompt={`Antigravity, generate feature specs from the roadmap items. For each item in 'agent-os/product/roadmap.md' that is not yet completed, create a spec folder in 'agent-os/specs/[feature-name]/' with spec.md and tasks.md files. Context: 1. Read 'product-plan/product-overview.md' for data model/flows. 2. Read 'design-system/design-tokens.md' and 'design-system/app-shell.md' for UI/UX constraints. 3. Use the shape-spec command: Read 'agent-os/commands/shape-spec/shape-spec.md'.`}
761
+ onClick={copyToClipboard}
762
+ small
763
+ primary
764
+ />
765
+ </div>
766
+ )}
783
767
  {state?.specs?.map(spec => (
784
768
  <div key={spec.name} className="bg-secondary/20 p-4 rounded-lg border border-border/50 hover:border-border transition-colors group relative">
785
769
  <button
@@ -867,6 +851,132 @@ function App() {
867
851
  </div>
868
852
  </section>
869
853
 
854
+ {/* Phase 4: Implementation */}
855
+ <section className="bg-card border border-border rounded-xl p-5 shadow-sm h-full flex flex-col transition-all hover:shadow-md hover:border-border/80">
856
+ <div className="flex items-center gap-3 mb-5 pb-4 border-b border-border/50">
857
+ <div className="p-2 bg-sidebar-primary/10 text-sidebar-primary rounded-lg">
858
+ <Package size={20} />
859
+ </div>
860
+ <h2 className="text-lg font-semibold">4. Implementation</h2>
861
+ </div>
862
+
863
+ <div className="space-y-3 flex-1">
864
+ {/* Scaffold Status */}
865
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
866
+ <span className="text-foreground font-medium text-sm flex items-center gap-2"><Layout size={16} className="text-muted-foreground" /> Scaffold</span>
867
+ {state?.implementation?.scaffolded ? (
868
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Done</span>
869
+ ) : (
870
+ <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">Pending</span>
871
+ )}
872
+ </div>
873
+
874
+ {/* Git Status */}
875
+ {state?.implementation?.git?.initialized && (
876
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
877
+ <span className="text-foreground font-medium text-sm flex items-center gap-2">
878
+ <svg className="w-4 h-4 text-muted-foreground" viewBox="0 0 24 24" fill="currentColor"><path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" /></svg>
879
+ {state.implementation.git.branch || 'main'}
880
+ </span>
881
+ <div className="flex items-center gap-2">
882
+ {state.implementation.git.uncommitted > 0 && (
883
+ <span className="text-amber-600 bg-amber-50 dark:bg-amber-900/20 px-2 py-0.5 rounded text-xs font-medium border border-amber-200 dark:border-amber-800">
884
+ {state.implementation.git.uncommitted} uncommitted
885
+ </span>
886
+ )}
887
+ {state.implementation.git.uncommitted === 0 && (
888
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">Clean</span>
889
+ )}
890
+ </div>
891
+ </div>
892
+ )}
893
+
894
+ {/* Tests & Coverage */}
895
+ {state?.implementation?.scaffolded && (
896
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
897
+ <span className="text-foreground font-medium text-sm flex items-center gap-2">
898
+ <CheckSquare size={16} className="text-muted-foreground" /> Tests
899
+ </span>
900
+ <div className="flex items-center gap-2">
901
+ {state?.implementation?.tests?.hasTests ? (
902
+ <span className="text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 px-2 py-0.5 rounded text-xs font-medium border border-emerald-200 dark:border-emerald-800">
903
+ {state.implementation.tests.count} files
904
+ </span>
905
+ ) : (
906
+ <span className="text-muted-foreground bg-secondary px-2 py-0.5 rounded text-xs font-medium border border-border">No tests</span>
907
+ )}
908
+ {state?.implementation?.coverage !== null && state?.implementation?.coverage !== undefined && (
909
+ <span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.coverage >= 80
910
+ ? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800'
911
+ : state.implementation.coverage >= 50
912
+ ? 'text-amber-600 bg-amber-50 dark:bg-amber-900/20 border-amber-200 dark:border-amber-800'
913
+ : 'text-red-600 bg-red-50 dark:bg-red-900/20 border-red-200 dark:border-red-800'
914
+ }`}>
915
+ {state.implementation.coverage}% coverage
916
+ </span>
917
+ )}
918
+ </div>
919
+ </div>
920
+ )}
921
+
922
+ {/* Spec Progress */}
923
+ {state?.implementation?.specs && state.implementation.specs.total > 0 && (
924
+ <div className="flex items-center justify-between p-3 bg-secondary/30 rounded-lg border border-border/50">
925
+ <span className="text-foreground font-medium text-sm flex items-center gap-2">
926
+ <FileText size={16} className="text-muted-foreground" /> Specs
927
+ </span>
928
+ <span className={`px-2 py-0.5 rounded text-xs font-medium border ${state.implementation.specs.completed === state.implementation.specs.total
929
+ ? 'text-emerald-600 bg-emerald-50 dark:bg-emerald-900/20 border-emerald-200 dark:border-emerald-800'
930
+ : 'text-blue-600 bg-blue-50 dark:bg-blue-900/20 border-blue-200 dark:border-blue-800'
931
+ }`}>
932
+ {state.implementation.specs.completed}/{state.implementation.specs.total} complete
933
+ </span>
934
+ </div>
935
+ )}
936
+ </div>
937
+
938
+ <div className="mt-6">
939
+ {!state?.implementation?.scaffolded ? (
940
+ <PromptButton
941
+ label="Scaffold App"
942
+ prompt="Antigravity, scaffold the implementation. Read 'agent-os/commands/scaffold-implementation/scaffold-implementation.md'."
943
+ onClick={copyToClipboard}
944
+ />
945
+ ) : (
946
+ <div className="p-3 bg-secondary/30 rounded-lg border border-border/50 text-sm text-muted-foreground text-center italic mb-4">
947
+ App scaffolded. Ready for implementation.
948
+ </div>
949
+ )}
950
+
951
+ {state?.implementation?.scaffolded && state?.design?.exportPrompts?.oneShot && (
952
+ <div className="pt-3 border-t border-border/50">
953
+ <p className="text-xs text-muted-foreground mb-2 font-medium">Implementation Options:</p>
954
+ <div className="space-y-2">
955
+ <PromptButton
956
+ label="Option A: One-Shot (All)"
957
+ prompt={`Antigravity, implement the app. Read 'product-plan/prompts/one-shot-prompt.md'. If Context7 is configured, use it to verify library documentation.`}
958
+ onClick={copyToClipboard}
959
+ small
960
+ primary
961
+ />
962
+ {state?.design?.exportPrompts?.section && (
963
+ <div className="relative">
964
+ <PromptButton
965
+ label="Option B: Incremental (Section)"
966
+ prompt={`Antigravity, implement a section. Read 'product-plan/prompts/section-prompt.md'. Use Context7 for latest docs if available.`}
967
+ onClick={copyToClipboard}
968
+ small
969
+ />
970
+ <p className="text-[10px] text-muted-foreground mt-1.5 text-center leading-tight">
971
+ Build by spec from <strong>Feature Specs</strong> &larr;
972
+ </p>
973
+ </div>
974
+ )}
975
+ </div>
976
+ </div>
977
+ )}
978
+ </div>
979
+ </section>
870
980
 
871
981
  </main>
872
982
  </div>
@@ -0,0 +1,80 @@
1
+ # Hook: AI Slop Guard
2
+
3
+ ## Purpose
4
+ Warn when code or design patterns that indicate generic AI-generated output are detected.
5
+
6
+ ## Trigger
7
+ - PreToolUse: Before writing to CSS/TSX/HTML files
8
+ - PreCommit: Before committing changes
9
+
10
+ ## Patterns to Watch For
11
+
12
+ ### Typography Slop
13
+ - ⚠️ `font-family: 'Inter'` — Generic AI default font
14
+ - ⚠️ `font-family: 'Roboto'` — Overused AI choice
15
+ - ⚠️ `font-family: 'Open Sans'` — Too common
16
+ - ⚠️ `font-family: 'Poppins'` — AI favorite
17
+
18
+ ### Color Slop
19
+ - ⚠️ `#808080` or `gray` — Dead gray
20
+ - ⚠️ `#cccccc`, `#dddddd` — Lifeless neutral
21
+ - ⚠️ `#f5f5f5` — The AI background color
22
+ - ⚠️ Pure `#000000` or `#ffffff` — No warmth
23
+ - ⚠️ `#3b82f6` or `#2563eb` — Everyone's blue
24
+
25
+ ### Layout Slop
26
+ - ⚠️ Cards in cards — Nested containers
27
+ - ⚠️ Hero with centered h1 + subtitle + CTA — The AI template
28
+ - ⚠️ 3-column feature grid — Overused pattern
29
+ - ⚠️ Centered everything — Lazy alignment
30
+
31
+ ### Effect Slop
32
+ - ⚠️ `backdrop-filter: blur(10px)` — Glassmorphism default
33
+ - ⚠️ `border-radius: 12px` — The AI corner radius
34
+ - ⚠️ `box-shadow: 0 4px 6px -1px rgba(0,0,0,0.1)` — Tailwind default
35
+
36
+ ### Animation Slop
37
+ - ⚠️ `transition: all` — Lazy animation
38
+ - ⚠️ `animate-bounce` — Generic micro-interaction
39
+ - ⚠️ `animate-pulse` — Loading skeleton overuse
40
+
41
+ ### Copy Slop
42
+ - ⚠️ "Streamline your workflow" — Generic SaaS copy
43
+ - ⚠️ "Unlock the power of" — AI cliché
44
+ - ⚠️ "Seamlessly integrate" — Meaningless buzzword
45
+ - ⚠️ "Best-in-class" — Tells nothing
46
+
47
+ ## Response When Detected
48
+
49
+ ```
50
+ ⚠️ AI Slop Guard Warning
51
+
52
+ Detected potentially generic AI patterns:
53
+ - [Pattern name]: [File:Line]
54
+
55
+ Consider:
56
+ 1. Running `/audit` to get specific recommendations
57
+ 2. Consulting the frontend-design skill for alternatives
58
+ 3. Using `/bolder` if design is too safe
59
+
60
+ Continue anyway? [y/N]
61
+ ```
62
+
63
+ ## Configuration
64
+ Users can customize in `.gemini/settings.json`:
65
+ ```json
66
+ {
67
+ "hooks": {
68
+ "ai-slop-guard": {
69
+ "enabled": true,
70
+ "strictness": "medium", // low, medium, high
71
+ "ignore": ["Inter"] // Patterns to ignore
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ## Notes
78
+ - This is a warning, not a blocker
79
+ - Some patterns are acceptable in context
80
+ - The goal is awareness, not restriction
@@ -0,0 +1,63 @@
1
+ # Hook: On Design Complete
2
+
3
+ ## Purpose
4
+ Automatically run quality checks when a design phase is completed.
5
+
6
+ ## Trigger
7
+ After completing:
8
+ - `/design-tokens`
9
+ - `/design-shell`
10
+ - `/design-screen`
11
+
12
+ ## Actions
13
+
14
+ ### 1. Run Auto-Audit
15
+ Execute a mini-audit focusing on the just-completed work:
16
+
17
+ **After `/design-tokens`:**
18
+ - Check for AI slop fonts
19
+ - Verify color hierarchy exists
20
+ - Ensure contrast ratios meet WCAG AA
21
+ - Validate color format consistency
22
+
23
+ **After `/design-shell`:**
24
+ - Check for AI slop layout patterns
25
+ - Verify touch targets (44x44 minimum)
26
+ - Check spacing consistency
27
+ - Validate responsive breakpoints
28
+
29
+ **After `/design-screen`:**
30
+ - Full component audit
31
+ - Accessibility check
32
+ - Performance impact assessment
33
+
34
+ ### 2. Generate Report
35
+ Save audit results to `design-system/QA/[phase]-audit.md`
36
+
37
+ ### 3. Suggest Next Steps
38
+ Based on audit results:
39
+ - If issues found: Suggest specific fix commands
40
+ - If clean: Suggest next phase
41
+
42
+ ## Output Format
43
+
44
+ ```
45
+ ✅ Design phase completed!
46
+
47
+ Auto-audit results:
48
+ ├── Accessibility: ✓ Pass
49
+ ├── Typography: ⚠️ 1 warning (consider font alternatives)
50
+ ├── Colors: ✓ Pass
51
+ └── Layout: ✓ Pass
52
+
53
+ Suggestions:
54
+ - Run `/critique` for UX evaluation
55
+ - Continue to `/design-shell` when ready
56
+
57
+ Full report: design-system/QA/tokens-audit.md
58
+ ```
59
+
60
+ ## Notes
61
+ - This hook runs automatically, no user action needed
62
+ - Reports accumulate, so you can see progress over time
63
+ - Hook can be disabled in settings if desired
@@ -0,0 +1,37 @@
1
+ import js from '@eslint/js'
2
+ import globals from 'globals'
3
+ import reactHooks from 'eslint-plugin-react-hooks'
4
+ import reactRefresh from 'eslint-plugin-react-refresh'
5
+ import tseslint from 'typescript-eslint'
6
+
7
+ export default tseslint.config(
8
+ { ignores: ['dist', 'node_modules', 'coverage', '.next', 'build'] },
9
+ {
10
+ extends: [js.configs.recommended, ...tseslint.configs.recommended],
11
+ files: ['**/*.{ts,tsx}'],
12
+ languageOptions: {
13
+ ecmaVersion: 2020,
14
+ globals: globals.browser,
15
+ },
16
+ plugins: {
17
+ 'react-hooks': reactHooks,
18
+ 'react-refresh': reactRefresh,
19
+ },
20
+ rules: {
21
+ ...reactHooks.configs.recommended.rules,
22
+ 'react-refresh/only-export-components': [
23
+ 'warn',
24
+ { allowConstantExport: true },
25
+ ],
26
+ // Strict TypeScript rules
27
+ '@typescript-eslint/no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
28
+ '@typescript-eslint/no-explicit-any': 'warn',
29
+ '@typescript-eslint/explicit-function-return-type': 'off',
30
+ '@typescript-eslint/explicit-module-boundary-types': 'off',
31
+ // Best practices
32
+ 'no-console': ['warn', { allow: ['warn', 'error'] }],
33
+ 'prefer-const': 'error',
34
+ 'no-var': 'error',
35
+ },
36
+ },
37
+ )
@@ -0,0 +1,6 @@
1
+ export default {
2
+ '*.{ts,tsx}': ['eslint --fix', 'prettier --write'],
3
+ '*.{js,jsx,cjs,mjs}': ['eslint --fix', 'prettier --write'],
4
+ '*.{json,md,yml,yaml}': ['prettier --write'],
5
+ '*.css': ['prettier --write'],
6
+ }
@@ -19,9 +19,32 @@
19
19
  "start:control": "cd control-center/frontend && npm run dev -- --port 5401",
20
20
  "start:app": "cd app && npm run dev -- --port 5402",
21
21
  "build": "npm run build --workspaces",
22
- "test": "npm run test --workspaces"
22
+ "test": "vitest run",
23
+ "test:watch": "vitest",
24
+ "test:coverage": "vitest run --coverage",
25
+ "lint": "eslint . --ext .ts,.tsx,.js,.jsx",
26
+ "lint:fix": "eslint . --ext .ts,.tsx,.js,.jsx --fix",
27
+ "format": "prettier --write .",
28
+ "format:check": "prettier --check .",
29
+ "typecheck": "tsc --noEmit",
30
+ "prepare": "husky"
23
31
  },
24
32
  "devDependencies": {
25
- "concurrently": "^8.2.2"
33
+ "@commitlint/cli": "^19.0.0",
34
+ "@commitlint/config-conventional": "^19.0.0",
35
+ "@eslint/js": "^9.0.0",
36
+ "@types/node": "^20.11.0",
37
+ "@vitest/coverage-v8": "^2.0.0",
38
+ "concurrently": "^8.2.2",
39
+ "eslint": "^9.0.0",
40
+ "eslint-plugin-react-hooks": "^5.0.0",
41
+ "eslint-plugin-react-refresh": "^0.4.0",
42
+ "globals": "^15.0.0",
43
+ "husky": "^9.0.0",
44
+ "lint-staged": "^15.0.0",
45
+ "prettier": "^3.2.0",
46
+ "prettier-plugin-tailwindcss": "^0.6.0",
47
+ "typescript-eslint": "^8.0.0",
48
+ "vitest": "^2.0.0"
26
49
  }
27
50
  }
@@ -0,0 +1,17 @@
1
+ import { describe, it, expect } from 'vitest'
2
+
3
+ describe('Example Test Suite', () => {
4
+ it('should pass a basic test', () => {
5
+ expect(1 + 1).toBe(2)
6
+ })
7
+
8
+ it('should handle strings', () => {
9
+ expect('hello world').toContain('world')
10
+ })
11
+
12
+ it('should handle arrays', () => {
13
+ const items = [1, 2, 3]
14
+ expect(items).toHaveLength(3)
15
+ expect(items).toContain(2)
16
+ })
17
+ })
@@ -0,0 +1,14 @@
1
+ import '@testing-library/jest-dom/vitest'
2
+
3
+ // Global test setup
4
+ beforeAll(() => {
5
+ // Setup code that runs before all tests
6
+ })
7
+
8
+ afterEach(() => {
9
+ // Cleanup after each test
10
+ })
11
+
12
+ afterAll(() => {
13
+ // Cleanup code that runs after all tests
14
+ })