k0ntext 3.3.0 → 3.5.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 +240 -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/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 +145 -1
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/repl/index.d.ts +4 -0
- package/dist/cli/repl/index.d.ts.map +1 -1
- package/dist/cli/repl/index.js +113 -95
- package/dist/cli/repl/index.js.map +1 -1
- package/dist/cli/repl/tui/panels/config.d.ts +71 -0
- package/dist/cli/repl/tui/panels/config.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/config.js +392 -0
- package/dist/cli/repl/tui/panels/config.js.map +1 -0
- package/dist/cli/repl/tui/panels/drift.d.ts +95 -0
- package/dist/cli/repl/tui/panels/drift.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/drift.js +353 -0
- package/dist/cli/repl/tui/panels/drift.js.map +1 -0
- package/dist/cli/repl/tui/panels/indexing.d.ts +86 -0
- package/dist/cli/repl/tui/panels/indexing.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/indexing.js +254 -0
- package/dist/cli/repl/tui/panels/indexing.js.map +1 -0
- package/dist/cli/repl/tui/panels/search.d.ts +66 -0
- package/dist/cli/repl/tui/panels/search.d.ts.map +1 -0
- package/dist/cli/repl/tui/panels/search.js +215 -0
- package/dist/cli/repl/tui/panels/search.js.map +1 -0
- 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/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 +64 -2
- package/dist/db/client.d.ts.map +1 -1
- package/dist/db/client.js +148 -2
- package/dist/db/client.js.map +1 -1
- 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/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 +167 -1
- package/src/cli/repl/index.ts +118 -105
- package/src/cli/repl/tui/panels/config.ts +457 -0
- package/src/cli/repl/tui/panels/drift.ts +458 -0
- package/src/cli/repl/tui/panels/indexing.ts +324 -0
- package/src/cli/repl/tui/panels/search.ts +272 -0
- package/src/cli/utils/backup-manager.ts +275 -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 +220 -3
- 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,358 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Template Manifest Manager
|
|
3
|
+
*
|
|
4
|
+
* Manages template manifest in both database and file system.
|
|
5
|
+
* Dual storage ensures reliability and easy inspection.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { promises as fs } from 'fs';
|
|
9
|
+
import path from 'path';
|
|
10
|
+
import type { DatabaseClient } from '../db/client.js';
|
|
11
|
+
import type { TemplateManifest, TemplateFileEntry } from './types.js';
|
|
12
|
+
import { TEMPLATE_SCHEMA_SQL } from '../db/schema.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Manifest file path in .claude directory
|
|
16
|
+
*/
|
|
17
|
+
const MANIFEST_FILE_NAME = '.k0ntext-manifest.json';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Manifest ID for database storage
|
|
21
|
+
*/
|
|
22
|
+
const MANIFEST_ID = 'current';
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Manages template manifest in both database and file system
|
|
26
|
+
*/
|
|
27
|
+
export class TemplateManifestManager {
|
|
28
|
+
private readonly manifestPath: string;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
private db: DatabaseClient,
|
|
32
|
+
private projectRoot: string = process.cwd()
|
|
33
|
+
) {
|
|
34
|
+
this.manifestPath = path.join(projectRoot, '.claude', MANIFEST_FILE_NAME);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load manifest from file system
|
|
39
|
+
*
|
|
40
|
+
* @returns Manifest object or null if doesn't exist
|
|
41
|
+
*/
|
|
42
|
+
async loadManifest(): Promise<TemplateManifest | null> {
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.readFile(this.manifestPath, 'utf8');
|
|
45
|
+
const manifest = JSON.parse(content) as TemplateManifest;
|
|
46
|
+
this.validateManifest(manifest);
|
|
47
|
+
return manifest;
|
|
48
|
+
} catch (error) {
|
|
49
|
+
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
// Invalid JSON or validation error
|
|
53
|
+
console.warn(`Warning: Invalid manifest file at ${this.manifestPath}`);
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Save manifest to file system
|
|
60
|
+
*
|
|
61
|
+
* @param manifest - Manifest to save
|
|
62
|
+
*/
|
|
63
|
+
async saveManifest(manifest: TemplateManifest): Promise<void> {
|
|
64
|
+
this.validateManifest(manifest);
|
|
65
|
+
|
|
66
|
+
// Ensure directory exists
|
|
67
|
+
const manifestDir = path.dirname(this.manifestPath);
|
|
68
|
+
await fs.mkdir(manifestDir, { recursive: true });
|
|
69
|
+
|
|
70
|
+
// Update timestamp
|
|
71
|
+
manifest.updatedAt = new Date().toISOString();
|
|
72
|
+
|
|
73
|
+
// Write manifest with proper formatting
|
|
74
|
+
await fs.writeFile(
|
|
75
|
+
this.manifestPath,
|
|
76
|
+
JSON.stringify(manifest, null, 2),
|
|
77
|
+
'utf8'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Get manifest from database
|
|
83
|
+
*
|
|
84
|
+
* @returns Manifest object or null if doesn't exist
|
|
85
|
+
*/
|
|
86
|
+
getDbManifest(): TemplateManifest | null {
|
|
87
|
+
try {
|
|
88
|
+
const stmt = this.db.getRawDb().prepare(`
|
|
89
|
+
SELECT manifest
|
|
90
|
+
FROM template_manifests
|
|
91
|
+
WHERE id = ?
|
|
92
|
+
ORDER BY created_at DESC
|
|
93
|
+
LIMIT 1
|
|
94
|
+
`);
|
|
95
|
+
|
|
96
|
+
const row = stmt.get(MANIFEST_ID) as { manifest: string } | undefined;
|
|
97
|
+
if (!row) return null;
|
|
98
|
+
|
|
99
|
+
const manifest = JSON.parse(row.manifest) as TemplateManifest;
|
|
100
|
+
this.validateManifest(manifest);
|
|
101
|
+
return manifest;
|
|
102
|
+
} catch {
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Save manifest to database
|
|
109
|
+
*
|
|
110
|
+
* @param manifest - Manifest to save
|
|
111
|
+
*/
|
|
112
|
+
saveDbManifest(manifest: TemplateManifest): void {
|
|
113
|
+
this.validateManifest(manifest);
|
|
114
|
+
|
|
115
|
+
// Update timestamp
|
|
116
|
+
manifest.updatedAt = new Date().toISOString();
|
|
117
|
+
|
|
118
|
+
const stmt = this.db.getRawDb().prepare(`
|
|
119
|
+
INSERT OR REPLACE INTO template_manifests
|
|
120
|
+
(id, k0ntext_version, template_version, manifest, created_at, updated_at)
|
|
121
|
+
VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))
|
|
122
|
+
`);
|
|
123
|
+
|
|
124
|
+
stmt.run(
|
|
125
|
+
MANIFEST_ID,
|
|
126
|
+
manifest.k0ntextVersion,
|
|
127
|
+
manifest.templateVersion,
|
|
128
|
+
JSON.stringify(manifest)
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Sync manifest between DB and file system
|
|
134
|
+
* Uses DB as source of truth by default
|
|
135
|
+
*
|
|
136
|
+
* @param manifest - Manifest to sync
|
|
137
|
+
* @param source - 'db' or 'file' to determine source of truth
|
|
138
|
+
*/
|
|
139
|
+
async syncManifest(manifest: TemplateManifest, source: 'db' | 'file' = 'db'): Promise<void> {
|
|
140
|
+
if (source === 'db') {
|
|
141
|
+
this.saveDbManifest(manifest);
|
|
142
|
+
await this.saveManifest(manifest);
|
|
143
|
+
} else {
|
|
144
|
+
await this.saveManifest(manifest);
|
|
145
|
+
this.saveDbManifest(manifest);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Load manifest from both sources and reconcile
|
|
151
|
+
* Prefers the most recently updated version
|
|
152
|
+
*
|
|
153
|
+
* @returns Reconciled manifest or null
|
|
154
|
+
*/
|
|
155
|
+
async loadReconciledManifest(): Promise<TemplateManifest | null> {
|
|
156
|
+
const fileManifest = await this.loadManifest();
|
|
157
|
+
const dbManifest = this.getDbManifest();
|
|
158
|
+
|
|
159
|
+
if (!fileManifest && !dbManifest) {
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!fileManifest) return dbManifest;
|
|
164
|
+
if (!dbManifest) return fileManifest;
|
|
165
|
+
|
|
166
|
+
// Both exist - prefer the most recently updated
|
|
167
|
+
const fileDate = new Date(fileManifest.updatedAt || fileManifest.createdAt);
|
|
168
|
+
const dbDate = new Date(dbManifest.updatedAt || dbManifest.createdAt);
|
|
169
|
+
|
|
170
|
+
return fileDate > dbDate ? fileManifest : dbManifest;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Get file entry from manifest
|
|
175
|
+
*
|
|
176
|
+
* @param relativePath - Relative path to file
|
|
177
|
+
* @param manifest - Optional manifest to use (otherwise loads)
|
|
178
|
+
* @returns File entry or null
|
|
179
|
+
*/
|
|
180
|
+
async getFileEntry(relativePath: string, manifest?: TemplateManifest): Promise<TemplateFileEntry | null> {
|
|
181
|
+
const activeManifest = manifest ?? await this.loadReconciledManifest();
|
|
182
|
+
if (!activeManifest) return null;
|
|
183
|
+
|
|
184
|
+
return activeManifest.files[relativePath] || null;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Mark file as user-modified
|
|
189
|
+
*
|
|
190
|
+
* @param relativePath - Relative path to file
|
|
191
|
+
* @param originalHash - Original template hash before modification
|
|
192
|
+
*/
|
|
193
|
+
async markUserModified(relativePath: string, originalHash?: string): Promise<void> {
|
|
194
|
+
const manifest = await this.loadReconciledManifest() ?? this.createEmptyManifest();
|
|
195
|
+
|
|
196
|
+
if (manifest.files[relativePath]) {
|
|
197
|
+
manifest.files[relativePath].userModified = true;
|
|
198
|
+
if (originalHash) {
|
|
199
|
+
manifest.files[relativePath].originalHash = originalHash;
|
|
200
|
+
}
|
|
201
|
+
await this.syncManifest(manifest);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Clear user-modified flag for a file
|
|
207
|
+
*
|
|
208
|
+
* @param relativePath - Relative path to file
|
|
209
|
+
*/
|
|
210
|
+
async clearUserModified(relativePath: string): Promise<void> {
|
|
211
|
+
const manifest = await this.loadReconciledManifest() ?? this.createEmptyManifest();
|
|
212
|
+
|
|
213
|
+
if (manifest.files[relativePath]) {
|
|
214
|
+
manifest.files[relativePath].userModified = false;
|
|
215
|
+
manifest.files[relativePath].originalHash = undefined;
|
|
216
|
+
await this.syncManifest(manifest);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Update file entry in manifest
|
|
222
|
+
*
|
|
223
|
+
* @param relativePath - Relative path to file
|
|
224
|
+
* @param entry - New file entry data
|
|
225
|
+
*/
|
|
226
|
+
async updateFileEntry(relativePath: string, entry: Partial<TemplateFileEntry>): Promise<void> {
|
|
227
|
+
const manifest = await this.loadReconciledManifest() ?? this.createEmptyManifest();
|
|
228
|
+
|
|
229
|
+
manifest.files[relativePath] = {
|
|
230
|
+
...manifest.files[relativePath],
|
|
231
|
+
...entry
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
await this.syncManifest(manifest);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Remove file entry from manifest
|
|
239
|
+
*
|
|
240
|
+
* @param relativePath - Relative path to file
|
|
241
|
+
*/
|
|
242
|
+
async removeFileEntry(relativePath: string): Promise<void> {
|
|
243
|
+
const manifest = await this.loadReconciledManifest();
|
|
244
|
+
if (!manifest) return;
|
|
245
|
+
|
|
246
|
+
delete manifest.files[relativePath];
|
|
247
|
+
await this.syncManifest(manifest);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get all user-modified files
|
|
252
|
+
*
|
|
253
|
+
* @returns Array of relative paths to user-modified files
|
|
254
|
+
*/
|
|
255
|
+
async getUserModifiedFiles(): Promise<string[]> {
|
|
256
|
+
const manifest = await this.loadReconciledManifest();
|
|
257
|
+
if (!manifest) return [];
|
|
258
|
+
|
|
259
|
+
return Object.entries(manifest.files)
|
|
260
|
+
.filter(([_, entry]) => entry.userModified)
|
|
261
|
+
.map(([path, _]) => path);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Check if manifest needs update based on version
|
|
266
|
+
*
|
|
267
|
+
* @param currentVersion - Current package version
|
|
268
|
+
* @returns true if manifest is outdated or missing
|
|
269
|
+
*/
|
|
270
|
+
async needsUpdate(currentVersion: string): Promise<boolean> {
|
|
271
|
+
const manifest = await this.loadReconciledManifest();
|
|
272
|
+
if (!manifest) return true;
|
|
273
|
+
|
|
274
|
+
return manifest.templateVersion !== currentVersion;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Create empty manifest with current versions
|
|
279
|
+
*
|
|
280
|
+
* @param k0ntextVersion - k0ntext package version
|
|
281
|
+
* @param templateVersion - Template version (usually same as k0ntext version)
|
|
282
|
+
* @returns New empty manifest
|
|
283
|
+
*/
|
|
284
|
+
createEmptyManifest(
|
|
285
|
+
k0ntextVersion: string = '0.0.0',
|
|
286
|
+
templateVersion: string = '0.0.0'
|
|
287
|
+
): TemplateManifest {
|
|
288
|
+
return {
|
|
289
|
+
k0ntextVersion,
|
|
290
|
+
templateVersion,
|
|
291
|
+
createdAt: new Date().toISOString(),
|
|
292
|
+
updatedAt: new Date().toISOString(),
|
|
293
|
+
files: {}
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Validate manifest structure
|
|
299
|
+
*
|
|
300
|
+
* @param manifest - Manifest to validate
|
|
301
|
+
* @throws Error if invalid
|
|
302
|
+
*/
|
|
303
|
+
private validateManifest(manifest: unknown): asserts manifest is TemplateManifest {
|
|
304
|
+
if (!manifest || typeof manifest !== 'object') {
|
|
305
|
+
throw new Error('Invalid manifest: not an object');
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const m = manifest as Partial<TemplateManifest>;
|
|
309
|
+
|
|
310
|
+
if (!m.k0ntextVersion || typeof m.k0ntextVersion !== 'string') {
|
|
311
|
+
throw new Error('Invalid manifest: missing or invalid k0ntextVersion');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
if (!m.templateVersion || typeof m.templateVersion !== 'string') {
|
|
315
|
+
throw new Error('Invalid manifest: missing or invalid templateVersion');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
if (!m.createdAt || typeof m.createdAt !== 'string') {
|
|
319
|
+
throw new Error('Invalid manifest: missing or invalid createdAt');
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (!m.files || typeof m.files !== 'object') {
|
|
323
|
+
throw new Error('Invalid manifest: missing or invalid files');
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Get manifest file path for display purposes
|
|
329
|
+
*/
|
|
330
|
+
getManifestPath(): string {
|
|
331
|
+
return this.manifestPath;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Check if manifest file exists
|
|
336
|
+
*/
|
|
337
|
+
async manifestExists(): Promise<boolean> {
|
|
338
|
+
try {
|
|
339
|
+
await fs.access(this.manifestPath);
|
|
340
|
+
return true;
|
|
341
|
+
} catch {
|
|
342
|
+
return false;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Delete manifest file (for cleanup/testing)
|
|
348
|
+
*/
|
|
349
|
+
async deleteManifest(): Promise<void> {
|
|
350
|
+
try {
|
|
351
|
+
await fs.unlink(this.manifestPath);
|
|
352
|
+
} catch (error) {
|
|
353
|
+
if ((error as NodeJS.ErrnoException).code !== 'ENOENT') {
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|