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
package/src/cli/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ import { fileURLToPath } from 'url';
|
|
|
16
16
|
|
|
17
17
|
import { createIntelligentAnalyzer } from '../analyzer/intelligent-analyzer.js';
|
|
18
18
|
import { hasOpenRouterKey } from '../embeddings/openrouter.js';
|
|
19
|
+
import type { DatabaseClient } from '../db/client.js';
|
|
19
20
|
import { generateCommand } from './generate.js';
|
|
20
21
|
import { syncCommand } from './sync.js';
|
|
21
22
|
import { cleanupCommand } from './commands/cleanup.js';
|
|
@@ -29,6 +30,11 @@ import { crossSyncCommand } from './commands/cross-sync.js';
|
|
|
29
30
|
import { hooksCommand } from './commands/hooks.js';
|
|
30
31
|
import { factCheckCommand } from './commands/fact-check.js';
|
|
31
32
|
import { batchIndexCommand } from './commands/batch-index.js';
|
|
33
|
+
import { versionCheckCommand } from './commands/version-check.js';
|
|
34
|
+
import { restoreCommand } from './commands/restore.js';
|
|
35
|
+
import { syncTemplatesCommand, templateStatusCommand } from './commands/sync-templates.js';
|
|
36
|
+
import { migrateCommand } from './commands/migrate.js';
|
|
37
|
+
import { embeddingsRefreshCommand } from './commands/embeddings-refresh.js';
|
|
32
38
|
|
|
33
39
|
const __filename = fileURLToPath(import.meta.url);
|
|
34
40
|
const __dirname = path.dirname(__filename);
|
|
@@ -145,6 +151,8 @@ function createProgram(): Command {
|
|
|
145
151
|
.description('Initialize AI context for a project with intelligent analysis')
|
|
146
152
|
.argument('[project-name]', 'Name of the project (defaults to current directory)')
|
|
147
153
|
.option('--no-intelligent', 'Skip OpenRouter-powered intelligent analysis')
|
|
154
|
+
.option('--no-version-check', 'Skip checking for outdated context files')
|
|
155
|
+
.option('--no-template-sync', 'Skip template synchronization')
|
|
148
156
|
.action(async (projectName, options) => {
|
|
149
157
|
showBanner();
|
|
150
158
|
|
|
@@ -204,6 +212,198 @@ function createProgram(): Command {
|
|
|
204
212
|
await configureMcpServer(targetDir);
|
|
205
213
|
spinner.succeed('MCP server configured');
|
|
206
214
|
|
|
215
|
+
// Sync templates from package
|
|
216
|
+
if (options.templateSync !== false) {
|
|
217
|
+
spinner.start('Checking template sync...');
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
const { TemplateSyncEngine } = await import('../template-sync/index.js');
|
|
221
|
+
const { DatabaseClient: DBClientClass } = await import('../db/client.js');
|
|
222
|
+
|
|
223
|
+
const db = new DBClientClass(targetDir);
|
|
224
|
+
const templateRoot = path.join(__dirname, '../../templates/base');
|
|
225
|
+
const engine = new TemplateSyncEngine(db, targetDir, templateRoot);
|
|
226
|
+
|
|
227
|
+
// Check if sync is needed
|
|
228
|
+
const needsSync = await engine.needsSync();
|
|
229
|
+
|
|
230
|
+
if (needsSync) {
|
|
231
|
+
spinner.text = 'Syncing templates from package...';
|
|
232
|
+
|
|
233
|
+
const syncResult = await engine.sync({
|
|
234
|
+
dryRun: false,
|
|
235
|
+
force: false,
|
|
236
|
+
verbose: options.verbose
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
if (syncResult.conflicts.length === 0) {
|
|
240
|
+
spinner.succeed(chalk.green(`Templates synced (${syncResult.updated} updated, ${syncResult.created} created)`));
|
|
241
|
+
} else {
|
|
242
|
+
spinner.warn(chalk.yellow(`Templates synced with ${syncResult.conflicts.length} conflict(s)`));
|
|
243
|
+
console.log(chalk.dim(` Run 'k0ntext sync-templates' to resolve conflicts`));
|
|
244
|
+
}
|
|
245
|
+
} else {
|
|
246
|
+
spinner.succeed('Templates already up to date');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
db.close();
|
|
250
|
+
} catch (error) {
|
|
251
|
+
spinner.stop();
|
|
252
|
+
// Template sync is optional, don't fail on error
|
|
253
|
+
if (options.verbose) {
|
|
254
|
+
console.warn(chalk.dim(`Template sync skipped: ${error instanceof Error ? error.message : error}`));
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Check for database migrations
|
|
260
|
+
spinner.start('Checking database migrations...');
|
|
261
|
+
|
|
262
|
+
try {
|
|
263
|
+
const { MigrationRunner } = await import('../db/migrations/index.js');
|
|
264
|
+
const { DatabaseClient: DBClientClass } = await import('../db/client.js');
|
|
265
|
+
|
|
266
|
+
const db = await DBClientClass.create(targetDir);
|
|
267
|
+
const runner = new MigrationRunner(db, targetDir);
|
|
268
|
+
|
|
269
|
+
const migrationStatus = await runner.getStatus();
|
|
270
|
+
|
|
271
|
+
if (migrationStatus.needsMigration) {
|
|
272
|
+
spinner.stop();
|
|
273
|
+
|
|
274
|
+
console.log('');
|
|
275
|
+
console.log(chalk.yellow(`⚠ Database schema updates available: ${migrationStatus.currentVersion || 'none'} → ${migrationStatus.targetVersion}`));
|
|
276
|
+
console.log('');
|
|
277
|
+
console.log(chalk.bold('Pending migrations:'));
|
|
278
|
+
for (const migration of migrationStatus.pending) {
|
|
279
|
+
const breaks = migration.breaks ? chalk.red(' [breaking]') : '';
|
|
280
|
+
console.log(chalk.dim(` • ${migration.version}: ${migration.description}${breaks}`));
|
|
281
|
+
}
|
|
282
|
+
console.log('');
|
|
283
|
+
|
|
284
|
+
const { confirm } = await import('@inquirer/prompts');
|
|
285
|
+
const shouldMigrate = await confirm({
|
|
286
|
+
message: 'Apply database migrations now?',
|
|
287
|
+
default: true
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
if (shouldMigrate) {
|
|
291
|
+
spinner.start('Applying migrations...');
|
|
292
|
+
|
|
293
|
+
const migrationResults = await runner.migrate({
|
|
294
|
+
backup: true,
|
|
295
|
+
onProgress: (current, total, migration) => {
|
|
296
|
+
spinner.text = `Applying ${current}/${total}: ${migration.description}`;
|
|
297
|
+
}
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
const successful = migrationResults.filter(r => r.success).length;
|
|
301
|
+
const failed = migrationResults.filter(r => !r.success).length;
|
|
302
|
+
|
|
303
|
+
if (failed === 0) {
|
|
304
|
+
spinner.succeed(chalk.green(`Applied ${successful} migration(s)`));
|
|
305
|
+
} else {
|
|
306
|
+
spinner.warn(chalk.yellow(`Applied ${successful} migration(s), ${failed} failed`));
|
|
307
|
+
if (options.verbose) {
|
|
308
|
+
for (const result of migrationResults) {
|
|
309
|
+
if (!result.success) {
|
|
310
|
+
console.log(chalk.red(` ✗ ${result.version}: ${result.error}`));
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
} else {
|
|
316
|
+
spinner.start('Continuing...');
|
|
317
|
+
spinner.succeed('Database migrations skipped');
|
|
318
|
+
console.log(chalk.dim('\n Run: k0ntext migrate up\n'));
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
spinner.succeed('Database schema up to date');
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
db.close();
|
|
325
|
+
} catch (error) {
|
|
326
|
+
spinner.warn(`Database migration check failed: ${error instanceof Error ? error.message : error}`);
|
|
327
|
+
if (options.verbose) {
|
|
328
|
+
console.warn(chalk.dim(` ${error instanceof Error ? error.stack : error}`));
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Check for outdated context files
|
|
333
|
+
if (options.versionCheck !== false) {
|
|
334
|
+
spinner.start('Checking context file versions...');
|
|
335
|
+
|
|
336
|
+
try {
|
|
337
|
+
const { checkContextFiles, showVersionSummary, promptRegeneration } = await import('./version/index.js');
|
|
338
|
+
const { DatabaseClient: DBClientClass } = await import('../db/client.js');
|
|
339
|
+
|
|
340
|
+
let db: DatabaseClient | undefined;
|
|
341
|
+
try {
|
|
342
|
+
db = new DBClientClass(targetDir);
|
|
343
|
+
} catch {
|
|
344
|
+
// Database might not exist yet
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
const result = await checkContextFiles({
|
|
348
|
+
projectRoot: targetDir,
|
|
349
|
+
currentVersion: packageJson.version,
|
|
350
|
+
checkModifications: !!db
|
|
351
|
+
}, db);
|
|
352
|
+
|
|
353
|
+
spinner.stop();
|
|
354
|
+
|
|
355
|
+
if (result.outdated.length > 0) {
|
|
356
|
+
showVersionSummary(result);
|
|
357
|
+
console.log('');
|
|
358
|
+
|
|
359
|
+
const promptResult = await promptRegeneration(result.outdated);
|
|
360
|
+
|
|
361
|
+
if (promptResult.choice !== 'skip') {
|
|
362
|
+
let filesToRegenerate = result.outdated;
|
|
363
|
+
|
|
364
|
+
if (promptResult.choice === 'select' && promptResult.selectedTools) {
|
|
365
|
+
filesToRegenerate = result.outdated.filter(f =>
|
|
366
|
+
promptResult.selectedTools?.includes(f.tool)
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
if (!promptResult.includeModified) {
|
|
371
|
+
filesToRegenerate = filesToRegenerate.filter(f => !f.userModified);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (filesToRegenerate.length > 0 && db) {
|
|
375
|
+
spinner.start('Regenerating context files...');
|
|
376
|
+
|
|
377
|
+
const { generateForTool } = await import('./generate.js');
|
|
378
|
+
|
|
379
|
+
for (const file of filesToRegenerate) {
|
|
380
|
+
spinner.text = `Regenerating ${file.tool} context...`;
|
|
381
|
+
try {
|
|
382
|
+
await generateForTool(file.tool, db, true, false, { verbose: options.verbose });
|
|
383
|
+
} catch (error) {
|
|
384
|
+
spinner.warn(chalk.yellow(`Failed to regenerate ${file.tool}`));
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
spinner.succeed(chalk.green(`Regenerated ${filesToRegenerate.length} file(s)`));
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
} else {
|
|
392
|
+
spinner.stop();
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
if (db) {
|
|
396
|
+
db.close();
|
|
397
|
+
}
|
|
398
|
+
} catch (error) {
|
|
399
|
+
spinner.stop();
|
|
400
|
+
// Version check is optional, don't fail on error
|
|
401
|
+
if (options.verbose) {
|
|
402
|
+
console.warn(chalk.dim(`Version check skipped: ${error instanceof Error ? error.message : error}`));
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
207
407
|
console.log(`\n${chalk.bold('Next Steps:')}`);
|
|
208
408
|
console.log(` ${chalk.cyan('1.')} Run ${chalk.white('k0ntext stats')} to view database statistics`);
|
|
209
409
|
console.log(` ${chalk.cyan('2.')} Run ${chalk.white('k0ntext mcp')} to start the MCP server`);
|
|
@@ -223,7 +423,7 @@ function createProgram(): Command {
|
|
|
223
423
|
program
|
|
224
424
|
.command('mcp')
|
|
225
425
|
.description('Start the MCP server for AI tools to connect')
|
|
226
|
-
.option('--db <path>', 'Database file path', '.
|
|
426
|
+
.option('--db <path>', 'Database file path', '.k0ntext.db')
|
|
227
427
|
.action(async (options) => {
|
|
228
428
|
const projectRoot = process.cwd();
|
|
229
429
|
|
|
@@ -277,6 +477,20 @@ function createProgram(): Command {
|
|
|
277
477
|
// ==================== Fact-Check Command ====================
|
|
278
478
|
program.addCommand(factCheckCommand);
|
|
279
479
|
|
|
480
|
+
// ==================== Version Check Command ====================
|
|
481
|
+
program.addCommand(versionCheckCommand);
|
|
482
|
+
|
|
483
|
+
// ==================== Restore Command ====================
|
|
484
|
+
program.addCommand(restoreCommand);
|
|
485
|
+
|
|
486
|
+
// ==================== Template Sync Commands ====================
|
|
487
|
+
program.addCommand(syncTemplatesCommand);
|
|
488
|
+
program.addCommand(templateStatusCommand);
|
|
489
|
+
|
|
490
|
+
// ==================== Migration Commands ====================
|
|
491
|
+
program.addCommand(migrateCommand);
|
|
492
|
+
program.addCommand(embeddingsRefreshCommand);
|
|
493
|
+
|
|
280
494
|
// ==================== Index Command ====================
|
|
281
495
|
program
|
|
282
496
|
.command('index')
|
|
@@ -290,6 +504,7 @@ function createProgram(): Command {
|
|
|
290
504
|
showBanner();
|
|
291
505
|
|
|
292
506
|
const spinner = ora();
|
|
507
|
+
const verbose = options.verbose || false;
|
|
293
508
|
let db: any;
|
|
294
509
|
|
|
295
510
|
try {
|
|
@@ -317,6 +532,11 @@ function createProgram(): Command {
|
|
|
317
532
|
|
|
318
533
|
// Store docs in database
|
|
319
534
|
for (const doc of docs) {
|
|
535
|
+
if (verbose) {
|
|
536
|
+
spinner.stop();
|
|
537
|
+
console.log(chalk.dim(` Indexing: ${doc.relativePath}`));
|
|
538
|
+
spinner.start();
|
|
539
|
+
}
|
|
320
540
|
const content = fs.existsSync(doc.path) ? fs.readFileSync(doc.path, 'utf-8').slice(0, 50000) : '';
|
|
321
541
|
const item = db.upsertItem({
|
|
322
542
|
type: 'doc',
|
|
@@ -332,6 +552,11 @@ function createProgram(): Command {
|
|
|
332
552
|
|
|
333
553
|
// Store tool configs in database
|
|
334
554
|
for (const config of tools) {
|
|
555
|
+
if (verbose) {
|
|
556
|
+
spinner.stop();
|
|
557
|
+
console.log(chalk.dim(` Indexing: ${config.relativePath}`));
|
|
558
|
+
spinner.start();
|
|
559
|
+
}
|
|
335
560
|
const content = fs.existsSync(config.path) ? fs.readFileSync(config.path, 'utf-8').slice(0, 50000) : '';
|
|
336
561
|
const item = db.upsertItem({
|
|
337
562
|
type: 'tool_config',
|
|
@@ -348,6 +573,11 @@ function createProgram(): Command {
|
|
|
348
573
|
// Store code in database (first N files to avoid overwhelming the db)
|
|
349
574
|
const maxCodeFiles = 100;
|
|
350
575
|
for (const codeFile of code.slice(0, maxCodeFiles)) {
|
|
576
|
+
if (verbose) {
|
|
577
|
+
spinner.stop();
|
|
578
|
+
console.log(chalk.dim(` Indexing: ${codeFile.relativePath}`));
|
|
579
|
+
spinner.start();
|
|
580
|
+
}
|
|
351
581
|
const content = fs.existsSync(codeFile.path) ? fs.readFileSync(codeFile.path, 'utf-8').slice(0, 20000) : '';
|
|
352
582
|
const item = db.upsertItem({
|
|
353
583
|
type: 'code',
|
|
@@ -369,6 +599,11 @@ function createProgram(): Command {
|
|
|
369
599
|
discoveredCount += docs.length;
|
|
370
600
|
spinner.text = `Indexing ${docs.length} docs...`;
|
|
371
601
|
for (const doc of docs) {
|
|
602
|
+
if (verbose) {
|
|
603
|
+
spinner.stop();
|
|
604
|
+
console.log(chalk.dim(` Indexing: ${doc.relativePath}`));
|
|
605
|
+
spinner.start();
|
|
606
|
+
}
|
|
372
607
|
const content = fs.existsSync(doc.path) ? fs.readFileSync(doc.path, 'utf-8').slice(0, 50000) : '';
|
|
373
608
|
const item = db.upsertItem({
|
|
374
609
|
type: 'doc',
|
|
@@ -388,6 +623,11 @@ function createProgram(): Command {
|
|
|
388
623
|
const maxCodeFiles = 100;
|
|
389
624
|
spinner.text = `Indexing code files...`;
|
|
390
625
|
for (const codeFile of code.slice(0, maxCodeFiles)) {
|
|
626
|
+
if (verbose) {
|
|
627
|
+
spinner.stop();
|
|
628
|
+
console.log(chalk.dim(` Indexing: ${codeFile.relativePath}`));
|
|
629
|
+
spinner.start();
|
|
630
|
+
}
|
|
391
631
|
const content = fs.existsSync(codeFile.path) ? fs.readFileSync(codeFile.path, 'utf-8').slice(0, 20000) : '';
|
|
392
632
|
const item = db.upsertItem({
|
|
393
633
|
type: 'code',
|
|
@@ -409,6 +649,11 @@ function createProgram(): Command {
|
|
|
409
649
|
discoveredCount += tools.length;
|
|
410
650
|
spinner.text = `Indexing ${tools.length} tool configs...`;
|
|
411
651
|
for (const config of tools) {
|
|
652
|
+
if (verbose) {
|
|
653
|
+
spinner.stop();
|
|
654
|
+
console.log(chalk.dim(` Indexing: ${config.relativePath}`));
|
|
655
|
+
spinner.start();
|
|
656
|
+
}
|
|
412
657
|
const content = fs.existsSync(config.path) ? fs.readFileSync(config.path, 'utf-8').slice(0, 50000) : '';
|
|
413
658
|
const item = db.upsertItem({
|
|
414
659
|
type: 'tool_config',
|
package/src/cli/repl/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ export class REPLShell {
|
|
|
39
39
|
private readline: readline.Interface;
|
|
40
40
|
private isActive: boolean = false;
|
|
41
41
|
private noTUI: boolean;
|
|
42
|
+
private readlineClosed: boolean = false;
|
|
42
43
|
|
|
43
44
|
// Enhanced panels
|
|
44
45
|
private searchPanel: AdvancedSearchPanel;
|
|
@@ -84,12 +85,12 @@ export class REPLShell {
|
|
|
84
85
|
*/
|
|
85
86
|
private setupEventHandlers(): void {
|
|
86
87
|
this.readline.on('line', async (input) => {
|
|
87
|
-
if (!this.isActive) return;
|
|
88
|
+
if (!this.isActive || this.readlineClosed) return;
|
|
88
89
|
|
|
89
90
|
const trimmed = input.trim();
|
|
90
91
|
|
|
91
92
|
if (!trimmed) {
|
|
92
|
-
this.readline.prompt();
|
|
93
|
+
if (!this.readlineClosed) this.readline.prompt();
|
|
93
94
|
return;
|
|
94
95
|
}
|
|
95
96
|
|
|
@@ -121,16 +122,17 @@ export class REPLShell {
|
|
|
121
122
|
console.log(K0NTEXT_THEME.warning('\n⚠ Invalid command. Type "help" for available commands.'));
|
|
122
123
|
}
|
|
123
124
|
|
|
124
|
-
this.readline.prompt();
|
|
125
|
+
if (!this.readlineClosed) this.readline.prompt();
|
|
125
126
|
});
|
|
126
127
|
|
|
127
128
|
this.readline.on('SIGINT', async () => {
|
|
128
129
|
console.log('');
|
|
129
130
|
console.log(K0NTEXT_THEME.warning('\n⚠ Use "exit" to quit the REPL.'));
|
|
130
|
-
this.readline.prompt();
|
|
131
|
+
if (!this.readlineClosed) this.readline.prompt();
|
|
131
132
|
});
|
|
132
133
|
|
|
133
134
|
this.readline.on('close', async () => {
|
|
135
|
+
this.readlineClosed = true;
|
|
134
136
|
await this.stop();
|
|
135
137
|
});
|
|
136
138
|
}
|
|
@@ -519,11 +521,19 @@ export class REPLShell {
|
|
|
519
521
|
* Stop the REPL
|
|
520
522
|
*/
|
|
521
523
|
async stop(): Promise<void> {
|
|
524
|
+
if (!this.isActive) return; // Already stopping
|
|
522
525
|
this.isActive = false;
|
|
523
526
|
this.session.end();
|
|
527
|
+
this.readlineClosed = true;
|
|
528
|
+
|
|
529
|
+
// Only print goodbye if not in piped mode
|
|
530
|
+
const isPiped = !process.stdin.isTTY;
|
|
524
531
|
this.readline.close();
|
|
525
|
-
|
|
526
|
-
|
|
532
|
+
|
|
533
|
+
if (!isPiped) {
|
|
534
|
+
console.log('');
|
|
535
|
+
console.log(K0NTEXT_THEME.success('✓ Session saved. Goodbye!'));
|
|
536
|
+
}
|
|
527
537
|
}
|
|
528
538
|
|
|
529
539
|
/**
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backup Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages backups of generated context files before overwriting.
|
|
5
|
+
* Supports both file-copy and git-stash based backup strategies.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import fs from 'fs/promises';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import { execSync } from 'child_process';
|
|
11
|
+
import type { DatabaseClient } from '../../db/client.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Backup result
|
|
15
|
+
*/
|
|
16
|
+
export interface BackupResult {
|
|
17
|
+
/** Whether backup was successful */
|
|
18
|
+
success: boolean;
|
|
19
|
+
/** Path to backup file */
|
|
20
|
+
backupPath?: string;
|
|
21
|
+
/** Backup method used */
|
|
22
|
+
method: 'file-copy' | 'git-stash' | 'none';
|
|
23
|
+
/** Error message if failed */
|
|
24
|
+
error?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Backup manager options
|
|
29
|
+
*/
|
|
30
|
+
export interface BackupManagerOptions {
|
|
31
|
+
/** Base directory for backups (default: .k0ntext/backups) */
|
|
32
|
+
backupDir?: string;
|
|
33
|
+
/** Use git stash if available (default: true) */
|
|
34
|
+
useGitStash?: boolean;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Backup manager
|
|
39
|
+
*
|
|
40
|
+
* Creates and manages backups of generated files.
|
|
41
|
+
*/
|
|
42
|
+
export class BackupManager {
|
|
43
|
+
private db: DatabaseClient;
|
|
44
|
+
private projectRoot: string;
|
|
45
|
+
private backupDir: string;
|
|
46
|
+
private useGitStash: boolean;
|
|
47
|
+
|
|
48
|
+
constructor(db: DatabaseClient, projectRoot: string, options: BackupManagerOptions = {}) {
|
|
49
|
+
this.db = db;
|
|
50
|
+
this.projectRoot = projectRoot;
|
|
51
|
+
this.backupDir = options.backupDir || path.join(projectRoot, '.k0ntext', 'backups');
|
|
52
|
+
this.useGitStash = options.useGitStash !== false;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Create a backup of a file before overwriting
|
|
57
|
+
*
|
|
58
|
+
* @param filePath - Path to file to backup (can be relative or absolute)
|
|
59
|
+
* @param tool - Tool name for organization
|
|
60
|
+
* @returns Backup result
|
|
61
|
+
*/
|
|
62
|
+
async createBackup(filePath: string, tool: string): Promise<BackupResult> {
|
|
63
|
+
const fullPath = path.isAbsolute(filePath) ? filePath : path.join(this.projectRoot, filePath);
|
|
64
|
+
|
|
65
|
+
try {
|
|
66
|
+
// Check if file exists
|
|
67
|
+
try {
|
|
68
|
+
await fs.access(fullPath);
|
|
69
|
+
} catch {
|
|
70
|
+
// File doesn't exist, no backup needed
|
|
71
|
+
return { success: true, method: 'none' };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Try git stash first if enabled
|
|
75
|
+
if (this.useGitStash && await this.isGitRepository()) {
|
|
76
|
+
const stashResult = await this.backupWithGitStash(fullPath, tool);
|
|
77
|
+
if (stashResult.success) {
|
|
78
|
+
return stashResult;
|
|
79
|
+
}
|
|
80
|
+
// Fall back to file copy if git stash fails
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Fall back to file copy
|
|
84
|
+
return await this.backupWithFileCopy(fullPath, tool);
|
|
85
|
+
} catch (error) {
|
|
86
|
+
return {
|
|
87
|
+
success: false,
|
|
88
|
+
method: 'none',
|
|
89
|
+
error: error instanceof Error ? error.message : String(error)
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Restore a file from backup
|
|
96
|
+
*
|
|
97
|
+
* @param backupPath - Path to backup file
|
|
98
|
+
* @param targetPath - Path to restore to (can be relative or absolute)
|
|
99
|
+
* @returns True if restore was successful
|
|
100
|
+
*/
|
|
101
|
+
async restoreFromBackup(backupPath: string, targetPath: string): Promise<boolean> {
|
|
102
|
+
const fullTargetPath = path.isAbsolute(targetPath) ? targetPath : path.join(this.projectRoot, targetPath);
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
// Ensure target directory exists
|
|
106
|
+
await fs.mkdir(path.dirname(fullTargetPath), { recursive: true });
|
|
107
|
+
|
|
108
|
+
// Copy backup to target
|
|
109
|
+
await fs.copyFile(backupPath, fullTargetPath);
|
|
110
|
+
return true;
|
|
111
|
+
} catch {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* List all backups for a tool
|
|
118
|
+
*
|
|
119
|
+
* @param tool - Tool name
|
|
120
|
+
* @returns Array of backup paths
|
|
121
|
+
*/
|
|
122
|
+
async listBackups(tool: string): Promise<string[]> {
|
|
123
|
+
const toolBackupDir = path.join(this.backupDir, tool);
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
await fs.access(toolBackupDir);
|
|
127
|
+
} catch {
|
|
128
|
+
return [];
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const entries = await fs.readdir(toolBackupDir, { withFileTypes: true });
|
|
132
|
+
return entries
|
|
133
|
+
.filter(e => e.isFile())
|
|
134
|
+
.map(e => path.join(toolBackupDir, e.name));
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Backup using file copy strategy
|
|
139
|
+
*
|
|
140
|
+
* @param fullPath - Full path to file
|
|
141
|
+
* @param tool - Tool name
|
|
142
|
+
* @returns Backup result
|
|
143
|
+
*/
|
|
144
|
+
private async backupWithFileCopy(fullPath: string, tool: string): Promise<BackupResult> {
|
|
145
|
+
try {
|
|
146
|
+
// Create backup directory
|
|
147
|
+
const toolBackupDir = path.join(this.backupDir, tool);
|
|
148
|
+
await fs.mkdir(toolBackupDir, { recursive: true });
|
|
149
|
+
|
|
150
|
+
// Generate backup filename with timestamp
|
|
151
|
+
const fileName = path.basename(fullPath);
|
|
152
|
+
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
153
|
+
const backupFileName = `${fileName}.${timestamp}.bak`;
|
|
154
|
+
const backupPath = path.join(toolBackupDir, backupFileName);
|
|
155
|
+
|
|
156
|
+
// Copy file
|
|
157
|
+
await fs.copyFile(fullPath, backupPath);
|
|
158
|
+
|
|
159
|
+
// Update database with backup path
|
|
160
|
+
const relativePath = path.relative(this.projectRoot, fullPath);
|
|
161
|
+
const record = this.db.getGeneratedFileInfo(tool, relativePath);
|
|
162
|
+
if (record) {
|
|
163
|
+
this.db.upsertGeneratedFile({
|
|
164
|
+
tool,
|
|
165
|
+
filePath: relativePath,
|
|
166
|
+
contentHash: record.contentHash,
|
|
167
|
+
backupPath
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return {
|
|
172
|
+
success: true,
|
|
173
|
+
backupPath,
|
|
174
|
+
method: 'file-copy'
|
|
175
|
+
};
|
|
176
|
+
} catch (error) {
|
|
177
|
+
return {
|
|
178
|
+
success: false,
|
|
179
|
+
method: 'none',
|
|
180
|
+
error: error instanceof Error ? error.message : String(error)
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Backup using git stash strategy
|
|
187
|
+
*
|
|
188
|
+
* @param fullPath - Full path to file
|
|
189
|
+
* @param tool - Tool name
|
|
190
|
+
* @returns Backup result
|
|
191
|
+
*/
|
|
192
|
+
private async backupWithGitStash(fullPath: string, tool: string): Promise<BackupResult> {
|
|
193
|
+
try {
|
|
194
|
+
const relativePath = path.relative(this.projectRoot, fullPath);
|
|
195
|
+
|
|
196
|
+
// Create a stash with a descriptive message
|
|
197
|
+
const stashMessage = `k0ntext-backup-${tool}-${Date.now()}`;
|
|
198
|
+
const stashCommand = `git stash push -m "${stashMessage}" -- "${relativePath}"`;
|
|
199
|
+
|
|
200
|
+
execSync(stashCommand, { cwd: this.projectRoot, stdio: 'pipe' });
|
|
201
|
+
|
|
202
|
+
// Get the stash hash for reference
|
|
203
|
+
const listCommand = 'git stash list --grep="^k0ntext-backup" -n 1';
|
|
204
|
+
const stashList = execSync(listCommand, { cwd: this.projectRoot, encoding: 'utf-8' });
|
|
205
|
+
const stashRef = stashList.split(':')[0].trim();
|
|
206
|
+
|
|
207
|
+
// Update database with stash reference
|
|
208
|
+
const record = this.db.getGeneratedFileInfo(tool, relativePath);
|
|
209
|
+
if (record) {
|
|
210
|
+
this.db.upsertGeneratedFile({
|
|
211
|
+
tool,
|
|
212
|
+
filePath: relativePath,
|
|
213
|
+
contentHash: record.contentHash,
|
|
214
|
+
backupPath: `git-stash:${stashRef}`
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
success: true,
|
|
220
|
+
backupPath: `git-stash:${stashRef}`,
|
|
221
|
+
method: 'git-stash'
|
|
222
|
+
};
|
|
223
|
+
} catch {
|
|
224
|
+
return {
|
|
225
|
+
success: false,
|
|
226
|
+
method: 'none',
|
|
227
|
+
error: 'Git stash failed'
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Check if current directory is a git repository
|
|
234
|
+
*
|
|
235
|
+
* @returns True if git repository
|
|
236
|
+
*/
|
|
237
|
+
private async isGitRepository(): Promise<boolean> {
|
|
238
|
+
try {
|
|
239
|
+
const gitDir = path.join(this.projectRoot, '.git');
|
|
240
|
+
await fs.access(gitDir);
|
|
241
|
+
return true;
|
|
242
|
+
} catch {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* Clean up old backups
|
|
249
|
+
*
|
|
250
|
+
* @param tool - Tool name (optional, if not provided cleans all)
|
|
251
|
+
* @param keepLast - Number of recent backups to keep (default: 5)
|
|
252
|
+
*/
|
|
253
|
+
async cleanupOldBackups(tool?: string, keepLast = 5): Promise<void> {
|
|
254
|
+
const tools = tool ? [tool] : ['claude', 'copilot', 'cline', 'antigravity', 'windsurf', 'aider', 'continue', 'cursor', 'gemini'];
|
|
255
|
+
|
|
256
|
+
for (const toolName of tools) {
|
|
257
|
+
const backups = await this.listBackups(toolName);
|
|
258
|
+
|
|
259
|
+
if (backups.length > keepLast) {
|
|
260
|
+
// Sort by name (which includes timestamp)
|
|
261
|
+
backups.sort();
|
|
262
|
+
|
|
263
|
+
// Delete oldest backups
|
|
264
|
+
const toDelete = backups.slice(0, backups.length - keepLast);
|
|
265
|
+
for (const backupPath of toDelete) {
|
|
266
|
+
try {
|
|
267
|
+
await fs.unlink(backupPath);
|
|
268
|
+
} catch {
|
|
269
|
+
// Ignore errors during cleanup
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|