k0ntext 3.3.1 → 3.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 (221) hide show
  1. package/README.md +225 -26
  2. package/dist/agents/cleanup-agent.d.ts.map +1 -1
  3. package/dist/agents/cleanup-agent.js +18 -6
  4. package/dist/agents/cleanup-agent.js.map +1 -1
  5. package/dist/agents/drift-agent.d.ts +7 -0
  6. package/dist/agents/drift-agent.d.ts.map +1 -1
  7. package/dist/agents/drift-agent.js +29 -8
  8. package/dist/agents/drift-agent.js.map +1 -1
  9. package/dist/cli/commands/cleanup.d.ts.map +1 -1
  10. package/dist/cli/commands/cleanup.js +8 -1
  11. package/dist/cli/commands/cleanup.js.map +1 -1
  12. package/dist/cli/commands/drift-detect.d.ts.map +1 -1
  13. package/dist/cli/commands/drift-detect.js +21 -1
  14. package/dist/cli/commands/drift-detect.js.map +1 -1
  15. package/dist/cli/commands/embeddings-refresh.d.ts +11 -0
  16. package/dist/cli/commands/embeddings-refresh.d.ts.map +1 -0
  17. package/dist/cli/commands/embeddings-refresh.js +114 -0
  18. package/dist/cli/commands/embeddings-refresh.js.map +1 -0
  19. package/dist/cli/commands/migrate.d.ts +11 -0
  20. package/dist/cli/commands/migrate.d.ts.map +1 -0
  21. package/dist/cli/commands/migrate.js +195 -0
  22. package/dist/cli/commands/migrate.js.map +1 -0
  23. package/dist/cli/commands/restore.d.ts +12 -0
  24. package/dist/cli/commands/restore.d.ts.map +1 -0
  25. package/dist/cli/commands/restore.js +261 -0
  26. package/dist/cli/commands/restore.js.map +1 -0
  27. package/dist/cli/commands/sync-templates.d.ts +15 -0
  28. package/dist/cli/commands/sync-templates.d.ts.map +1 -0
  29. package/dist/cli/commands/sync-templates.js +181 -0
  30. package/dist/cli/commands/sync-templates.js.map +1 -0
  31. package/dist/cli/commands/version-check.d.ts +12 -0
  32. package/dist/cli/commands/version-check.d.ts.map +1 -0
  33. package/dist/cli/commands/version-check.js +133 -0
  34. package/dist/cli/commands/version-check.js.map +1 -0
  35. package/dist/cli/generate.d.ts +5 -0
  36. package/dist/cli/generate.d.ts.map +1 -1
  37. package/dist/cli/generate.js +80 -16
  38. package/dist/cli/generate.js.map +1 -1
  39. package/dist/cli/index.js +215 -1
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/repl/index.d.ts +1 -0
  42. package/dist/cli/repl/index.d.ts.map +1 -1
  43. package/dist/cli/repl/index.js +18 -6
  44. package/dist/cli/repl/index.js.map +1 -1
  45. package/dist/cli/utils/backup-manager.d.ts +94 -0
  46. package/dist/cli/utils/backup-manager.d.ts.map +1 -0
  47. package/dist/cli/utils/backup-manager.js +230 -0
  48. package/dist/cli/utils/backup-manager.js.map +1 -0
  49. package/dist/cli/utils/db-backup-manager.d.ts +55 -0
  50. package/dist/cli/utils/db-backup-manager.d.ts.map +1 -0
  51. package/dist/cli/utils/db-backup-manager.js +115 -0
  52. package/dist/cli/utils/db-backup-manager.js.map +1 -0
  53. package/dist/cli/utils/file-detector.d.ts +87 -0
  54. package/dist/cli/utils/file-detector.d.ts.map +1 -0
  55. package/dist/cli/utils/file-detector.js +131 -0
  56. package/dist/cli/utils/file-detector.js.map +1 -0
  57. package/dist/cli/utils/index.d.ts +9 -0
  58. package/dist/cli/utils/index.d.ts.map +1 -0
  59. package/dist/cli/utils/index.js +9 -0
  60. package/dist/cli/utils/index.js.map +1 -0
  61. package/dist/cli/utils/modification-prompt.d.ts +41 -0
  62. package/dist/cli/utils/modification-prompt.d.ts.map +1 -0
  63. package/dist/cli/utils/modification-prompt.js +84 -0
  64. package/dist/cli/utils/modification-prompt.js.map +1 -0
  65. package/dist/cli/version/checker.d.ts +47 -0
  66. package/dist/cli/version/checker.d.ts.map +1 -0
  67. package/dist/cli/version/checker.js +143 -0
  68. package/dist/cli/version/checker.js.map +1 -0
  69. package/dist/cli/version/comparator.d.ts +46 -0
  70. package/dist/cli/version/comparator.d.ts.map +1 -0
  71. package/dist/cli/version/comparator.js +99 -0
  72. package/dist/cli/version/comparator.js.map +1 -0
  73. package/dist/cli/version/index.d.ts +11 -0
  74. package/dist/cli/version/index.d.ts.map +1 -0
  75. package/dist/cli/version/index.js +11 -0
  76. package/dist/cli/version/index.js.map +1 -0
  77. package/dist/cli/version/parser.d.ts +38 -0
  78. package/dist/cli/version/parser.d.ts.map +1 -0
  79. package/dist/cli/version/parser.js +90 -0
  80. package/dist/cli/version/parser.js.map +1 -0
  81. package/dist/cli/version/prompt.d.ts +40 -0
  82. package/dist/cli/version/prompt.d.ts.map +1 -0
  83. package/dist/cli/version/prompt.js +162 -0
  84. package/dist/cli/version/prompt.js.map +1 -0
  85. package/dist/cli/version/types.d.ts +89 -0
  86. package/dist/cli/version/types.d.ts.map +1 -0
  87. package/dist/cli/version/types.js +7 -0
  88. package/dist/cli/version/types.js.map +1 -0
  89. package/dist/db/client.d.ts +79 -4
  90. package/dist/db/client.d.ts.map +1 -1
  91. package/dist/db/client.js +207 -12
  92. package/dist/db/client.js.map +1 -1
  93. package/dist/db/migrations/files/0014_add_schema_migrations_table.d.ts +14 -0
  94. package/dist/db/migrations/files/0014_add_schema_migrations_table.d.ts.map +1 -0
  95. package/dist/db/migrations/files/0014_add_schema_migrations_table.js +25 -0
  96. package/dist/db/migrations/files/0014_add_schema_migrations_table.js.map +1 -0
  97. package/dist/db/migrations/index.d.ts +9 -0
  98. package/dist/db/migrations/index.d.ts.map +1 -0
  99. package/dist/db/migrations/index.js +9 -0
  100. package/dist/db/migrations/index.js.map +1 -0
  101. package/dist/db/migrations/loader.d.ts +27 -0
  102. package/dist/db/migrations/loader.d.ts.map +1 -0
  103. package/dist/db/migrations/loader.js +106 -0
  104. package/dist/db/migrations/loader.js.map +1 -0
  105. package/dist/db/migrations/runner.d.ts +56 -0
  106. package/dist/db/migrations/runner.d.ts.map +1 -0
  107. package/dist/db/migrations/runner.js +266 -0
  108. package/dist/db/migrations/runner.js.map +1 -0
  109. package/dist/db/migrations/types.d.ts +71 -0
  110. package/dist/db/migrations/types.d.ts.map +1 -0
  111. package/dist/db/migrations/types.js +7 -0
  112. package/dist/db/migrations/types.js.map +1 -0
  113. package/dist/db/schema.d.ts +41 -2
  114. package/dist/db/schema.d.ts.map +1 -1
  115. package/dist/db/schema.js +77 -2
  116. package/dist/db/schema.js.map +1 -1
  117. package/dist/mcp.js +2 -2
  118. package/dist/mcp.js.map +1 -1
  119. package/dist/template-engine/data-transformer.d.ts +17 -0
  120. package/dist/template-engine/data-transformer.d.ts.map +1 -0
  121. package/dist/template-engine/data-transformer.js +343 -0
  122. package/dist/template-engine/data-transformer.js.map +1 -0
  123. package/dist/template-engine/engine.d.ts +74 -0
  124. package/dist/template-engine/engine.d.ts.map +1 -0
  125. package/dist/template-engine/engine.js +183 -0
  126. package/dist/template-engine/engine.js.map +1 -0
  127. package/dist/template-engine/helpers.d.ts +81 -0
  128. package/dist/template-engine/helpers.d.ts.map +1 -0
  129. package/dist/template-engine/helpers.js +153 -0
  130. package/dist/template-engine/helpers.js.map +1 -0
  131. package/dist/template-engine/index.d.ts +10 -0
  132. package/dist/template-engine/index.d.ts.map +1 -0
  133. package/dist/template-engine/index.js +10 -0
  134. package/dist/template-engine/index.js.map +1 -0
  135. package/dist/template-engine/types.d.ts +147 -0
  136. package/dist/template-engine/types.d.ts.map +1 -0
  137. package/dist/template-engine/types.js +7 -0
  138. package/dist/template-engine/types.js.map +1 -0
  139. package/dist/template-sync/comparator.d.ts +138 -0
  140. package/dist/template-sync/comparator.d.ts.map +1 -0
  141. package/dist/template-sync/comparator.js +353 -0
  142. package/dist/template-sync/comparator.js.map +1 -0
  143. package/dist/template-sync/conflict-resolver.d.ts +112 -0
  144. package/dist/template-sync/conflict-resolver.d.ts.map +1 -0
  145. package/dist/template-sync/conflict-resolver.js +328 -0
  146. package/dist/template-sync/conflict-resolver.js.map +1 -0
  147. package/dist/template-sync/engine.d.ts +93 -0
  148. package/dist/template-sync/engine.d.ts.map +1 -0
  149. package/dist/template-sync/engine.js +350 -0
  150. package/dist/template-sync/engine.js.map +1 -0
  151. package/dist/template-sync/hasher.d.ts +67 -0
  152. package/dist/template-sync/hasher.d.ts.map +1 -0
  153. package/dist/template-sync/hasher.js +94 -0
  154. package/dist/template-sync/hasher.js.map +1 -0
  155. package/dist/template-sync/index.d.ts +20 -0
  156. package/dist/template-sync/index.d.ts.map +1 -0
  157. package/dist/template-sync/index.js +14 -0
  158. package/dist/template-sync/index.js.map +1 -0
  159. package/dist/template-sync/manifest.d.ts +131 -0
  160. package/dist/template-sync/manifest.d.ts.map +1 -0
  161. package/dist/template-sync/manifest.js +309 -0
  162. package/dist/template-sync/manifest.js.map +1 -0
  163. package/dist/template-sync/merger.d.ts +125 -0
  164. package/dist/template-sync/merger.d.ts.map +1 -0
  165. package/dist/template-sync/merger.js +371 -0
  166. package/dist/template-sync/merger.js.map +1 -0
  167. package/dist/template-sync/scanner.d.ts +106 -0
  168. package/dist/template-sync/scanner.d.ts.map +1 -0
  169. package/dist/template-sync/scanner.js +196 -0
  170. package/dist/template-sync/scanner.js.map +1 -0
  171. package/dist/template-sync/types.d.ts +199 -0
  172. package/dist/template-sync/types.d.ts.map +1 -0
  173. package/dist/template-sync/types.js +30 -0
  174. package/dist/template-sync/types.js.map +1 -0
  175. package/package.json +2 -1
  176. package/src/agents/cleanup-agent.ts +21 -6
  177. package/src/agents/drift-agent.ts +31 -8
  178. package/src/cli/commands/cleanup.ts +9 -1
  179. package/src/cli/commands/drift-detect.ts +24 -1
  180. package/src/cli/commands/embeddings-refresh.ts +135 -0
  181. package/src/cli/commands/migrate.ts +231 -0
  182. package/src/cli/commands/restore.ts +318 -0
  183. package/src/cli/commands/sync-templates.ts +210 -0
  184. package/src/cli/commands/version-check.ts +158 -0
  185. package/src/cli/generate.ts +99 -17
  186. package/src/cli/index.ts +246 -1
  187. package/src/cli/repl/index.ts +16 -6
  188. package/src/cli/utils/backup-manager.ts +275 -0
  189. package/src/cli/utils/db-backup-manager.ts +146 -0
  190. package/src/cli/utils/file-detector.ts +181 -0
  191. package/src/cli/utils/index.ts +9 -0
  192. package/src/cli/utils/modification-prompt.ts +112 -0
  193. package/src/cli/version/checker.ts +172 -0
  194. package/src/cli/version/comparator.ts +106 -0
  195. package/src/cli/version/index.ts +11 -0
  196. package/src/cli/version/parser.ts +101 -0
  197. package/src/cli/version/prompt.ts +208 -0
  198. package/src/cli/version/types.ts +95 -0
  199. package/src/db/client.ts +285 -18
  200. package/src/db/migrations/files/0014_add_schema_migrations_table.sql +19 -0
  201. package/src/db/migrations/files/0014_add_schema_migrations_table.ts +30 -0
  202. package/src/db/migrations/index.ts +9 -0
  203. package/src/db/migrations/loader.ts +129 -0
  204. package/src/db/migrations/runner.ts +316 -0
  205. package/src/db/migrations/types.ts +71 -0
  206. package/src/db/schema.ts +109 -2
  207. package/src/mcp.ts +2 -2
  208. package/src/template-engine/data-transformer.ts +367 -0
  209. package/src/template-engine/engine.ts +213 -0
  210. package/src/template-engine/helpers.ts +163 -0
  211. package/src/template-engine/index.ts +10 -0
  212. package/src/template-engine/types.ts +158 -0
  213. package/src/template-sync/comparator.ts +452 -0
  214. package/src/template-sync/conflict-resolver.ts +401 -0
  215. package/src/template-sync/engine.ts +417 -0
  216. package/src/template-sync/hasher.ts +104 -0
  217. package/src/template-sync/index.ts +60 -0
  218. package/src/template-sync/manifest.ts +358 -0
  219. package/src/template-sync/merger.ts +454 -0
  220. package/src/template-sync/scanner.ts +254 -0
  221. package/src/template-sync/types.ts +247 -0
