codingbuddy-rules 4.4.0 → 5.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/.ai-rules/adapters/antigravity.md +6 -6
- package/.ai-rules/adapters/claude-code.md +107 -4
- package/.ai-rules/adapters/codex.md +5 -5
- package/.ai-rules/adapters/cursor.md +2 -2
- package/.ai-rules/adapters/kiro.md +8 -8
- package/.ai-rules/adapters/opencode.md +7 -7
- package/.ai-rules/adapters/q.md +2 -2
- package/.ai-rules/agents/README.md +66 -16
- package/.ai-rules/agents/accessibility-specialist.json +2 -1
- package/.ai-rules/agents/act-mode.json +2 -1
- package/.ai-rules/agents/agent-architect.json +8 -7
- package/.ai-rules/agents/ai-ml-engineer.json +1 -0
- package/.ai-rules/agents/architecture-specialist.json +1 -0
- package/.ai-rules/agents/auto-mode.json +4 -2
- package/.ai-rules/agents/backend-developer.json +1 -0
- package/.ai-rules/agents/code-quality-specialist.json +1 -0
- package/.ai-rules/agents/code-reviewer.json +65 -64
- package/.ai-rules/agents/data-engineer.json +8 -7
- package/.ai-rules/agents/data-scientist.json +10 -9
- package/.ai-rules/agents/devops-engineer.json +1 -0
- package/.ai-rules/agents/documentation-specialist.json +1 -0
- package/.ai-rules/agents/eval-mode.json +20 -19
- package/.ai-rules/agents/event-architecture-specialist.json +1 -0
- package/.ai-rules/agents/frontend-developer.json +1 -0
- package/.ai-rules/agents/i18n-specialist.json +2 -1
- package/.ai-rules/agents/integration-specialist.json +1 -0
- package/.ai-rules/agents/migration-specialist.json +1 -0
- package/.ai-rules/agents/mobile-developer.json +8 -7
- package/.ai-rules/agents/observability-specialist.json +1 -0
- package/.ai-rules/agents/parallel-orchestrator.json +346 -0
- package/.ai-rules/agents/performance-specialist.json +1 -0
- package/.ai-rules/agents/plan-mode.json +3 -1
- package/.ai-rules/agents/plan-reviewer.json +208 -0
- package/.ai-rules/agents/platform-engineer.json +1 -0
- package/.ai-rules/agents/security-engineer.json +9 -8
- package/.ai-rules/agents/security-specialist.json +2 -1
- package/.ai-rules/agents/seo-specialist.json +1 -0
- package/.ai-rules/agents/software-engineer.json +1 -0
- package/.ai-rules/agents/solution-architect.json +11 -10
- package/.ai-rules/agents/systems-developer.json +9 -8
- package/.ai-rules/agents/technical-planner.json +11 -10
- package/.ai-rules/agents/test-engineer.json +7 -6
- package/.ai-rules/agents/test-strategy-specialist.json +1 -0
- package/.ai-rules/agents/tooling-engineer.json +4 -3
- package/.ai-rules/agents/ui-ux-designer.json +1 -0
- package/.ai-rules/keyword-modes.json +4 -4
- package/.ai-rules/rules/clarification-guide.md +14 -14
- package/.ai-rules/rules/core.md +90 -1
- package/.ai-rules/rules/parallel-execution.md +217 -0
- package/.ai-rules/skills/README.md +23 -1
- package/.ai-rules/skills/agent-design/SKILL.md +5 -0
- package/.ai-rules/skills/agent-design/examples/agent-template.json +58 -0
- package/.ai-rules/skills/agent-design/references/expertise-guidelines.md +112 -0
- package/.ai-rules/skills/agent-discussion/SKILL.md +199 -0
- package/.ai-rules/skills/agent-discussion-panel/SKILL.md +448 -0
- package/.ai-rules/skills/api-design/SKILL.md +5 -0
- package/.ai-rules/skills/api-design/examples/error-response.json +159 -0
- package/.ai-rules/skills/api-design/examples/openapi-template.yaml +393 -0
- package/.ai-rules/skills/build-fix/SKILL.md +234 -0
- package/.ai-rules/skills/code-explanation/SKILL.md +4 -0
- package/.ai-rules/skills/context-management/SKILL.md +1 -0
- package/.ai-rules/skills/cost-budget/SKILL.md +348 -0
- package/.ai-rules/skills/cross-repo-issues/SKILL.md +257 -0
- package/.ai-rules/skills/database-migration/SKILL.md +1 -0
- package/.ai-rules/skills/deepsearch/SKILL.md +214 -0
- package/.ai-rules/skills/deployment-checklist/SKILL.md +1 -0
- package/.ai-rules/skills/error-analysis/SKILL.md +1 -0
- package/.ai-rules/skills/finishing-a-development-branch/SKILL.md +281 -0
- package/.ai-rules/skills/frontend-design/SKILL.md +5 -0
- package/.ai-rules/skills/frontend-design/examples/component-template.tsx +203 -0
- package/.ai-rules/skills/frontend-design/references/css-patterns.md +243 -0
- package/.ai-rules/skills/git-master/SKILL.md +358 -0
- package/.ai-rules/skills/incident-response/SKILL.md +1 -0
- package/.ai-rules/skills/legacy-modernization/SKILL.md +1 -0
- package/.ai-rules/skills/mcp-builder/SKILL.md +7 -0
- package/.ai-rules/skills/mcp-builder/examples/resource-example.ts +233 -0
- package/.ai-rules/skills/mcp-builder/examples/tool-example.ts +203 -0
- package/.ai-rules/skills/mcp-builder/references/protocol-spec.md +215 -0
- package/.ai-rules/skills/performance-optimization/SKILL.md +3 -0
- package/.ai-rules/skills/plan-and-review/SKILL.md +115 -0
- package/.ai-rules/skills/pr-all-in-one/SKILL.md +15 -13
- package/.ai-rules/skills/pr-all-in-one/configuration-guide.md +7 -7
- package/.ai-rules/skills/pr-all-in-one/pr-templates.md +10 -10
- package/.ai-rules/skills/pr-review/SKILL.md +4 -0
- package/.ai-rules/skills/receiving-code-review/SKILL.md +347 -0
- package/.ai-rules/skills/refactoring/SKILL.md +1 -0
- package/.ai-rules/skills/requesting-code-review/SKILL.md +348 -0
- package/.ai-rules/skills/rule-authoring/SKILL.md +5 -0
- package/.ai-rules/skills/rule-authoring/examples/rule-template.md +142 -0
- package/.ai-rules/skills/rule-authoring/examples/trigger-patterns.md +126 -0
- package/.ai-rules/skills/security-audit/SKILL.md +4 -0
- package/.ai-rules/skills/skill-creator/SKILL.md +461 -0
- package/.ai-rules/skills/skill-creator/agents/analyzer.md +206 -0
- package/.ai-rules/skills/skill-creator/agents/comparator.md +167 -0
- package/.ai-rules/skills/skill-creator/agents/grader.md +152 -0
- package/.ai-rules/skills/skill-creator/assets/eval_review.html +289 -0
- package/.ai-rules/skills/skill-creator/assets/skill-template.md +43 -0
- package/.ai-rules/skills/skill-creator/eval-viewer/generate_review.py +496 -0
- package/.ai-rules/skills/skill-creator/references/frontmatter-guide.md +632 -0
- package/.ai-rules/skills/skill-creator/references/multi-tool-compat.md +480 -0
- package/.ai-rules/skills/skill-creator/references/schemas.md +784 -0
- package/.ai-rules/skills/skill-creator/scripts/aggregate_benchmark.py +302 -0
- package/.ai-rules/skills/skill-creator/scripts/init_skill.sh +196 -0
- package/.ai-rules/skills/skill-creator/scripts/run_loop.py +327 -0
- package/.ai-rules/skills/systematic-debugging/SKILL.md +1 -0
- package/.ai-rules/skills/tech-debt/SKILL.md +1 -0
- package/.ai-rules/skills/test-coverage-gate/SKILL.md +303 -0
- package/.ai-rules/skills/tmux-master/SKILL.md +491 -0
- package/.ai-rules/skills/using-git-worktrees/SKILL.md +368 -0
- package/.ai-rules/skills/verification-before-completion/SKILL.md +234 -0
- package/.ai-rules/skills/widget-slot-architecture/SKILL.md +6 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/parallel-route-setup.tsx +206 -0
- package/.ai-rules/skills/widget-slot-architecture/examples/widget-component.tsx +250 -0
- package/.ai-rules/skills/writing-plans/SKILL.md +78 -0
- package/bin/cli.js +178 -0
- package/lib/init/detect-stack.js +148 -0
- package/lib/init/generate-config.js +31 -0
- package/lib/init/index.js +86 -0
- package/lib/init/prompt.js +60 -0
- package/lib/init/scaffold.js +67 -0
- package/lib/init/suggest-agent.js +46 -0
- package/package.json +10 -2
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Parallel Route Setup Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates how to set up a complete Next.js App Router page
|
|
5
|
+
* with multiple Parallel Route slots following the Widget-Slot Architecture.
|
|
6
|
+
*
|
|
7
|
+
* File structure this example represents:
|
|
8
|
+
*
|
|
9
|
+
* app/dashboard/
|
|
10
|
+
* ├── layout.tsx ← Root layout defining slot positions
|
|
11
|
+
* ├── page.tsx ← Main content (children)
|
|
12
|
+
* ├── @sidebar/
|
|
13
|
+
* │ ├── page.tsx ← Sidebar slot (imports SidebarWidget)
|
|
14
|
+
* │ ├── loading.tsx ← Sidebar loading state
|
|
15
|
+
* │ ├── error.tsx ← Sidebar error boundary
|
|
16
|
+
* │ └── default.tsx ← Sidebar fallback
|
|
17
|
+
* └── @activity/
|
|
18
|
+
* ├── page.tsx ← Activity slot (imports ActivityWidget)
|
|
19
|
+
* ├── loading.tsx ← Activity loading state
|
|
20
|
+
* ├── error.tsx ← Activity error boundary
|
|
21
|
+
* └── default.tsx ← Activity fallback
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// ═══════════════════════════════════════════════════════════
|
|
25
|
+
// 1. Dashboard Layout — defines slot positions (static shell)
|
|
26
|
+
// File: app/dashboard/layout.tsx
|
|
27
|
+
// ═══════════════════════════════════════════════════════════
|
|
28
|
+
|
|
29
|
+
import { type ReactNode } from 'react';
|
|
30
|
+
|
|
31
|
+
interface DashboardLayoutProps {
|
|
32
|
+
children: ReactNode; // Main content (app/dashboard/page.tsx)
|
|
33
|
+
sidebar: ReactNode; // Parallel route: @sidebar
|
|
34
|
+
activity: ReactNode; // Parallel route: @activity
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export default function DashboardLayout({
|
|
38
|
+
children,
|
|
39
|
+
sidebar,
|
|
40
|
+
activity,
|
|
41
|
+
}: DashboardLayoutProps) {
|
|
42
|
+
return (
|
|
43
|
+
<div className="dashboard-layout">
|
|
44
|
+
{/* Static shell — no business logic, only structure */}
|
|
45
|
+
<aside className="dashboard-layout__sidebar">
|
|
46
|
+
{sidebar}
|
|
47
|
+
</aside>
|
|
48
|
+
<main className="dashboard-layout__content">
|
|
49
|
+
{children}
|
|
50
|
+
</main>
|
|
51
|
+
<section className="dashboard-layout__activity">
|
|
52
|
+
{activity}
|
|
53
|
+
</section>
|
|
54
|
+
</div>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// ═══════════════════════════════════════════════════════════
|
|
59
|
+
// 2. Slot Page — connects one Widget to one Slot
|
|
60
|
+
// File: app/dashboard/@sidebar/page.tsx
|
|
61
|
+
// ═══════════════════════════════════════════════════════════
|
|
62
|
+
|
|
63
|
+
// import NavigationWidget from '@/Widgets/Navigation';
|
|
64
|
+
//
|
|
65
|
+
// export default function SidebarSlotPage() {
|
|
66
|
+
// return <NavigationWidget section="dashboard" />;
|
|
67
|
+
// }
|
|
68
|
+
|
|
69
|
+
// ═══════════════════════════════════════════════════════════
|
|
70
|
+
// 3. Slot Loading — independent loading state per slot
|
|
71
|
+
// File: app/dashboard/@sidebar/loading.tsx
|
|
72
|
+
// ═══════════════════════════════════════════════════════════
|
|
73
|
+
|
|
74
|
+
export function SidebarLoading() {
|
|
75
|
+
return (
|
|
76
|
+
<div className="slot-loading" aria-label="Loading sidebar">
|
|
77
|
+
<div className="slot-loading__skeleton" style={{ height: '2rem', width: '60%' }} />
|
|
78
|
+
<div className="slot-loading__skeleton" style={{ height: '1rem', width: '80%' }} />
|
|
79
|
+
<div className="slot-loading__skeleton" style={{ height: '1rem', width: '70%' }} />
|
|
80
|
+
<div className="slot-loading__skeleton" style={{ height: '1rem', width: '75%' }} />
|
|
81
|
+
</div>
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// ═══════════════════════════════════════════════════════════
|
|
86
|
+
// 4. Slot Error — fault isolation per slot
|
|
87
|
+
// File: app/dashboard/@sidebar/error.tsx
|
|
88
|
+
// ═══════════════════════════════════════════════════════════
|
|
89
|
+
|
|
90
|
+
// 'use client'; ← error.tsx must be a Client Component
|
|
91
|
+
//
|
|
92
|
+
// interface SlotErrorProps {
|
|
93
|
+
// error: Error & { digest?: string };
|
|
94
|
+
// reset: () => void;
|
|
95
|
+
// }
|
|
96
|
+
//
|
|
97
|
+
// export default function SidebarError({ error, reset }: SlotErrorProps) {
|
|
98
|
+
// return (
|
|
99
|
+
// <div className="slot-error" role="alert">
|
|
100
|
+
// <p className="slot-error__message">
|
|
101
|
+
// This section encountered an issue.
|
|
102
|
+
// </p>
|
|
103
|
+
// <button
|
|
104
|
+
// className="slot-error__retry"
|
|
105
|
+
// onClick={reset}
|
|
106
|
+
// type="button"
|
|
107
|
+
// >
|
|
108
|
+
// Try again
|
|
109
|
+
// </button>
|
|
110
|
+
// </div>
|
|
111
|
+
// );
|
|
112
|
+
// }
|
|
113
|
+
|
|
114
|
+
// ═══════════════════════════════════════════════════════════
|
|
115
|
+
// 5. Slot Default — fallback when no matching route
|
|
116
|
+
// File: app/dashboard/@sidebar/default.tsx
|
|
117
|
+
// ═══════════════════════════════════════════════════════════
|
|
118
|
+
|
|
119
|
+
export function SidebarDefault() {
|
|
120
|
+
return (
|
|
121
|
+
<div className="slot-default">
|
|
122
|
+
<p>Select a section to view navigation.</p>
|
|
123
|
+
</div>
|
|
124
|
+
);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ═══════════════════════════════════════════════════════════
|
|
128
|
+
// 6. CSS — Dashboard layout styles
|
|
129
|
+
// File: app/dashboard/dashboard.module.css
|
|
130
|
+
// ═══════════════════════════════════════════════════════════
|
|
131
|
+
|
|
132
|
+
/*
|
|
133
|
+
.dashboard-layout {
|
|
134
|
+
display: grid;
|
|
135
|
+
grid-template-columns: 280px 1fr 320px;
|
|
136
|
+
grid-template-rows: 1fr;
|
|
137
|
+
min-height: 100dvh;
|
|
138
|
+
gap: 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
.dashboard-layout__sidebar {
|
|
142
|
+
border-right: 1px solid var(--color-border);
|
|
143
|
+
overflow-y: auto;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.dashboard-layout__content {
|
|
147
|
+
padding: clamp(1rem, 3vw, 2rem);
|
|
148
|
+
overflow-y: auto;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
.dashboard-layout__activity {
|
|
152
|
+
border-left: 1px solid var(--color-border);
|
|
153
|
+
overflow-y: auto;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/* Responsive: stack on mobile */
|
|
157
|
+
@media (max-width: 768px) {
|
|
158
|
+
.dashboard-layout {
|
|
159
|
+
grid-template-columns: 1fr;
|
|
160
|
+
grid-template-rows: auto 1fr auto;
|
|
161
|
+
}
|
|
162
|
+
.dashboard-layout__sidebar {
|
|
163
|
+
border-right: none;
|
|
164
|
+
border-bottom: 1px solid var(--color-border);
|
|
165
|
+
}
|
|
166
|
+
.dashboard-layout__activity {
|
|
167
|
+
border-left: none;
|
|
168
|
+
border-top: 1px solid var(--color-border);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/* Slot loading skeleton */
|
|
173
|
+
.slot-loading {
|
|
174
|
+
display: flex;
|
|
175
|
+
flex-direction: column;
|
|
176
|
+
gap: 0.75rem;
|
|
177
|
+
padding: 1.5rem;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
.slot-loading__skeleton {
|
|
181
|
+
background: linear-gradient(90deg, var(--color-surface-raised) 25%, var(--color-border) 50%, var(--color-surface-raised) 75%);
|
|
182
|
+
background-size: 200% 100%;
|
|
183
|
+
animation: shimmer 1.5s ease-in-out infinite;
|
|
184
|
+
border-radius: 0.25rem;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@keyframes shimmer {
|
|
188
|
+
0% { background-position: 200% 0; }
|
|
189
|
+
100% { background-position: -200% 0; }
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/* Slot error */
|
|
193
|
+
.slot-error {
|
|
194
|
+
padding: 1.5rem;
|
|
195
|
+
text-align: center;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
.slot-error__retry {
|
|
199
|
+
margin-top: 0.75rem;
|
|
200
|
+
padding: 0.5rem 1rem;
|
|
201
|
+
border: 1px solid var(--color-border);
|
|
202
|
+
border-radius: 0.375rem;
|
|
203
|
+
background: var(--color-surface);
|
|
204
|
+
cursor: pointer;
|
|
205
|
+
}
|
|
206
|
+
*/
|
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Widget Component Example
|
|
3
|
+
*
|
|
4
|
+
* Demonstrates a complete Widget following the Widget-Slot Architecture:
|
|
5
|
+
* - Server Component entry point (data fetching)
|
|
6
|
+
* - Client Component UI (interactions)
|
|
7
|
+
* - Colocated Server Actions (mutations)
|
|
8
|
+
* - Barrel file exports
|
|
9
|
+
*
|
|
10
|
+
* File structure:
|
|
11
|
+
*
|
|
12
|
+
* Widgets/TaskBoard/
|
|
13
|
+
* ├── index.tsx ← Entry point (this file concept - Server Component)
|
|
14
|
+
* ├── types.ts ← Widget-specific types
|
|
15
|
+
* ├── ui/
|
|
16
|
+
* │ ├── index.ts ← Barrel file
|
|
17
|
+
* │ ├── TaskBoardUI.tsx ← Main UI (Client Component)
|
|
18
|
+
* │ └── TaskCard.tsx ← Sub-component (Client Component)
|
|
19
|
+
* ├── hooks/
|
|
20
|
+
* │ └── useTaskDrag.ts ← Widget-specific hook
|
|
21
|
+
* └── actions/
|
|
22
|
+
* ├── getTasks.ts ← Data fetching (cached)
|
|
23
|
+
* └── updateTask.ts ← Data mutation (revalidates)
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
// ═══════════════════════════════════════════════════════════
|
|
27
|
+
// types.ts — Widget-specific types
|
|
28
|
+
// ═══════════════════════════════════════════════════════════
|
|
29
|
+
|
|
30
|
+
export interface Task {
|
|
31
|
+
id: string;
|
|
32
|
+
title: string;
|
|
33
|
+
status: 'todo' | 'in_progress' | 'done';
|
|
34
|
+
assignee: string | null;
|
|
35
|
+
priority: 'low' | 'medium' | 'high';
|
|
36
|
+
createdAt: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface TaskBoardProps {
|
|
40
|
+
/** Project identifier to fetch tasks for */
|
|
41
|
+
projectId: string;
|
|
42
|
+
/** Optional filter for task status */
|
|
43
|
+
statusFilter?: Task['status'][];
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// ═══════════════════════════════════════════════════════════
|
|
47
|
+
// actions/getTasks.ts — Cached data fetching
|
|
48
|
+
// ═══════════════════════════════════════════════════════════
|
|
49
|
+
|
|
50
|
+
// 'use server';
|
|
51
|
+
// import { cache } from 'react';
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Fetch tasks for a project. Results are cached with a tag
|
|
55
|
+
* so other widgets can trigger re-fetch via revalidateTag.
|
|
56
|
+
*/
|
|
57
|
+
// export const getTasks = cache(async (projectId: string): Promise<Task[]> => {
|
|
58
|
+
// const res = await fetch(`${process.env.API_URL}/projects/${projectId}/tasks`, {
|
|
59
|
+
// next: { tags: [`tasks-${projectId}`] },
|
|
60
|
+
// });
|
|
61
|
+
//
|
|
62
|
+
// if (!res.ok) {
|
|
63
|
+
// throw new Error(`Failed to fetch tasks: ${res.status}`);
|
|
64
|
+
// }
|
|
65
|
+
//
|
|
66
|
+
// return res.json();
|
|
67
|
+
// });
|
|
68
|
+
|
|
69
|
+
// ═══════════════════════════════════════════════════════════
|
|
70
|
+
// actions/updateTask.ts — Data mutation with cache invalidation
|
|
71
|
+
// ═══════════════════════════════════════════════════════════
|
|
72
|
+
|
|
73
|
+
// 'use server';
|
|
74
|
+
// import { revalidateTag } from 'next/cache';
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Update a task's status. After mutation, invalidates the cache tag
|
|
78
|
+
* so all widgets consuming this project's tasks will re-fetch.
|
|
79
|
+
*/
|
|
80
|
+
// export async function updateTaskStatus(
|
|
81
|
+
// projectId: string,
|
|
82
|
+
// taskId: string,
|
|
83
|
+
// newStatus: Task['status'],
|
|
84
|
+
// ): Promise<{ success: boolean }> {
|
|
85
|
+
// const res = await fetch(
|
|
86
|
+
// `${process.env.API_URL}/projects/${projectId}/tasks/${taskId}`,
|
|
87
|
+
// {
|
|
88
|
+
// method: 'PATCH',
|
|
89
|
+
// headers: { 'Content-Type': 'application/json' },
|
|
90
|
+
// body: JSON.stringify({ status: newStatus }),
|
|
91
|
+
// },
|
|
92
|
+
// );
|
|
93
|
+
//
|
|
94
|
+
// if (!res.ok) {
|
|
95
|
+
// throw new Error(`Failed to update task: ${res.status}`);
|
|
96
|
+
// }
|
|
97
|
+
//
|
|
98
|
+
// // Invalidate cache so all widgets showing these tasks re-fetch
|
|
99
|
+
// revalidateTag(`tasks-${projectId}`);
|
|
100
|
+
//
|
|
101
|
+
// return { success: true };
|
|
102
|
+
// }
|
|
103
|
+
|
|
104
|
+
// ═══════════════════════════════════════════════════════════
|
|
105
|
+
// index.tsx — Widget Entry Point (Server Component)
|
|
106
|
+
// ═══════════════════════════════════════════════════════════
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* TaskBoard Widget — Server Component entry point.
|
|
110
|
+
*
|
|
111
|
+
* Principles:
|
|
112
|
+
* 1. Receives only minimal props (projectId) — fetches own data
|
|
113
|
+
* 2. Starts as Server Component — delegates interactivity to Client UI
|
|
114
|
+
* 3. Portable — works in any slot that provides a projectId
|
|
115
|
+
*/
|
|
116
|
+
|
|
117
|
+
// import { getTasks } from './actions/getTasks';
|
|
118
|
+
// import { TaskBoardUI } from './ui';
|
|
119
|
+
//
|
|
120
|
+
// export default async function TaskBoardWidget({
|
|
121
|
+
// projectId,
|
|
122
|
+
// statusFilter,
|
|
123
|
+
// }: TaskBoardProps) {
|
|
124
|
+
// const allTasks = await getTasks(projectId);
|
|
125
|
+
//
|
|
126
|
+
// const tasks = statusFilter
|
|
127
|
+
// ? allTasks.filter((t) => statusFilter.includes(t.status))
|
|
128
|
+
// : allTasks;
|
|
129
|
+
//
|
|
130
|
+
// if (tasks.length === 0) {
|
|
131
|
+
// return (
|
|
132
|
+
// <div className="task-board--empty">
|
|
133
|
+
// <p>No tasks found. Create your first task to get started.</p>
|
|
134
|
+
// </div>
|
|
135
|
+
// );
|
|
136
|
+
// }
|
|
137
|
+
//
|
|
138
|
+
// return <TaskBoardUI tasks={tasks} projectId={projectId} />;
|
|
139
|
+
// }
|
|
140
|
+
|
|
141
|
+
// ═══════════════════════════════════════════════════════════
|
|
142
|
+
// ui/TaskBoardUI.tsx — Main UI (Client Component)
|
|
143
|
+
// ═══════════════════════════════════════════════════════════
|
|
144
|
+
|
|
145
|
+
// 'use client';
|
|
146
|
+
//
|
|
147
|
+
// import { type Task } from '../types';
|
|
148
|
+
// import { TaskCard } from './TaskCard';
|
|
149
|
+
// import { updateTaskStatus } from '../actions/updateTask';
|
|
150
|
+
//
|
|
151
|
+
// interface TaskBoardUIProps {
|
|
152
|
+
// tasks: Task[];
|
|
153
|
+
// projectId: string;
|
|
154
|
+
// }
|
|
155
|
+
//
|
|
156
|
+
// const COLUMNS: Array<{ status: Task['status']; label: string }> = [
|
|
157
|
+
// { status: 'todo', label: 'To Do' },
|
|
158
|
+
// { status: 'in_progress', label: 'In Progress' },
|
|
159
|
+
// { status: 'done', label: 'Done' },
|
|
160
|
+
// ];
|
|
161
|
+
//
|
|
162
|
+
// export function TaskBoardUI({ tasks, projectId }: TaskBoardUIProps) {
|
|
163
|
+
// const handleStatusChange = async (taskId: string, newStatus: Task['status']) => {
|
|
164
|
+
// await updateTaskStatus(projectId, taskId, newStatus);
|
|
165
|
+
// };
|
|
166
|
+
//
|
|
167
|
+
// return (
|
|
168
|
+
// <div className="task-board">
|
|
169
|
+
// {COLUMNS.map(({ status, label }) => (
|
|
170
|
+
// <div key={status} className="task-board__column">
|
|
171
|
+
// <h3 className="task-board__column-header">
|
|
172
|
+
// {label}
|
|
173
|
+
// <span className="task-board__count">
|
|
174
|
+
// {tasks.filter((t) => t.status === status).length}
|
|
175
|
+
// </span>
|
|
176
|
+
// </h3>
|
|
177
|
+
// <div className="task-board__cards">
|
|
178
|
+
// {tasks
|
|
179
|
+
// .filter((t) => t.status === status)
|
|
180
|
+
// .map((task) => (
|
|
181
|
+
// <TaskCard
|
|
182
|
+
// key={task.id}
|
|
183
|
+
// task={task}
|
|
184
|
+
// onStatusChange={handleStatusChange}
|
|
185
|
+
// />
|
|
186
|
+
// ))}
|
|
187
|
+
// </div>
|
|
188
|
+
// </div>
|
|
189
|
+
// ))}
|
|
190
|
+
// </div>
|
|
191
|
+
// );
|
|
192
|
+
// }
|
|
193
|
+
|
|
194
|
+
// ═══════════════════════════════════════════════════════════
|
|
195
|
+
// ui/TaskCard.tsx — Sub-component (Client Component)
|
|
196
|
+
// ═══════════════════════════════════════════════════════════
|
|
197
|
+
|
|
198
|
+
// 'use client';
|
|
199
|
+
//
|
|
200
|
+
// import { type Task } from '../types';
|
|
201
|
+
//
|
|
202
|
+
// interface TaskCardProps {
|
|
203
|
+
// task: Task;
|
|
204
|
+
// onStatusChange: (taskId: string, newStatus: Task['status']) => Promise<void>;
|
|
205
|
+
// }
|
|
206
|
+
//
|
|
207
|
+
// const PRIORITY_COLORS: Record<Task['priority'], string> = {
|
|
208
|
+
// low: 'var(--color-success)',
|
|
209
|
+
// medium: 'var(--color-warning)',
|
|
210
|
+
// high: 'var(--color-danger)',
|
|
211
|
+
// };
|
|
212
|
+
//
|
|
213
|
+
// export function TaskCard({ task, onStatusChange }: TaskCardProps) {
|
|
214
|
+
// return (
|
|
215
|
+
// <article className="task-card" data-priority={task.priority}>
|
|
216
|
+
// <div
|
|
217
|
+
// className="task-card__priority-indicator"
|
|
218
|
+
// style={{ backgroundColor: PRIORITY_COLORS[task.priority] }}
|
|
219
|
+
// aria-label={`Priority: ${task.priority}`}
|
|
220
|
+
// />
|
|
221
|
+
// <h4 className="task-card__title">{task.title}</h4>
|
|
222
|
+
// {task.assignee && (
|
|
223
|
+
// <span className="task-card__assignee">{task.assignee}</span>
|
|
224
|
+
// )}
|
|
225
|
+
// </article>
|
|
226
|
+
// );
|
|
227
|
+
// }
|
|
228
|
+
|
|
229
|
+
// ═══════════════════════════════════════════════════════════
|
|
230
|
+
// ui/index.ts — Barrel file
|
|
231
|
+
// ═══════════════════════════════════════════════════════════
|
|
232
|
+
|
|
233
|
+
// export { TaskBoardUI } from './TaskBoardUI';
|
|
234
|
+
// export { TaskCard } from './TaskCard';
|
|
235
|
+
|
|
236
|
+
// ═══════════════════════════════════════════════════════════
|
|
237
|
+
// Slot connection example
|
|
238
|
+
// File: app/dashboard/@tasks/page.tsx
|
|
239
|
+
// ═══════════════════════════════════════════════════════════
|
|
240
|
+
|
|
241
|
+
// import TaskBoardWidget from '@/Widgets/TaskBoard';
|
|
242
|
+
//
|
|
243
|
+
// export default function TasksSlotPage({
|
|
244
|
+
// searchParams,
|
|
245
|
+
// }: {
|
|
246
|
+
// searchParams: { project?: string };
|
|
247
|
+
// }) {
|
|
248
|
+
// const projectId = searchParams.project ?? 'default';
|
|
249
|
+
// return <TaskBoardWidget projectId={projectId} />;
|
|
250
|
+
// }
|
|
@@ -41,9 +41,75 @@ Assume they are a skilled developer, but know almost nothing about our toolset o
|
|
|
41
41
|
|
|
42
42
|
**Tech Stack:** [Key technologies/libraries]
|
|
43
43
|
|
|
44
|
+
## Alternatives
|
|
45
|
+
|
|
46
|
+
### [Decision: e.g. State Management Approach]
|
|
47
|
+
|
|
48
|
+
| Criteria | Approach A: [Name] | Approach B: [Name] |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| Performance | ... | ... |
|
|
51
|
+
| Complexity | ... | ... |
|
|
52
|
+
| Maintainability | ... | ... |
|
|
53
|
+
|
|
54
|
+
**Decision:** [Chosen approach] — [One-line rationale]
|
|
55
|
+
|
|
44
56
|
---
|
|
45
57
|
```
|
|
46
58
|
|
|
59
|
+
## Alternatives Exploration
|
|
60
|
+
|
|
61
|
+
Every non-trivial decision in the plan MUST include at least two approaches with trade-off analysis.
|
|
62
|
+
|
|
63
|
+
**What counts as non-trivial:**
|
|
64
|
+
- Architecture choices (e.g. where to put new code)
|
|
65
|
+
- Data flow design (e.g. state management approach)
|
|
66
|
+
- API design (e.g. endpoint structure, payload shape)
|
|
67
|
+
- Testing strategy (e.g. unit vs integration)
|
|
68
|
+
|
|
69
|
+
**What does NOT need alternatives:**
|
|
70
|
+
- File naming that follows existing conventions
|
|
71
|
+
- Import statements
|
|
72
|
+
- Trivial implementation details
|
|
73
|
+
|
|
74
|
+
**Format for each decision:**
|
|
75
|
+
|
|
76
|
+
```markdown
|
|
77
|
+
### [Decision: Short Description]
|
|
78
|
+
|
|
79
|
+
| Criteria | Approach A: [Name] | Approach B: [Name] |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| Performance | ... | ... |
|
|
82
|
+
| Complexity | ... | ... |
|
|
83
|
+
| Maintainability | ... | ... |
|
|
84
|
+
|
|
85
|
+
**Decision:** [Chosen approach] — [One-line rationale]
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Rules:**
|
|
89
|
+
- Minimum 2 approaches per non-trivial decision
|
|
90
|
+
- Always include performance, complexity, and maintainability rows
|
|
91
|
+
- Add extra criteria rows when relevant (e.g. security, testability, migration cost)
|
|
92
|
+
- State the chosen approach with a clear rationale
|
|
93
|
+
- If only one viable approach exists, explain why alternatives were ruled out
|
|
94
|
+
|
|
95
|
+
## API Verification
|
|
96
|
+
|
|
97
|
+
When the plan references external APIs, SDKs, or protocols:
|
|
98
|
+
|
|
99
|
+
1. **Document every API assumption** with its source URL
|
|
100
|
+
2. **Create an "API Assumptions" table** in the plan:
|
|
101
|
+
|
|
102
|
+
| Assumption | Verified? | Source |
|
|
103
|
+
|-----------|:---------:|--------|
|
|
104
|
+
| Hook stdin has `tool_name` | ✅ | [Hooks Reference](url) |
|
|
105
|
+
| Hook stdin has `session_cost` | ❌ UNVERIFIED | — |
|
|
106
|
+
|
|
107
|
+
1. **All assumptions must be verified before ACT phase**
|
|
108
|
+
2. **WebFetch/WebSearch the official docs** — never rely on memory or guesses
|
|
109
|
+
|
|
110
|
+
### Why This Matters
|
|
111
|
+
Unverified API assumptions propagate into implementation, causing features to be unimplementable as designed. Verification at PLAN time costs minutes; rework at ACT time costs hours.
|
|
112
|
+
|
|
47
113
|
## Task Structure
|
|
48
114
|
|
|
49
115
|
```markdown
|
|
@@ -94,6 +160,18 @@ git commit -m "feat: add specific feature"
|
|
|
94
160
|
- Reference relevant skills with @ syntax
|
|
95
161
|
- DRY, YAGNI, TDD, frequent commits
|
|
96
162
|
|
|
163
|
+
## Self-Review Gate
|
|
164
|
+
|
|
165
|
+
**Before submitting the plan, verify ALL items pass:**
|
|
166
|
+
|
|
167
|
+
- [ ] All file paths verified (existing files confirmed, new files marked as `Create:`)
|
|
168
|
+
- [ ] Steps are bite-sized (2-5 minutes each, one action per step)
|
|
169
|
+
- [ ] Risks identified with mitigation (what could go wrong and how to handle it)
|
|
170
|
+
- [ ] TDD applied where appropriate (test-first for core logic, test-after for UI)
|
|
171
|
+
- [ ] No over-engineering (YAGNI check — remove anything not directly needed)
|
|
172
|
+
|
|
173
|
+
**If any item fails:** Fix the plan before proceeding. Do not hand off an incomplete plan.
|
|
174
|
+
|
|
97
175
|
## Execution Handoff
|
|
98
176
|
|
|
99
177
|
After saving the plan, offer execution choice:
|