@minhduydev/mdpi 0.4.1 → 0.6.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.
Files changed (41) hide show
  1. package/dist/index.js +4 -2
  2. package/dist/template/.pi/AGENTS.md +1 -1
  3. package/dist/template/.pi/README.md +2 -3
  4. package/dist/template/.pi/VERSION +1 -1
  5. package/dist/template/.pi/agents/explore.md +1 -1
  6. package/dist/template/.pi/agents/scout.md +1 -1
  7. package/dist/template/.pi/extensions/templates-injector.ts +35 -7
  8. package/dist/template/.pi/prompts/INDEX.md +3 -9
  9. package/dist/template/.pi/prompts/gc.md +2 -1
  10. package/dist/template/.pi/prompts/verify.md +24 -0
  11. package/dist/template/.pi/skills/INDEX.md +40 -8
  12. package/dist/template/.pi/skills/dcp-hygiene/SKILL.md +1 -1
  13. package/dist/template/.pi/skills/frontend-design/SKILL.md +1 -1
  14. package/dist/template/.pi/skills/frontend-design/references/animation/motion-advanced.md +88 -15
  15. package/dist/template/.pi/skills/frontend-design/references/animation/motion-core.md +148 -13
  16. package/dist/template/.pi/skills/frontend-design/references/shadcn/setup.md +127 -20
  17. package/dist/template/.pi/skills/nextjs-app-router/SKILL.md +334 -0
  18. package/dist/template/.pi/skills/nextjs-cache/SKILL.md +262 -0
  19. package/dist/template/.pi/skills/react-best-practices/SKILL.md +79 -1
  20. package/dist/template/.pi/skills/react-compiler/SKILL.md +237 -0
  21. package/dist/template/.pi/skills/react-hook-form/SKILL.md +374 -0
  22. package/dist/template/.pi/skills/react-server-actions/SKILL.md +299 -0
  23. package/dist/template/.pi/skills/shadcn-ui/SKILL.md +404 -0
  24. package/dist/template/.pi/skills/tanstack-query/SKILL.md +330 -0
  25. package/dist/template/.pi/skills/v0/SKILL.md +264 -0
  26. package/dist/template/.pi/skills/zustand/SKILL.md +333 -0
  27. package/package.json +1 -1
  28. package/dist/template/.pi/context/fallow.md +0 -137
  29. package/dist/template/.pi/prompts/loop-check.md +0 -87
  30. package/dist/template/.pi/prompts/loop-init.md +0 -157
  31. package/dist/template/.pi/prompts/loop-review.md +0 -90
  32. package/dist/template/.pi/skills/loop-audit/SKILL.md +0 -141
  33. package/dist/template/.pi/skills/loop-cost/SKILL.md +0 -130
  34. package/dist/template/.pi/skills/loop-engineering/SKILL.md +0 -175
  35. package/dist/template/.pi/templates/loop-github-action.yml +0 -162
  36. package/dist/template/.pi/templates/loop-orchestrator.sh +0 -514
  37. package/dist/template/.pi/templates/loop-orchestrator.test.ts +0 -332
  38. package/dist/template/.pi/templates/loop-orchestrator.ts +0 -936
  39. package/dist/template/.pi/templates/loop-state.json +0 -24
  40. package/dist/template/.pi/templates/loop-state.md +0 -98
  41. package/dist/template/.pi/templates/loop-vision.md +0 -110