@@ -0,0 +1,146 @@
1
+ /**
2
+ * Database Backup Manager
3
+ *
4
+ * Manages database backups for migration safety.
5
+ */
6
+
7
+ import fs from 'fs/promises';
8
+ import path from 'path';
9
+ import type { DatabaseClient } from '../../db/client.js';
10
+
11
+ /**
12
+ * Backup result
13
+ */
14
+ export interface DbBackupResult {
15
+ /** Whether backup was successful */
16
+ success: boolean;
17
+ /** Path to backup file */
18
+ backupPath?: string;
19
+ /** Size of backup in bytes */
20
+ size?: number;
21
+ /** Error message if failed */
22
+ error?: string;
23
+ }
24
+
25
+ /**
26
+ * Database backup manager
27
+ *
28
+ * Creates and manages backups of the SQLite database for migration safety.
29
+ */
30
+ export class DbBackupManager {
31
+ private db: DatabaseClient;
32
+ private projectRoot: string;
33
+ private readonly BACKUP_DIR = '.k0ntext/db-backups';
34
+ private readonly KEEP_BACKUPS = 5;
35
+
36
+ constructor(db: DatabaseClient, projectRoot: string) {
37
+ this.db = db;
38
+ this.projectRoot = projectRoot;
39
+ }
40
+
41
+ /**
42
+ * Create a backup of the database
43
+ *
44
+ * @returns Backup result
45
+ */
46
+ async createBackup(): Promise<DbBackupResult> {
47
+ try {
48
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
49
+ const backupFileName = `db-${timestamp}.db`;
50
+ const backupPath = path.join(this.projectRoot, this.BACKUP_DIR, backupFileName);
51
+
52
+ // Ensure backup directory exists
53
+ await fs.mkdir(path.dirname(backupPath), { recursive: true });
54
+
55
+ // Use existing backup method from DatabaseClient
56
+ this.db.backup(backupPath);
57
+
58
+ // Get file size
59
+ const stats = await fs.stat(backupPath);
60
+
61
+ // Clean old backups (keep last 5)
62
+ await this.cleanupOldBackups();
63
+
64
+ return {
65
+ success: true,
66
+ backupPath,
67
+ size: stats.size
68
+ };
69
+ } catch (error) {
70
+ return {
71
+ success: false,
72
+ error: error instanceof Error ? error.message : String(error)
73
+ };
74
+ }
75
+ }
76
+
77
+ /**
78
+ * List all database backups
79
+ *
80
+ * @returns Array of backup file names
81
+ */
82
+ async listBackups(): Promise<string[]> {
83
+ const backupDir = path.join(this.projectRoot, this.BACKUP_DIR);
84
+
85
+ try {
86
+ await fs.access(backupDir);
87
+ } catch {
88
+ return [];
89
+ }
90
+
91
+ const files = await fs.readdir(backupDir);
92
+ return files
93
+ .filter(f => f.endsWith('.db'))
94
+ .sort()
95
+ .reverse();
96
+ }
97
+
98
+ /**
99
+ * Restore from a backup
100
+ *
101
+ * @param backupFileName - Name of backup file to restore
102
+ * @returns True if restore was successful
103
+ */
104
+ async restoreFromBackup(backupFileName: string): Promise<boolean> {
105
+ const backupDir = path.join(this.projectRoot, this.BACKUP_DIR);
106
+ const backupPath = path.join(backupDir, backupFileName);
107
+
108
+ try {
109
+ // Verify backup exists
110
+ await fs.access(backupPath);
111
+
112
+ // Get database path from DatabaseClient
113
+ const dbPath = (this.db as any).dbPath;
114
+
115
+ // Copy backup to main database location
116
+ await fs.copyFile(backupPath, dbPath);
117
+
118
+ return true;
119
+ } catch {
120
+ return false;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * Clean up old backups (keep last 5)
126
+ */
127
+ private async cleanupOldBackups(): Promise<void> {
128
+ const backupDir = path.join(this.projectRoot, this.BACKUP_DIR);
129
+
130
+ try {
131
+ const files = await fs.readdir(backupDir);
132
+ const backups = files
133
+ .filter(f => f.endsWith('.db'))
134
+ .sort();
135
+
136
+ // Keep last 5
137
+ const toDelete = backups.slice(0, backups.length - this.KEEP_BACKUPS);
138
+
139
+ for (const file of toDelete) {
140
+ await fs.unlink(path.join(backupDir, file));
141
+ }
142
+ } catch {
143
+ // Ignore cleanup errors
144
+ }
145
+ }
146
+ }
@@ -0,0 +1,181 @@
1
+ /**
2
+ * File Modification Detector
3
+ *
4
+ * Detects when user has modified generated context files.
5
+ * Uses content hashing for efficient change detection.
6
+ */
7
+
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import type { DatabaseClient } from '../../db/client.js';
11
+
12
+ /**
13
+ * File modification status
14
+ */
15
+ export interface FileModificationStatus {
16
+ /** Tool name */
17
+ tool: string;
18
+ /** Full file path */
19
+ filePath: string;
20
+ /** Whether file exists on disk */
21
+ exists: boolean;
22
+ /** Whether file was generated by k0ntext */
23
+ wasGenerated: boolean;
24
+ /** Current content hash */
25
+ currentHash: string | null;
26
+ /** Hash stored in database when generated */
27
+ storedHash: string | null;
28
+ /** Whether file has been modified since generation */
29
+ isModified: boolean;
30
+ /** Last time file was generated */
31
+ generatedAt?: string;
32
+ /** Last time file was verified */
33
+ lastVerifiedAt?: string;
34
+ }
35
+
36
+ /**
37
+ * File modification detector options
38
+ */
39
+ export interface ModificationDetectorOptions {
40
+ /** Include files that don't exist in results */
41
+ includeNonExistent?: boolean;
42
+ }
43
+
44
+ /**
45
+ * File modification detector
46
+ *
47
+ * Checks if user has modified generated context files.
48
+ */
49
+ export class FileModificationDetector {
50
+ private db: DatabaseClient;
51
+ private projectRoot: string;
52
+
53
+ constructor(db: DatabaseClient, projectRoot: string = process.cwd()) {
54
+ this.db = db;
55
+ this.projectRoot = projectRoot;
56
+ }
57
+
58
+ /**
59
+ * Convert absolute path to relative path for database lookup
60
+ */
61
+ private toRelativePath(filePath: string): string {
62
+ if (path.isAbsolute(filePath)) {
63
+ return path.relative(this.projectRoot, filePath);
64
+ }
65
+ return filePath;
66
+ }
67
+
68
+ /**
69
+ * Check a single file for modifications
70
+ *
71
+ * @param tool - Tool name
72
+ * @param filePath - Full path to file
73
+ * @returns File modification status
74
+ */
75
+ async checkFile(tool: string, filePath: string): Promise<FileModificationStatus> {
76
+ // Check if file exists
77
+ let exists = false;
78
+ let currentContent = '';
79
+ try {
80
+ currentContent = await fs.readFile(filePath, 'utf-8');
81
+ exists = true;
82
+ } catch {
83
+ // File doesn't exist
84
+ }
85
+
86
+ // Get current hash
87
+ const currentHash = exists ? this.db.hashContent(currentContent) : null;
88
+
89
+ // Get stored record - convert absolute path to relative for lookup
90
+ const relativePath = this.toRelativePath(filePath);
91
+ const stored = this.db.getGeneratedFileInfo(tool, relativePath);
92
+
93
+ const wasGenerated = stored !== null;
94
+ const storedHash = stored?.contentHash || null;
95
+ const isModified = storedHash !== null && currentHash !== null && currentHash !== storedHash;
96
+
97
+ return {
98
+ tool,
99
+ filePath,
100
+ exists,
101
+ wasGenerated,
102
+ currentHash,
103
+ storedHash,
104
+ isModified,
105
+ generatedAt: stored?.generatedAt,
106
+ lastVerifiedAt: stored?.lastVerifiedAt
107
+ };
108
+ }
109
+
110
+ /**
111
+ * Check all generated files for modifications
112
+ *
113
+ * @param options - Detector options
114
+ * @returns Array of file modification statuses
115
+ */
116
+ async checkAll(options: ModificationDetectorOptions = {}): Promise<FileModificationStatus[]> {
117
+ const results: FileModificationStatus[] = [];
118
+
119
+ // Get all tracked tools
120
+ const tools = ['claude', 'copilot', 'cline', 'antigravity', 'windsurf', 'aider', 'continue', 'cursor', 'gemini'];
121
+
122
+ for (const tool of tools) {
123
+ const generatedFiles = this.db.getGeneratedFiles(tool);
124
+
125
+ for (const file of generatedFiles) {
126
+ // Convert relative path to absolute for file operations
127
+ const absolutePath = path.isAbsolute(file.filePath)
128
+ ? file.filePath
129
+ : path.join(this.projectRoot, file.filePath);
130
+
131
+ const status = await this.checkFile(file.tool, absolutePath);
132
+
133
+ if (options.includeNonExistent || status.exists) {
134
+ results.push(status);
135
+ }
136
+ }
137
+ }
138
+
139
+ return results;
140
+ }
141
+
142
+ /**
143
+ * Get only modified files
144
+ *
145
+ * @returns Array of modified file statuses
146
+ */
147
+ async getModifiedFiles(): Promise<FileModificationStatus[]> {
148
+ const all = await this.checkAll();
149
+ return all.filter(f => f.isModified);
150
+ }
151
+
152
+ /**
153
+ * Mark a file as verified (update last_verified_at)
154
+ *
155
+ * @param tool - Tool name
156
+ * @param filePath - Full path to file
157
+ */
158
+ markAsVerified(tool: string, filePath: string): void {
159
+ const relativePath = this.toRelativePath(filePath);
160
+ const record = this.db.getGeneratedFileInfo(tool, relativePath);
161
+ if (record) {
162
+ this.db.upsertGeneratedFile({
163
+ tool,
164
+ filePath: relativePath,
165
+ contentHash: record.contentHash,
166
+ backupPath: record.backupPath
167
+ });
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Mark a file as user-modified
173
+ *
174
+ * @param tool - Tool name
175
+ * @param filePath - Full path to file
176
+ */
177
+ markAsModified(tool: string, filePath: string): void {
178
+ const relativePath = this.toRelativePath(filePath);
179
+ this.db.markUserModified(tool, relativePath);
180
+ }
181
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * CLI Utilities Module
3
+ *
4
+ * Exports all utility functions and classes.
5
+ */
6
+
7
+ export * from './file-detector.js';
8
+ export * from './backup-manager.js';
9
+ export * from './modification-prompt.js';
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Modification Prompt
3
+ *
4
+ * Interactive prompts for handling user-modified files.
5
+ */
6
+
7
+ import chalk from 'chalk';
8
+ import { confirm, select } from '@inquirer/prompts';
9
+ import type { FileModificationStatus } from './file-detector.js';
10
+
11
+ /**
12
+ * Modification prompt options
13
+ */
14
+ export interface ModificationPromptOptions {
15
+ /** Modified file to prompt about */
16
+ file: FileModificationStatus;
17
+ /** Default choice */
18
+ defaultChoice?: 'skip' | 'backup' | 'overwrite';
19
+ }
20
+
21
+ /**
22
+ * Prompt result for single file
23
+ */
24
+ export type PromptResult = 'skip' | 'backup' | 'overwrite' | 'cancel';
25
+
26
+ /**
27
+ * Prompt for single file modification
28
+ *
29
+ * @param options - Prompt options
30
+ * @returns User's choice
31
+ */
32
+ export async function promptFileModification(options: ModificationPromptOptions): Promise<PromptResult> {
33
+ const { file } = options;
34
+
35
+ console.log('');
36
+ console.log(chalk.yellow('⚠ Modified file detected:'), chalk.bold(path.basename(file.filePath)));
37
+ console.log(chalk.dim(`Path: ${file.filePath}`));
38
+ console.log(chalk.dim(`This file was generated by k0ntext but has been modified.`));
39
+ console.log('');
40
+
41
+ const choice = await select<PromptResult>({
42
+ message: 'What would you like to do?',
43
+ choices: [
44
+ {
45
+ name: 'Skip this file (keep current version)',
46
+ value: 'skip',
47
+ description: 'Leave the file as-is and continue'
48
+ },
49
+ {
50
+ name: 'Backup then overwrite',
51
+ value: 'backup',
52
+ description: 'Create a backup of current version, then regenerate'
53
+ },
54
+ {
55
+ name: 'Overwrite without backup',
56
+ value: 'overwrite',
57
+ description: 'Discard current changes and regenerate (no backup)'
58
+ },
59
+ {
60
+ name: 'Cancel generation',
61
+ value: 'cancel',
62
+ description: 'Stop the generation process'
63
+ }
64
+ ],
65
+ default: options.defaultChoice || 'skip'
66
+ });
67
+
68
+ return choice;
69
+ }
70
+
71
+ /**
72
+ * Show a summary of modified files
73
+ *
74
+ * @param modifications - Array of modification statuses
75
+ * @returns True if user wants to proceed
76
+ */
77
+ export async function showSummary(modifications: FileModificationStatus[]): Promise<boolean> {
78
+ if (modifications.length === 0) {
79
+ return true;
80
+ }
81
+
82
+ console.log('');
83
+ console.log(chalk.yellow(`⚠ ${modifications.length} modified file(s) detected:`));
84
+ console.log('');
85
+
86
+ for (const mod of modifications) {
87
+ console.log(chalk.dim(' •'), mod.filePath);
88
+ }
89
+
90
+ console.log('');
91
+
92
+ const proceed = await confirm({
93
+ message: 'These files have custom modifications. Continue?',
94
+ default: false
95
+ });
96
+
97
+ return proceed;
98
+ }
99
+
100
+ /**
101
+ * Format modification status for display
102
+ *
103
+ * @param status - Modification status
104
+ * @returns Formatted string
105
+ */
106
+ export function formatModificationStatus(status: FileModificationStatus): string {
107
+ const icon = status.isModified ? '⚠' : '✓';
108
+ const modified = status.isModified ? chalk.yellow('modified') : chalk.green('unmodified');
109
+ return `${icon} ${status.filePath} (${modified})`;
110
+ }
111
+
112
+ import path from 'path';
@@ -0,0 +1,172 @@
1
+ /**
2
+ * Version Checker
3
+ *
4
+ * Checks context files for outdated versions.
5
+ * Integrates with database for efficient caching.
6
+ */
7
+
8
+ import fs from 'fs/promises';
9
+ import path from 'path';
10
+ import type { DatabaseClient } from '../../db/client.js';
11
+ import { parseVersion, hasVersionMarker } from './parser.js';
12
+ import { needsUpdate, getUpdateType } from './comparator.js';
13
+ import type {
14
+ CheckOptions,
15
+ FileVersionStatus,
16
+ VersionCheckResult,
17
+ OutdatedFile
18
+ } from './types.js';
19
+
20
+ /**
21
+ * Tool to file path mappings
22
+ */
23
+ const TOOL_FILES: Record<string, string[]> = {
24
+ claude: ['AI_CONTEXT.md', 'CLAUDE.md', '.claude/AI_CONTEXT.md'],
25
+ copilot: ['.github/copilot-instructions.md'],
26
+ cline: ['.clinerules'],
27
+ antigravity: ['.agent/README.md'],
28
+ windsurf: ['.windsurf/rules.md'],
29
+ aider: ['.aider.conf.yml'],
30
+ continue: ['.continue/config.json'],
31
+ cursor: ['.cursorrules'],
32
+ gemini: ['.gemini/config.md']
33
+ };
34
+
35
+ /**
36
+ * Check a single file for version status
37
+ *
38
+ * @param filePath - Full path to the file
39
+ * @param tool - Tool name
40
+ * @param currentVersion - Current k0ntext version
41
+ * @param db - Database client (optional, for modification detection)
42
+ * @returns File version status
43
+ */
44
+ export async function checkSingleFile(
45
+ filePath: string,
46
+ tool: string,
47
+ currentVersion: string,
48
+ db?: DatabaseClient
49
+ ): Promise<FileVersionStatus> {
50
+ // Check if file exists
51
+ let exists = false;
52
+ let content = '';
53
+ try {
54
+ content = await fs.readFile(filePath, 'utf-8');
55
+ exists = true;
56
+ } catch {
57
+ // File doesn't exist
58
+ }
59
+
60
+ // Check for version marker
61
+ const hasMarker = hasVersionMarker(content);
62
+ const fileVersion = parseVersion(content);
63
+
64
+ // Determine if outdated
65
+ const isOutdated = fileVersion ? needsUpdate(fileVersion, currentVersion) : false;
66
+ const updateType = fileVersion ? getUpdateType(fileVersion, currentVersion) : 'none';
67
+
68
+ // Check if user modified (from database)
69
+ let userModified = false;
70
+ if (db && exists) {
71
+ const tracked = db.getGeneratedFileInfo(tool, filePath);
72
+ if (tracked?.userModified) {
73
+ userModified = true;
74
+ }
75
+ }
76
+
77
+ return {
78
+ tool,
79
+ filePath,
80
+ exists,
81
+ fileVersion,
82
+ hasMarker,
83
+ isOutdated,
84
+ updateType,
85
+ userModified
86
+ };
87
+ }
88
+
89
+ /**
90
+ * Check all context files for version status
91
+ *
92
+ * @param options - Check options
93
+ * @param db - Database client (optional)
94
+ * @returns Version check result
95
+ */
96
+ export async function checkContextFiles(
97
+ options: CheckOptions,
98
+ db?: DatabaseClient
99
+ ): Promise<VersionCheckResult> {
100
+ const { projectRoot, currentVersion, tools: toolsToCheck } = options;
101
+
102
+ const tools = toolsToCheck || Object.keys(TOOL_FILES);
103
+ const results: VersionCheckResult = {
104
+ currentVersion,
105
+ checked: 0,
106
+ outdated: [],
107
+ upToDate: [],
108
+ noVersion: []
109
+ };
110
+
111
+ for (const tool of tools) {
112
+ const files = TOOL_FILES[tool] || [];
113
+ const toolName = tool;
114
+
115
+ for (const relativePath of files) {
116
+ const fullPath = path.join(projectRoot, relativePath);
117
+
118
+ const status = await checkSingleFile(fullPath, toolName, currentVersion, db);
119
+ results.checked++;
120
+
121
+ if (!status.exists) {
122
+ continue; // Skip non-existent files
123
+ }
124
+
125
+ if (status.isOutdated) {
126
+ results.outdated.push({
127
+ tool: status.tool,
128
+ filePath: status.filePath,
129
+ fileVersion: status.fileVersion || 'unknown',
130
+ currentVersion,
131
+ updateType: status.updateType,
132
+ userModified: status.userModified
133
+ });
134
+ } else if (status.hasMarker) {
135
+ results.upToDate.push(status);
136
+ } else {
137
+ results.noVersion.push(status);
138
+ }
139
+ }
140
+ }
141
+
142
+ return results;
143
+ }
144
+
145
+ /**
146
+ * Get all context file paths for a tool
147
+ *
148
+ * @param tool - Tool name
149
+ * @returns Array of file paths
150
+ */
151
+ export function getToolFiles(tool: string): string[] {
152
+ return TOOL_FILES[tool] || [];
153
+ }
154
+
155
+ /**
156
+ * Check if a tool is supported
157
+ *
158
+ * @param tool - Tool name
159
+ * @returns True if supported
160
+ */
161
+ export function isSupportedTool(tool: string): boolean {
162
+ return tool in TOOL_FILES;
163
+ }
164
+
165
+ /**
166
+ * Get all supported tools
167
+ *
168
+ * @returns Array of tool names
169
+ */
170
+ export function getSupportedTools(): string[] {
171
+ return Object.keys(TOOL_FILES);
172
+ }