@comfanion/workflow 4.37.2 → 4.38.1-dev.1

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.
@@ -44,6 +44,7 @@ interface SessionContext {
44
44
  story: StoryContext | null
45
45
  relevantFiles: string[]
46
46
  activeAgent: string | null
47
+ activeCommand: string | null // /dev-story, /dev-epic, /dev-sprint
47
48
  }
48
49
 
49
50
  // Base files ALL agents need after compaction (to remember who they are)
@@ -53,20 +54,18 @@ const BASE_FILES = [
53
54
  ]
54
55
 
55
56
  // Agent-specific file priorities (added to BASE_FILES)
57
+ // These are OPTIONAL files for context, not mandatory
56
58
  const AGENT_FILES: Record<string, string[]> = {
57
59
  dev: [
58
60
  ...BASE_FILES,
59
61
  "docs/coding-standards/README.md",
60
62
  "docs/coding-standards/patterns.md",
61
- "docs/coding-standards/testing.md",
62
- "docs/prd.md",
63
- "docs/architecture.md",
63
+ // NO prd.md, NO architecture.md - too large, story has context
64
64
  // story path added dynamically
65
65
  ],
66
66
  coder: [
67
67
  ...BASE_FILES,
68
68
  "docs/coding-standards/patterns.md",
69
- "docs/coding-standards/testing.md",
70
69
  ],
71
70
  architect: [
72
71
  ...BASE_FILES,
@@ -110,19 +109,19 @@ const DEFAULT_FILES = [
110
109
  ]
111
110
 
112
111
  // Files agent MUST Read after compaction (commands generated)
112
+ // MINIMAL CONTEXT: ~70KB for dev, not 200KB+
113
+ // Note: coding-standards/README.md is standard path created by /coding-standards command
113
114
  const MUST_READ_FILES: Record<string, string[]> = {
114
115
  dev: [
115
116
  "AGENTS.md",
116
117
  "CLAUDE.md",
117
- "docs/prd.md",
118
- "docs/architecture.md",
119
- // story path added dynamically
118
+ "docs/coding-standards/README.md", // if exists
119
+ // story/epic state path added dynamically
120
120
  ],
121
121
  coder: [
122
122
  "AGENTS.md",
123
123
  "CLAUDE.md",
124
- "docs/prd.md",
125
- "docs/architecture.md",
124
+ "docs/coding-standards/README.md",
126
125
  ],
127
126
  architect: [
128
127
  "AGENTS.md",
@@ -134,7 +133,6 @@ const MUST_READ_FILES: Record<string, string[]> = {
134
133
  "AGENTS.md",
135
134
  "CLAUDE.md",
136
135
  "docs/prd.md",
137
- "docs/architecture.md",
138
136
  ],
139
137
  analyst: [
140
138
  "AGENTS.md",
@@ -148,8 +146,6 @@ const MUST_READ_FILES: Record<string, string[]> = {
148
146
  default: [
149
147
  "AGENTS.md",
150
148
  "CLAUDE.md",
151
- "docs/prd.md",
152
- "docs/architecture.md",
153
149
  ],
154
150
  }
155
151
 
@@ -173,13 +169,28 @@ export const CustomCompactionPlugin: Plugin = async (ctx) => {
173
169
  /**
174
170
  * Generate Read commands that agent MUST execute after compaction
175
171
  */
176
- function generateReadCommands(agent: string | null, story: StoryContext | null): string {
172
+ async function generateReadCommands(agent: string | null, story: StoryContext | null, activeCommand: string | null): Promise<string> {
177
173
  const agentKey = (typeof agent === 'string' ? agent.toLowerCase() : null) || "default"
178
174
  const filesToRead = [...(MUST_READ_FILES[agentKey] || MUST_READ_FILES.default)]
179
175
 
180
- // For dev/coder: add story file if active
181
- if ((agentKey === "dev" || agentKey === "coder") && story) {
182
- filesToRead.unshift(story.path) // Story first!
176
+ // For dev/coder: add command file first
177
+ if ((agentKey === "dev" || agentKey === "coder") && activeCommand) {
178
+ const commandFile = activeCommand.replace("/", "") + ".md"
179
+ filesToRead.unshift(`.opencode/commands/${commandFile}`)
180
+ }
181
+
182
+ // For dev/coder: add epic state file if in epic workflow
183
+ if ((agentKey === "dev" || agentKey === "coder")) {
184
+ const epicState = await getActiveEpicState()
185
+ if (epicState) {
186
+ // Epic state file (has all context)
187
+ filesToRead.unshift(epicState.statePath.replace(directory + "/", ""))
188
+ }
189
+
190
+ // Then story file if active
191
+ if (story) {
192
+ filesToRead.unshift(story.path) // Story first!
193
+ }
183
194
  }
184
195
 
185
196
  const commands = filesToRead.map((f, i) => `${i + 1}. Read("${f}")`).join("\n")
@@ -203,8 +214,158 @@ DO NOT skip this step. DO NOT ask user what to do. Just read these files first.`
203
214
  }
204
215
  }
205
216
 
217
+ interface EpicState {
218
+ statePath: string
219
+ epicId: string
220
+ epicTitle: string
221
+ status: string
222
+ currentStoryIndex: number
223
+ totalStories: number
224
+ nextAction: string | null
225
+ nextStoryPath: string | null
226
+ completedCount: number
227
+ pendingCount: number
228
+ }
229
+
230
+ async function getActiveEpicState(): Promise<EpicState | null> {
231
+ try {
232
+ // Search for epic state files in all sprint folders
233
+ const sprintArtifactsPath = join(directory, "docs", "sprint-artifacts")
234
+ const entries = await readdir(sprintArtifactsPath)
235
+
236
+ for (const entry of entries) {
237
+ if (entry.startsWith("sprint-")) {
238
+ const statePath = join(sprintArtifactsPath, entry, ".sprint-state")
239
+ try {
240
+ const stateFiles = await readdir(statePath)
241
+ for (const stateFile of stateFiles) {
242
+ if (stateFile.endsWith("-state.yaml")) {
243
+ const fullPath = join(statePath, stateFile)
244
+ const content = await readFile(fullPath, "utf-8")
245
+
246
+ // Check if this epic is in-progress
247
+ if (content.includes("status: \"in-progress\"") || content.includes("status: in-progress")) {
248
+ // Parse epic state
249
+ const epicIdMatch = content.match(/epic_id:\s*["']?([^"'\n]+)["']?/i)
250
+ const epicTitleMatch = content.match(/epic_title:\s*["']?([^"'\n]+)["']?/i)
251
+ const statusMatch = content.match(/status:\s*["']?([^"'\n]+)["']?/i)
252
+ const currentIndexMatch = content.match(/current_story_index:\s*(\d+)/i)
253
+ const totalStoriesMatch = content.match(/total_stories:\s*(\d+)/i)
254
+ const nextActionMatch = content.match(/next_action:\s*["']?([^"'\n]+)["']?/i)
255
+
256
+ // Count completed/pending stories
257
+ const completedSection = content.match(/completed_stories:([\s\S]*?)(?=pending_stories:|$)/i)
258
+ const pendingSection = content.match(/pending_stories:([\s\S]*?)(?=\n\w+:|$)/i)
259
+
260
+ const completedCount = completedSection
261
+ ? (completedSection[1].match(/- path:/g) || []).length
262
+ : 0
263
+ const pendingCount = pendingSection
264
+ ? (pendingSection[1].match(/- path:/g) || []).length
265
+ : 0
266
+
267
+ // Extract next story path from next_action
268
+ let nextStoryPath: string | null = null
269
+ if (nextActionMatch) {
270
+ const actionText = nextActionMatch[1]
271
+ const storyFileMatch = actionText.match(/story-[\w-]+\.md/i)
272
+ if (storyFileMatch) {
273
+ // Find full path in pending_stories
274
+ const pathMatch = content.match(new RegExp(`path:\\s*["']?([^"'\\n]*${storyFileMatch[0]}[^"'\\n]*)["']?`, 'i'))
275
+ if (pathMatch) {
276
+ nextStoryPath = pathMatch[1]
277
+ }
278
+ }
279
+ }
280
+
281
+ return {
282
+ statePath: fullPath.replace(directory + "/", ""),
283
+ epicId: epicIdMatch?.[1] || "unknown",
284
+ epicTitle: epicTitleMatch?.[1] || "Unknown Epic",
285
+ status: statusMatch?.[1] || "in-progress",
286
+ currentStoryIndex: currentIndexMatch ? parseInt(currentIndexMatch[1]) : 0,
287
+ totalStories: totalStoriesMatch ? parseInt(totalStoriesMatch[1]) : 0,
288
+ nextAction: nextActionMatch?.[1] || null,
289
+ nextStoryPath,
290
+ completedCount,
291
+ pendingCount
292
+ }
293
+ }
294
+ }
295
+ }
296
+ } catch {
297
+ // No .sprint-state folder in this sprint
298
+ }
299
+ }
300
+ }
301
+ return null
302
+ } catch {
303
+ return null
304
+ }
305
+ }
306
+
206
307
  async function getActiveStory(): Promise<StoryContext | null> {
207
308
  try {
309
+ // First, try to find epic state file
310
+ const epicState = await getActiveEpicState()
311
+ if (epicState) {
312
+ // Parse epic state to get current story
313
+ const storyPathMatch = epicState.content.match(/next_action:\s*["']?Execute\s+(.+?)["']?$/m)
314
+ if (storyPathMatch) {
315
+ const storyFileName = storyPathMatch[1]
316
+ // Find story file
317
+ const sprintMatch = epicState.statePath.match(/sprint-(\d+)/)
318
+ if (sprintMatch) {
319
+ const storyPath = `docs/sprint-artifacts/sprint-${sprintMatch[1]}/stories/${storyFileName}`
320
+ const storyContent = await readFile(join(directory, storyPath), "utf-8")
321
+
322
+ const titleMatch = storyContent.match(/^#\s+(.+)/m)
323
+ const statusMatch = storyContent.match(/\*\*Status:\*\*\s*(\w+)/i)
324
+
325
+ const completedTasks: string[] = []
326
+ const pendingTasks: string[] = []
327
+ let currentTask: string | null = null
328
+
329
+ // Parse tasks with more detail
330
+ const taskRegex = /- \[([ x])\]\s+\*\*T(\d+)\*\*[:\s]+(.+?)(?=\n|$)/g
331
+ let match
332
+ while ((match = taskRegex.exec(storyContent)) !== null) {
333
+ const [, checked, taskId, taskName] = match
334
+ const taskInfo = `T${taskId}: ${taskName.trim()}`
335
+ if (checked === "x") {
336
+ completedTasks.push(taskInfo)
337
+ } else {
338
+ if (!currentTask) currentTask = taskInfo
339
+ pendingTasks.push(taskInfo)
340
+ }
341
+ }
342
+
343
+ // Parse acceptance criteria
344
+ const acceptanceCriteria: string[] = []
345
+ const acSection = storyContent.match(/## Acceptance Criteria[\s\S]*?(?=##|$)/i)
346
+ if (acSection) {
347
+ const acRegex = /- \[([ x])\]\s+(.+?)(?=\n|$)/g
348
+ while ((match = acRegex.exec(acSection[0])) !== null) {
349
+ const [, checked, criteria] = match
350
+ acceptanceCriteria.push(`${checked === "x" ? "✅" : "⬜"} ${criteria.trim()}`)
351
+ }
352
+ }
353
+
354
+ return {
355
+ path: storyPath,
356
+ title: titleMatch?.[1] || "Unknown Story",
357
+ status: statusMatch?.[1] || "unknown",
358
+ currentTask,
359
+ completedTasks,
360
+ pendingTasks,
361
+ acceptanceCriteria,
362
+ fullContent: storyContent
363
+ }
364
+ }
365
+ }
366
+ }
367
+
368
+ // Fallback: try old sprint-status.yaml format
208
369
  const sprintStatusPath = join(directory, "docs", "sprint-artifacts", "sprint-status.yaml")
209
370
  const content = await readFile(sprintStatusPath, "utf-8")
210
371
 
@@ -300,21 +461,72 @@ DO NOT skip this step. DO NOT ask user what to do. Just read these files first.`
300
461
  return relevantPaths
301
462
  }
302
463
 
464
+ async function detectActiveCommand(todos: TaskStatus[], epicState: EpicState | null): Promise<string | null> {
465
+ // Detect command from TODO structure
466
+ if (todos.length === 0) return null
467
+
468
+ // Check if TODOs are epics (sprint mode)
469
+ const hasEpicTodos = todos.some(t => t.content.toLowerCase().includes("epic"))
470
+ if (hasEpicTodos) return "/dev-sprint"
471
+
472
+ // Check if TODOs are stories (epic mode)
473
+ const hasStoryTodos = todos.some(t => t.content.toLowerCase().includes("story"))
474
+ if (hasStoryTodos || epicState) return "/dev-epic"
475
+
476
+ // Regular story mode
477
+ return "/dev-story"
478
+ }
479
+
303
480
  async function buildContext(agent: string | null): Promise<SessionContext> {
304
481
  const [todos, story] = await Promise.all([
305
482
  getTodoList(),
306
483
  getActiveStory()
307
484
  ])
308
485
 
486
+ const epicState = await getActiveEpicState()
309
487
  const relevantFiles = await getRelevantFiles(agent, story)
488
+ const activeCommand = await detectActiveCommand(todos, epicState)
310
489
 
311
- return { todos, story, relevantFiles, activeAgent: agent }
490
+ return { todos, story, relevantFiles, activeAgent: agent, activeCommand }
312
491
  }
313
492
 
314
- function formatDevContext(ctx: SessionContext): string {
493
+ async function formatDevContext(ctx: SessionContext): Promise<string> {
315
494
  const sections: string[] = []
316
495
 
317
- if (ctx.story) {
496
+ // Check if we're in epic workflow
497
+ const epicState = await getActiveEpicState()
498
+
499
+ if (epicState) {
500
+ // Epic/Sprint workflow mode - show epic progress
501
+ const progress = epicState.totalStories > 0
502
+ ? ((epicState.completedCount / epicState.totalStories) * 100).toFixed(0)
503
+ : 0
504
+
505
+ sections.push(`## 🎯 Epic Workflow: ${epicState.epicTitle}
506
+
507
+ **Epic ID:** ${epicState.epicId}
508
+ **Epic State:** \`${epicState.statePath}\` ← READ THIS FIRST
509
+ **Progress:** ${progress}% (${epicState.completedCount}/${epicState.totalStories} stories)
510
+
511
+ ### Next Action (DO THIS NOW)
512
+ \`\`\`
513
+ ${epicState.nextAction || "All stories complete - run epic integration tests"}
514
+ \`\`\`
515
+
516
+ ${epicState.nextStoryPath ? `**Next Story:** \`${epicState.nextStoryPath}\` ← READ THIS SECOND` : ""}
517
+
518
+ ### Epic Progress
519
+ **Completed Stories:** ${epicState.completedCount}
520
+ **Pending Stories:** ${epicState.pendingCount}
521
+ **Current Index:** ${epicState.currentStoryIndex}
522
+
523
+ ---
524
+
525
+ 💡 **Note:** If this is part of /dev-sprint, after epic completes:
526
+ 1. Update sprint-status.yaml (mark epic done)
527
+ 2. Continue to next epic automatically`)
528
+ } else if (ctx.story) {
529
+ // Regular story mode
318
530
  const s = ctx.story
319
531
  const total = s.completedTasks.length + s.pendingTasks.length
320
532
  const progress = total > 0 ? (s.completedTasks.length / total * 100).toFixed(0) : 0
@@ -433,13 +645,13 @@ ${ctx.relevantFiles.map(f => `- \`${f}\``).join("\n")}`)
433
645
  return sections.join("\n\n---\n\n")
434
646
  }
435
647
 
436
- function formatContext(ctx: SessionContext): string {
648
+ async function formatContext(ctx: SessionContext): Promise<string> {
437
649
  const agent = ctx.activeAgent?.toLowerCase()
438
650
 
439
651
  switch (agent) {
440
652
  case "dev":
441
653
  case "coder":
442
- return formatDevContext(ctx)
654
+ return await formatDevContext(ctx)
443
655
  case "architect":
444
656
  return formatArchitectContext(ctx)
445
657
  case "pm":
@@ -453,12 +665,15 @@ ${ctx.relevantFiles.map(f => `- \`${f}\``).join("\n")}`)
453
665
  }
454
666
  }
455
667
 
456
- function formatInstructions(ctx: SessionContext): string {
668
+ async function formatInstructions(ctx: SessionContext): Promise<string> {
457
669
  const agent = ctx.activeAgent?.toLowerCase()
458
670
  const hasInProgressTasks = ctx.todos.some(t => t.status === "in_progress")
459
671
  const hasInProgressStory = ctx.story?.status === "in-progress"
672
+
673
+ // Check if we're in epic workflow
674
+ const epicState = await getActiveEpicState()
460
675
 
461
- if (!hasInProgressTasks && !hasInProgressStory) {
676
+ if (!hasInProgressTasks && !hasInProgressStory && !epicState) {
462
677
  return `## Status: COMPLETED ✅
463
678
 
464
679
  Previous task was completed successfully.
@@ -469,7 +684,67 @@ Previous task was completed successfully.
469
684
  3. Ask user for next task`
470
685
  }
471
686
 
472
- // Dev-specific instructions
687
+ // Sprint workflow instructions
688
+ if (ctx.activeCommand === "/dev-sprint" && (agent === "dev" || agent === "coder")) {
689
+ const nextEpicTodo = ctx.todos.find(t => t.status === "in_progress" && t.content.toLowerCase().includes("epic"))
690
+ return `## Status: SPRINT IN PROGRESS 🔄
691
+
692
+ **Active Command:** ${ctx.activeCommand}
693
+ **Active Agent:** @${agent}
694
+ **Next Epic:** ${nextEpicTodo?.content || "check TODO"}
695
+
696
+ ### Resume Protocol (AUTOMATIC - DO NOT ASK USER)
697
+ 1. **Read command:** \`.opencode/commands/dev-sprint.md\`
698
+ 2. **Read sprint-status.yaml**
699
+ 3. **Find next epic** from TODO or sprint-status.yaml
700
+ 4. **Execute epic** via /dev-epic workflow
701
+ 5. **After epic done:**
702
+ - Update sprint-status.yaml (mark epic done)
703
+ - Update TODO (mark epic completed, next epic in_progress)
704
+ - Continue next epic automatically
705
+
706
+ ### DO NOT
707
+ - Ask user what to do (TODO + sprint-status.yaml tell you)
708
+ - Re-read completed epics
709
+ - Wait for confirmation between epics (auto-continue)
710
+
711
+ ### IMPORTANT
712
+ This is /dev-sprint autopilot mode. Execute epics sequentially until sprint done.`
713
+ }
714
+
715
+ // Epic workflow instructions
716
+ if ((ctx.activeCommand === "/dev-epic" || epicState) && (agent === "dev" || agent === "coder")) {
717
+ return `## Status: EPIC IN PROGRESS 🔄
718
+
719
+ **Active Command:** ${ctx.activeCommand || "/dev-epic"}
720
+ **Active Agent:** @${agent}
721
+ **Epic:** ${epicState?.epicTitle || "check epic state"}
722
+ **Next Action:** ${epicState?.nextAction || "check TODO"}
723
+
724
+ ### Resume Protocol (AUTOMATIC - DO NOT ASK USER)
725
+ 1. **Read command:** \`.opencode/commands/dev-epic.md\`
726
+ 2. **Read epic state:** \`${epicState?.statePath || "find in .sprint-state/"}\`
727
+ 3. **Read next story:** \`${epicState?.nextStoryPath || "check epic state"}\`
728
+ 4. **Load skill:** \`.opencode/skills/dev-story/SKILL.md\`
729
+ 5. **Execute story** following /dev-story workflow
730
+ 6. **After story done:**
731
+ - Update epic state file (move story to completed)
732
+ - Update TODO (mark story completed, next story in_progress)
733
+ - Increment current_story_index
734
+ - Set next_action to next story
735
+ - Continue next story automatically
736
+
737
+ ### DO NOT
738
+ - Ask user what to do (epic state + TODO tell you)
739
+ - Re-read completed stories
740
+ - Re-read epic file (info in state)
741
+ - Wait for confirmation between stories (auto-continue)
742
+
743
+ ### IMPORTANT
744
+ This is /dev-epic autopilot mode. Execute stories sequentially until epic done.`
745
+ }
746
+
747
+ // Dev-specific instructions (regular story)
473
748
  if ((agent === "dev" || agent === "coder") && ctx.story) {
474
749
  return `## Status: IN PROGRESS 🔄
475
750
 
@@ -554,9 +829,9 @@ Previous task was completed successfully.
554
829
  await log(directory, ` todos: ${ctx.todos.length}`)
555
830
  await log(directory, ` relevantFiles: ${ctx.relevantFiles.length}`)
556
831
 
557
- const context = formatContext(ctx)
558
- const instructions = formatInstructions(ctx)
559
- const readCommands = generateReadCommands(agent, ctx.story)
832
+ const context = await formatContext(ctx)
833
+ const instructions = await formatInstructions(ctx)
834
+ const readCommands = await generateReadCommands(agent, ctx.story, ctx.activeCommand)
560
835
 
561
836
  // Agent identity reminder
562
837
  const agentIdentity = agent
@@ -13,9 +13,26 @@ metadata:
13
13
 
14
14
  You are a Senior Staff Engineer specializing in establishing coding standards. You create **modular documentation** - multiple focused files (1-10 files), each under 2000 lines.
15
15
 
16
- ## Core Principle: Modular Documentation
16
+ ## Core Principle: README.md is the Main File
17
17
 
18
- **CRITICAL RULE:** Each file MUST be under 2000 lines. Split into multiple files when needed.
18
+ **CRITICAL:** During development, agents read ONLY `README.md` from coding-standards (to save context ~65KB).
19
+
20
+ Therefore `README.md` MUST contain:
21
+ - **ALL critical rules** (not just links!)
22
+ - **Common patterns with examples**
23
+ - **Naming conventions**
24
+ - **Project structure overview**
25
+ - Links to other files for deep dive
26
+
27
+ Other files (testing.md, api.md, etc.) are for **deep dive only** - agents read them when story's "Required Reading" points to them.
28
+
29
+ ## Size Guidelines
30
+
31
+ | File | Max Size | Purpose |
32
+ |------|----------|---------|
33
+ | README.md | 20-30KB | Main file, all critical rules |
34
+ | Other files | 10-20KB each | Deep dive on specific topics |
35
+ | Total | <2000 lines/file | Keep files readable |
19
36
 
20
37
  ## Documentation Structure
21
38
 
@@ -35,7 +52,7 @@ docs/coding-standards/
35
52
 
36
53
  ## File Templates
37
54
 
38
- ### README.md (Index)
55
+ ### README.md (Main File - Agents Read This!)
39
56
 
40
57
  ```markdown
41
58
  # Coding Standards
@@ -44,33 +61,72 @@ docs/coding-standards/
44
61
  **Tech Stack:** [languages, frameworks]
45
62
  **Last Updated:** YYYY-MM-DD
46
63
 
47
- ## Quick Reference
64
+ ## Project Structure
65
+
66
+ \`\`\`
67
+ project/
68
+ ├── src/ # Source code
69
+ │ ├── modules/ # Business modules (domain, service, handler)
70
+ │ └── internal/ # Shared infrastructure
71
+ ├── docs/ # Documentation
72
+ └── tests/ # Test files
73
+ \`\`\`
74
+
75
+ ## Naming Conventions
76
+
77
+ ### Files
78
+ - `snake_case` for files: `user_service.go`, `auth_handler.ts`
79
+ - Test files: `*_test.go`, `*.test.ts`
80
+
81
+ ### Code
82
+ - Types/Classes: `PascalCase` - `UserService`, `AuthHandler`
83
+ - Functions: `camelCase` (TS) or `PascalCase` (Go exported)
84
+ - Variables: `camelCase` - `userId`, `isValid`
85
+ - Constants: `UPPER_SNAKE_CASE` - `MAX_RETRIES`
48
86
 
49
- | Topic | Document | Key Rules |
50
- |-------|----------|-----------|
51
- | Project Layout | [project-structure.md](./project-structure.md) | Modules, structure |
52
- | Go Code | [go-standards.md](./go-standards.md) | Naming, errors, imports |
53
- | Testing | [testing-standards.md](./testing-standards.md) | 80% coverage, table-driven |
54
- | API | [api-standards.md](./api-standards.md) | REST, error format |
55
- | Database | [database-standards.md](./database-standards.md) | Migrations, SQLC |
56
- | Security | [security-standards.md](./security-standards.md) | Input validation |
57
- | Libraries | [libraries.md](./libraries.md) | Approved list |
58
- | Git | [git-workflow.md](./git-workflow.md) | Conventional commits |
87
+ ## Common Patterns
88
+
89
+ ### Service Pattern
90
+ [Example of service implementation in your language]
91
+
92
+ ### Repository Pattern
93
+ [Example of repository implementation]
59
94
 
60
- ## Critical Rules (MUST follow)
95
+ ### Error Handling
96
+ [Example of error handling pattern]
61
97
 
62
- 1. [Most important rule]
63
- 2. [Second most important]
64
- 3. [Third most important]
98
+ ## Critical Rules
65
99
 
66
- ## Getting Started
100
+ 1. **No business logic in handlers** - handlers only validate input and call services
101
+ 2. **All errors must be wrapped** with context
102
+ 3. **No hardcoded values** - use config or constants
103
+ 4. **Tests required** for all business logic
67
104
 
68
- New to the project? Read in this order:
69
- 1. [project-structure.md](./project-structure.md)
70
- 2. [[language]-standards.md](./)
71
- 3. [architecture-patterns.md](./architecture-patterns.md)
105
+ ## API Response Format
106
+
107
+ ### Success
108
+ \`\`\`json
109
+ { "data": { ... } }
110
+ \`\`\`
111
+
112
+ ### Error
113
+ \`\`\`json
114
+ { "error": { "code": "VALIDATION_ERROR", "message": "..." } }
115
+ \`\`\`
116
+
117
+ ## Deep Dive Documents
118
+
119
+ | Topic | File | When to Read |
120
+ |-------|------|--------------|
121
+ | Testing | [testing.md](./testing.md) | Writing tests |
122
+ | API Design | [api.md](./api.md) | Creating endpoints |
123
+ | Database | [database.md](./database.md) | Schema changes |
124
+ | Security | [security.md](./security.md) | Auth, validation |
125
+ | Git | [git.md](./git.md) | Commits, PRs |
72
126
  ```
73
127
 
128
+ **Note:** This README should be 20-30KB with real examples, not placeholders.
129
+
74
130
  ### project-structure.md
75
131
 
76
132
  ```markdown