devmind 1.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.
Files changed (141) hide show
  1. package/LICENSE +191 -0
  2. package/README.md +148 -0
  3. package/dist/cli.d.ts +6 -0
  4. package/dist/cli.js +232 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/codebase/generators/architecture.d.ts +4 -0
  7. package/dist/codebase/generators/architecture.js +33 -0
  8. package/dist/codebase/generators/architecture.js.map +1 -0
  9. package/dist/codebase/generators/modules.d.ts +9 -0
  10. package/dist/codebase/generators/modules.js +46 -0
  11. package/dist/codebase/generators/modules.js.map +1 -0
  12. package/dist/codebase/generators/overview.d.ts +5 -0
  13. package/dist/codebase/generators/overview.js +34 -0
  14. package/dist/codebase/generators/overview.js.map +1 -0
  15. package/dist/codebase/generators/skeleton.d.ts +8 -0
  16. package/dist/codebase/generators/skeleton.js +57 -0
  17. package/dist/codebase/generators/skeleton.js.map +1 -0
  18. package/dist/codebase/index.d.ts +24 -0
  19. package/dist/codebase/index.js +50 -0
  20. package/dist/codebase/index.js.map +1 -0
  21. package/dist/codebase/parsers/typescript.d.ts +14 -0
  22. package/dist/codebase/parsers/typescript.js +145 -0
  23. package/dist/codebase/parsers/typescript.js.map +1 -0
  24. package/dist/codebase/scanners/filesystem.d.ts +17 -0
  25. package/dist/codebase/scanners/filesystem.js +152 -0
  26. package/dist/codebase/scanners/filesystem.js.map +1 -0
  27. package/dist/codebase/utils/hashing.d.ts +15 -0
  28. package/dist/codebase/utils/hashing.js +50 -0
  29. package/dist/codebase/utils/hashing.js.map +1 -0
  30. package/dist/codebase/utils/stats.d.ts +12 -0
  31. package/dist/codebase/utils/stats.js +55 -0
  32. package/dist/codebase/utils/stats.js.map +1 -0
  33. package/dist/codebase/utils/tree.d.ts +10 -0
  34. package/dist/codebase/utils/tree.js +22 -0
  35. package/dist/codebase/utils/tree.js.map +1 -0
  36. package/dist/commands/analyze.d.ts +6 -0
  37. package/dist/commands/analyze.js +97 -0
  38. package/dist/commands/analyze.js.map +1 -0
  39. package/dist/commands/context.d.ts +4 -0
  40. package/dist/commands/context.js +54 -0
  41. package/dist/commands/context.js.map +1 -0
  42. package/dist/core/config.d.ts +20 -0
  43. package/dist/core/config.js +40 -0
  44. package/dist/core/config.js.map +1 -0
  45. package/dist/core/errors.d.ts +18 -0
  46. package/dist/core/errors.js +53 -0
  47. package/dist/core/errors.js.map +1 -0
  48. package/dist/core/fileio.d.ts +10 -0
  49. package/dist/core/fileio.js +57 -0
  50. package/dist/core/fileio.js.map +1 -0
  51. package/dist/core/index.d.ts +8 -0
  52. package/dist/core/index.js +9 -0
  53. package/dist/core/index.js.map +1 -0
  54. package/dist/core/logger.d.ts +15 -0
  55. package/dist/core/logger.js +40 -0
  56. package/dist/core/logger.js.map +1 -0
  57. package/dist/core/types.d.ts +106 -0
  58. package/dist/core/types.js +5 -0
  59. package/dist/core/types.js.map +1 -0
  60. package/dist/database/cli.d.ts +7 -0
  61. package/dist/database/cli.js +132 -0
  62. package/dist/database/cli.js.map +1 -0
  63. package/dist/database/commands/checkpoint.d.ts +13 -0
  64. package/dist/database/commands/checkpoint.js +136 -0
  65. package/dist/database/commands/checkpoint.js.map +1 -0
  66. package/dist/database/commands/generate.d.ts +21 -0
  67. package/dist/database/commands/generate.js +269 -0
  68. package/dist/database/commands/generate.js.map +1 -0
  69. package/dist/database/commands/handoff.d.ts +15 -0
  70. package/dist/database/commands/handoff.js +332 -0
  71. package/dist/database/commands/handoff.js.map +1 -0
  72. package/dist/database/commands/history.d.ts +13 -0
  73. package/dist/database/commands/history.js +148 -0
  74. package/dist/database/commands/history.js.map +1 -0
  75. package/dist/database/commands/init.d.ts +10 -0
  76. package/dist/database/commands/init.js +28 -0
  77. package/dist/database/commands/init.js.map +1 -0
  78. package/dist/database/commands/learn.d.ts +12 -0
  79. package/dist/database/commands/learn.js +93 -0
  80. package/dist/database/commands/learn.js.map +1 -0
  81. package/dist/database/commands/memory.d.ts +90 -0
  82. package/dist/database/commands/memory.js +353 -0
  83. package/dist/database/commands/memory.js.map +1 -0
  84. package/dist/database/commands/show.d.ts +9 -0
  85. package/dist/database/commands/show.js +136 -0
  86. package/dist/database/commands/show.js.map +1 -0
  87. package/dist/database/commands/validate.d.ts +9 -0
  88. package/dist/database/commands/validate.js +200 -0
  89. package/dist/database/commands/validate.js.map +1 -0
  90. package/dist/database/commands/watch.d.ts +9 -0
  91. package/dist/database/commands/watch.js +77 -0
  92. package/dist/database/commands/watch.js.map +1 -0
  93. package/dist/database/extractors/drizzle.d.ts +62 -0
  94. package/dist/database/extractors/drizzle.js +251 -0
  95. package/dist/database/extractors/drizzle.js.map +1 -0
  96. package/dist/database/extractors/firebase.d.ts +39 -0
  97. package/dist/database/extractors/firebase.js +192 -0
  98. package/dist/database/extractors/firebase.js.map +1 -0
  99. package/dist/database/extractors/index.d.ts +69 -0
  100. package/dist/database/extractors/index.js +345 -0
  101. package/dist/database/extractors/index.js.map +1 -0
  102. package/dist/database/extractors/mongodb.d.ts +44 -0
  103. package/dist/database/extractors/mongodb.js +198 -0
  104. package/dist/database/extractors/mongodb.js.map +1 -0
  105. package/dist/database/extractors/mysql.d.ts +61 -0
  106. package/dist/database/extractors/mysql.js +173 -0
  107. package/dist/database/extractors/mysql.js.map +1 -0
  108. package/dist/database/extractors/postgres.d.ts +47 -0
  109. package/dist/database/extractors/postgres.js +141 -0
  110. package/dist/database/extractors/postgres.js.map +1 -0
  111. package/dist/database/extractors/prisma.d.ts +71 -0
  112. package/dist/database/extractors/prisma.js +270 -0
  113. package/dist/database/extractors/prisma.js.map +1 -0
  114. package/dist/database/extractors/sqlite.d.ts +50 -0
  115. package/dist/database/extractors/sqlite.js +148 -0
  116. package/dist/database/extractors/sqlite.js.map +1 -0
  117. package/dist/database/generators/learning-generator.d.ts +48 -0
  118. package/dist/database/generators/learning-generator.js +221 -0
  119. package/dist/database/generators/learning-generator.js.map +1 -0
  120. package/dist/database/generators/templates.d.ts +103 -0
  121. package/dist/database/generators/templates.js +1557 -0
  122. package/dist/database/generators/templates.js.map +1 -0
  123. package/dist/database/index.d.ts +15 -0
  124. package/dist/database/index.js +16 -0
  125. package/dist/database/index.js.map +1 -0
  126. package/dist/database/utils/json-output.d.ts +26 -0
  127. package/dist/database/utils/json-output.js +37 -0
  128. package/dist/database/utils/json-output.js.map +1 -0
  129. package/dist/generators/unified.d.ts +1 -0
  130. package/dist/generators/unified.js +173 -0
  131. package/dist/generators/unified.js.map +1 -0
  132. package/dist/index.d.ts +4 -0
  133. package/dist/index.js +5 -0
  134. package/dist/index.js.map +1 -0
  135. package/dist/utils/config-detector.d.ts +1 -0
  136. package/dist/utils/config-detector.js +40 -0
  137. package/dist/utils/config-detector.js.map +1 -0
  138. package/dist/utils/config-loader.d.ts +16 -0
  139. package/dist/utils/config-loader.js +20 -0
  140. package/dist/utils/config-loader.js.map +1 -0
  141. package/package.json +78 -0