@@ -0,0 +1,333 @@
1
+ ---
2
+ name: zustand
3
+ description: Use when implementing global or shared state management in React with Zustand v5. Covers store creation, slices pattern, middleware, selective subscriptions, React 19 + SSR integration. MUST load before any state management implementation.
4
+ ---
5
+
6
+ # Zustand v5
7
+
8
+ ## When to Use
9
+
10
+ - Managing global or shared client-side state in React
11
+ - Replacing Redux, Jotai, or Context for state management
12
+ - Creating stores that are used across multiple components
13
+ - Implementing slices for domain-separated state
14
+ - Persisting state to localStorage or sessionStorage
15
+ - Using middleware (immer, devtools, persist)
16
+
17
+ ## When NOT to Use
18
+
19
+ - Server state (use TanStack Query or Server Components)
20
+ - Form state (use React Hook Form or Server Actions)
21
+ - Single-component local state (use `useState` or `useReducer`)
22
+ - Server Components (Zustand is client-only)
23
+
24
+ ## Setup
25
+
26
+ ```bash
27
+ npm install zustand
28
+ ```
29
+
30
+ ## Basic Store
31
+
32
+ ```tsx
33
+ // stores/counter.ts
34
+ import { create } from 'zustand'
35
+
36
+ interface CounterState {
37
+ count: number
38
+ increment: () => void
39
+ decrement: () => void
40
+ reset: () => void
41
+ }
42
+
43
+ export const useCounterStore = create<CounterState>((set) => ({
44
+ count: 0,
45
+ increment: () => set((state) => ({ count: state.count + 1 })),
46
+ decrement: () => set((state) => ({ count: state.count - 1 })),
47
+ reset: () => set({ count: 0 }),
48
+ }))
49
+ ```
50
+
51
+ ```tsx
52
+ // components/Counter.tsx
53
+ 'use client'
54
+
55
+ import { useCounterStore } from '@/stores/counter'
56
+
57
+ export function Counter() {
58
+ const { count, increment, decrement } = useCounterStore()
59
+
60
+ return (
61
+ <div>
62
+ <p>Count: {count}</p>
63
+ <button onClick={increment}>+</button>
64
+ <button onClick={decrement}>-</button>
65
+ </div>
66
+ )
67
+ }
68
+ ```
69
+
70
+ ## Slices Pattern
71
+
72
+ Split store into domain slices:
73
+
74
+ ```tsx
75
+ // stores/index.ts
76
+ import { create } from 'zustand'
77
+ import { createAuthSlice, type AuthSlice } from './slices/auth'
78
+ import { createCartSlice, type CartSlice } from './slices/cart'
79
+
80
+ type Store = AuthSlice & CartSlice
81
+
82
+ export const useStore = create<Store>()((...args) => ({
83
+ ...createAuthSlice(...args),
84
+ ...createCartSlice(...args),
85
+ }))
86
+ ```
87
+
88
+ ```tsx
89
+ // stores/slices/auth.ts
90
+ import type { StateCreator } from 'zustand'
91
+
92
+ export interface AuthSlice {
93
+ user: User | null
94
+ login: (credentials: Credentials) => Promise<void>
95
+ logout: () => void
96
+ }
97
+
98
+ export const createAuthSlice: StateCreator<AuthSlice, [], [], AuthSlice> = (set) => ({
99
+ user: null,
100
+ login: async (credentials) => {
101
+ const user = await api.login(credentials)
102
+ set({ user })
103
+ },
104
+ logout: () => set({ user: null }),
105
+ })
106
+ ```
107
+
108
+ ## Selective Subscriptions
109
+
110
+ Zustand re-renders only when **used** state changes:
111
+
112
+ ```tsx
113
+ // ❌ Whole store — re-renders on any change
114
+ const { count, name } = useStore()
115
+
116
+ // ✅ Selective — re-renders only when count changes
117
+ const count = useStore((state) => state.count)
118
+ const increment = useStore((state) => state.increment) // Stable reference
119
+
120
+ // ✅ Multiple values — useShallow for objects
121
+ import { useShallow } from 'zustand/react/shallow'
122
+
123
+ const { name, email } = useStore(
124
+ useShallow((state) => ({ name: state.user.name, email: state.user.email }))
125
+ )
126
+ ```
127
+
128
+ `useShallow` does shallow equality — avoids re-render when both values are the same.
129
+
130
+ ## Middleware
131
+
132
+ ### Persist (localStorage)
133
+
134
+ ```tsx
135
+ import { create } from 'zustand'
136
+ import { persist } from 'zustand/middleware'
137
+
138
+ export const useSettingsStore = create(
139
+ persist(
140
+ (set) => ({
141
+ theme: 'light',
142
+ setTheme: (theme: 'light' | 'dark') => set({ theme }),
143
+ }),
144
+ {
145
+ name: 'app-settings', // localStorage key
146
+ partialize: (state) => ({ theme: state.theme }), // Only persist theme
147
+ }
148
+ )
149
+ )
150
+ ```
151
+
152
+ ### Immer (Immutable Updates)
153
+
154
+ ```tsx
155
+ import { create } from 'zustand'
156
+ import { immer } from 'zustand/middleware/immer'
157
+
158
+ export const useTodoStore = create(
159
+ immer((set) => ({
160
+ todos: [] as Todo[],
161
+ addTodo: (text: string) =>
162
+ set((state) => {
163
+ state.todos.push({ id: crypto.randomUUID(), text, done: false })
164
+ }),
165
+ toggleTodo: (id: string) =>
166
+ set((state) => {
167
+ const todo = state.todos.find((t) => t.id === id)
168
+ if (todo) todo.done = !todo.done
169
+ }),
170
+ }))
171
+ )
172
+ ```
173
+
174
+ ### DevTools
175
+
176
+ ```tsx
177
+ import { create } from 'zustand'
178
+ import { devtools } from 'zustand/middleware'
179
+
180
+ export const useStore = create(
181
+ devtools(
182
+ (set) => ({
183
+ count: 0,
184
+ increment: () => set((s) => ({ count: s.count + 1 }), false, 'increment'),
185
+ }),
186
+ { name: 'AppStore' } // Name in Redux DevTools
187
+ )
188
+ )
189
+ ```
190
+
191
+ ### Combining Multiple Middleware
192
+
193
+ ```tsx
194
+ import { create } from 'zustand'
195
+ import { devtools, persist, immer } from 'zustand/middleware'
196
+
197
+ export const useStore = create(
198
+ devtools(
199
+ persist(
200
+ immer((set) => ({
201
+ // store...
202
+ })),
203
+ { name: 'app-storage' }
204
+ ),
205
+ { name: 'AppStore' }
206
+ )
207
+ )
208
+ ```
209
+
210
+ ## React 19 + Server Components
211
+
212
+ Zustand is **client-only**. Pattern for Next.js App Router:
213
+
214
+ ```tsx
215
+ // stores/useStore.ts
216
+ import { create } from 'zustand'
217
+
218
+ export const useStore = create<Store>((set) => ({
219
+ // ...
220
+ }))
221
+ ```
222
+
223
+ ```tsx
224
+ // components/ClientWrapper.tsx
225
+ 'use client'
226
+
227
+ import { useStore } from '@/stores/useStore'
228
+
229
+ export function ClientWrapper({ children }) {
230
+ const data = useStore((s) => s.data)
231
+
232
+ return <div>{children}</div>
233
+ }
234
+ ```
235
+
236
+ **Rules**:
237
+ - Stores are defined outside components (module scope)
238
+ - Store consumers must be in `'use client'` components
239
+ - Server Components can import the store type but cannot `useStore()`
240
+ - Use React Context to provide a store instance if you need SSR hydration
241
+
242
+ ## SSR Hydration Pattern
243
+
244
+ ```tsx
245
+ // app/providers.tsx
246
+ 'use client'
247
+
248
+ import { type ReactNode, createContext, useContext, useRef } from 'react'
249
+ import { type StoreApi, useStore } from 'zustand'
250
+
251
+ // Create context for store
252
+ const StoreContext = createContext<StoreApi<AppStore> | null>(null)
253
+
254
+ export function StoreProvider({ children }: { children: ReactNode }) {
255
+ const storeRef = useRef<StoreApi<AppStore>>()
256
+
257
+ if (!storeRef.current) {
258
+ storeRef.current = createAppStore()
259
+ }
260
+
261
+ return (
262
+ <StoreContext.Provider value={storeRef.current}>
263
+ {children}
264
+ </StoreContext.Provider>
265
+ )
266
+ }
267
+
268
+ // Hook to use the store
269
+ export function useAppStore<T>(selector: (state: AppStore) => T): T {
270
+ const store = useContext(StoreContext)
271
+ if (!store) throw new Error('Missing StoreProvider')
272
+ return useStore(store, selector)
273
+ }
274
+ ```
275
+
276
+ ## Async Actions
277
+
278
+ ```tsx
279
+ // Async actions are just async functions in the store:
280
+ interface UserStore {
281
+ user: User | null
282
+ loading: boolean
283
+ error: Error | null
284
+ fetchUser: (id: string) => Promise<void>
285
+ }
286
+
287
+ export const useUserStore = create<UserStore>((set) => ({
288
+ user: null,
289
+ loading: false,
290
+ error: null,
291
+ fetchUser: async (id) => {
292
+ set({ loading: true, error: null })
293
+ try {
294
+ const user = await api.getUser(id)
295
+ set({ user, loading: false })
296
+ } catch (error) {
297
+ set({ error: error as Error, loading: false })
298
+ }
299
+ },
300
+ }))
301
+ ```
302
+
303
+ ## When to Use Zustand vs Context vs TanStack Query
304
+
305
+ | Tool | Best for |
306
+ |------|----------|
307
+ | **Zustand** | Global client state: theme, auth, cart, UI preferences |
308
+ | **React Context** | Dependency injection, theming, auth provider — static values that rarely change |
309
+ | **TanStack Query** | Server state: data fetching, caching, mutations |
310
+ | **useState/useReducer** | Local component state |
311
+
312
+ ## Common Pitfalls
313
+
314
+ | Pitfall | Fix |
315
+ |---------|-----|
316
+ | Using Zustand for server state | Use TanStack Query for fetched data — Zustand for client-only state |
317
+ | `useStore()` without selector — re-renders on any change | Always use selectors: `useStore(s => s.count)` |
318
+ | Multiple values returned as new object every render | Use `useShallow` for object selectors |
319
+ | Store in Server Component | Move store usage to `'use client'` |
320
+ | `getState()` in render | `getState()` is for callbacks/outside React, not render |
321
+ | Large stores with everything in one file | Use slices pattern to separate domains |
322
+ | Recreating store on every render | Define store outside component or use `useRef` for context pattern |
323
+
324
+ ## Verification
325
+
326
+ - [ ] Store defined outside component (module scope) or via `useRef` in provider
327
+ - [ ] All store consumers are in `'use client'` components
328
+ - [ ] Selectors used for granular subscriptions — no destructured `useStore()`
329
+ - [ ] `useShallow` used for multi-value object selectors
330
+ - [ ] Server state (fetched data) managed by TanStack Query, not Zustand
331
+ - [ ] Slices pattern used for stores with multiple domains
332
+ - [ ] `persist` middleware configured with `partialize` to avoid storing sensitive data
333
+ - [ ] DevTools middleware enabled (development only)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@minhduydev/mdpi",
3
- "version": "0.4.1",
3
+ "version": "0.6.0",
4
4
  "description": "CLI to scaffold and manage a Pi coding-agent kit (.pi/) in any repo",
