@context-forge/core 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +70 -0
  3. package/dist/index.d.ts +4 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +4 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/node.d.ts +6 -0
  8. package/dist/node.d.ts.map +1 -0
  9. package/dist/node.js +10 -0
  10. package/dist/node.js.map +1 -0
  11. package/dist/services/ContextGenerator.d.ts +19 -0
  12. package/dist/services/ContextGenerator.d.ts.map +1 -0
  13. package/dist/services/ContextGenerator.js +66 -0
  14. package/dist/services/ContextGenerator.js.map +1 -0
  15. package/dist/services/ContextIntegrator.d.ts +84 -0
  16. package/dist/services/ContextIntegrator.d.ts.map +1 -0
  17. package/dist/services/ContextIntegrator.js +211 -0
  18. package/dist/services/ContextIntegrator.js.map +1 -0
  19. package/dist/services/ContextTemplateEngine.d.ts +65 -0
  20. package/dist/services/ContextTemplateEngine.d.ts.map +1 -0
  21. package/dist/services/ContextTemplateEngine.js +239 -0
  22. package/dist/services/ContextTemplateEngine.js.map +1 -0
  23. package/dist/services/CoreServiceFactory.d.ts +14 -0
  24. package/dist/services/CoreServiceFactory.d.ts.map +1 -0
  25. package/dist/services/CoreServiceFactory.js +23 -0
  26. package/dist/services/CoreServiceFactory.js.map +1 -0
  27. package/dist/services/ProjectPathService.d.ts +14 -0
  28. package/dist/services/ProjectPathService.d.ts.map +1 -0
  29. package/dist/services/ProjectPathService.js +142 -0
  30. package/dist/services/ProjectPathService.js.map +1 -0
  31. package/dist/services/SectionBuilder.d.ts +70 -0
  32. package/dist/services/SectionBuilder.d.ts.map +1 -0
  33. package/dist/services/SectionBuilder.js +303 -0
  34. package/dist/services/SectionBuilder.js.map +1 -0
  35. package/dist/services/StatementManager.d.ts +44 -0
  36. package/dist/services/StatementManager.d.ts.map +1 -0
  37. package/dist/services/StatementManager.js +216 -0
  38. package/dist/services/StatementManager.js.map +1 -0
  39. package/dist/services/SystemPromptParser.d.ts +72 -0
  40. package/dist/services/SystemPromptParser.d.ts.map +1 -0
  41. package/dist/services/SystemPromptParser.js +271 -0
  42. package/dist/services/SystemPromptParser.js.map +1 -0
  43. package/dist/services/TemplateProcessor.d.ts +35 -0
  44. package/dist/services/TemplateProcessor.d.ts.map +1 -0
  45. package/dist/services/TemplateProcessor.js +150 -0
  46. package/dist/services/TemplateProcessor.js.map +1 -0
  47. package/dist/services/constants.d.ts +8 -0
  48. package/dist/services/constants.d.ts.map +1 -0
  49. package/dist/services/constants.js +62 -0
  50. package/dist/services/constants.js.map +1 -0
  51. package/dist/services/index.d.ts +8 -0
  52. package/dist/services/index.d.ts.map +1 -0
  53. package/dist/services/index.js +12 -0
  54. package/dist/services/index.js.map +1 -0
  55. package/dist/services/interfaces.d.ts +21 -0
  56. package/dist/services/interfaces.d.ts.map +1 -0
  57. package/dist/services/interfaces.js +2 -0
  58. package/dist/services/interfaces.js.map +1 -0
  59. package/dist/storage/FileProjectStore.d.ts +24 -0
  60. package/dist/storage/FileProjectStore.d.ts.map +1 -0
  61. package/dist/storage/FileProjectStore.js +141 -0
  62. package/dist/storage/FileProjectStore.js.map +1 -0
  63. package/dist/storage/FileStorageService.d.ts +14 -0
  64. package/dist/storage/FileStorageService.d.ts.map +1 -0
  65. package/dist/storage/FileStorageService.js +114 -0
  66. package/dist/storage/FileStorageService.js.map +1 -0
  67. package/dist/storage/backupService.d.ts +28 -0
  68. package/dist/storage/backupService.d.ts.map +1 -0
  69. package/dist/storage/backupService.js +84 -0
  70. package/dist/storage/backupService.js.map +1 -0
  71. package/dist/storage/index.d.ts +6 -0
  72. package/dist/storage/index.d.ts.map +1 -0
  73. package/dist/storage/index.js +7 -0
  74. package/dist/storage/index.js.map +1 -0
  75. package/dist/storage/interfaces.d.ts +23 -0
  76. package/dist/storage/interfaces.d.ts.map +1 -0
  77. package/dist/storage/interfaces.js +2 -0
  78. package/dist/storage/interfaces.js.map +1 -0
  79. package/dist/storage/storagePaths.d.ts +9 -0
  80. package/dist/storage/storagePaths.d.ts.map +1 -0
  81. package/dist/storage/storagePaths.js +20 -0
  82. package/dist/storage/storagePaths.js.map +1 -0
  83. package/dist/types/context.d.ts +36 -0
  84. package/dist/types/context.d.ts.map +1 -0
  85. package/dist/types/context.js +2 -0
  86. package/dist/types/context.js.map +1 -0
  87. package/dist/types/index.d.ts +9 -0
  88. package/dist/types/index.d.ts.map +1 -0
  89. package/dist/types/index.js +3 -0
  90. package/dist/types/index.js.map +1 -0
  91. package/dist/types/paths.d.ts +18 -0
  92. package/dist/types/paths.d.ts.map +1 -0
  93. package/dist/types/paths.js +2 -0
  94. package/dist/types/paths.js.map +1 -0
  95. package/dist/types/project.d.ts +48 -0
  96. package/dist/types/project.d.ts.map +1 -0
  97. package/dist/types/project.js +2 -0
  98. package/dist/types/project.js.map +1 -0
  99. package/dist/types/prompts.d.ts +49 -0
  100. package/dist/types/prompts.d.ts.map +1 -0
  101. package/dist/types/prompts.js +10 -0
  102. package/dist/types/prompts.js.map +1 -0
  103. package/dist/types/sections.d.ts +64 -0
  104. package/dist/types/sections.d.ts.map +1 -0
  105. package/dist/types/sections.js +13 -0
  106. package/dist/types/sections.js.map +1 -0
  107. package/dist/types/statements.d.ts +40 -0
  108. package/dist/types/statements.d.ts.map +1 -0
  109. package/dist/types/statements.js +2 -0
  110. package/dist/types/statements.js.map +1 -0
  111. package/package.json +54 -0
