@cxtmanager/core 1.0.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 (42) hide show
  1. package/README.md +132 -0
  2. package/dist/context-manager.d.ts +55 -0
  3. package/dist/context-manager.d.ts.map +1 -0
  4. package/dist/context-manager.js +676 -0
  5. package/dist/context-manager.js.map +1 -0
  6. package/dist/file-watcher.d.ts +31 -0
  7. package/dist/file-watcher.d.ts.map +1 -0
  8. package/dist/file-watcher.js +121 -0
  9. package/dist/file-watcher.js.map +1 -0
  10. package/dist/git-hooks-manager.d.ts +40 -0
  11. package/dist/git-hooks-manager.d.ts.map +1 -0
  12. package/dist/git-hooks-manager.js +210 -0
  13. package/dist/git-hooks-manager.js.map +1 -0
  14. package/dist/git-repository.d.ts +65 -0
  15. package/dist/git-repository.d.ts.map +1 -0
  16. package/dist/git-repository.js +352 -0
  17. package/dist/git-repository.js.map +1 -0
  18. package/dist/index.d.ts +13 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +37 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/plan-manager.d.ts +55 -0
  23. package/dist/plan-manager.d.ts.map +1 -0
  24. package/dist/plan-manager.js +208 -0
  25. package/dist/plan-manager.js.map +1 -0
  26. package/dist/plan-templates.d.ts +15 -0
  27. package/dist/plan-templates.d.ts.map +1 -0
  28. package/dist/plan-templates.js +127 -0
  29. package/dist/plan-templates.js.map +1 -0
  30. package/dist/types/index.d.ts +143 -0
  31. package/dist/types/index.d.ts.map +1 -0
  32. package/dist/types/index.js +26 -0
  33. package/dist/types/index.js.map +1 -0
  34. package/dist/types.d.ts +176 -0
  35. package/dist/types.d.ts.map +1 -0
  36. package/dist/types.js +3 -0
  37. package/dist/types.js.map +1 -0
  38. package/dist/validation-engine.d.ts +26 -0
  39. package/dist/validation-engine.d.ts.map +1 -0
  40. package/dist/validation-engine.js +344 -0
  41. package/dist/validation-engine.js.map +1 -0
  42. package/package.json +63 -0