@@ -0,0 +1,1557 @@
1
+ /**
2
+ * Template Generator - Enhanced Version
3
+ * Generates CLAUDE.md and AGENTS.md from schema information with full relationship support
4
+ */
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+ // ============================================================================
8
+ // Type Mappings
9
+ // ============================================================================
10
+ const TYPE_MAPPINGS = [
11
+ // PostgreSQL
12
+ { dbType: 'uuid', tsType: 'string', notes: 'UUID v4' },
13
+ { dbType: 'text', tsType: 'string' },
14
+ { dbType: 'varchar', tsType: 'string' },
15
+ { dbType: 'integer', tsType: 'number' },
16
+ { dbType: 'bigint', tsType: 'number' },
17
+ { dbType: 'boolean', tsType: 'boolean' },
18
+ { dbType: 'timestamp', tsType: 'Date' },
19
+ { dbType: 'jsonb', tsType: 'Record<string, unknown>' },
20
+ // MySQL
21
+ { dbType: 'int', tsType: 'number' },
22
+ { dbType: 'datetime', tsType: 'Date' },
23
+ { dbType: 'enum', tsType: 'string', notes: 'Enum values' },
24
+ // SQLite
25
+ { dbType: 'INTEGER', tsType: 'number' },
26
+ { dbType: 'TEXT', tsType: 'string' },
27
+ { dbType: 'REAL', tsType: 'number' },
28
+ { dbType: 'BLOB', tsType: 'Buffer' },
29
+ // Prisma
30
+ { dbType: 'String', tsType: 'string' },
31
+ { dbType: 'Int', tsType: 'number' },
32
+ { dbType: 'Boolean', tsType: 'boolean' },
33
+ { dbType: 'DateTime', tsType: 'Date' },
34
+ ];
35
+ // ============================================================================
36
+ // Unified Schema Converter
37
+ // ============================================================================
38
+ export class UnifiedSchemaConverter {
39
+ static fromPostgres(schema) {
40
+ return {
41
+ tables: schema.tables.map((table) => ({
42
+ name: table.name,
43
+ description: table.description || undefined,
44
+ columns: table.columns.map((col) => ({
45
+ name: col.name,
46
+ type: col.type,
47
+ nullable: col.nullable,
48
+ default: col.default,
49
+ isPrimaryKey: table.primaryKey.includes(col.name),
50
+ isUnique: table.indexes.some((i) => i.unique && i.columns.includes(col.name)),
51
+ isForeignKey: table.foreignKeys.some((fk) => fk.column === col.name),
52
+ referencesTable: table.foreignKeys.find((fk) => fk.column === col.name)?.referencesTable,
53
+ referencesColumn: table.foreignKeys.find((fk) => fk.column === col.name)
54
+ ?.referencesColumn,
55
+ onDelete: table.foreignKeys.find((fk) => fk.column === col.name)?.onDelete || undefined,
56
+ description: col.description || undefined,
57
+ })),
58
+ indexes: table.indexes.map((idx) => ({
59
+ name: idx.name,
60
+ columns: idx.columns,
61
+ unique: idx.unique,
62
+ isPrimaryKey: idx.isPrimaryKey,
63
+ })),
64
+ relations: table.foreignKeys.map((fk) => ({
65
+ fromTable: table.name,
66
+ fromColumn: fk.column,
67
+ toTable: fk.referencesTable,
68
+ toColumn: fk.referencesColumn,
69
+ cardinality: 'N:1',
70
+ onDelete: fk.onDelete || undefined,
71
+ })),
72
+ primaryKey: table.primaryKey,
73
+ })),
74
+ databaseType: 'postgresql',
75
+ schemaName: schema.schemaName,
76
+ };
77
+ }
78
+ static fromMySQL(schema) {
79
+ return {
80
+ tables: schema.tables.map((table) => ({
81
+ name: table.name,
82
+ description: table.comment || undefined,
83
+ columns: table.columns.map((col) => ({
84
+ name: col.name,
85
+ type: col.type,
86
+ nullable: col.nullable,
87
+ default: col.default,
88
+ isPrimaryKey: table.primaryKey.includes(col.name),
89
+ isUnique: table.indexes.some((i) => i.unique && i.columns.includes(col.name)),
90
+ isForeignKey: table.foreignKeys.some((fk) => fk.column === col.name),
91
+ referencesTable: table.foreignKeys.find((fk) => fk.column === col.name)?.referencesTable,
92
+ referencesColumn: table.foreignKeys.find((fk) => fk.column === col.name)
93
+ ?.referencesColumn,
94
+ onDelete: table.foreignKeys.find((fk) => fk.column === col.name)?.onDelete || undefined,
95
+ description: col.comment || undefined,
96
+ })),
97
+ indexes: table.indexes.map((idx) => ({
98
+ name: idx.name,
99
+ columns: idx.columns,
100
+ unique: idx.unique,
101
+ isPrimaryKey: idx.isPrimaryKey,
102
+ })),
103
+ relations: table.foreignKeys.map((fk) => ({
104
+ fromTable: table.name,
105
+ fromColumn: fk.column,
106
+ toTable: fk.referencesTable,
107
+ toColumn: fk.referencesColumn,
108
+ cardinality: 'N:1',
109
+ onDelete: fk.onDelete || undefined,
110
+ })),
111
+ primaryKey: table.primaryKey,
112
+ })),
113
+ databaseType: 'mysql',
114
+ schemaName: schema.databaseName,
115
+ };
116
+ }
117
+ static fromSQLite(schema) {
118
+ return {
119
+ tables: schema.tables.map((table) => ({
120
+ name: table.name,
121
+ columns: table.columns.map((col) => ({
122
+ name: col.name,
123
+ type: col.type,
124
+ nullable: col.nullable,
125
+ default: col.default,
126
+ isPrimaryKey: table.primaryKey.includes(col.name),
127
+ isUnique: table.indexes.some((i) => i.unique && i.columns.includes(col.name)),
128
+ isForeignKey: table.foreignKeys.some((fk) => fk.from === col.name),
129
+ referencesTable: table.foreignKeys.find((fk) => fk.from === col.name)?.table,
130
+ referencesColumn: table.foreignKeys.find((fk) => fk.from === col.name)?.to,
131
+ onDelete: table.foreignKeys.find((fk) => fk.from === col.name)?.onDelete,
132
+ })),
133
+ indexes: table.indexes.map((idx) => ({
134
+ name: idx.name,
135
+ columns: idx.columns,
136
+ unique: idx.unique,
137
+ isPrimaryKey: idx.isPrimaryKey,
138
+ })),
139
+ relations: table.foreignKeys.map((fk) => ({
140
+ fromTable: table.name,
141
+ fromColumn: fk.from,
142
+ toTable: fk.table,
143
+ toColumn: fk.to,
144
+ cardinality: 'N:1',
145
+ onDelete: fk.onDelete || undefined,
146
+ })),
147
+ primaryKey: table.primaryKey,
148
+ })),
149
+ databaseType: 'sqlite',
150
+ };
151
+ }
152
+ static fromPrisma(schema) {
153
+ return {
154
+ tables: schema.tables.map((table) => ({
155
+ name: table.name,
156
+ description: table.description || undefined,
157
+ columns: table.columns.map((col) => ({
158
+ name: col.name,
159
+ type: col.type,
160
+ nullable: col.isOptional,
161
+ default: col.defaultValue,
162
+ isPrimaryKey: table.primaryKey.includes(col.name),
163
+ isUnique: col.isUnique,
164
+ isForeignKey: col.isRelation,
165
+ referencesTable: col.isRelation ? col.type : undefined,
166
+ onDelete: col.onDelete || undefined,
167
+ })),
168
+ indexes: table.indexes.map((idx) => ({
169
+ name: idx.name,
170
+ columns: idx.columns,
171
+ unique: idx.unique,
172
+ isPrimaryKey: false,
173
+ })),
174
+ relations: table.relations.map((rel) => ({
175
+ fromTable: rel.fromTable,
176
+ fromColumn: rel.fromFields[0] || '',
177
+ toTable: rel.toTable,
178
+ toColumn: rel.toFields[0] || '',
179
+ cardinality: rel.type === 'one-to-one'
180
+ ? '1:1'
181
+ : rel.type === 'one-to-many'
182
+ ? '1:N'
183
+ : rel.type === 'many-to-one'
184
+ ? 'N:1'
185
+ : 'N:M',
186
+ onDelete: rel.onDelete || undefined,
187
+ })),
188
+ primaryKey: table.primaryKey,
189
+ })),
190
+ databaseType: 'prisma',
191
+ schemaName: schema.generator?.output || 'prisma',
192
+ };
193
+ }
194
+ static fromDrizzle(schema) {
195
+ return {
196
+ tables: schema.tables.map((table) => ({
197
+ name: table.name,
198
+ description: table.description || undefined,
199
+ columns: table.columns.map((col) => ({
200
+ name: col.name,
201
+ type: col.type,
202
+ nullable: col.isOptional,
203
+ default: col.defaultValue,
204
+ isPrimaryKey: col.isPrimaryKey,
205
+ isUnique: col.isUnique,
206
+ isForeignKey: col.isRelation,
207
+ referencesTable: col.relationName || undefined,
208
+ onDelete: col.onDelete || undefined,
209
+ })),
210
+ indexes: table.indexes.map((idx) => ({
211
+ name: idx.name,
212
+ columns: idx.columns,
213
+ unique: idx.unique,
214
+ isPrimaryKey: false,
215
+ })),
216
+ relations: table.relations.map((rel) => ({
217
+ fromTable: rel.fromTable,
218
+ fromColumn: rel.fromColumns[0] || '',
219
+ toTable: rel.toTable,
220
+ toColumn: rel.toColumns[0] || '',
221
+ cardinality: rel.type === 'one-to-one'
222
+ ? '1:1'
223
+ : rel.type === 'one-to-many'
224
+ ? '1:N'
225
+ : rel.type === 'many-to-one'
226
+ ? 'N:1'
227
+ : 'N:M',
228
+ onDelete: rel.onDelete || undefined,
229
+ })),
230
+ primaryKey: table.primaryKey,
231
+ })),
232
+ databaseType: 'drizzle',
233
+ schemaName: schema.dialect,
234
+ };
235
+ }
236
+ }
237
+ // ============================================================================
238
+ // Template Generator
239
+ // ============================================================================
240
+ export class TemplateGenerator {
241
+ templateDir;
242
+ outputDir;
243
+ constructor(templateDir = 'templates', outputDir = '.ai') {
244
+ this.templateDir = templateDir;
245
+ this.outputDir = outputDir;
246
+ }
247
+ generate(schema) {
248
+ const data = this.buildTemplateData(schema);
249
+ const claudeMd = this.renderClaudeMd(data);
250
+ const agentsMd = this.renderAgentsMd(data);
251
+ const queries = this.generateAllQueryTemplates(schema);
252
+ const edgeCasesMd = this.renderEdgeCasesMd(data);
253
+ const constraintsMd = this.renderConstraintsMd(data);
254
+ const testTemplates = this.generateTestTemplates(schema);
255
+ const memoryPatterns = this.generateMemoryPatterns(schema);
256
+ const handoffTemplates = this.generateHandoffTemplates(schema);
257
+ const decisionTemplates = this.generateDecisionTemplates(schema);
258
+ return {
259
+ claudeMd,
260
+ agentsMd,
261
+ queries,
262
+ edgeCasesMd,
263
+ constraintsMd,
264
+ testTemplates,
265
+ memoryPatterns,
266
+ handoffTemplates,
267
+ decisionTemplates,
268
+ };
269
+ }
270
+ async save(outputPath, schema) {
271
+ const { claudeMd, agentsMd, queries, edgeCasesMd, constraintsMd, testTemplates, memoryPatterns, handoffTemplates, decisionTemplates, } = this.generate(schema);
272
+ const fullOutputPath = path.resolve(outputPath);
273
+ await fs.promises.mkdir(fullOutputPath, { recursive: true });
274
+ await fs.promises.writeFile(path.join(fullOutputPath, 'CLAUDE.md'), claudeMd);
275
+ await fs.promises.writeFile(path.join(fullOutputPath, 'AGENTS.md'), agentsMd);
276
+ await fs.promises.writeFile(path.join(fullOutputPath, 'edge-cases.md'), edgeCasesMd);
277
+ await fs.promises.writeFile(path.join(fullOutputPath, 'constraints.md'), constraintsMd);
278
+ const queriesDir = path.join(fullOutputPath, 'queries');
279
+ await fs.promises.mkdir(queriesDir, { recursive: true });
280
+ for (const [filename, content] of Object.entries(queries)) {
281
+ await fs.promises.writeFile(path.join(queriesDir, filename), content);
282
+ }
283
+ // Save test templates
284
+ const testDir = path.join(fullOutputPath, 'test-templates');
285
+ await fs.promises.mkdir(testDir, { recursive: true });
286
+ for (const [filename, content] of Object.entries(testTemplates)) {
287
+ await fs.promises.writeFile(path.join(testDir, filename), content);
288
+ }
289
+ // Save memory patterns
290
+ const memoryDir = path.join(fullOutputPath, 'memory');
291
+ await fs.promises.mkdir(memoryDir, { recursive: true });
292
+ for (const [filename, content] of Object.entries(memoryPatterns)) {
293
+ await fs.promises.writeFile(path.join(memoryDir, filename), content);
294
+ }
295
+ // Save handoff templates
296
+ const handoffsDir = path.join(fullOutputPath, 'handoffs');
297
+ await fs.promises.mkdir(handoffsDir, { recursive: true });
298
+ for (const [filename, content] of Object.entries(handoffTemplates)) {
299
+ await fs.promises.writeFile(path.join(handoffsDir, filename), content);
300
+ }
301
+ // Save decision templates
302
+ const decisionsDir = path.join(fullOutputPath, 'decisions');
303
+ await fs.promises.mkdir(decisionsDir, { recursive: true });
304
+ for (const [filename, content] of Object.entries(decisionTemplates)) {
305
+ await fs.promises.writeFile(path.join(decisionsDir, filename), content);
306
+ }
307
+ // Save context templates
308
+ const contextDir = path.join(fullOutputPath, 'context');
309
+ await fs.promises.mkdir(contextDir, { recursive: true });
310
+ await fs.promises.writeFile(path.join(contextDir, 'SESSION_CONTEXT.json'), JSON.stringify({
311
+ version: '1.0.2',
312
+ session: {
313
+ id: 'sess_TEMPLATE',
314
+ agentId: 'agent_TEMPLATE',
315
+ timestamp: '2026-02-08T10:00:00Z',
316
+ status: 'in_progress',
317
+ },
318
+ state: {
319
+ phase: 'initializing',
320
+ progress: 0,
321
+ lastAction: 'Template generated',
322
+ nextAction: 'Define goals',
323
+ },
324
+ variables: {},
325
+ schema: {
326
+ databaseType: schema.databaseType,
327
+ tablesModified: schema.tables.map((t) => t.name),
328
+ schemaHash: 'TEMPLATE',
329
+ },
330
+ decisions: [],
331
+ handoffs: [],
332
+ errors: [],
333
+ }, null, 2));
334
+ }
335
+ buildTemplateData(schema) {
336
+ const tables = schema.tables.map((table) => ({
337
+ name: table.name,
338
+ description: table.description || '',
339
+ columns: table.columns.map((col) => ({
340
+ name: col.name,
341
+ type: col.type,
342
+ nullable: col.nullable,
343
+ default: col.default,
344
+ isKey: col.isPrimaryKey || col.isForeignKey,
345
+ description: col.description || '',
346
+ })),
347
+ indexes: table.indexes.map((idx) => ({
348
+ name: idx.name,
349
+ columns: idx.columns,
350
+ unique: idx.unique,
351
+ purpose: this.getIndexPurpose(idx, table),
352
+ })),
353
+ relations: table.relations.map((rel) => `- \`${rel.fromColumn}\` → \`${rel.toTable}\` (${rel.cardinality})`),
354
+ primaryKey: table.primaryKey.join(', ') || 'N/A',
355
+ foreignKeyCount: table.relations.length,
356
+ columnList: table.columns.map((c) => c.name + ': ' + c.type).join('\n'),
357
+ usagePattern: this.getUsagePattern(table),
358
+ }));
359
+ const relationships = this.buildRelationships(schema);
360
+ return {
361
+ timestamp: new Date().toISOString(),
362
+ databaseType: schema.databaseType,
363
+ schemaName: schema.schemaName || 'default',
364
+ source: schema.source || '',
365
+ tables,
366
+ relationships,
367
+ cardinalitySummary: this.getCardinalitySummary(schema),
368
+ businessRules: this.getBusinessRules(schema),
369
+ conventions: this.getConventions(schema),
370
+ ownershipRules: this.getOwnershipRules(schema),
371
+ typeMappings: TYPE_MAPPINGS,
372
+ performanceTips: this.getPerformanceTips(schema),
373
+ version: '1.0.2',
374
+ };
375
+ }
376
+ buildRelationships(schema) {
377
+ const relationships = [];
378
+ for (const table of schema.tables) {
379
+ for (const rel of table.relations) {
380
+ relationships.push({
381
+ fromTable: rel.fromTable,
382
+ toTable: rel.toTable,
383
+ cardinality: rel.cardinality,
384
+ onDelete: rel.onDelete,
385
+ description: this.getRelationshipDescription(rel),
386
+ });
387
+ }
388
+ }
389
+ return relationships;
390
+ }
391
+ getCardinalitySummary(schema) {
392
+ const counts = { '1:1': 0, '1:N': 0, 'N:1': 0, 'N:M': 0 };
393
+ for (const table of schema.tables) {
394
+ for (const rel of table.relations) {
395
+ counts[rel.cardinality] = (counts[rel.cardinality] || 0) + 1;
396
+ }
397
+ }
398
+ const parts = [];
399
+ if (counts['1:1'] > 0)
400
+ parts.push(`${counts['1:1']} one-to-one`);
401
+ if (counts['1:N'] > 0)
402
+ parts.push(`${counts['1:N']} one-to-many`);
403
+ if (counts['N:1'] > 0)
404
+ parts.push(`${counts['N:1']} many-to-one`);
405
+ if (counts['N:M'] > 0)
406
+ parts.push(`${counts['N:M']} many-to-many`);
407
+ return parts.length > 0 ? parts.join(', ') : 'No relationships defined';
408
+ }
409
+ getIndexPurpose(index, table) {
410
+ if (index.isPrimaryKey)
411
+ return 'Primary key constraint';
412
+ if (index.unique)
413
+ return 'Unique constraint';
414
+ if (index.columns.includes('organization_id'))
415
+ return 'Multi-tenant isolation';
416
+ if (index.columns.includes('deleted_at'))
417
+ return 'Soft delete filtering';
418
+ if (index.columns.includes('created_at'))
419
+ return 'Sorting and ordering';
420
+ if (index.columns.some((c) => table.relations.some((r) => r.fromColumn === c)))
421
+ return 'Foreign key index';
422
+ return 'Performance optimization';
423
+ }
424
+ getRelationshipDescription(rel) {
425
+ switch (rel.cardinality) {
426
+ case '1:1':
427
+ return `Each \`${rel.fromTable}\` has exactly one \`${rel.toTable}\``;
428
+ case '1:N':
429
+ return `Each \`${rel.fromTable}\` has many \`${rel.toTable}\``;
430
+ case 'N:1':
431
+ return `Many \`${rel.fromTable}\` belong to one \`${rel.toTable}\``;
432
+ case 'N:M':
433
+ return `\`${rel.fromTable}\` and \`${rel.toTable}\` have a many-to-many relationship`;
434
+ default:
435
+ return '';
436
+ }
437
+ }
438
+ getUsagePattern(table) {
439
+ if (table.name.endsWith('users') || table.name.endsWith('user')) {
440
+ return 'Core user table. All operations require organization_id check.';
441
+ }
442
+ if (table.name.endsWith('organizations') || table.name.endsWith('company')) {
443
+ return 'Root entity. No organization_id (is root).';
444
+ }
445
+ if (table.name.endsWith('sessions') || table.name.endsWith('tokens')) {
446
+ return 'Auth-related. Include user_id check. Soft delete recommended.';
447
+ }
448
+ if (table.relations.length === 0) {
449
+ return 'Standalone table. No foreign key dependencies.';
450
+ }
451
+ return 'Standard CRUD with potential organization_id isolation.';
452
+ }
453
+ getBusinessRules(schema) {
454
+ const rules = [
455
+ {
456
+ name: 'Organization Isolation',
457
+ description: 'All tables with organization_id must be filtered by it for multi-tenant security',
458
+ rule: 'SELECT * FROM table WHERE id = $1 AND organization_id = $2',
459
+ examples: [
460
+ '-- Correct: Include organization check',
461
+ 'SELECT * FROM users WHERE id = $1 AND organization_id = $2;',
462
+ '-- Wrong: Missing organization check',
463
+ 'SELECT * FROM users WHERE id = $1;',
464
+ ],
465
+ },
466
+ {
467
+ name: 'Soft Deletes',
468
+ description: 'Use deleted_at for soft deletes instead of hard deletes to preserve data integrity',
469
+ rule: 'UPDATE table SET deleted_at = NOW() WHERE id = $1',
470
+ examples: [
471
+ '-- Soft delete (recommended)',
472
+ 'UPDATE users SET deleted_at = NOW() WHERE id = $1;',
473
+ '-- Query without deleted records',
474
+ 'SELECT * FROM users WHERE deleted_at IS NULL;',
475
+ ],
476
+ },
477
+ ];
478
+ const hasTimestamps = schema.tables.some((t) => t.columns.some((c) => c.name === 'created_at' || c.name === 'updated_at'));
479
+ if (hasTimestamps) {
480
+ rules.push({
481
+ name: 'Timestamp Conventions',
482
+ description: 'Use created_at for ordering, updated_at for change detection',
483
+ rule: 'ORDER BY created_at DESC, updated_at ASC',
484
+ examples: [
485
+ '-- Recent items first',
486
+ 'SELECT * FROM table ORDER BY created_at DESC LIMIT 10;',
487
+ '-- Detect changes since last sync',
488
+ 'SELECT * FROM table WHERE updated_at > $1;',
489
+ ],
490
+ });
491
+ }
492
+ return rules;
493
+ }
494
+ getConventions(schema) {
495
+ const conventions = [
496
+ 'Use parameterized queries to prevent SQL injection',
497
+ 'Always include organization_id in WHERE clauses for multi-tenant tables',
498
+ 'Use COALESCE for nullable column handling',
499
+ 'Prefer ORDER BY created_at DESC for lists',
500
+ 'Use EXPLAIN ANALYZE to optimize slow queries',
501
+ ];
502
+ return conventions;
503
+ }
504
+ getOwnershipRules(schema) {
505
+ let rules = '- **Multi-Tenant Isolation**: All tables include `organization_id` for tenant separation\n';
506
+ rules += '- **Ownership Verification**: Check ownership before any write operation\n';
507
+ return rules;
508
+ }
509
+ getPerformanceTips(schema) {
510
+ const tips = [
511
+ 'Use indexes on frequently filtered columns (organization_id, deleted_at)',
512
+ 'Avoid SELECT *, specify needed columns for better performance',
513
+ 'Batch inserts with bulk operations when inserting multiple rows',
514
+ ];
515
+ const hasJoins = schema.tables.some((t) => t.relations.length > 0);
516
+ if (hasJoins) {
517
+ tips.push('Ensure foreign key columns are indexed for join performance');
518
+ tips.push('Use eager loading to avoid N+1 query problems');
519
+ }
520
+ return tips;
521
+ }
522
+ renderClaudeMd(data) {
523
+ let tablesSection = '';
524
+ for (const table of data.tables) {
525
+ let section = `### ${table.name}\n\n`;
526
+ section += '| Column | Type | Nullable | Key | Notes |\n';
527
+ section += '|--------|------|----------|-----|-------|\n';
528
+ for (const col of table.columns) {
529
+ const nullable = col.nullable ? 'yes' : 'no';
530
+ const key = col.isKey ? 'PK/FK' : '-';
531
+ const desc = col.description || '-';
532
+ section += `| \`${col.name}\` | \`${col.type}\` | ${nullable} | ${key} | ${desc} |\n`;
533
+ }
534
+ if (table.indexes.length > 0) {
535
+ section += '\n**Indexes:**\n';
536
+ for (const idx of table.indexes) {
537
+ const unique = idx.unique ? ' (unique)' : '';
538
+ section += `- \`${idx.name}\` on \`(${idx.columns.join(', ')})\`${unique} - ${idx.purpose}\n`;
539
+ }
540
+ }
541
+ if (table.relations.length > 0) {
542
+ section += '\n**Foreign Keys:**\n';
543
+ for (const rel of table.relations) {
544
+ section += `${rel}\n`;
545
+ }
546
+ }
547
+ tablesSection += section + '\n';
548
+ }
549
+ let relationshipsSection = '';
550
+ for (const rel of data.relationships) {
551
+ const onDelete = rel.onDelete ? ` (ON DELETE ${rel.onDelete})` : '';
552
+ relationshipsSection += `| ${rel.fromTable} | ${rel.cardinality} | ${rel.toTable} | ${onDelete} |\n`;
553
+ }
554
+ let businessRulesSection = '';
555
+ for (const rule of data.businessRules) {
556
+ businessRulesSection += `\n### ${rule.name}\n\n`;
557
+ businessRulesSection += `${rule.description}\n\n`;
558
+ businessRulesSection += `**Rule:** \n\`\`\`sql\n${rule.rule}\n\`\`\`\n`;
559
+ if (rule.examples.length > 0) {
560
+ businessRulesSection += '\n**Examples:**\n```sql\n' + rule.examples.join('\n') + '\n```\n';
561
+ }
562
+ }
563
+ let conventionsSection = '';
564
+ for (const conv of data.conventions) {
565
+ conventionsSection += `- ${conv}\n`;
566
+ }
567
+ // Build output with agent-aware callouts
568
+ let output = '# Database Context\n\n';
569
+ output += '> AUTO-GENERATED by cohere-db. Works with Claude, Codex, Antigravity, Xcode.\n\n';
570
+ // Agent-specific callouts
571
+ output += '> [!NOTE|CLAUDE]\n';
572
+ output += '> **Claude Code**: This file auto-loads.\n';
573
+ output += '> ```bash\n';
574
+ output += '> claude db:validate # Verify queries\n';
575
+ output += '> claude test:run # Run tests\n';
576
+ output += '> ```\n\n';
577
+ output += '> [!NOTE|CODEX]\n';
578
+ output += '> **Codex**: You MUST explicitly read this file:\n';
579
+ output += '> ```\n';
580
+ output += '> Read the CLAUDE.md file in this directory.\n';
581
+ output += '> ```\n';
582
+ output += '> Codex does NOT auto-load context files.\n\n';
583
+ output += '> [!NOTE|ANTIGRAVITY]\n';
584
+ output += '> **Antigravity**: Use with MCP. Coordinate via `.ai/memory/`.\n\n';
585
+ output += '## Overview\n\n';
586
+ output += `**Database Type:** ${data.databaseType}\n`;
587
+ output += `**Schema:** ${data.schemaName}\n`;
588
+ output += `**Tables:** ${data.tables.length}\n\n`;
589
+ output += '## Tables\n\n';
590
+ output += tablesSection;
591
+ output += '## Relationships\n\n';
592
+ output += '| From | Cardinality | To | Actions |\n';
593
+ output += '|------|-------------|-----|--------|\n';
594
+ output += relationshipsSection;
595
+ output += '\n> [!NOTE|ALL]\n';
596
+ output += '> **ALL AGENTS**: Foreign key columns MUST be indexed.\n\n';
597
+ output += '## Business Rules\n\n';
598
+ output += businessRulesSection;
599
+ output += '## Conventions\n\n';
600
+ output += conventionsSection;
601
+ output += '\n> [!NOTE|ALL]\n';
602
+ output += '> **ALL AGENTS**: Parameterized queries required. Never interpolate strings.\n\n';
603
+ output += '## Multi-Tenant Isolation\n\n';
604
+ output += data.ownershipRules + '\n\n';
605
+ output += '## Additional Resources\n\n';
606
+ output += '| Resource | Purpose |\n';
607
+ output += '|----------|----------|\n';
608
+ output += '| `.ai/edge-cases.md` | Edge case patterns |\n';
609
+ output += '| `.ai/constraints.md` | Query limits |\n';
610
+ output += '| `.ai/test-templates/` | Edge case tests |\n';
611
+ output += '| `.ai/memory/` | Checkpoint patterns |\n';
612
+ return output;
613
+ }
614
+ renderAgentsMd(data) {
615
+ let tableQuickRef = '';
616
+ for (const table of data.tables) {
617
+ const pk = table.primaryKey || 'N/A';
618
+ const sampleCols = table.columns
619
+ .slice(0, 3)
620
+ .map((c) => c.name)
621
+ .join(', ');
622
+ tableQuickRef += `| [${table.name}](#${table.name}) | ${pk} | ${table.foreignKeyCount} | ${sampleCols}... |\n`;
623
+ }
624
+ let tableDetails = '';
625
+ for (const table of data.tables) {
626
+ tableDetails += `\n### ${table.name}\n\n`;
627
+ tableDetails += `**Description:** ${table.description || 'No description available'}\n\n`;
628
+ tableDetails += '**Columns:**\n```\n';
629
+ tableDetails += table.columnList + '\n';
630
+ tableDetails += '```\n\n';
631
+ if (table.relations.length > 0) {
632
+ tableDetails += '**Relationships:**\n';
633
+ tableDetails += table.relations.join('\n') + '\n\n';
634
+ }
635
+ tableDetails += `**Usage Pattern:** ${table.usagePattern}\n`;
636
+ }
637
+ let cardinalitySection = '';
638
+ cardinalitySection += `**Relationship Summary:** ${data.cardinalitySummary}\n\n`;
639
+ cardinalitySection += '| Type | Description |\n';
640
+ cardinalitySection += '|------|-------------|\n';
641
+ cardinalitySection += '| 1:1 | One-to-one relationship |\n';
642
+ cardinalitySection += '| 1:N | One-to-many relationship |\n';
643
+ cardinalitySection += '| N:1 | Many-to-one relationship |\n';
644
+ cardinalitySection += '| N:M | Many-to-many relationship |\n';
645
+ let typeMappingsSection = '';
646
+ for (const tm of data.typeMappings) {
647
+ typeMappingsSection += `| \`${tm.dbType}\` | \`${tm.tsType}\` ${tm.notes ? `(${tm.notes})` : ''} |\n`;
648
+ }
649
+ // Build output with agent-aware callouts
650
+ let output = '# Database Context for AI Assistants\n\n';
651
+ output += '> AUTO-GENERATED by cohere. Core content works for all agents.\n\n';
652
+ output += '## Quick Reference\n\n';
653
+ output += '| Table | Primary Key | Foreign Keys | Sample Columns |\n';
654
+ output += '|-------|-------------|--------------|----------------|\n';
655
+ output += tableQuickRef;
656
+ output += cardinalitySection;
657
+ output += '\n## Table Details\n\n';
658
+ output += tableDetails;
659
+ output += '\n## Type Mappings\n\n';
660
+ output += '| Database Type | TypeScript Type | Notes |\n';
661
+ output += '|---------------|-----------------|-------|\n';
662
+ output += typeMappingsSection;
663
+ output += '\n## Performance Tips\n\n';
664
+ for (const tip of data.performanceTips) {
665
+ output += `- ${tip}\n`;
666
+ }
667
+ // Add agent-specific warnings
668
+ output += '\n## Platform Warnings\n\n';
669
+ output += '> [!WARNING|CODEX]\n';
670
+ output += '> **Codex**: Add to AGENTS.md:\n';
671
+ output += '> ```\n';
672
+ output += '> All queries MUST include organization_id filter.\n';
673
+ output += '> Pattern: SELECT ... WHERE id = $1 AND organization_id = $2\n';
674
+ output += '> ```\n\n';
675
+ output += '> [!WARNING|ANTIGRAVITY]\n';
676
+ output +=
677
+ '> **Antigravity**: Parallel agents share context. Use `.ai/memory/checkpoints/`.\n\n';
678
+ output += '> [!WARNING|ALL]\n';
679
+ output += '> **ALL AGENTS**: Checkpoint every 5-10 minutes to prevent context loss.\n';
680
+ return output;
681
+ }
682
+ generateAllQueryTemplates(schema) {
683
+ const queries = {};
684
+ for (const table of schema.tables) {
685
+ queries[`${table.name}.sql`] = this.generateQueryTemplate(table);
686
+ }
687
+ queries['transactions.sql'] = this.generateTransactionPatterns();
688
+ return queries;
689
+ }
690
+ generateQueryTemplate(table) {
691
+ const pk = table.primaryKey[0] || 'id';
692
+ const columns = table.columns.map((c) => c.name);
693
+ const hasOrgId = table.columns.some((c) => c.name === 'organization_id');
694
+ const hasDeletedAt = table.columns.some((c) => c.name === 'deleted_at');
695
+ const nonDefaultCols = columns.filter((c) => c !== 'created_at' && c !== 'updated_at');
696
+ let output = `-- ${table.name} queries\n`;
697
+ output += `-- AUTO-GENERATED by cohere\n\n`;
698
+ // READ operations
699
+ output += `-- === READ ===\n\n`;
700
+ output += `-- Get by ${pk}\n`;
701
+ output += `SELECT * FROM ${table.name} WHERE ${pk} = $1;\n\n`;
702
+ if (hasOrgId) {
703
+ output += `-- Get with organization check\n`;
704
+ output += `SELECT * FROM ${table.name} WHERE ${pk} = $1 AND organization_id = $2;\n\n`;
705
+ }
706
+ output += `-- List with pagination\n`;
707
+ output += `SELECT * FROM ${table.name}\n`;
708
+ if (hasOrgId) {
709
+ output += `WHERE organization_id = $1\n`;
710
+ }
711
+ output += `ORDER BY created_at DESC\n`;
712
+ output += `LIMIT $2 OFFSET $3;\n\n`;
713
+ if (hasDeletedAt) {
714
+ output += `-- List without deleted records\n`;
715
+ output += `SELECT * FROM ${table.name}\n`;
716
+ if (hasOrgId) {
717
+ output += `WHERE organization_id = $1\n`;
718
+ }
719
+ output += `AND deleted_at IS NULL\n`;
720
+ output += `ORDER BY created_at DESC;\n\n`;
721
+ }
722
+ // CREATE operations
723
+ output += `-- === CREATE ===\n\n`;
724
+ output += `-- Insert single record\n`;
725
+ output += `INSERT INTO ${table.name} (${nonDefaultCols.join(', ')})\n`;
726
+ output += `VALUES (${nonDefaultCols.map((_, i) => '$' + (i + 1)).join(', ')});\n`;
727
+ return output;
728
+ }
729
+ generateTransactionPatterns() {
730
+ return `-- Transaction Examples
731
+ -- Use transactions for atomic operations
732
+
733
+ BEGIN TRANSACTION;
734
+
735
+ -- 1. Create parent record
736
+ INSERT INTO orders (user_id, total, status)
737
+ VALUES ($1, $2, 'pending')
738
+ RETURNING id;
739
+
740
+ -- 2. Create child records
741
+ INSERT INTO order_items (order_id, product_id, quantity, price)
742
+ VALUES
743
+ ($3, $4, $5, $6),
744
+ ($3, $7, $8, $9);
745
+
746
+ -- 3. Update inventory
747
+ UPDATE products SET stock = stock - $5 WHERE id = $4;
748
+ UPDATE products SET stock = stock - $8 WHERE id = $7;
749
+
750
+ COMMIT;
751
+ `;
752
+ }
753
+ // ============================================================================
754
+ // Edge Cases Generation
755
+ // ============================================================================
756
+ renderEdgeCasesMd(data) {
757
+ let output = '# Edge Cases Handling Guide\n\n';
758
+ output += `> AUTO-GENERATED by cohere. Last updated: ${data.timestamp}\n\n`;
759
+ // Session Restart Amnesia
760
+ output += '## 1. Session Restart Amnesia\n\n';
761
+ output += '### Checkpoint Pattern\n';
762
+ output += '```typescript\n';
763
+ output += `// Checkpoint file: .ai/memory/checkpoint-{timestamp}.json\n`;
764
+ output += 'interface AgentCheckpoint {\n';
765
+ output += ' timestamp: string;\n';
766
+ output += ' sessionId: string;\n';
767
+ output += ' schemaVersion: string;\n';
768
+ output += ' currentTask?: TaskContext;\n';
769
+ output += ' discoveredPatterns: DiscoveredPattern[];\n';
770
+ output += '}\n';
771
+ output += '```\n\n';
772
+ // Tool Misalignment
773
+ output += '## 2. Tool Misalignment\n\n';
774
+ output += '### Type Constraint Warnings\n';
775
+ output += '```sql\n';
776
+ output += '-- Verify column types before queries\n';
777
+ output += 'SELECT column_name, data_type\n';
778
+ output += 'FROM information_schema.columns\n';
779
+ output += `WHERE table_name = '${data.tables[0]?.name || 'table_name'}';\n`;
780
+ output += '```\n\n';
781
+ // Infinite Context Exploration
782
+ output += '## 3. Infinite Context Exploration\n\n';
783
+ output += '### Query Limits\n';
784
+ output += '| Operation | Limit | Reason |\n';
785
+ output += '|-----------|-------|--------|\n';
786
+ output += '| SELECT rows | 10,000 max | Prevent context exhaustion |\n';
787
+ output += '| JOIN depth | 5 max | Query complexity |\n';
788
+ output += '| Query timeout | 30s | Resource sharing |\n\n';
789
+ // Verification Gaps
790
+ output += '## 4. Verification Gaps\n\n';
791
+ output += '### Duplicate Detection\n';
792
+ output += '```sql\n';
793
+ output += '-- Find potential duplicate records\n';
794
+ output += 'SELECT email, COUNT(*) as cnt\n';
795
+ output += `FROM ${data.tables[0]?.name || 'users'}\n`;
796
+ output += 'GROUP BY email\n';
797
+ output += 'HAVING COUNT(*) > 1;\n';
798
+ output += '```\n\n';
799
+ // Database-specific tables
800
+ output += '## 5. Schema-Specific Edge Cases\n\n';
801
+ for (const table of data.tables) {
802
+ const hasOrgId = table.columns.some((c) => c.name === 'organization_id');
803
+ const hasDeletedAt = table.columns.some((c) => c.name === 'deleted_at');
804
+ if (hasOrgId || hasDeletedAt) {
805
+ output += `### ${table.name}\n\n`;
806
+ if (hasOrgId) {
807
+ output +=
808
+ '- **Organization isolation required**: All queries must include `organization_id` check\n';
809
+ }
810
+ if (hasDeletedAt) {
811
+ output +=
812
+ '- **Soft delete pattern**: Use `deleted_at IS NULL` to exclude deleted records\n';
813
+ }
814
+ output += '\n';
815
+ }
816
+ }
817
+ return output;
818
+ }
819
+ renderConstraintsMd(data) {
820
+ let output = '# Query Constraints & Limits\n\n';
821
+ output += `> AUTO-GENERATED by cohere. Last updated: ${data.timestamp}\n\n`;
822
+ output += '## Query Limits\n\n';
823
+ output += '| Operation | Limit | Reason |\n';
824
+ output += '|-----------|-------|--------|\n';
825
+ output += '| SELECT rows | 10,000 max | Prevent context exhaustion |\n';
826
+ output += '| SELECT with OFFSET | 1,000 max | Performance degradation |\n';
827
+ output += '| JOIN depth | 5 max | Query complexity |\n';
828
+ output += '| INSERT rows (batch) | 1,000 max | Transaction size |\n';
829
+ output += '| Query timeout | 30s | Resource sharing |\n\n';
830
+ output += '## Required Patterns\n\n';
831
+ output += '### LIMIT Required\n';
832
+ output += '```sql\n';
833
+ output += '-- ✓ CORRECT: Always use LIMIT\n';
834
+ output += `SELECT * FROM ${data.tables[0]?.name || 'table'} LIMIT 100;\n`;
835
+ output += '-- ✗ WRONG: No LIMIT on potential large tables\n';
836
+ output += `SELECT * FROM ${data.tables[0]?.name || 'table'};\n`;
837
+ output += '```\n\n';
838
+ // Check if tables have organization_id
839
+ const hasOrgIsolation = data.tables.some((t) => t.columns.some((c) => c.name === 'organization_id'));
840
+ if (hasOrgIsolation) {
841
+ output += '### Organization Isolation Required\n';
842
+ output += '```sql\n';
843
+ output += '-- ✓ CORRECT: Organization check included\n';
844
+ output += `SELECT * FROM ${data.tables[0]?.name || 'users'} WHERE organization_id = $1;\n`;
845
+ output += '-- ✗ WRONG: Missing organization_id (security risk!)\n';
846
+ output += `SELECT * FROM ${data.tables[0]?.name || 'users'};\n`;
847
+ output += '```\n\n';
848
+ }
849
+ output += '## Pagination Patterns\n\n';
850
+ output += '### Keyset Pagination (Recommended)\n';
851
+ output += '```typescript\n';
852
+ output += 'async function getNextPage(lastId: string, limit: number = 50) {\n';
853
+ output += ` return query(\`SELECT * FROM ${data.tables[0]?.name || 'items'} WHERE id > $1 ORDER BY id LIMIT $2\`, [lastId, limit]);\n`;
854
+ output += '}\n';
855
+ output += '```\n\n';
856
+ output += '## Context Guardrails\n\n';
857
+ output += '```typescript\n';
858
+ output += 'const QUERY_CONSTRAINTS = {\n';
859
+ output += ' maxRows: 1000,\n';
860
+ output += ' maxDepth: 5,\n';
861
+ output += ' timeoutMs: 30000,\n';
862
+ output += ' requireOrderBy: true,\n';
863
+ output += ' requireLimit: true,\n';
864
+ output += '};\n';
865
+ output += '```\n\n';
866
+ return output;
867
+ }
868
+ generateTestTemplates(schema) {
869
+ const templates = {};
870
+ // Main edge cases test file
871
+ templates['edge-cases.test.ts'] = this.generateEdgeCasesTest(schema);
872
+ // CRUD edge cases
873
+ const crudTemplates = this.generateCRUDEdgeCases(schema);
874
+ for (const [name, content] of Object.entries(crudTemplates)) {
875
+ templates[name] = content;
876
+ }
877
+ return templates;
878
+ }
879
+ generateEdgeCasesTest(schema) {
880
+ const tableName = schema.tables[0]?.name || 'table_name';
881
+ const pk = schema.tables[0]?.primaryKey || 'id';
882
+ return `/**
883
+ * Edge Case Tests for ${tableName}
884
+ * AUTO-GENERATED by cohere
885
+ */
886
+
887
+ describe('${tableName} Edge Cases', () => {
888
+ const orgId = 'test-org-id';
889
+
890
+ describe('Empty Result Sets', () => {
891
+ test('SELECT returns empty array when no rows match', async () => {
892
+ const result = await query(
893
+ 'SELECT * FROM ${tableName} WHERE id = $1 AND organization_id = $2',
894
+ ['nonexistent-id', orgId]
895
+ );
896
+ expect(result.rows).toEqual([]);
897
+ });
898
+ });
899
+
900
+ describe('Primary Key Edge Cases', () => {
901
+ test('INSERT fails on duplicate primary key', async () => {
902
+ await expect(query(
903
+ 'INSERT INTO ${tableName} (id) VALUES ($1)',
904
+ ['existing-id']
905
+ )).rejects.toThrow('duplicate_key');
906
+ });
907
+
908
+ test('UPDATE modifies correct row', async () => {
909
+ const result = await query(
910
+ 'UPDATE ${tableName} SET updated_at = NOW() WHERE id = $1 AND organization_id = $2 RETURNING id',
911
+ ['target-id', orgId]
912
+ );
913
+ expect(result.rowCount).toBe(1);
914
+ });
915
+ });
916
+
917
+ describe('Soft Delete Edge Cases', () => {
918
+ test('SELECT excludes soft-deleted records by default', async () => {
919
+ const result = await query(
920
+ 'SELECT * FROM ${tableName} WHERE organization_id = $1',
921
+ [orgId]
922
+ );
923
+ expect(result.rows.every(r => r.deleted_at === null)).toBe(true);
924
+ });
925
+ });
926
+ });
927
+ `;
928
+ }
929
+ generateCRUDEdgeCases(schema) {
930
+ const templates = {};
931
+ for (const table of schema.tables) {
932
+ const testContent = this.generateSingleTableTests(table);
933
+ templates[table.name + '.test.ts'] = testContent;
934
+ }
935
+ return templates;
936
+ }
937
+ generateSingleTableTests(table) {
938
+ const orgIdLine = table.columns.some((c) => c.name === 'organization_id')
939
+ ? " const orgId = 'test-org-id';"
940
+ : ' // No organization_id column in this table';
941
+ const softDeleteTest = table.columns.some((c) => c.name === 'deleted_at')
942
+ ? `
943
+ test('Soft-deleted records are excluded by default', async () => {
944
+ const result = await query('SELECT * FROM ' + table.name);
945
+ expect(result.rows.every(r => r.deleted_at === null)).toBe(true);
946
+ });`
947
+ : '';
948
+ const updateTest = table.primaryKey.length > 0
949
+ ? `
950
+ test('UPDATE with valid ' + table.primaryKey[0] + ' succeeds', async () => {
951
+ const result = await query(
952
+ 'UPDATE ' + table.name + ' SET updated_at = NOW() WHERE id = $1 RETURNING id',
953
+ ['test-id']
954
+ );
955
+ expect(result.rowCount).toBeGreaterThanOrEqual(0);
956
+ });`
957
+ : '';
958
+ return ('/**\n' +
959
+ ' * ' +
960
+ table.name +
961
+ ' Edge Case Tests\n' +
962
+ ' * AUTO-GENERATED by cohere\n' +
963
+ ' */\n\n' +
964
+ "describe('" +
965
+ table.name +
966
+ " Edge Cases', () => {\n" +
967
+ orgIdLine +
968
+ softDeleteTest +
969
+ updateTest +
970
+ '\n});\n');
971
+ }
972
+ // ============================================================================
973
+ // Memory Patterns Generation
974
+ // ============================================================================
975
+ generateMemoryPatterns(schema) {
976
+ const patterns = {};
977
+ patterns['checkpoint-patterns.md'] = this.generateCheckpointPatterns(schema);
978
+ patterns['session-template.md'] = this.generateSessionTemplate(schema);
979
+ patterns['accumulated-learnings.md'] = this.generateEmptyLearnings();
980
+ return patterns;
981
+ }
982
+ generateCheckpointPatterns(schema) {
983
+ const tablesList = schema.tables
984
+ .map((t) => '- **' +
985
+ t.name +
986
+ '**: ' +
987
+ t.columns.length +
988
+ ' columns, ' +
989
+ t.relations.length +
990
+ ' relationships')
991
+ .join('\n');
992
+ return ('# Memory & Checkpoint Patterns\n\n' +
993
+ '> AUTO-GENERATED by cohere-db. Pattern version: 1.0.2\n\n' +
994
+ '## Directory Structure\n\n' +
995
+ '```\n' +
996
+ '.ai/\n' +
997
+ '├── memory/\n' +
998
+ '│ ├── checkpoints/ # Session checkpoints\n' +
999
+ '│ │ └── checkpoint-{timestamp}.json\n' +
1000
+ '│ ├── accumulated-learnings.md # Cross-session knowledge\n' +
1001
+ '│ ├── edge-cases.md # Documented edge cases\n' +
1002
+ '│ └── session-history.md # Recent session notes\n' +
1003
+ '```\n\n' +
1004
+ '## Checkpoint Interface\n\n' +
1005
+ '```typescript\n' +
1006
+ 'interface AgentCheckpoint {\n' +
1007
+ ' timestamp: string;\n' +
1008
+ ' sessionId: string;\n' +
1009
+ ' schemaVersion: string;\n' +
1010
+ ' schemaHash: string; // ' +
1011
+ schema.tables.length +
1012
+ ' tables in current schema\n' +
1013
+ ' currentTask?: TaskContext;\n' +
1014
+ ' discoveredPatterns: DiscoveredPattern[];\n' +
1015
+ ' pendingQueries: PendingQuery[];\n' +
1016
+ ' agentMemory: Record<string, unknown>;\n' +
1017
+ '}\n\n' +
1018
+ 'interface TaskContext {\n' +
1019
+ ' taskId: string;\n' +
1020
+ " status: 'in_progress' | 'paused' | 'completed';\n" +
1021
+ ' lastAction?: string;\n' +
1022
+ ' progress: number;\n' +
1023
+ '}\n' +
1024
+ '```\n\n' +
1025
+ '## Tables in Schema\n' +
1026
+ tablesList +
1027
+ '\n\n' +
1028
+ '## Usage\n\n' +
1029
+ '```typescript\n' +
1030
+ "import { saveCheckpoint, restoreLatestCheckpoint } from './memory/checkpoint';\n\n" +
1031
+ "const checkpoint = await restoreLatestCheckpoint('.ai');\n" +
1032
+ 'if (checkpoint) {\n' +
1033
+ " console.log('Resuming from:', checkpoint.timestamp);\n" +
1034
+ '}\n' +
1035
+ '// ... perform work ...\n' +
1036
+ "await saveCheckpoint('.ai', newCheckpoint);\n" +
1037
+ '```\n');
1038
+ }
1039
+ generateSessionTemplate(schema) {
1040
+ const tablesList = schema.tables.map((t) => '- ' + t.name).join('\n');
1041
+ return ('# Session Template\n\n' +
1042
+ '> AUTO-GENERATED by cohere\n\n' +
1043
+ '## Current Session\n\n' +
1044
+ '**Session ID:** ${SESSION_ID}\n' +
1045
+ '**Start Time:** ${START_TIME}\n' +
1046
+ '**Schema:** ' +
1047
+ schema.databaseType +
1048
+ '\n\n' +
1049
+ '## Tables in Scope\n' +
1050
+ tablesList +
1051
+ '\n\n' +
1052
+ '## Goals\n' +
1053
+ '- \n\n' +
1054
+ '## Progress\n' +
1055
+ '- [ ] \n\n' +
1056
+ '## Notes\n' +
1057
+ '- \n\n' +
1058
+ '---\n' +
1059
+ '*Delete this header and fill in your session details*\n');
1060
+ }
1061
+ generateEmptyLearnings() {
1062
+ return ('# Accumulated Learnings\n\n' +
1063
+ '> AUTO-GENERATED by cohere\n\n' +
1064
+ 'Cross-session knowledge and patterns discovered during agent work.\n\n' +
1065
+ '## Getting Started\n\n' +
1066
+ 'Copy patterns discovered across sessions here:\n\n' +
1067
+ '```markdown\n' +
1068
+ '## YYYY-MM-DD\n\n' +
1069
+ '**Category:** query|schema|relationship|edge-case\n\n' +
1070
+ '**Description:** \n\n' +
1071
+ '**Example:**\n' +
1072
+ '```sql\n' +
1073
+ '-- Your SQL example here\n' +
1074
+ '```\n\n' +
1075
+ '**Usage Count:** N\n' +
1076
+ '```\n\n' +
1077
+ '---\n' +
1078
+ '*This file is updated automatically by checkpoint patterns*\n');
1079
+ }
1080
+ // ============================================================================
1081
+ // Handoff Templates Generation
1082
+ // ============================================================================
1083
+ generateHandoffTemplates(schema) {
1084
+ const templates = {};
1085
+ templates['handoff-template.md'] = this.generateHandoffTemplate(schema);
1086
+ templates['MULTI_AGENT_PROTOCOL.md'] = this.generateMultiAgentProtocol(schema);
1087
+ return templates;
1088
+ }
1089
+ generateHandoffTemplate(schema) {
1090
+ const tablesList = schema.tables.map((t) => `- \`${t.name}\``).join('\n');
1091
+ return `# Agent Handoff Record
1092
+
1093
+ > AUTO-GENERATED by cohere. Read this before continuing.
1094
+
1095
+ ## Session Info
1096
+
1097
+ | Field | Value |
1098
+ |-------|-------|
1099
+ | **Agent ID** | \`{agentId}\` |
1100
+ | **Session ID** | \`{sessionId}\` |
1101
+ | **Timestamp** | \`{timestamp}\` |
1102
+ | **Duration** | \`{duration}\` |
1103
+ | **Status** | \`{status}\` |
1104
+
1105
+ ---
1106
+
1107
+ ## What Was Attempted
1108
+
1109
+ ### Goals
1110
+ - Goal 1
1111
+ - Goal 2
1112
+ - Goal 3
1113
+
1114
+ ### Actions Taken
1115
+
1116
+ | Step | Action | Result |
1117
+ |------|--------|--------|
1118
+ | 1 | Action description | ✅ Success / ❌ Failed |
1119
+ | 2 | Action description | ✅ Success / ❌ Failed |
1120
+ | 3 | Action description | ⏸️ Paused |
1121
+
1122
+ ---
1123
+
1124
+ ## What Succeeded
1125
+
1126
+ - ✅ Success item 1
1127
+ - ✅ Success item 2
1128
+
1129
+ ---
1130
+
1131
+ ## What Failed
1132
+
1133
+ - ❌ Failed item 1
1134
+ - ❌ Failed item 2 with error: \`{error}\`
1135
+
1136
+ ### Error Details
1137
+ \`\`\`
1138
+ {errorStackTrace}
1139
+ \`\`\`
1140
+
1141
+ ### Recovery Attempts
1142
+ 1. Attempt 1: Result
1143
+ 2. Attempt 2: Result
1144
+
1145
+ ---
1146
+
1147
+ ## Decisions Made
1148
+
1149
+ | Decision ID | Choice | Rationale |
1150
+ |-------------|--------|-----------|
1151
+ | DECISION_001 | Choice A | Rationale |
1152
+ | DECISION_002 | Choice B | Rationale |
1153
+
1154
+ > See \`.ai/decisions/\` for detailed decision logs.
1155
+
1156
+ ---
1157
+
1158
+ ## Current State
1159
+
1160
+ ### Last Completed Step
1161
+ \`\`\`
1162
+ {lastCompletedStep}
1163
+ \`\`\`
1164
+
1165
+ ### Pending Work
1166
+ - [ ] Pending item 1
1167
+ - [ ] Pending item 2
1168
+
1169
+ ### Known Issues
1170
+ - Issue 1: Description
1171
+ - Issue 2: Description
1172
+
1173
+ ---
1174
+
1175
+ ## Context for Next Agent
1176
+
1177
+ ### Variables
1178
+ \`\`\`json
1179
+ {variablesJson}
1180
+ \`\`\`
1181
+
1182
+ ### Schema State
1183
+ - Tables modified: {modifiedTables}
1184
+ - New relationships: {newRelationships}
1185
+
1186
+ ### Tables in Scope
1187
+ ${tablesList}
1188
+
1189
+ ---
1190
+
1191
+ ## Next Steps
1192
+
1193
+ ### Immediate Actions
1194
+ 1. Next action 1
1195
+ 2. Next action 2
1196
+
1197
+ ### Recommended Approach
1198
+ \`\`\`
1199
+ {recommendedApproach}
1200
+ \`\`\`
1201
+
1202
+ ### Expected Duration
1203
+ - \`{estimatedDuration}\`
1204
+
1205
+ ---
1206
+
1207
+ ## Files Generated
1208
+
1209
+ | File | Purpose |
1210
+ |------|---------|
1211
+ | \`.ai/state/CURRENT_STATE.md\` | Progress snapshot |
1212
+ | \`.ai/context/SESSION_CONTEXT.json\` | Machine-readable state |
1213
+ | \`.ai/decisions/\` | Decision logs |
1214
+
1215
+ ---
1216
+
1217
+ > **PROTOCOL**: Before spawning subagents:
1218
+ > 1. Generate handoff file
1219
+ > 2. Pass handoff to next agent
1220
+ > 3. Next agent reads \`.ai/state/CURRENT_STATE.md\` first
1221
+ `;
1222
+ }
1223
+ generateMultiAgentProtocol(schema) {
1224
+ const tablesList = schema.tables.map((t) => t.name).join(', ');
1225
+ return `# Multi-Agent Protocol
1226
+
1227
+ > AUTO-GENERATED by cohere. Protocol for agent-to-agent handoff.
1228
+
1229
+ ## Overview
1230
+
1231
+ This document defines the protocol for safe handoff between AI agents working on the same codebase.
1232
+
1233
+ ## Protocol Steps
1234
+
1235
+ ### 1. Pre-Handoff (Current Agent)
1236
+
1237
+ Before ending your session:
1238
+
1239
+ \`\`\`bash
1240
+ cohere-db handoff --record --status {status} --agentId {agentId}
1241
+ \`\`\`
1242
+
1243
+ This creates:
1244
+ - \`.ai/handoffs/HANDOFF_<timestamp>.md\` - Full handoff record
1245
+ - \`.ai/state/CURRENT_STATE.md\` - Current progress snapshot
1246
+ - \`.ai/context/SESSION_CONTEXT.json\` - Machine-readable state
1247
+
1248
+ ### 2. Handoff Transfer
1249
+
1250
+ Share these files with the next agent:
1251
+ - Handoff file location
1252
+ - Session context JSON
1253
+ - Any modified files
1254
+
1255
+ ### 3. Post-Handoff (Next Agent)
1256
+
1257
+ On receiving handoff:
1258
+
1259
+ \`\`\`bash
1260
+ # List available sessions
1261
+ cohere-db handoff --list
1262
+
1263
+ # Resume specific session
1264
+ cohere-db handoff --resume <session_id>
1265
+
1266
+ # Read state first
1267
+ cat .ai/state/CURRENT_STATE.md
1268
+ \`\`\`
1269
+
1270
+ ---
1271
+
1272
+ ## State Files
1273
+
1274
+ ### CURRENT_STATE.md
1275
+
1276
+ | Section | Purpose |
1277
+ |---------|---------|
1278
+ | Session Status | Agent ID, timestamp, status |
1279
+ | Progress | Completed steps, current step, remaining |
1280
+ | Work Queue | Pending tasks, blocked tasks |
1281
+ | Variables | Key state variables |
1282
+ | Checkpoints | Saved checkpoints |
1283
+
1284
+ ### SESSION_CONTEXT.json
1285
+
1286
+ Machine-readable state for programmatic access:
1287
+
1288
+ \`\`\`json
1289
+ {
1290
+ "session": { "id", "agentId", "status" },
1291
+ "state": { "phase", "progress" },
1292
+ "variables": { /* key-value pairs */ },
1293
+ "schema": { "databaseType", "tablesModified" },
1294
+ "decisions": [ { "id", "title", "choice" } ]
1295
+ }
1296
+ \`\`\`
1297
+
1298
+ ---
1299
+
1300
+ ## Tables in Scope
1301
+
1302
+ ${tablesList}
1303
+
1304
+ ---
1305
+
1306
+ ## Constraints
1307
+
1308
+ | Constraint | Value |
1309
+ |------------|-------|
1310
+ | Max handoff size | 10KB |
1311
+ | Checkpoint interval | 5 minutes |
1312
+ | Decision logging | Required for major choices |
1313
+
1314
+ ---
1315
+
1316
+ ## CLI Commands
1317
+
1318
+ | Command | Description |
1319
+ |---------|-------------|
1320
+ | \`cohere-db handoff --record\` | Record current session |
1321
+ | \`cohere-db handoff --list\` | List available sessions |
1322
+ | \`cohere-db handoff --resume <id>\` | Resume from session |
1323
+ | \`cohere-db handoff --status completed\` | Mark session complete |
1324
+
1325
+ ---
1326
+
1327
+ ## Best Practices
1328
+
1329
+ 1. **Checkpoint frequently** - Save state every 5-10 minutes
1330
+ 2. **Log decisions** - Use decision IDs in handoffs
1331
+ 3. **Document failures** - Include error context in handoff
1332
+ 4. **Atomic handoffs** - Complete handoff before ending session
1333
+
1334
+ ---
1335
+
1336
+ ## Example Flow
1337
+
1338
+ \`\`\`
1339
+ Agent A (in progress)
1340
+
1341
+ cohere-db handoff --record --status paused
1342
+
1343
+ Agent B receives files
1344
+
1345
+ cohere-db handoff --resume sess_abc123
1346
+
1347
+ Agent B continues work
1348
+
1349
+ cohere-db handoff --record --status completed
1350
+ \`\`\`
1351
+ `;
1352
+ }
1353
+ // ============================================================================
1354
+ // Decision Templates Generation
1355
+ // ============================================================================
1356
+ generateDecisionTemplates(schema) {
1357
+ const templates = {};
1358
+ templates['decision-template.md'] = this.generateDecisionTemplate(schema);
1359
+ templates['DECISION_LOG.md'] = this.generateDecisionLog(schema);
1360
+ return templates;
1361
+ }
1362
+ generateDecisionTemplate(schema) {
1363
+ return `# Decision: {decisionTitle}
1364
+
1365
+ > AUTO-GENERATED by cohere. Decision ID: \`{decisionId}\`
1366
+
1367
+ ## Metadata
1368
+
1369
+ | Field | Value |
1370
+ |-------|-------|
1371
+ | **Decision ID** | \`{decisionId}\` |
1372
+ | **Timestamp** | \`{timestamp}\` |
1373
+ | **Agent ID** | \`{agentId}\` |
1374
+ | **Session ID** | \`{sessionId}\` |
1375
+ | **Status** | \`{status}\` |
1376
+
1377
+ ---
1378
+
1379
+ ## Context
1380
+
1381
+ ### Problem Statement
1382
+
1383
+ {problemDescription}
1384
+
1385
+ ### Constraints
1386
+
1387
+ - Constraint 1
1388
+ - Constraint 2
1389
+ - Constraint 3
1390
+
1391
+ ### Requirements
1392
+
1393
+ 1. Requirement 1
1394
+ 2. Requirement 2
1395
+
1396
+ ---
1397
+
1398
+ ## Options Considered
1399
+
1400
+ ### Option A: {optionATitle}
1401
+
1402
+ | Attribute | Value |
1403
+ |-----------|-------|
1404
+ | Pros | Pro 1, Pro 2 |
1405
+ | Cons | Con 1, Con 2 |
1406
+ | Risk | Low/Medium/High |
1407
+ | Effort | Small/Medium/Large |
1408
+
1409
+ ### Option B: {optionBTitle}
1410
+
1411
+ | Attribute | Value |
1412
+ |-----------|-------|
1413
+ | Pros | Pro 1, Pro 2 |
1414
+ | Cons | Con 1, Con 2 |
1415
+ | Risk | Low/Medium/High |
1416
+ | Effort | Small/Medium/Large |
1417
+
1418
+ ---
1419
+
1420
+ ## Decision
1421
+
1422
+ > **{chosenOption}** was selected
1423
+
1424
+ ### Rationale
1425
+
1426
+ {choiceRationale}
1427
+
1428
+ ### Trade-offs
1429
+
1430
+ | Factor | Impact |
1431
+ |--------|--------|
1432
+ | Factor 1 | {impact1} |
1433
+ | Factor 2 | {impact2} |
1434
+
1435
+ ---
1436
+
1437
+ ## Implementation
1438
+
1439
+ ### Chosen Approach
1440
+
1441
+ {implementationDetails}
1442
+
1443
+ ### Files Modified
1444
+
1445
+ | File | Change |
1446
+ |------|--------|
1447
+ | file1.ts | Modification A |
1448
+ | file2.sql | Modification B |
1449
+
1450
+ ---
1451
+
1452
+ ## Outcome
1453
+
1454
+ ### Expected Results
1455
+
1456
+ 1. Expected outcome 1
1457
+ 2. Expected outcome 2
1458
+
1459
+ ### Potential Risks
1460
+
1461
+ 1. Risk 1 - Mitigation
1462
+ 2. Risk 2 - Mitigation
1463
+
1464
+ ### Follow-up Required
1465
+
1466
+ - [ ] Follow-up item 1
1467
+ - [ ] Follow-up item 2
1468
+
1469
+ ---
1470
+
1471
+ ## Related Decisions
1472
+
1473
+ | ID | Decision | Relationship |
1474
+ |----|----------|--------------|
1475
+ | DECISION_001 | Related decision | Precedes |
1476
+ | DECISION_003 | Related decision | Extends |
1477
+
1478
+ ---
1479
+
1480
+ > **USAGE**: Use this format for all significant decisions.
1481
+ > Decision IDs are referenced in handoffs for context.
1482
+ `;
1483
+ }
1484
+ generateDecisionLog(schema) {
1485
+ const tablesList = schema.tables.map((t) => `- \`${t.name}\``).join('\n');
1486
+ return `# Decision Log
1487
+
1488
+ > AUTO-GENERATED by cohere. All decisions made during this session.
1489
+
1490
+ ## Overview
1491
+
1492
+ This log tracks all significant decisions made by agents working on this codebase.
1493
+
1494
+ ## Decision Index
1495
+
1496
+ | ID | Date | Title | Status |
1497
+ |----|------|-------|--------|
1498
+ | DECISION_001 | YYYY-MM-DD | Decision title | Active |
1499
+ | DECISION_002 | YYYY-MM-DD | Decision title | Active |
1500
+
1501
+ ---
1502
+
1503
+ ## Active Decisions
1504
+
1505
+ ### DECISION_001: {decisionTitle}
1506
+
1507
+ **Status:** Active
1508
+ **Date:** {date}
1509
+ **Agent:** {agentId}
1510
+
1511
+ **Summary:** Brief summary of decision
1512
+
1513
+ **Impact:** Tables affected: ${tablesList}
1514
+
1515
+ ---
1516
+
1517
+ ## Archived Decisions
1518
+
1519
+ ### DECISION_XXX: {archivedTitle}
1520
+
1521
+ **Status:** Archived
1522
+ **Date:** {date}
1523
+ **Reason:** Context no longer relevant
1524
+
1525
+ ---
1526
+
1527
+ ## Adding New Decisions
1528
+
1529
+ Use the template in \`.ai/decisions/decision-template.md\`:
1530
+
1531
+ \`\`\`bash
1532
+ # Create new decision
1533
+ cp .ai/decisions/decision-template.md .ai/decisions/DECISION_003.md
1534
+
1535
+ # Edit the file with your decision
1536
+ # Update this log
1537
+ \`\`\`
1538
+
1539
+ ---
1540
+
1541
+ ## Decision Categories
1542
+
1543
+ | Category | Description |
1544
+ |----------|-------------|
1545
+ | schema | Database schema changes |
1546
+ | query | Query pattern choices |
1547
+ | architecture | System design decisions |
1548
+ | workflow | Process/flow decisions |
1549
+
1550
+ ---
1551
+
1552
+ > **NOTE**: Decision IDs are referenced in handoffs.
1553
+ > Always update this log when creating new decisions.
1554
+ `;
1555
+ }
1556
+ }
1557
+ //# sourceMappingURL=templates.js.map