@@ -0,0 +1,24 @@
1
+ import type { ProjectData, CreateProjectData, UpdateProjectData } from '../types/project.js';
2
+ import type { IProjectStore } from './interfaces.js';
3
+ /**
4
+ * Filesystem-backed project CRUD store using FileStorageService.
5
+ * Lazy-initializes on first access, migrating from legacy Electron location if needed.
6
+ */
7
+ export declare class FileProjectStore implements IProjectStore {
8
+ private readonly storage;
9
+ private readonly storagePath;
10
+ private initialized;
11
+ constructor();
12
+ private ensureInitialized;
13
+ getAll(): Promise<ProjectData[]>;
14
+ getById(id: string): Promise<ProjectData | undefined>;
15
+ create(data: CreateProjectData): Promise<ProjectData>;
16
+ update(id: string, updates: UpdateProjectData): Promise<void>;
17
+ delete(id: string): Promise<void>;
18
+ /**
19
+ * Copy projects.json (and backup) from legacy Electron location to new location.
20
+ * Only runs if new location has no data and legacy location has data.
21
+ */
22
+ private migrateFromLegacyLocation;
23
+ }
24
+ //# sourceMappingURL=FileProjectStore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileProjectStore.d.ts","sourceRoot":"","sources":["../../src/storage/FileProjectStore.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,WAAW,EACX,iBAAiB,EACjB,iBAAiB,EAClB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA6BrD;;;GAGG;AACH,qBAAa,gBAAiB,YAAW,aAAa;IACpD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAqB;IAC7C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IACrC,OAAO,CAAC,WAAW,CAAS;;YAOd,iBAAiB;IAUzB,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC;IA2BhC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;IAKrD,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC;IA6BrD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;IAiB7D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAevC;;;OAGG;YACW,yBAAyB;CA0BxC"}
@@ -0,0 +1,141 @@
1
+ import { copyFile, mkdir } from 'fs/promises';
2
+ import { existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { FileStorageService } from './FileStorageService.js';
5
+ import { getStoragePath, getLegacyElectronPath } from './storagePaths.js';
6
+ const PROJECTS_FILE = 'projects.json';
7
+ function generateProjectId() {
8
+ return `project_${Date.now()}_${Math.random().toString(36).substring(2, 11)}`;
9
+ }
10
+ /** Apply field-migration defaults for projects missing newer fields. */
11
+ function migrateProjectFields(project) {
12
+ const base = project;
13
+ return {
14
+ ...base,
15
+ taskFile: typeof project.taskFile === 'string' ? base.taskFile : '',
16
+ instruction: typeof project.instruction === 'string'
17
+ ? base.instruction
18
+ : 'implementation',
19
+ isMonorepo: typeof project.isMonorepo === 'boolean' ? base.isMonorepo : false,
20
+ customData: project.customData && typeof project.customData === 'object'
21
+ ? base.customData
22
+ : {},
23
+ };
24
+ }
25
+ /**
26
+ * Filesystem-backed project CRUD store using FileStorageService.
27
+ * Lazy-initializes on first access, migrating from legacy Electron location if needed.
28
+ */
29
+ export class FileProjectStore {
30
+ storage;
31
+ storagePath;
32
+ initialized = false;
33
+ constructor() {
34
+ this.storagePath = getStoragePath();
35
+ this.storage = new FileStorageService(this.storagePath);
36
+ }
37
+ async ensureInitialized() {
38
+ if (this.initialized)
39
+ return;
40
+ this.initialized = true;
41
+ const hasData = await this.storage.exists(PROJECTS_FILE);
42
+ if (!hasData) {
43
+ await this.migrateFromLegacyLocation();
44
+ }
45
+ }
46
+ async getAll() {
47
+ await this.ensureInitialized();
48
+ try {
49
+ const result = await this.storage.read(PROJECTS_FILE);
50
+ const parsed = JSON.parse(result.data);
51
+ if (!Array.isArray(parsed)) {
52
+ return [];
53
+ }
54
+ return parsed.map((p) => migrateProjectFields(p));
55
+ }
56
+ catch (err) {
57
+ // File not found — empty store
58
+ if (err instanceof Error &&
59
+ 'code' in err &&
60
+ err.code === 'ENOENT') {
61
+ return [];
62
+ }
63
+ throw err;
64
+ }
65
+ }
66
+ async getById(id) {
67
+ const all = await this.getAll();
68
+ return all.find((p) => p.id === id);
69
+ }
70
+ async create(data) {
71
+ await this.ensureInitialized();
72
+ const now = new Date().toISOString();
73
+ const project = {
74
+ id: generateProjectId(),
75
+ name: data.name,
76
+ template: data.template,
77
+ slice: data.slice,
78
+ taskFile: data.taskFile ?? '',
79
+ instruction: data.instruction ?? 'implementation',
80
+ developmentPhase: data.developmentPhase,
81
+ workType: data.workType,
82
+ projectDate: data.projectDate,
83
+ isMonorepo: data.isMonorepo,
84
+ isMonorepoEnabled: data.isMonorepoEnabled,
85
+ projectPath: data.projectPath,
86
+ customData: data.customData ?? {},
87
+ createdAt: now,
88
+ updatedAt: now,
89
+ };
90
+ const existing = await this.getAll();
91
+ existing.push(project);
92
+ await this.storage.write(PROJECTS_FILE, JSON.stringify(existing, null, 2));
93
+ return project;
94
+ }
95
+ async update(id, updates) {
96
+ const all = await this.getAll();
97
+ const index = all.findIndex((p) => p.id === id);
98
+ if (index === -1) {
99
+ throw new Error(`Project not found: ${id}`);
100
+ }
101
+ all[index] = {
102
+ ...all[index],
103
+ ...updates,
104
+ updatedAt: new Date().toISOString(),
105
+ };
106
+ await this.storage.write(PROJECTS_FILE, JSON.stringify(all, null, 2));
107
+ }
108
+ async delete(id) {
109
+ const all = await this.getAll();
110
+ const index = all.findIndex((p) => p.id === id);
111
+ if (index === -1) {
112
+ throw new Error(`Project not found: ${id}`);
113
+ }
114
+ const filtered = all.filter((p) => p.id !== id);
115
+ await this.storage.write(PROJECTS_FILE, JSON.stringify(filtered, null, 2));
116
+ }
117
+ /**
118
+ * Copy projects.json (and backup) from legacy Electron location to new location.
119
+ * Only runs if new location has no data and legacy location has data.
120
+ */
121
+ async migrateFromLegacyLocation() {
122
+ const legacyPath = getLegacyElectronPath();
123
+ if (!legacyPath)
124
+ return false;
125
+ const newFile = join(this.storagePath, PROJECTS_FILE);
126
+ const legacyFile = join(legacyPath, PROJECTS_FILE);
127
+ if (existsSync(newFile) || !existsSync(legacyFile)) {
128
+ return false;
129
+ }
130
+ await mkdir(this.storagePath, { recursive: true });
131
+ await copyFile(legacyFile, newFile);
132
+ // Also copy single backup if it exists
133
+ const legacyBackup = join(legacyPath, `${PROJECTS_FILE}.backup`);
134
+ if (existsSync(legacyBackup)) {
135
+ await copyFile(legacyBackup, join(this.storagePath, `${PROJECTS_FILE}.backup`));
136
+ }
137
+ console.log(`Migrated projects.json from legacy location: ${legacyPath}`);
138
+ return true;
139
+ }
140
+ }
141
+ //# sourceMappingURL=FileProjectStore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileProjectStore.js","sourceRoot":"","sources":["../../src/storage/FileProjectStore.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { IStorageService, StorageReadResult } from './interfaces.js';
2
+ /**
3
+ * Low-level filesystem storage service implementing atomic writes,
4
+ * backup on write, and recovery from corrupted files.
5
+ */
6
+ export declare class FileStorageService implements IStorageService {
7
+ private readonly storagePath;
8
+ constructor(storagePath: string);
9
+ read(filename: string): Promise<StorageReadResult>;
10
+ write(filename: string, data: string): Promise<void>;
11
+ createBackup(filename: string): Promise<void>;
12
+ exists(filename: string): Promise<boolean>;
13
+ }
14
+ //# sourceMappingURL=FileStorageService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileStorageService.d.ts","sourceRoot":"","sources":["../../src/storage/FileStorageService.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,iBAAiB,CAAC;AAsB1E;;;GAGG;AACH,qBAAa,kBAAmB,YAAW,eAAe;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW;gBAAX,WAAW,EAAE,MAAM;IAE1C,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC;IA6ClD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA4CpD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW7C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;CAIjD"}
@@ -0,0 +1,114 @@
1
+ import { readFile, writeFile, copyFile, rename, unlink, mkdir } from 'fs/promises';
2
+ import { existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { checkWriteGuard } from './backupService.js';
5
+ /** Error thrown when a filename contains path-traversal characters. */
6
+ class InvalidFilenameError extends Error {
7
+ constructor(filename) {
8
+ super(`Invalid filename: ${filename}`);
9
+ this.name = 'InvalidFilenameError';
10
+ }
11
+ }
12
+ /** Validate a filename — reject path-traversal characters. */
13
+ function validateFilename(filename) {
14
+ if (filename.includes('..') ||
15
+ filename.includes('/') ||
16
+ filename.includes('\\')) {
17
+ throw new InvalidFilenameError(filename);
18
+ }
19
+ }
20
+ /**
21
+ * Low-level filesystem storage service implementing atomic writes,
22
+ * backup on write, and recovery from corrupted files.
23
+ */
24
+ export class FileStorageService {
25
+ storagePath;
26
+ constructor(storagePath) {
27
+ this.storagePath = storagePath;
28
+ }
29
+ async read(filename) {
30
+ validateFilename(filename);
31
+ const filePath = join(this.storagePath, filename);
32
+ const backupPath = join(this.storagePath, `${filename}.backup`);
33
+ // Try main file first
34
+ try {
35
+ const data = await readFile(filePath, 'utf-8');
36
+ JSON.parse(data); // validate JSON
37
+ return { data };
38
+ }
39
+ catch (mainError) {
40
+ // Main file missing or corrupted — try backup recovery
41
+ try {
42
+ const backupData = await readFile(backupPath, 'utf-8');
43
+ JSON.parse(backupData); // validate backup JSON
44
+ // Restore main file from backup
45
+ try {
46
+ await copyFile(backupPath, filePath);
47
+ }
48
+ catch {
49
+ // Recovery of main file failed — still return backup data
50
+ }
51
+ return {
52
+ data: backupData,
53
+ recovered: true,
54
+ message: 'Data recovered from backup file',
55
+ };
56
+ }
57
+ catch {
58
+ // Neither main nor backup available/valid
59
+ if (mainError instanceof Error &&
60
+ 'code' in mainError &&
61
+ mainError.code === 'ENOENT') {
62
+ throw mainError; // propagate file-not-found
63
+ }
64
+ throw new Error('File corrupted and no valid backup available');
65
+ }
66
+ }
67
+ }
68
+ async write(filename, data) {
69
+ validateFilename(filename);
70
+ // Ensure storage directory exists
71
+ if (!existsSync(this.storagePath)) {
72
+ await mkdir(this.storagePath, { recursive: true });
73
+ }
74
+ const filePath = join(this.storagePath, filename);
75
+ const tempPath = join(this.storagePath, `${filename}.tmp`);
76
+ const backupPath = join(this.storagePath, `${filename}.backup`);
77
+ // Write guard: prevent catastrophic data loss for projects.json
78
+ const guardError = await checkWriteGuard(this.storagePath, filename, data);
79
+ if (guardError) {
80
+ throw new Error(guardError);
81
+ }
82
+ // Backup existing file (single .backup, overwritten each write)
83
+ if (existsSync(filePath)) {
84
+ await copyFile(filePath, backupPath);
85
+ }
86
+ // Write to temp file
87
+ await writeFile(tempPath, data, 'utf-8');
88
+ // Validate JSON
89
+ try {
90
+ JSON.parse(data);
91
+ }
92
+ catch {
93
+ if (existsSync(tempPath)) {
94
+ await unlink(tempPath);
95
+ }
96
+ throw new Error('Invalid JSON data');
97
+ }
98
+ // Atomic rename
99
+ await rename(tempPath, filePath);
100
+ }
101
+ async createBackup(filename) {
102
+ validateFilename(filename);
103
+ const filePath = join(this.storagePath, filename);
104
+ const backupPath = join(this.storagePath, `${filename}.backup`);
105
+ if (existsSync(filePath)) {
106
+ await copyFile(filePath, backupPath);
107
+ }
108
+ }
109
+ async exists(filename) {
110
+ validateFilename(filename);
111
+ return existsSync(join(this.storagePath, filename));
112
+ }
113
+ }
114
+ //# sourceMappingURL=FileStorageService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FileStorageService.js","sourceRoot":"","sources":["../../src/storage/FileStorageService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,28 @@
1
+ /** Maximum number of versioned backups to retain per file. */
2
+ export declare const MAX_VERSIONED_BACKUPS = 10;
3
+ /** Filesystem operations used by backup functions — injectable for testing. */
4
+ export interface BackupFsDeps {
5
+ existsSync: (path: string) => boolean;
6
+ copyFile: (src: string, dest: string) => Promise<void>;
7
+ readdir: (path: string) => Promise<string[]>;
8
+ unlink: (path: string) => Promise<void>;
9
+ readFile: (path: string, encoding: string) => Promise<string>;
10
+ }
11
+ /**
12
+ * Create a versioned timestamped backup of a file in the storage directory,
13
+ * then prune old backups beyond the retention limit.
14
+ */
15
+ export declare function createVersionedBackup(storagePath: string, filename: string, fs?: BackupFsDeps): Promise<void>;
16
+ /**
17
+ * Prune old versioned backups beyond the retention limit.
18
+ * Rotation failure must not block the caller.
19
+ */
20
+ export declare function pruneOldBackups(storagePath: string, filename: string, fs?: BackupFsDeps): Promise<void>;
21
+ /**
22
+ * Write guard for projects.json: prevents catastrophic data loss by refusing
23
+ * to overwrite a multi-project file with near-empty data.
24
+ *
25
+ * Returns null if write is allowed, or an error message string if blocked.
26
+ */
27
+ export declare function checkWriteGuard(storagePath: string, filename: string, incomingData: string, fs?: BackupFsDeps): Promise<string | null>;
28
+ //# sourceMappingURL=backupService.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backupService.d.ts","sourceRoot":"","sources":["../../src/storage/backupService.ts"],"names":[],"mappings":"AAIA,8DAA8D;AAC9D,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;IACtC,QAAQ,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACvD,OAAO,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,EAAE,CAAC,CAAC;IAC7C,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACxC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/D;AAWD;;;GAGG;AACH,wBAAsB,qBAAqB,CACzC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,EAAE,GAAE,YAA0B,GAC7B,OAAO,CAAC,IAAI,CAAC,CAUf;AAED;;;GAGG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,EAAE,GAAE,YAA0B,GAC7B,OAAO,CAAC,IAAI,CAAC,CA0Bf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CACnC,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAChB,YAAY,EAAE,MAAM,EACpB,EAAE,GAAE,YAA0B,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA6BxB"}
@@ -0,0 +1,84 @@
1
+ import { copyFile, readdir, unlink, readFile } from 'fs/promises';
2
+ import { existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ /** Maximum number of versioned backups to retain per file. */
5
+ export const MAX_VERSIONED_BACKUPS = 10;
6
+ /** Default deps using real fs — used in production. */
7
+ const defaultDeps = {
8
+ existsSync,
9
+ copyFile,
10
+ readdir: readdir,
11
+ unlink,
12
+ readFile: readFile,
13
+ };
14
+ /**
15
+ * Create a versioned timestamped backup of a file in the storage directory,
16
+ * then prune old backups beyond the retention limit.
17
+ */
18
+ export async function createVersionedBackup(storagePath, filename, fs = defaultDeps) {
19
+ const filePath = join(storagePath, filename);
20
+ if (!fs.existsSync(filePath))
21
+ return;
22
+ const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
23
+ const versionedPath = join(storagePath, `${filename}.${timestamp}.backup`);
24
+ await fs.copyFile(filePath, versionedPath);
25
+ console.log(`Versioned backup created: ${filename}.${timestamp}.backup`);
26
+ await pruneOldBackups(storagePath, filename, fs);
27
+ }
28
+ /**
29
+ * Prune old versioned backups beyond the retention limit.
30
+ * Rotation failure must not block the caller.
31
+ */
32
+ export async function pruneOldBackups(storagePath, filename, fs = defaultDeps) {
33
+ try {
34
+ const entries = await fs.readdir(storagePath);
35
+ const versionedPattern = `${filename}.`;
36
+ const versionedBackups = entries
37
+ .filter((e) => e.startsWith(versionedPattern) &&
38
+ e.endsWith('.backup') &&
39
+ e !== `${filename}.backup`)
40
+ .sort()
41
+ .reverse(); // newest first (ISO timestamps sort lexicographically)
42
+ if (versionedBackups.length > MAX_VERSIONED_BACKUPS) {
43
+ const toDelete = versionedBackups.slice(MAX_VERSIONED_BACKUPS);
44
+ for (const old of toDelete) {
45
+ await fs.unlink(join(storagePath, old));
46
+ }
47
+ console.log(`Pruned ${toDelete.length} old versioned backup(s) for ${filename}`);
48
+ }
49
+ }
50
+ catch (err) {
51
+ console.error(`Backup rotation failed for ${filename}:`, err);
52
+ }
53
+ }
54
+ /**
55
+ * Write guard for projects.json: prevents catastrophic data loss by refusing
56
+ * to overwrite a multi-project file with near-empty data.
57
+ *
58
+ * Returns null if write is allowed, or an error message string if blocked.
59
+ */
60
+ export async function checkWriteGuard(storagePath, filename, incomingData, fs = defaultDeps) {
61
+ if (filename !== 'projects.json')
62
+ return null;
63
+ const filePath = join(storagePath, filename);
64
+ if (!fs.existsSync(filePath))
65
+ return null;
66
+ try {
67
+ const existing = await fs.readFile(filePath, 'utf-8');
68
+ const existingParsed = JSON.parse(existing);
69
+ const incomingParsed = JSON.parse(incomingData);
70
+ if (Array.isArray(existingParsed) && Array.isArray(incomingParsed)) {
71
+ if (existingParsed.length > 2 && incomingParsed.length <= 1) {
72
+ const msg = `Write guard: significant data reduction detected (${existingParsed.length} → ${incomingParsed.length})`;
73
+ console.error(`Write guard: refusing to overwrite ${existingParsed.length} projects with ${incomingParsed.length}`);
74
+ return msg;
75
+ }
76
+ }
77
+ }
78
+ catch (err) {
79
+ // Guard itself failed (e.g., existing file unparseable) — allow write through
80
+ console.error(`Write guard check failed for ${filename}, allowing write:`, err);
81
+ }
82
+ return null;
83
+ }
84
+ //# sourceMappingURL=backupService.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backupService.js","sourceRoot":"","sources":["../../src/storage/backupService.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ export * from './interfaces.js';
2
+ export { FileStorageService } from './FileStorageService.js';
3
+ export { FileProjectStore } from './FileProjectStore.js';
4
+ export { getStoragePath, getLegacyElectronPath } from './storagePaths.js';
5
+ export { createVersionedBackup, pruneOldBackups, checkWriteGuard, MAX_VERSIONED_BACKUPS, type BackupFsDeps, } from './backupService.js';
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAEA,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,qBAAqB,EACrB,KAAK,YAAY,GAClB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,7 @@
1
+ // Storage module barrel — re-exports all storage interfaces, services, and utilities
2
+ export * from './interfaces.js';
3
+ export { FileStorageService } from './FileStorageService.js';
4
+ export { FileProjectStore } from './FileProjectStore.js';
5
+ export { getStoragePath, getLegacyElectronPath } from './storagePaths.js';
6
+ export { createVersionedBackup, pruneOldBackups, checkWriteGuard, MAX_VERSIONED_BACKUPS, } from './backupService.js';
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/storage/index.ts"],"names":[],"mappings":"AAAA,qFAAqF;AAErF,cAAc,iBAAiB,CAAC;AAChC,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EACL,qBAAqB,EACrB,eAAe,EACf,eAAe,EACf,qBAAqB,GAEtB,MAAM,oBAAoB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { ProjectData, CreateProjectData, UpdateProjectData } from '../types/project.js';
2
+ /** Result of a storage read operation, with optional recovery metadata. */
3
+ export interface StorageReadResult {
4
+ data: string;
5
+ recovered?: boolean;
6
+ message?: string;
7
+ }
8
+ /** Low-level storage service for reading/writing arbitrary JSON files with backup support. */
9
+ export interface IStorageService {
10
+ read(filename: string): Promise<StorageReadResult>;
11
+ write(filename: string, data: string): Promise<void>;
12
+ createBackup(filename: string): Promise<void>;
13
+ exists(filename: string): Promise<boolean>;
14
+ }
15
+ /** High-level project CRUD store. */
16
+ export interface IProjectStore {
17
+ getAll(): Promise<ProjectData[]>;
18
+ getById(id: string): Promise<ProjectData | undefined>;
19
+ create(data: CreateProjectData): Promise<ProjectData>;
20
+ update(id: string, updates: UpdateProjectData): Promise<void>;
21
+ delete(id: string): Promise<void>;
22
+ }
23
+ //# sourceMappingURL=interfaces.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.d.ts","sourceRoot":"","sources":["../../src/storage/interfaces.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAE7F,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,8FAA8F;AAC9F,MAAM,WAAW,eAAe;IAC9B,IAAI,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAAC;IACnD,KAAK,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACrD,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC5C;AAED,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC5B,MAAM,IAAI,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;IACjC,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC,CAAC;IACtD,MAAM,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;IACtD,MAAM,CAAC,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9D,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACnC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=interfaces.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"interfaces.js","sourceRoot":"","sources":["../../src/storage/interfaces.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ /** Canonical storage path for context-forge data. Respects CONTEXT_FORGE_DATA_DIR override. */
2
+ export declare function getStoragePath(): string;
3
+ /**
4
+ * Legacy Electron storage path (macOS only).
5
+ * Returns the path Electron's `app.getPath('userData')` + `/context-forge` used,
6
+ * or null on non-macOS platforms where env-paths typically resolves to the same location.
7
+ */
8
+ export declare function getLegacyElectronPath(): string | null;
9
+ //# sourceMappingURL=storagePaths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storagePaths.d.ts","sourceRoot":"","sources":["../../src/storage/storagePaths.ts"],"names":[],"mappings":"AAMA,+FAA+F;AAC/F,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,IAAI,MAAM,GAAG,IAAI,CAKrD"}
@@ -0,0 +1,20 @@
1
+ import envPaths from 'env-paths';
2
+ import { join } from 'path';
3
+ import { homedir } from 'os';
4
+ const paths = envPaths('context-forge', { suffix: '' });
5
+ /** Canonical storage path for context-forge data. Respects CONTEXT_FORGE_DATA_DIR override. */
6
+ export function getStoragePath() {
7
+ return process.env.CONTEXT_FORGE_DATA_DIR || paths.config;
8
+ }
9
+ /**
10
+ * Legacy Electron storage path (macOS only).
11
+ * Returns the path Electron's `app.getPath('userData')` + `/context-forge` used,
12
+ * or null on non-macOS platforms where env-paths typically resolves to the same location.
13
+ */
14
+ export function getLegacyElectronPath() {
15
+ if (process.platform === 'darwin') {
16
+ return join(homedir(), 'Library', 'Application Support', 'context-forge', 'context-forge');
17
+ }
18
+ return null;
19
+ }
20
+ //# sourceMappingURL=storagePaths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storagePaths.js","sourceRoot":"","sources":["../../src/storage/storagePaths.ts"],"names":[],"mappings":"AAAA,OAAO,QAAQ,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAE7B,MAAM,KAAK,GAAG,QAAQ,CAAC,eAAe,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC,CAAC;AAExD,+FAA+F;AAC/F,MAAM,UAAU,cAAc;IAC5B,OAAO,OAAO,CAAC,GAAG,CAAC,sBAAsB,IAAI,KAAK,CAAC,MAAM,CAAC;AAC5D,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB;IACnC,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;QAClC,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,qBAAqB,EAAE,eAAe,EAAE,eAAe,CAAC,CAAC;IAC7F,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Data structure for context generation.
3
+ * Maps directly to template variables.
4
+ * Consolidated from renderer superset (includes taskFile, developmentPhase, workType, projectDate).
5
+ */
6
+ export interface ContextData {
7
+ projectName: string;
8
+ template: string;
9
+ slice: string;
10
+ taskFile: string;
11
+ instruction: string;
12
+ developmentPhase?: string;
13
+ workType?: 'start' | 'continue';
14
+ projectDate?: string;
15
+ isMonorepo: boolean;
16
+ recentEvents: string;
17
+ additionalNotes: string;
18
+ }
19
+ /**
20
+ * Enhanced context data with additional fields for template system.
21
+ * Single canonical definition — consolidates previous duplicates in
22
+ * ContextData.ts and ContextSection.ts across main/renderer.
23
+ */
24
+ export interface EnhancedContextData extends ContextData {
25
+ availableTools?: string[];
26
+ mcpServers?: string[];
27
+ templateVersion?: string;
28
+ customSections?: Record<string, string>;
29
+ customData?: {
30
+ recentEvents?: string;
31
+ additionalNotes?: string;
32
+ monorepoNote?: string;
33
+ availableTools?: string;
34
+ };
35
+ }
36
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../src/types/context.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,EAAE,OAAO,GAAG,UAAU,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,OAAO,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,mBAAoB,SAAQ,WAAW;IACtD,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;IACtB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxC,UAAU,CAAC,EAAE;QACX,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,eAAe,CAAC,EAAE,MAAM,CAAC;QACzB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,cAAc,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACH"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../../src/types/context.ts"],"names":[],"mappings":""}
@@ -0,0 +1,9 @@
1
+ export type { ContextData, EnhancedContextData } from './context.js';
2
+ export type { ContextSection, ContextTemplate, SectionBuilderConfig, SectionValidation } from './sections.js';
3
+ export { SectionKeys } from './sections.js';
4
+ export type { TemplateStatement, StatementConfig, StatementFileMetadata, ParsedStatement } from './statements.js';
5
+ export type { SystemPrompt, ParsedPromptFile, PromptCacheEntry } from './prompts.js';
6
+ export { SpecialPromptKeys } from './prompts.js';
7
+ export type { ProjectData, CreateProjectData, UpdateProjectData } from './project.js';
8
+ export type { PathValidationResult, DirectoryListResult } from './paths.js';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,WAAW,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAGrE,YAAY,EAAE,cAAc,EAAE,eAAe,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAC9G,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAG5C,YAAY,EAAE,iBAAiB,EAAE,eAAe,EAAE,qBAAqB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAGlH,YAAY,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGjD,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGtF,YAAY,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ export { SectionKeys } from './sections.js';
2
+ export { SpecialPromptKeys } from './prompts.js';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAO5C,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC"}