@@ -0,0 +1,676 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ContextManager = void 0;
37
+ const fs = __importStar(require("fs-extra"));
38
+ const path = __importStar(require("path"));
39
+ const git_repository_1 = require("./git-repository");
40
+ const file_watcher_1 = require("./file-watcher");
41
+ const validation_engine_1 = require("./validation-engine");
42
+ const plan_manager_1 = require("./plan-manager");
43
+ class ContextManager {
44
+ constructor(projectRoot = process.cwd()) {
45
+ this.planManager = null;
46
+ this.config = null;
47
+ this.projectRoot = projectRoot;
48
+ this.cxtPath = path.join(projectRoot, '.cxt');
49
+ this.configPath = path.join(this.cxtPath, '.cxtconfig.json');
50
+ this.gitRepo = new git_repository_1.GitRepository(projectRoot);
51
+ this.fileWatcher = new file_watcher_1.FileWatcher(this.cxtPath);
52
+ this.validationEngine = new validation_engine_1.ValidationEngine(this.cxtPath);
53
+ }
54
+ async init(options) {
55
+ try {
56
+ // Ensure we're in a Git repository
57
+ await this.gitRepo.ensureGitRepo();
58
+ }
59
+ catch (error) {
60
+ if (error.message.includes('EACCES') || error.message.includes('permission denied')) {
61
+ throw new Error('Permission denied. Cannot initialize Git repository.\n' +
62
+ ' 💡 Check file system permissions\n' +
63
+ ' 💡 Ensure you have write access to the current directory');
64
+ }
65
+ throw error;
66
+ }
67
+ try {
68
+ // Create .cxt structure
69
+ await this.createCxtStructure();
70
+ }
71
+ catch (error) {
72
+ if (error.message.includes('EACCES') || error.message.includes('permission denied')) {
73
+ throw new Error('Permission denied. Cannot create .cxt/ folder.\n' +
74
+ ' 💡 Check file system permissions\n' +
75
+ ' 💡 Ensure you have write access to the current directory');
76
+ }
77
+ throw error;
78
+ }
79
+ // Analyze the project
80
+ const analysis = await this.analyzeProject();
81
+ // Create configuration
82
+ await this.createConfig(options);
83
+ // Configure .gitignore based on trackInGit setting
84
+ const trackInGit = options.trackInGit !== false; // Default to true
85
+ await this.gitRepo.ensureGitignore(trackInGit);
86
+ // Generate content based on mode
87
+ switch (options.mode) {
88
+ case 'manual':
89
+ await this.createManualTemplates();
90
+ break;
91
+ case 'auto':
92
+ await this.createBasicContent(analysis);
93
+ break;
94
+ default:
95
+ await this.createBasicContent(analysis);
96
+ }
97
+ // Initial Git commit (only if tracking in Git)
98
+ if (trackInGit) {
99
+ await this.gitRepo.addAndCommit(['.cxt/'], `feat: initialize CxtManager with ${options.mode} mode\n\nCreated context files: context.md, plan.md, guardrail.md`, 'CxtManager Init');
100
+ }
101
+ }
102
+ async isInitialized() {
103
+ const cxtExists = await fs.pathExists(this.cxtPath);
104
+ const configExists = await fs.pathExists(this.configPath);
105
+ return cxtExists && configExists;
106
+ }
107
+ async status() {
108
+ if (!await this.isInitialized()) {
109
+ throw new Error('CxtManager not initialized');
110
+ }
111
+ const gitStatus = await this.gitRepo.getStatus();
112
+ const health = await this.validate();
113
+ const contextFiles = await this.getContextFileStatus();
114
+ return {
115
+ gitStatus,
116
+ health,
117
+ contextFiles,
118
+ lastUpdated: new Date()
119
+ };
120
+ }
121
+ async validate(quick = false) {
122
+ if (!await this.isInitialized()) {
123
+ throw new Error('CxtManager not initialized');
124
+ }
125
+ // Update validation engine with current config thresholds
126
+ const config = await this.loadConfig();
127
+ const thresholds = config.context?.template_thresholds || {
128
+ well_populated: 30,
129
+ mild_warning: 50,
130
+ critical: 70
131
+ };
132
+ this.validationEngine = new validation_engine_1.ValidationEngine(this.cxtPath, thresholds);
133
+ return await this.validationEngine.checkHealth(quick);
134
+ }
135
+ /**
136
+ * Sync .gitignore with track_in_git setting from config
137
+ * Call this after manually changing git_integration.track_in_git in .cxtconfig.json
138
+ */
139
+ async syncGitignore() {
140
+ if (!await this.isInitialized()) {
141
+ throw new Error('CxtManager not initialized');
142
+ }
143
+ const config = await this.loadConfig();
144
+ const trackInGit = config.git_integration?.track_in_git !== false; // Default to true
145
+ await this.gitRepo.ensureGitignore(trackInGit);
146
+ }
147
+ async autoHeal(dryRun = false) {
148
+ if (!await this.isInitialized()) {
149
+ throw new Error('CxtManager not initialized');
150
+ }
151
+ const health = await this.validate();
152
+ return await this.validationEngine.autoHeal(health.issues, dryRun);
153
+ }
154
+ async loadConfig() {
155
+ if (!this.config) {
156
+ if (!await fs.pathExists(this.configPath)) {
157
+ throw new Error('Configuration file not found');
158
+ }
159
+ const configData = await fs.readJson(this.configPath);
160
+ this.config = configData;
161
+ }
162
+ if (!this.config) {
163
+ throw new Error('Failed to load configuration');
164
+ }
165
+ return this.config;
166
+ }
167
+ /**
168
+ * Get the current configuration (public API)
169
+ */
170
+ async getConfig() {
171
+ return await this.loadConfig();
172
+ }
173
+ async getContextFiles() {
174
+ const files = ['context.md', 'plan.md', 'guardrail.md'];
175
+ const contextFiles = [];
176
+ for (const file of files) {
177
+ const filePath = path.join(this.cxtPath, file);
178
+ if (await fs.pathExists(filePath)) {
179
+ const content = await fs.readFile(filePath, 'utf-8');
180
+ const stats = await fs.stat(filePath);
181
+ contextFiles.push({
182
+ name: file,
183
+ path: filePath,
184
+ content,
185
+ lastModified: stats.mtime,
186
+ size: stats.size
187
+ });
188
+ }
189
+ }
190
+ return contextFiles;
191
+ }
192
+ /**
193
+ * Get or create PlanManager instance
194
+ */
195
+ async getPlanManager() {
196
+ if (!this.planManager) {
197
+ const config = await this.loadConfig();
198
+ this.planManager = new plan_manager_1.PlanManager(this.cxtPath, this.gitRepo, config);
199
+ }
200
+ return this.planManager;
201
+ }
202
+ /**
203
+ * Sync plan.md for current branch (save current, restore for new branch)
204
+ */
205
+ async syncPlan(options) {
206
+ if (!await this.isInitialized()) {
207
+ throw new Error('CxtManager not initialized');
208
+ }
209
+ const planManager = await this.getPlanManager();
210
+ return await planManager.syncPlan(options);
211
+ }
212
+ /**
213
+ * Get list of branches with saved plans
214
+ */
215
+ async getBranchPlans() {
216
+ if (!await this.isInitialized()) {
217
+ throw new Error('CxtManager not initialized');
218
+ }
219
+ const planManager = await this.getPlanManager();
220
+ return await planManager.listBranchPlans();
221
+ }
222
+ async createCxtStructure() {
223
+ await fs.ensureDir(this.cxtPath);
224
+ // Create .plan-history directory for branch-aware plan.md
225
+ await fs.ensureDir(path.join(this.cxtPath, '.plan-history'));
226
+ }
227
+ async analyzeProject() {
228
+ const packageJsonPath = path.join(this.projectRoot, 'package.json');
229
+ const readmePath = path.join(this.projectRoot, 'README.md');
230
+ let packageJson = null;
231
+ let readme = '';
232
+ if (await fs.pathExists(packageJsonPath)) {
233
+ packageJson = await fs.readJson(packageJsonPath);
234
+ }
235
+ if (await fs.pathExists(readmePath)) {
236
+ readme = await fs.readFile(readmePath, 'utf-8');
237
+ }
238
+ const structure = await this.analyzeStructure();
239
+ const gitInfo = await this.gitRepo.getInfo();
240
+ const dependencies = packageJson?.dependencies ? Object.keys(packageJson.dependencies) : [];
241
+ const technologies = this.detectTechnologies(dependencies, structure);
242
+ return {
243
+ packageJson,
244
+ readme,
245
+ structure,
246
+ gitInfo,
247
+ technologies
248
+ };
249
+ }
250
+ async analyzeStructure() {
251
+ // Simple structure analysis
252
+ const entries = await fs.readdir(this.projectRoot);
253
+ const structure = {
254
+ hasPackageJson: entries.includes('package.json'),
255
+ hasSrc: entries.includes('src'),
256
+ hasTests: entries.some(e => e.includes('test') || e.includes('spec')),
257
+ hasConfig: entries.some(e => e.includes('config') || e.includes('.config')),
258
+ directories: entries.filter(async (e) => {
259
+ const stat = await fs.stat(path.join(this.projectRoot, e));
260
+ return stat.isDirectory();
261
+ })
262
+ };
263
+ return structure;
264
+ }
265
+ detectTechnologies(dependencies, structure) {
266
+ const technologies = [];
267
+ // Detect based on dependencies
268
+ if (dependencies.includes('react'))
269
+ technologies.push('React');
270
+ if (dependencies.includes('vue'))
271
+ technologies.push('Vue');
272
+ if (dependencies.includes('angular'))
273
+ technologies.push('Angular');
274
+ if (dependencies.includes('express'))
275
+ technologies.push('Express');
276
+ if (dependencies.includes('next'))
277
+ technologies.push('Next.js');
278
+ if (dependencies.includes('typescript'))
279
+ technologies.push('TypeScript');
280
+ // Detect based on structure
281
+ if (structure.hasSrc)
282
+ technologies.push('JavaScript/TypeScript');
283
+ if (structure.hasTests)
284
+ technologies.push('Testing');
285
+ return technologies;
286
+ }
287
+ async createManualTemplates() {
288
+ const templates = {
289
+ 'context.md': this.getManualTemplate('context'),
290
+ 'plan.md': this.getManualTemplate('plan'),
291
+ 'guardrail.md': this.getManualTemplate('guardrail')
292
+ };
293
+ for (const [fileName, content] of Object.entries(templates)) {
294
+ const filePath = path.join(this.cxtPath, fileName);
295
+ await fs.writeFile(filePath, content);
296
+ }
297
+ }
298
+ async createBasicContent(analysis) {
299
+ // Create basic content based on analysis
300
+ const projectName = analysis.packageJson?.name || 'Project';
301
+ const readmeFirstLine = analysis.readme.split('\n')[0] || '';
302
+ const projectDescription = readmeFirstLine || 'Add your project description here';
303
+ const contextContent = `# ${projectName} Context
304
+
305
+ *This file contains stable project information that doesn't change per branch.*
306
+ *See plan.md for branch-specific implementation details.*
307
+
308
+ *Last Updated: ${new Date().toISOString().split('T')[0]}*
309
+
310
+ ## Project Purpose
311
+
312
+ ${projectDescription}
313
+
314
+ <!--
315
+ GUIDANCE: Describe what this project does and why it exists.
316
+ This helps AI understand the project's goals when providing assistance.
317
+
318
+ TIP: Be specific about the problem you're solving and who benefits from it.
319
+ -->
320
+
321
+ ## Core Problem
322
+
323
+ <!--
324
+ GUIDANCE: What problem does this project solve?
325
+ This helps AI understand the motivation behind the project.
326
+ Example: "Remote teams struggle to track tasks across multiple tools"
327
+
328
+ TIP: Focus on the user's pain point, not the technical solution.
329
+ -->
330
+
331
+ ## Solution
332
+
333
+ <!--
334
+ GUIDANCE: How does this project address the problem?
335
+ This helps AI understand your approach to solving the problem.
336
+ Example: "Unified task management with Slack integration for seamless workflow"
337
+
338
+ TIP: Explain the high-level approach, not implementation details (those go in plan.md).
339
+ -->
340
+
341
+ ## Target Users
342
+
343
+ <!--
344
+ GUIDANCE: Who will use this project?
345
+ This helps AI tailor suggestions to your audience.
346
+ Example: "Remote teams of 5-50 people using Slack for communication"
347
+
348
+ TIP: Be specific about user characteristics, needs, and constraints.
349
+ -->
350
+
351
+ ## Key Features
352
+
353
+ <!--
354
+ GUIDANCE: What are the main features and capabilities?
355
+ This helps AI understand what functionality is important.
356
+ Example: "Task creation, team collaboration, Slack notifications, deadline tracking"
357
+
358
+ TIP: List the core features that define your project's value proposition.
359
+ -->
360
+ `;
361
+ await fs.writeFile(path.join(this.cxtPath, 'context.md'), contextContent);
362
+ await fs.writeFile(path.join(this.cxtPath, 'plan.md'), this.getManualTemplate('plan'));
363
+ await fs.writeFile(path.join(this.cxtPath, 'guardrail.md'), this.getManualTemplate('guardrail'));
364
+ }
365
+ getManualTemplate(type) {
366
+ const templates = {
367
+ context: `# Project Context
368
+
369
+ *This file contains stable project information that doesn't change per branch.*
370
+ *See plan.md for branch-specific implementation details.*
371
+
372
+ *Last Updated: ${new Date().toISOString().split('T')[0]}*
373
+
374
+ ## Project Purpose
375
+ <!--
376
+ GUIDANCE: Describe what this project does and why it exists.
377
+ This helps AI understand the project's goals when providing assistance.
378
+ Example: "A task management app for remote teams"
379
+
380
+ TIP: Be specific about the problem you're solving and who benefits from it.
381
+ -->
382
+
383
+ ## Core Problem
384
+ <!--
385
+ GUIDANCE: What problem does this project solve?
386
+ This helps AI understand the motivation behind the project.
387
+ Example: "Remote teams struggle to track tasks across multiple tools"
388
+
389
+ TIP: Focus on the user's pain point, not the technical solution.
390
+ -->
391
+
392
+ ## Solution
393
+ <!--
394
+ GUIDANCE: How does this project address the problem?
395
+ This helps AI understand your approach to solving the problem.
396
+ Example: "Unified task management with Slack integration for seamless workflow"
397
+
398
+ TIP: Explain the high-level approach, not implementation details (those go in plan.md).
399
+ -->
400
+
401
+ ## Target Users
402
+ <!--
403
+ GUIDANCE: Who will use this project?
404
+ This helps AI tailor suggestions to your audience.
405
+ Example: "Remote teams of 5-50 people using Slack for communication"
406
+
407
+ TIP: Be specific about user characteristics, needs, and constraints.
408
+ -->
409
+
410
+ ## Key Features
411
+ <!--
412
+ GUIDANCE: What are the main features and capabilities?
413
+ This helps AI understand what functionality is important.
414
+ Example: "Task creation, team collaboration, Slack notifications, deadline tracking"
415
+
416
+ TIP: List the core features that define your project's value proposition.
417
+ -->
418
+ `,
419
+ plan: `# Development Plan
420
+
421
+ *This file contains branch-specific implementation details.*
422
+ *When you switch branches, this file automatically switches to that branch's plan.*
423
+ *See context.md for stable project background.*
424
+
425
+ *Last Updated: ${new Date().toISOString().split('T')[0]}*
426
+ *References: context.md for project vision*
427
+
428
+ ## Architecture Overview
429
+ <!--
430
+ GUIDANCE: Describe the technical architecture for this branch/feature.
431
+ This helps AI understand how the code is structured.
432
+ Example: "REST API with Express.js, PostgreSQL database, React frontend"
433
+
434
+ TIP: Focus on the architecture decisions relevant to current work.
435
+ AI will update this as implementation progresses.
436
+ -->
437
+
438
+ ## Development Phases
439
+ <!--
440
+ GUIDANCE: Break down development into phases or milestones.
441
+ This helps AI understand the development timeline and priorities.
442
+ Example: "Phase 1: Authentication, Phase 2: Core features, Phase 3: Integrations"
443
+
444
+ TIP: Update this as work progresses. AI can help track completion status.
445
+ -->
446
+
447
+ ## Technology Stack
448
+ <!--
449
+ GUIDANCE: List technologies, frameworks, and tools being used.
450
+ This helps AI make appropriate suggestions and avoid incompatible solutions.
451
+ Example: "Node.js 18+, TypeScript, React 18, PostgreSQL 14, Docker"
452
+
453
+ TIP: Include versions and important constraints (e.g., "must support Node 16+").
454
+ -->
455
+
456
+ ## Success Criteria
457
+ <!--
458
+ GUIDANCE: Define what success looks like for this branch/feature.
459
+ This helps AI understand when work is complete.
460
+ Example: "Users can create accounts, login, and reset passwords"
461
+
462
+ TIP: Be specific and measurable. AI can help verify completion.
463
+ -->
464
+ `,
465
+ guardrail: `# Development Guardrails
466
+
467
+ *This file contains universal project constraints and rules.*
468
+ *These apply across all branches and should be respected by AI tools.*
469
+
470
+ *Last Updated: ${new Date().toISOString().split('T')[0]}*
471
+
472
+ ## Code Standards
473
+ <!--
474
+ GUIDANCE: Coding standards and conventions.
475
+ This helps AI generate code that matches your project's style.
476
+ Example: "Use TypeScript strict mode, ESLint rules, 2-space indentation"
477
+
478
+ TIP: Reference your linter config, style guide, or team conventions.
479
+ -->
480
+
481
+ ## Architecture Rules
482
+ <!--
483
+ GUIDANCE: Architectural constraints and patterns.
484
+ This helps AI make decisions that align with your architecture.
485
+ Example: "No direct database access from components, use service layer"
486
+
487
+ TIP: Document patterns to follow and anti-patterns to avoid.
488
+ -->
489
+
490
+ ## Security Requirements
491
+ <!--
492
+ GUIDANCE: Security guidelines and requirements.
493
+ This helps AI avoid security vulnerabilities.
494
+ Example: "All user input must be sanitized, use HTTPS only, no secrets in code"
495
+
496
+ TIP: Include authentication, authorization, data protection requirements.
497
+ -->
498
+
499
+ ## Performance Constraints
500
+ <!--
501
+ GUIDANCE: Performance requirements and limits.
502
+ This helps AI optimize code appropriately.
503
+ Example: "API responses < 200ms, support 1000 concurrent users"
504
+
505
+ TIP: Include response time targets, scalability requirements, resource limits.
506
+ -->
507
+ `
508
+ };
509
+ return templates[type] || '';
510
+ }
511
+ async createConfig(options) {
512
+ const trackInGit = options.trackInGit !== false; // Default to true
513
+ const config = {
514
+ version: '1.0.0',
515
+ mode: options.mode,
516
+ git_integration: {
517
+ enabled: true,
518
+ hooks: {
519
+ post_checkout: 'sync-plan',
520
+ pre_commit: 'validate',
521
+ post_merge: 'auto-heal'
522
+ },
523
+ silent_mode: true,
524
+ auto_install_hooks: true, // MVP: Auto-install hooks on init
525
+ track_in_git: trackInGit
526
+ },
527
+ plan_management: {
528
+ backup_on_switch: true,
529
+ template: 'minimal',
530
+ auto_commit_ai_changes: true,
531
+ archive_completed: false
532
+ },
533
+ mcp: {
534
+ enabled: false,
535
+ sources: {
536
+ local_files: {
537
+ enabled: true,
538
+ readme: true,
539
+ package_json: true,
540
+ git_history: true
541
+ }
542
+ }
543
+ },
544
+ context: {
545
+ auto_sync: false,
546
+ health_checks: true,
547
+ ai_attribution: true,
548
+ update_mode: 'manual',
549
+ drift_detection: true,
550
+ warn_threshold: 3,
551
+ template_thresholds: {
552
+ well_populated: 30,
553
+ mild_warning: 50,
554
+ critical: 70
555
+ },
556
+ show_in_changed_files: true,
557
+ auto_commit_context_updates: false
558
+ },
559
+ created: new Date().toISOString()
560
+ };
561
+ await fs.writeJson(this.configPath, config, { spaces: 2 });
562
+ this.config = config;
563
+ }
564
+ async getContextFileStatus() {
565
+ const files = ['context.md', 'plan.md', 'guardrail.md'];
566
+ const gitStatus = await this.gitRepo.getStatus();
567
+ const results = await Promise.all(files.map(async (file) => {
568
+ const filePath = `.cxt/${file}`;
569
+ const fullPath = path.join(this.cxtPath, file);
570
+ let status = 'clean';
571
+ let staged = false;
572
+ let contentStatus = 'populated';
573
+ let fileSize = 0;
574
+ if (gitStatus.untracked.includes(filePath)) {
575
+ status = 'new';
576
+ }
577
+ else if (gitStatus.modified.includes(filePath)) {
578
+ status = 'modified';
579
+ }
580
+ if (gitStatus.staged.includes(filePath)) {
581
+ staged = true;
582
+ }
583
+ // Check file content status
584
+ let templatePercentage;
585
+ if (await fs.pathExists(fullPath)) {
586
+ const stats = await fs.stat(fullPath);
587
+ fileSize = stats.size;
588
+ if (fileSize === 0) {
589
+ contentStatus = 'empty';
590
+ templatePercentage = 100; // Empty is 100% template
591
+ }
592
+ else {
593
+ const content = await fs.readFile(fullPath, 'utf-8');
594
+ const detectionResult = this.detectContentStatus(content, file);
595
+ contentStatus = detectionResult.status;
596
+ templatePercentage = detectionResult.templatePercentage;
597
+ }
598
+ }
599
+ else {
600
+ contentStatus = 'empty';
601
+ templatePercentage = 100; // Missing file is 100% template
602
+ }
603
+ return {
604
+ file,
605
+ status,
606
+ staged,
607
+ contentStatus,
608
+ templatePercentage,
609
+ size: fileSize
610
+ };
611
+ }));
612
+ return results;
613
+ }
614
+ /**
615
+ * Detect if file content is mostly template/placeholder content
616
+ * Returns status and percentage of template content (0-100)
617
+ */
618
+ detectContentStatus(content, fileName) {
619
+ if (!content || content.trim().length === 0) {
620
+ return { status: 'empty', templatePercentage: 100 };
621
+ }
622
+ const lines = content.split('\n');
623
+ let templateChars = 0;
624
+ let totalChars = 0;
625
+ // Analyze each line
626
+ for (const line of lines) {
627
+ const trimmed = line.trim();
628
+ if (trimmed.length === 0) {
629
+ continue; // Skip empty lines
630
+ }
631
+ totalChars += line.length;
632
+ // Check if line is template/guidance content
633
+ const isTemplateLine = trimmed.startsWith('<!--') ||
634
+ trimmed.includes('GUIDANCE:') ||
635
+ trimmed.includes('TIP:') ||
636
+ trimmed.includes('Example:') ||
637
+ trimmed.startsWith('*') && (trimmed.includes('Last Updated') || trimmed.includes('References') || trimmed.includes('This file')) ||
638
+ trimmed === '-->';
639
+ if (isTemplateLine) {
640
+ templateChars += line.length;
641
+ }
642
+ }
643
+ // Calculate percentage
644
+ const templatePercentage = totalChars > 0
645
+ ? Math.round((templateChars / totalChars) * 100)
646
+ : 100;
647
+ // Count actual content lines
648
+ const contentLines = lines.filter(line => {
649
+ const trimmed = line.trim();
650
+ return trimmed.length > 0 &&
651
+ !trimmed.startsWith('<!--') &&
652
+ !trimmed.startsWith('*') &&
653
+ !trimmed.startsWith('#') &&
654
+ trimmed !== '-->' &&
655
+ !trimmed.includes('GUIDANCE:') &&
656
+ !trimmed.includes('TIP:') &&
657
+ !trimmed.includes('Example:');
658
+ });
659
+ // Determine status based on percentage (unified threshold: 70%)
660
+ // If very few content lines, consider it template-only even if percentage is lower
661
+ let status;
662
+ if (templatePercentage >= 70 || (contentLines.length <= 3 && templatePercentage > 30)) {
663
+ status = 'template-only';
664
+ // If low content lines but percentage is borderline, adjust to reflect severity
665
+ if (contentLines.length <= 3 && templatePercentage <= 70) {
666
+ return { status, templatePercentage: Math.max(templatePercentage, 70) };
667
+ }
668
+ }
669
+ else {
670
+ status = 'populated';
671
+ }
672
+ return { status, templatePercentage };
673
+ }
674
+ }
675
+ exports.ContextManager = ContextManager;
676
+ //# sourceMappingURL=context-manager.js.map