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.
- package/README.md +225 -26
- package/dist/agents/cleanup-agent.d.ts.map +1 -1
- package/dist/agents/cleanup-agent.js +18 -6
- package/dist/agents/cleanup-agent.js.map +1 -1
- package/dist/agents/drift-agent.d.ts +7 -0
- package/dist/agents/drift-agent.d.ts.map +1 -1
- package/dist/agents/drift-agent.js +29 -8
- package/dist/agents/drift-agent.js.map +1 -1
- package/dist/cli/commands/cleanup.d.ts.map +1 -1
- package/dist/cli/commands/cleanup.js +8 -1
- package/dist/cli/commands/cleanup.js.map +1 -1
- package/dist/cli/commands/drift-detect.d.ts.map +1 -1
- package/dist/cli/commands/drift-detect.js +21 -1
- package/dist/cli/commands/drift-detect.js.map +1 -1
- package/dist/cli/commands/embeddings-refresh.d.ts +11 -0
- package/dist/cli/commands/embeddings-refresh.d.ts.map +1 -0
- package/dist/cli/commands/embeddings-refresh.js +114 -0
- package/dist/cli/commands/embeddings-refresh.js.map +1 -0
- package/dist/cli/commands/migrate.d.ts +11 -0
- package/dist/cli/commands/migrate.d.ts.map +1 -0
- package/dist/cli/commands/migrate.js +195 -0
- package/dist/cli/commands/migrate.js.map +1 -0
- package/dist/cli/commands/restore.d.ts +12 -0
- package/dist/cli/commands/restore.d.ts.map +1 -0
- package/dist/cli/commands/restore.js +261 -0
- package/dist/cli/commands/restore.js.map +1 -0
- package/dist/cli/commands/sync-templates.d.ts +15 -0
- package/dist/cli/commands/sync-templates.d.ts.map +1 -0
- package/dist/cli/commands/sync-templates.js +181 -0
- package/dist/cli/commands/sync-templates.js.map +1 -0
- package/dist/cli/commands/version-check.d.ts +12 -0
- package/dist/cli/commands/version-check.d.ts.map +1 -0
- package/dist/cli/commands/version-check.js +133 -0
- package/dist/cli/commands/version-check.js.map +1 -0
- package/dist/cli/generate.d.ts +5 -0
- package/dist/cli/generate.d.ts.map +1 -1
- package/dist/cli/generate.js +80 -16
- package/dist/cli/generate.js.map +1 -1
- package/dist/cli/index.js +215 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/repl/index.d.ts +1 -0
- package/dist/cli/repl/index.d.ts.map +1 -1
- package/dist/cli/repl/index.js +18 -6
- package/dist/cli/repl/index.js.map +1 -1
- package/dist/cli/utils/backup-manager.d.ts +94 -0
- package/dist/cli/utils/backup-manager.d.ts.map +1 -0
- package/dist/cli/utils/backup-manager.js +230 -0
- package/dist/cli/utils/backup-manager.js.map +1 -0
- package/dist/cli/utils/db-backup-manager.d.ts +55 -0
- package/dist/cli/utils/db-backup-manager.d.ts.map +1 -0
- package/dist/cli/utils/db-backup-manager.js +115 -0
- package/dist/cli/utils/db-backup-manager.js.map +1 -0
- package/dist/cli/utils/file-detector.d.ts +87 -0
- package/dist/cli/utils/file-detector.d.ts.map +1 -0
- package/dist/cli/utils/file-detector.js +131 -0
- package/dist/cli/utils/file-detector.js.map +1 -0
- package/dist/cli/utils/index.d.ts +9 -0
- package/dist/cli/utils/index.d.ts.map +1 -0
- package/dist/cli/utils/index.js +9 -0
- package/dist/cli/utils/index.js.map +1 -0
- package/dist/cli/utils/modification-prompt.d.ts +41 -0
- package/dist/cli/utils/modification-prompt.d.ts.map +1 -0
- package/dist/cli/utils/modification-prompt.js +84 -0
- package/dist/cli/utils/modification-prompt.js.map +1 -0
- package/dist/cli/version/checker.d.ts +47 -0
- package/dist/cli/version/checker.d.ts.map +1 -0
- package/dist/cli/version/checker.js +143 -0
- package/dist/cli/version/checker.js.map +1 -0
- package/dist/cli/version/comparator.d.ts +46 -0
- package/dist/cli/version/comparator.d.ts.map +1 -0
- package/dist/cli/version/comparator.js +99 -0
- package/dist/cli/version/comparator.js.map +1 -0
- package/dist/cli/version/index.d.ts +11 -0
- package/dist/cli/version/index.d.ts.map +1 -0
- package/dist/cli/version/index.js +11 -0
- package/dist/cli/version/index.js.map +1 -0
- package/dist/cli/version/parser.d.ts +38 -0
- package/dist/cli/version/parser.d.ts.map +1 -0
- package/dist/cli/version/parser.js +90 -0
- package/dist/cli/version/parser.js.map +1 -0
- package/dist/cli/version/prompt.d.ts +40 -0
- package/dist/cli/version/prompt.d.ts.map +1 -0
- package/dist/cli/version/prompt.js +162 -0
- package/dist/cli/version/prompt.js.map +1 -0
- package/dist/cli/version/types.d.ts +89 -0
- package/dist/cli/version/types.d.ts.map +1 -0
- package/dist/cli/version/types.js +7 -0
- package/dist/cli/version/types.js.map +1 -0
- package/dist/db/client.d.ts +79 -4
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +207 -12
- package/dist/db/client.js.map +1 -1
- package/dist/db/migrations/files/0014_add_schema_migrations_table.d.ts +14 -0
- package/dist/db/migrations/files/0014_add_schema_migrations_table.d.ts.map +1 -0
- package/dist/db/migrations/files/0014_add_schema_migrations_table.js +25 -0
- package/dist/db/migrations/files/0014_add_schema_migrations_table.js.map +1 -0
- package/dist/db/migrations/index.d.ts +9 -0
- package/dist/db/migrations/index.d.ts.map +1 -0
- package/dist/db/migrations/index.js +9 -0
- package/dist/db/migrations/index.js.map +1 -0
- package/dist/db/migrations/loader.d.ts +27 -0
- package/dist/db/migrations/loader.d.ts.map +1 -0
- package/dist/db/migrations/loader.js +106 -0
- package/dist/db/migrations/loader.js.map +1 -0
- package/dist/db/migrations/runner.d.ts +56 -0
- package/dist/db/migrations/runner.d.ts.map +1 -0
- package/dist/db/migrations/runner.js +266 -0
- package/dist/db/migrations/runner.js.map +1 -0
- package/dist/db/migrations/types.d.ts +71 -0
- package/dist/db/migrations/types.d.ts.map +1 -0
- package/dist/db/migrations/types.js +7 -0
- package/dist/db/migrations/types.js.map +1 -0
- package/dist/db/schema.d.ts +41 -2
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +77 -2
- package/dist/db/schema.js.map +1 -1
- package/dist/mcp.js +2 -2
- package/dist/mcp.js.map +1 -1
- package/dist/template-engine/data-transformer.d.ts +17 -0
- package/dist/template-engine/data-transformer.d.ts.map +1 -0
- package/dist/template-engine/data-transformer.js +343 -0
- package/dist/template-engine/data-transformer.js.map +1 -0
- package/dist/template-engine/engine.d.ts +74 -0
- package/dist/template-engine/engine.d.ts.map +1 -0
- package/dist/template-engine/engine.js +183 -0
- package/dist/template-engine/engine.js.map +1 -0
- package/dist/template-engine/helpers.d.ts +81 -0
- package/dist/template-engine/helpers.d.ts.map +1 -0
- package/dist/template-engine/helpers.js +153 -0
- package/dist/template-engine/helpers.js.map +1 -0
- package/dist/template-engine/index.d.ts +10 -0
- package/dist/template-engine/index.d.ts.map +1 -0
- package/dist/template-engine/index.js +10 -0
- package/dist/template-engine/index.js.map +1 -0
- package/dist/template-engine/types.d.ts +147 -0
- package/dist/template-engine/types.d.ts.map +1 -0
- package/dist/template-engine/types.js +7 -0
- package/dist/template-engine/types.js.map +1 -0
- package/dist/template-sync/comparator.d.ts +138 -0
- package/dist/template-sync/comparator.d.ts.map +1 -0
- package/dist/template-sync/comparator.js +353 -0
- package/dist/template-sync/comparator.js.map +1 -0
- package/dist/template-sync/conflict-resolver.d.ts +112 -0
- package/dist/template-sync/conflict-resolver.d.ts.map +1 -0
- package/dist/template-sync/conflict-resolver.js +328 -0
- package/dist/template-sync/conflict-resolver.js.map +1 -0
- package/dist/template-sync/engine.d.ts +93 -0
- package/dist/template-sync/engine.d.ts.map +1 -0
- package/dist/template-sync/engine.js +350 -0
- package/dist/template-sync/engine.js.map +1 -0
- package/dist/template-sync/hasher.d.ts +67 -0
- package/dist/template-sync/hasher.d.ts.map +1 -0
- package/dist/template-sync/hasher.js +94 -0
- package/dist/template-sync/hasher.js.map +1 -0
- package/dist/template-sync/index.d.ts +20 -0
- package/dist/template-sync/index.d.ts.map +1 -0
- package/dist/template-sync/index.js +14 -0
- package/dist/template-sync/index.js.map +1 -0
- package/dist/template-sync/manifest.d.ts +131 -0
- package/dist/template-sync/manifest.d.ts.map +1 -0
- package/dist/template-sync/manifest.js +309 -0
- package/dist/template-sync/manifest.js.map +1 -0
- package/dist/template-sync/merger.d.ts +125 -0
- package/dist/template-sync/merger.d.ts.map +1 -0
- package/dist/template-sync/merger.js +371 -0
- package/dist/template-sync/merger.js.map +1 -0
- package/dist/template-sync/scanner.d.ts +106 -0
- package/dist/template-sync/scanner.d.ts.map +1 -0
- package/dist/template-sync/scanner.js +196 -0
- package/dist/template-sync/scanner.js.map +1 -0
- package/dist/template-sync/types.d.ts +199 -0
- package/dist/template-sync/types.d.ts.map +1 -0
- package/dist/template-sync/types.js +30 -0
- package/dist/template-sync/types.js.map +1 -0
- package/package.json +2 -1
- package/src/agents/cleanup-agent.ts +21 -6
- package/src/agents/drift-agent.ts +31 -8
- package/src/cli/commands/cleanup.ts +9 -1
- package/src/cli/commands/drift-detect.ts +24 -1
- package/src/cli/commands/embeddings-refresh.ts +135 -0
- package/src/cli/commands/migrate.ts +231 -0
- package/src/cli/commands/restore.ts +318 -0
- package/src/cli/commands/sync-templates.ts +210 -0
- package/src/cli/commands/version-check.ts +158 -0
- package/src/cli/generate.ts +99 -17
- package/src/cli/index.ts +246 -1
- package/src/cli/repl/index.ts +16 -6
- package/src/cli/utils/backup-manager.ts +275 -0
- package/src/cli/utils/db-backup-manager.ts +146 -0
- package/src/cli/utils/file-detector.ts +181 -0
- package/src/cli/utils/index.ts +9 -0
- package/src/cli/utils/modification-prompt.ts +112 -0
- package/src/cli/version/checker.ts +172 -0
- package/src/cli/version/comparator.ts +106 -0
- package/src/cli/version/index.ts +11 -0
- package/src/cli/version/parser.ts +101 -0
- package/src/cli/version/prompt.ts +208 -0
- package/src/cli/version/types.ts +95 -0
- package/src/db/client.ts +285 -18
- package/src/db/migrations/files/0014_add_schema_migrations_table.sql +19 -0
- package/src/db/migrations/files/0014_add_schema_migrations_table.ts +30 -0
- package/src/db/migrations/index.ts +9 -0
- package/src/db/migrations/loader.ts +129 -0
- package/src/db/migrations/runner.ts +316 -0
- package/src/db/migrations/types.ts +71 -0
- package/src/db/schema.ts +109 -2
- package/src/mcp.ts +2 -2
- package/src/template-engine/data-transformer.ts +367 -0
- package/src/template-engine/engine.ts +213 -0
- package/src/template-engine/helpers.ts +163 -0
- package/src/template-engine/index.ts +10 -0
- package/src/template-engine/types.ts +158 -0
- package/src/template-sync/comparator.ts +452 -0
- package/src/template-sync/conflict-resolver.ts +401 -0
- package/src/template-sync/engine.ts +417 -0
- package/src/template-sync/hasher.ts +104 -0
- package/src/template-sync/index.ts +60 -0
- package/src/template-sync/manifest.ts +358 -0
- package/src/template-sync/merger.ts +454 -0
- package/src/template-sync/scanner.ts +254 -0
- 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,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
|
+
}
|