5
5
  "keywords": [
6
6
  "pi",
@@ -1,137 +0,0 @@
1
- ---
2
- purpose: Fallow codebase intelligence commands for AI agents — dead code, duplication, complexity, and audit gating
3
- updated: 2026-06-04
4
- ---
5
-
6
- # Fallow — Codebase Intelligence Reference
7
-
8
- ## Overview
9
-
10
- Fallow is a Rust-native, deterministic static analysis tool for TypeScript/JavaScript codebases.
11
- **No AI inside the analyzer** — same input always produces the same output.
12
- It builds a complete module graph to find issues no linter or type checker can see.
13
-
14
- ---
15
-
16
- ## Commands
17
-
18
- ### Full Analysis (single pass)
19
-
20
- ```bash
21
- npx fallow # All analyses: dead code + duplication + health
22
- npx fallow --format json # Structured output for agent parsing
23
- ```
24
-
25
- ### Dead Code
26
-
27
- ```bash
28
- npx fallow dead-code # Full dead code report
29
- npx fallow dead-code --format json --quiet # JSON for agents
30
- npx fallow dead-code --unused-exports # Only unused exports
31
- npx fallow dead-code --unused-dependencies # Only unused deps
32
- npx fallow dead-code --circular # Only circular deps
33
- npx fallow fix --dry-run # Preview safe auto-fixes
34
- npx fallow fix --yes # Apply auto-fixes
35
- ```
36
-
37
- ### Trace (investigate before deleting)
38
-
39
- ```bash
40
- npx fallow dead-code --trace FILE:EXPORT_NAME # Why is this export flagged?
41
- npx fallow dead-code --trace-dependency PACKAGE_NAME # Where is this dep imported?
42
- ```
43
-
44
- ### Duplication
45
-
46
- ```bash
47
- npx fallow dupes # Find code clones
48
- npx fallow dupes --mode strict # Exact matches only
49
- npx fallow dupes --mode weak # Structural matches
50
- npx fallow dupes --trace FILE:LINE # Deep-dive a specific clone group
51
- ```
52
-
53
- ### Health (complexity)
54
-
55
- ```bash
56
- npx fallow health # Complexity hotspots + refactoring targets
57
- npx fallow health --format json # Structured output
58
- ```
59
-
60
- ### Audit Gate (for CI / pre-commit)
61
-
62
- ```bash
63
- npx fallow audit # Check changed files (verdict: pass/warn/fail)
64
- npx fallow audit --format json # Structured verdict for agents
65
- npx fallow audit --gate new-only # Only flag new issues, not pre-existing
66
- ```
67
-
68
- ---
69
-
70
- ## Workflow Patterns
71
-
72
- ### Post-Edit Verification Loop
73
-
74
- ```bash
75
- # 1. Make changes
76
- # 2. Run audit
77
- npx fallow audit --format json --quiet
78
- # 3. If verdict is "fail", inspect findings
79
- # 4. Fix or investigate with --trace
80
- # 5. Re-run audit until pass
81
- ```
82
-
83
- ### Codebase Cleanup
84
-
85
- ```bash
86
- npx fallow # Full picture
87
- npx fallow dead-code --format json # Find unused code
88
- npx fallow fix --dry-run # Preview auto-removals
89
- npx fallow fix --yes # Apply auto-fixes
90
- npx fallow dupes # Find duplication
91
- npx fallow health # Find complexity hotspots
92
- ```
93
-
94
- ### Monorepo / Workspace
95
-
96
- ```bash
97
- npx fallow --workspace # Analyze all workspaces
98
- npx fallow --workspace packages/pkg # Analyze specific workspace
99
- ```
100
-
101
- ---
102
-
103
- ## Understanding Output
104
-
105
- Every finding in `--format json` includes:
106
-
107
- ```json
108
- {
109
- "path": "src/utils/example.ts:42",
110
- "issue_type": "unused-exports",
111
- "actions": [
112
- {
113
- "type": "delete-export",
114
- "auto_fixable": true,
115
- "description": "Remove unused export"
116
- }
117
- ]
118
- }
119
- ```
120
-
121
- The `actions[]` array is machine-actionable. Agents can inspect `auto_fixable` flags and apply safe fixes programmatically.
122
-
123
- ---
124
-
125
- ## Config
126
-
127
- Fallow auto-detects your project. For custom config, run:
128
-
129
- ```bash
130
- npx fallow init # Generates .fallow/config.yaml with auto-detected settings
131
- ```
132
-
133
- Common config patterns:
134
- - `ignorePatterns` — exclude directories from analysis (e.g., `.pi/`)
135
- - `entry` — declare additional entry points
136
- - `publicPackages` — packages with public API surface
137
- - `rules` — custom issue severity rules
@@ -1,87 +0,0 @@
1
- ---
2
- description: NO-GO qualification gate — decide whether a task is safe to run as an unattended loop
3
- argument-hint: "<task description> [--help]"
4
- ---
5
-
6
- # Loop-Check: $ARGUMENTS
7
-
8
- Qualify a task before it is ever scheduled as an unattended loop. Emit a single GO/NO-GO verdict with the cited condition that produced it. This prompt is the gate *before* the gate — it refuses work a loop cannot honestly judge.
9
-
10
- > Refuse first, test second, checklist last. Never let "looks automatable" override the refuse-list.
11
-
12
- ## Parse Arguments
13
-
14
- | Argument | Default | Description |
15
- | ------------- | -------- | ------------------------------------------------ |
16
- | `<task>` | required | The task proposed for unattended looping |
17
- | `--help` | false | Show this usage |
18
-
19
- ## Step 1 — Refuse-List (Immediate NO-GO)
20
-
21
- If the task touches any of these, **stop here** and emit NO-GO. Do not proceed to the 2-condition test.
22
-
23
- - **auth** — login, sessions, tokens, permissions, auth flows, auth module rewrites
24
- - **payments** — billing, checkout, refunds, subscriptions, payment provider integration
25
- - **architecture** — framework/library switches, schema migrations, new DB tables, new services, breaking API changes
26
-
27
- > Rationale: these need human judgement a loop cannot provide. The refuse-list is the honest ceiling, not a configurable preference.
28
-
29
- If a refuse-list category is hit → `CONDITION: <refused category> refused` (e.g. `CONDITION: architecture refused`).
30
-
31
- ## Step 2 — The 2-Condition Test
32
-
33
- Both conditions must hold. If either fails, emit NO-GO citing the failed condition.
34
-
35
- 1. **Verification is automated.** There exists an objective command whose **exit code** decides pass/fail — `npm test`, `tsc --noEmit`, `eslint`, a custom gate script. The stop condition is computational (exit code), never an LLM's opinion. If the only "check" is "the agent says it looks good" → FAIL.
36
- 2. **The token budget absorbs the waste.** The cost of one wasted run (gate fails, no-op, early-exit) is small enough that running on the scheduled cadence does not blow the budget. If a single failed run is expensive (large model, long context, many turns) and there is no per-run cap → FAIL.
37
-
38
- If condition 1 fails → `CONDITION: verification not automated (no exit-code gate)`.
39
- If condition 2 fails → `CONDITION: budget does not absorb waste (uncapped/expensive failed run)`.
40
-
41
- ## Step 3 — 30-Second Checklist
42
-
43
- Confirm every item. Any miss → NO-GO citing the missing item.
44
-
45
- - [ ] **Objective gate exists** — a named command with a pass = exit 0 contract (write it down; "tests" without a command is not a gate).
46
- - [ ] **Hard stop exists** — a non-negotiable exit condition: repeated gate failure, budget cap hit, or forbidden path touched. The loop exits; it does not improvise.
47
- - [ ] **Human approves merge/deploy/dependency changes** — the loop may open PRs and stage work; a human merges, deploys, and changes deps. No auto-merge.
48
-
49
- Missing gate → `CONDITION: no objective gate`.
50
- Missing hard stop → `CONDITION: no hard stop`.
51
- No human-approval boundary → `CONDITION: no human-approval boundary on merge/deploy/deps`.
52
-
53
- ## Step 4 — Emit Verdict
54
-
55
- Output **exactly** these two lines and nothing else as the decision:
56
-
57
- ```
58
- VERDICT: GO
59
- CONDITION: verification automated + budget absorbs waste
60
- ```
61
-
62
- or
63
-
64
- ```
65
- VERDICT: NO-GO
66
- CONDITION: <cited condition from Step 1, 2, or 3>
67
- ```
68
-
69
- - `VERDICT` is `GO` or `NO-GO` — no other values.
70
- - `CONDITION` cites the specific condition that produced the verdict. For GO, cite both passing conditions. For NO-GO, cite the first failing condition (Step 1 overrides Step 2 overrides Step 3).
71
- - Do not add rationale, recommendations, or next steps after the verdict lines. The verdict is the contract.
72
-
73
- ## Worked Examples
74
-
75
- - **"triage failing CI nightly"** with `npm test` gate, no-op early-exit <5k tokens → `VERDICT: GO` / `CONDITION: verification automated + budget absorbs waste`
76
- - **"rewrite auth module"** → `VERDICT: NO-GO` / `CONDITION: auth refused`
77
- - **"migrate from REST to GraphQL"** → `VERDICT: NO-GO` / `CONDITION: architecture refused`
78
- - **"keep docs in sync with code weekly"** with no gate command → `VERDICT: NO-GO` / `CONDITION: verification not automated (no exit-code gate)`
79
-
80
- ## Related
81
-
82
- | Companion | Role |
83
- |---|---|
84
- | `loop-engineering` skill | The 2-condition test, refuse-list, and 5 building blocks this gate codifies |
85
- | `/loop-init` | Scaffold `.pi/loops/<name>/` once a task is GO |
86
- | `/loop-review` | Maker/checker verification — the gate *during* a run |
87
- | `loop-audit` | Readiness scoring (L0-L3) for an already-GO loop |