autosnippet 2.8.3 → 2.10.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 (110) hide show
  1. package/README.md +5 -5
  2. package/bin/cli.js +5 -33
  3. package/config/constitution.yaml +9 -2
  4. package/dashboard/dist/assets/{icons-B_Xg4B-s.js → icons-BkT3XrKf.js} +105 -100
  5. package/dashboard/dist/assets/index-BsB7DzW4.css +1 -0
  6. package/dashboard/dist/assets/index-DdmQMrJJ.js +155 -0
  7. package/dashboard/dist/index.html +3 -3
  8. package/lib/cli/AiScanService.js +13 -11
  9. package/lib/cli/KnowledgeSyncService.js +343 -0
  10. package/lib/cli/SetupService.js +9 -27
  11. package/lib/core/ast/ProjectGraph.js +160 -0
  12. package/lib/core/gateway/GatewayActionRegistry.js +48 -58
  13. package/lib/domain/index.js +16 -11
  14. package/lib/domain/knowledge/KnowledgeEntry.js +351 -0
  15. package/lib/domain/knowledge/KnowledgeRepository.js +123 -0
  16. package/lib/domain/knowledge/Lifecycle.js +109 -0
  17. package/lib/domain/knowledge/index.js +27 -0
  18. package/lib/domain/knowledge/values/Constraints.js +125 -0
  19. package/lib/domain/knowledge/values/Content.js +86 -0
  20. package/lib/domain/knowledge/values/Quality.js +93 -0
  21. package/lib/domain/knowledge/values/Reasoning.js +69 -0
  22. package/lib/domain/knowledge/values/Relations.js +168 -0
  23. package/lib/domain/knowledge/values/Stats.js +87 -0
  24. package/lib/domain/knowledge/values/index.js +9 -0
  25. package/lib/external/ai/AiProvider.js +48 -0
  26. package/lib/external/ai/providers/GoogleGeminiProvider.js +12 -3
  27. package/lib/external/mcp/McpServer.js +7 -5
  28. package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +3 -2
  29. package/lib/external/mcp/handlers/bootstrap.js +121 -12
  30. package/lib/external/mcp/handlers/browse.js +77 -73
  31. package/lib/external/mcp/handlers/candidate.js +29 -276
  32. package/lib/external/mcp/handlers/guard.js +2 -0
  33. package/lib/external/mcp/handlers/knowledge.js +205 -0
  34. package/lib/external/mcp/handlers/skill.js +4 -2
  35. package/lib/external/mcp/handlers/structure.js +25 -23
  36. package/lib/external/mcp/handlers/system.js +10 -12
  37. package/lib/external/mcp/tools.js +125 -138
  38. package/lib/http/HttpServer.js +4 -8
  39. package/lib/http/middleware/requestLogger.js +3 -3
  40. package/lib/http/routes/ai.js +17 -1
  41. package/lib/http/routes/extract.js +48 -4
  42. package/lib/http/routes/knowledge.js +246 -0
  43. package/lib/http/routes/search.js +12 -17
  44. package/lib/http/routes/skills.js +44 -1
  45. package/lib/infrastructure/cache/GraphCache.js +143 -0
  46. package/lib/infrastructure/database/migrations/015_create_token_usage.js +27 -0
  47. package/lib/infrastructure/database/migrations/016_unified_knowledge_entries.js +395 -0
  48. package/lib/infrastructure/external/XcodeAutomation.js +187 -103
  49. package/lib/infrastructure/realtime/RealtimeService.js +14 -2
  50. package/lib/injection/ServiceContainer.js +164 -63
  51. package/lib/repository/knowledge/KnowledgeRepository.impl.js +373 -0
  52. package/lib/repository/token/TokenUsageStore.js +162 -0
  53. package/lib/service/automation/DirectiveDetector.js +2 -3
  54. package/lib/service/automation/FileWatcher.js +67 -28
  55. package/lib/service/automation/XcodeIntegration.js +931 -156
  56. package/lib/service/automation/handlers/AlinkHandler.js +6 -4
  57. package/lib/service/automation/handlers/CreateHandler.js +53 -18
  58. package/lib/service/automation/handlers/GuardHandler.js +183 -20
  59. package/lib/service/automation/handlers/SearchHandler.js +35 -17
  60. package/lib/service/chat/AnalystAgent.js +25 -14
  61. package/lib/service/chat/CandidateGuardrail.js +1 -1
  62. package/lib/service/chat/ChatAgent.js +280 -48
  63. package/lib/service/chat/ContextWindow.js +92 -8
  64. package/lib/service/chat/HandoffProtocol.js +26 -1
  65. package/lib/service/chat/ProducerAgent.js +11 -9
  66. package/lib/service/chat/tools.js +298 -194
  67. package/lib/service/guard/GuardCheckEngine.js +114 -10
  68. package/lib/service/guard/GuardService.js +59 -48
  69. package/lib/service/knowledge/ConfidenceRouter.js +159 -0
  70. package/lib/service/knowledge/KnowledgeFileWriter.js +602 -0
  71. package/lib/service/knowledge/KnowledgeService.js +725 -0
  72. package/lib/service/search/SearchEngine.js +92 -19
  73. package/lib/service/skills/SignalCollector.js +15 -9
  74. package/lib/service/skills/SkillAdvisor.js +13 -11
  75. package/lib/service/snippet/SnippetFactory.js +5 -5
  76. package/lib/service/spm/SpmService.js +119 -18
  77. package/package.json +1 -1
  78. package/scripts/install-cursor-skill.js +0 -6
  79. package/scripts/migrate-md-to-knowledge.mjs +364 -0
  80. package/skills/autosnippet-analysis/SKILL.md +15 -7
  81. package/skills/autosnippet-candidates/SKILL.md +6 -6
  82. package/skills/autosnippet-coldstart/SKILL.md +7 -3
  83. package/skills/autosnippet-concepts/SKILL.md +7 -6
  84. package/skills/autosnippet-create/SKILL.md +13 -13
  85. package/skills/autosnippet-intent/SKILL.md +3 -2
  86. package/skills/autosnippet-lifecycle/SKILL.md +5 -5
  87. package/skills/autosnippet-recipes/SKILL.md +16 -4
  88. package/templates/constitution.yaml +1 -1
  89. package/templates/copilot-instructions.md +6 -6
  90. package/templates/recipes-setup/README.md +3 -3
  91. package/dashboard/dist/assets/index-CkIih2CC.css +0 -1
  92. package/dashboard/dist/assets/index-Duc8Qk-c.js +0 -197
  93. package/lib/cli/CandidateSyncService.js +0 -261
  94. package/lib/cli/SyncService.js +0 -356
  95. package/lib/domain/candidate/Candidate.js +0 -196
  96. package/lib/domain/candidate/CandidateRepository.js +0 -107
  97. package/lib/domain/candidate/Reasoning.js +0 -52
  98. package/lib/domain/recipe/Recipe.js +0 -421
  99. package/lib/domain/recipe/RecipeRepository.js +0 -54
  100. package/lib/domain/types/CandidateStatus.js +0 -52
  101. package/lib/http/routes/candidates.js +0 -559
  102. package/lib/http/routes/recipes.js +0 -397
  103. package/lib/repository/candidate/CandidateRepository.impl.js +0 -230
  104. package/lib/repository/recipe/RecipeRepository.impl.js +0 -498
  105. package/lib/service/candidate/CandidateAggregator.js +0 -52
  106. package/lib/service/candidate/CandidateFileWriter.js +0 -383
  107. package/lib/service/candidate/CandidateService.js +0 -973
  108. package/lib/service/recipe/RecipeFileWriter.js +0 -514
  109. package/lib/service/recipe/RecipeService.js +0 -786
  110. package/lib/service/recipe/RecipeStatsTracker.js +0 -148
@@ -3,11 +3,15 @@ import Logger from '../infrastructure/logging/Logger.js';
3
3
  import AuditStore from '../infrastructure/audit/AuditStore.js';
4
4
  import AuditLogger from '../infrastructure/audit/AuditLogger.js';
5
5
  import Gateway from '../core/gateway/Gateway.js';
6
-
7
- import { CandidateRepositoryImpl } from '../repository/candidate/CandidateRepository.impl.js';
8
- import { RecipeRepositoryImpl } from '../repository/recipe/RecipeRepository.impl.js';
6
+ import { readdirSync, statSync } from 'node:fs';
7
+ import { join as pathJoin, relative as pathRelative, extname as pathExtname } from 'node:path';
8
+
9
+ import { KnowledgeRepositoryImpl } from '../repository/knowledge/KnowledgeRepository.impl.js';
10
+ import { KnowledgeFileWriter } from '../service/knowledge/KnowledgeFileWriter.js';
11
+ import { KnowledgeSyncService } from '../cli/KnowledgeSyncService.js';
12
+ import { KnowledgeService } from '../service/knowledge/KnowledgeService.js';
13
+ import { ConfidenceRouter } from '../service/knowledge/ConfidenceRouter.js';
9
14
  import { SnippetFactory } from '../service/snippet/SnippetFactory.js';
10
- import { RecipeService } from '../service/recipe/RecipeService.js';
11
15
  import { GuardService } from '../service/guard/GuardService.js';
12
16
  import { KnowledgeGraphService } from '../service/knowledge/KnowledgeGraphService.js';
13
17
  import { SearchEngine } from '../service/search/SearchEngine.js';
@@ -20,13 +24,9 @@ import { RetrievalFunnel } from '../service/search/RetrievalFunnel.js';
20
24
  import { JsonVectorAdapter } from '../infrastructure/vector/JsonVectorAdapter.js';
21
25
  import { IndexingPipeline } from '../infrastructure/vector/IndexingPipeline.js';
22
26
 
23
- // ─── P1: Injection / Snippet / Recipe ─────────────────
27
+ // ─── P1: Injection / Snippet ─────────────────
24
28
  import { RecipeParser } from '../service/recipe/RecipeParser.js';
25
- import { RecipeStatsTracker } from '../service/recipe/RecipeStatsTracker.js';
26
29
  import { RecipeCandidateValidator } from '../service/recipe/RecipeCandidateValidator.js';
27
- import { RecipeFileWriter } from '../service/recipe/RecipeFileWriter.js';
28
- import { CandidateFileWriter } from '../service/candidate/CandidateFileWriter.js';
29
- import { CandidateService } from '../service/candidate/CandidateService.js';
30
30
  import { SnippetInstaller } from '../service/snippet/SnippetInstaller.js';
31
31
 
32
32
  // ─── P1: Guard Advanced ──────────────────────────────
@@ -34,6 +34,9 @@ import { ExclusionManager } from '../service/guard/ExclusionManager.js';
34
34
  import { RuleLearner } from '../service/guard/RuleLearner.js';
35
35
  import { ViolationsStore } from '../service/guard/ViolationsStore.js';
36
36
 
37
+ // ─── P1: Token Usage Tracking ─────────────────────────
38
+ import { TokenUsageStore } from '../repository/token/TokenUsageStore.js';
39
+
37
40
  // ─── P2: Quality ──────────────────────────────────────
38
41
  import { QualityScorer } from '../service/quality/QualityScorer.js';
39
42
  import { FeedbackCollector } from '../service/quality/FeedbackCollector.js';
@@ -53,6 +56,7 @@ import { SkillHooks } from '../service/skills/SkillHooks.js';
53
56
 
54
57
  // ─── v3.0: AST ProjectGraph ──────────────────────────
55
58
  import ProjectGraph from '../core/ast/ProjectGraph.js';
59
+ import { GraphCache } from '../infrastructure/cache/GraphCache.js';
56
60
 
57
61
  // ─── P3: Infrastructure ──────────────────────────────
58
62
  import { EventBus } from '../infrastructure/event/EventBus.js';
@@ -232,22 +236,31 @@ export class ServiceContainer {
232
236
  * 注册仓储
233
237
  */
234
238
  _registerRepositories() {
235
- // CandidateRepository
236
- this.register('candidateRepository', () => {
237
- if (!this.singletons.candidateRepository) {
239
+ // KnowledgeRepository (统一知识实体)
240
+ this.register('knowledgeRepository', () => {
241
+ if (!this.singletons.knowledgeRepository) {
238
242
  const database = this.get('database');
239
- this.singletons.candidateRepository = new CandidateRepositoryImpl(database);
243
+ this.singletons.knowledgeRepository = new KnowledgeRepositoryImpl(database);
240
244
  }
241
- return this.singletons.candidateRepository;
245
+ return this.singletons.knowledgeRepository;
242
246
  });
243
247
 
244
- // RecipeRepository
245
- this.register('recipeRepository', () => {
246
- if (!this.singletons.recipeRepository) {
247
- const database = this.get('database');
248
- this.singletons.recipeRepository = new RecipeRepositoryImpl(database);
248
+ // KnowledgeFileWriter (统一 .md 序列化/落盘)
249
+ this.register('knowledgeFileWriter', () => {
250
+ if (!this.singletons.knowledgeFileWriter) {
251
+ const projectRoot = this.singletons._projectRoot || process.cwd();
252
+ this.singletons.knowledgeFileWriter = new KnowledgeFileWriter(projectRoot);
253
+ }
254
+ return this.singletons.knowledgeFileWriter;
255
+ });
256
+
257
+ // KnowledgeSyncService (统一 .md ↔ DB 同步)
258
+ this.register('knowledgeSyncService', () => {
259
+ if (!this.singletons.knowledgeSyncService) {
260
+ const projectRoot = this.singletons._projectRoot || process.cwd();
261
+ this.singletons.knowledgeSyncService = new KnowledgeSyncService(projectRoot);
249
262
  }
250
- return this.singletons.recipeRepository;
263
+ return this.singletons.knowledgeSyncService;
251
264
  });
252
265
  }
253
266
 
@@ -255,54 +268,45 @@ export class ServiceContainer {
255
268
  * 注册服务
256
269
  */
257
270
  _registerServices() {
258
- // CandidateService
259
- this.register('candidateService', () => {
260
- if (!this.singletons.candidateService) {
261
- const candidateRepository = this.get('candidateRepository');
262
- const auditLogger = this.get('auditLogger');
263
- const gateway = this.get('gateway');
264
- const projectRoot = this.singletons._projectRoot || process.cwd();
265
- const candidateFileWriter = new CandidateFileWriter(projectRoot);
266
- const skillHooks = this.get('skillHooks');
267
- this.singletons.candidateService = new CandidateService(
268
- candidateRepository,
269
- auditLogger,
270
- gateway,
271
- { fileWriter: candidateFileWriter, skillHooks }
272
- );
271
+ // ConfidenceRouter (V3: 知识条目自动审核路由)
272
+ this.register('confidenceRouter', () => {
273
+ if (!this.singletons.confidenceRouter) {
274
+ const qualityScorer = this.get('qualityScorer');
275
+ this.singletons.confidenceRouter = new ConfidenceRouter({}, qualityScorer);
273
276
  }
274
- return this.singletons.candidateService;
277
+ return this.singletons.confidenceRouter;
275
278
  });
276
279
 
277
- // RecipeService
278
- this.register('recipeService', () => {
279
- if (!this.singletons.recipeService) {
280
- const recipeRepository = this.get('recipeRepository');
280
+ // KnowledgeService (V3: 统一知识服务)
281
+ this.register('knowledgeService', () => {
282
+ if (!this.singletons.knowledgeService) {
283
+ const knowledgeRepository = this.get('knowledgeRepository');
281
284
  const auditLogger = this.get('auditLogger');
282
285
  const gateway = this.get('gateway');
283
286
  const knowledgeGraphService = this.get('knowledgeGraphService');
284
- const projectRoot = this.singletons._projectRoot || process.cwd();
285
- const fileWriter = new RecipeFileWriter(projectRoot);
287
+ const fileWriter = this.get('knowledgeFileWriter');
286
288
  const skillHooks = this.get('skillHooks');
287
- this.singletons.recipeService = new RecipeService(
288
- recipeRepository,
289
+ const confidenceRouter = this.get('confidenceRouter');
290
+ const qualityScorer = this.get('qualityScorer');
291
+ this.singletons.knowledgeService = new KnowledgeService(
292
+ knowledgeRepository,
289
293
  auditLogger,
290
294
  gateway,
291
295
  knowledgeGraphService,
292
- { fileWriter, skillHooks }
296
+ { fileWriter, skillHooks, confidenceRouter, qualityScorer }
293
297
  );
294
298
  }
295
- return this.singletons.recipeService;
299
+ return this.singletons.knowledgeService;
296
300
  });
297
301
 
298
- // GuardService (now uses recipeRepository)
302
+ // GuardService (V3: uses knowledgeRepository)
299
303
  this.register('guardService', () => {
300
304
  if (!this.singletons.guardService) {
301
- const recipeRepository = this.get('recipeRepository');
305
+ const knowledgeRepository = this.get('knowledgeRepository');
302
306
  const auditLogger = this.get('auditLogger');
303
307
  const gateway = this.get('gateway');
304
308
  this.singletons.guardService = new GuardService(
305
- recipeRepository,
309
+ knowledgeRepository,
306
310
  auditLogger,
307
311
  gateway
308
312
  );
@@ -388,15 +392,6 @@ export class ServiceContainer {
388
392
  return this.singletons.recipeParser;
389
393
  });
390
394
 
391
- // RecipeStatsTracker
392
- this.register('recipeStatsTracker', () => {
393
- if (!this.singletons.recipeStatsTracker) {
394
- const projectRoot = this.singletons._projectRoot || process.cwd();
395
- this.singletons.recipeStatsTracker = new RecipeStatsTracker(projectRoot);
396
- }
397
- return this.singletons.recipeStatsTracker;
398
- });
399
-
400
395
  // RecipeCandidateValidator
401
396
  this.register('recipeCandidateValidator', () => {
402
397
  if (!this.singletons.recipeCandidateValidator) {
@@ -405,11 +400,11 @@ export class ServiceContainer {
405
400
  return this.singletons.recipeCandidateValidator;
406
401
  });
407
402
 
408
- // SnippetFactory (no DB — generates from recipes on-the-fly)
403
+ // SnippetFactory (V3: uses knowledgeRepository)
409
404
  this.register('snippetFactory', () => {
410
405
  if (!this.singletons.snippetFactory) {
411
- const recipeRepo = this.get('recipeRepository');
412
- this.singletons.snippetFactory = new SnippetFactory(recipeRepo);
406
+ const knowledgeRepo = this.get('knowledgeRepository');
407
+ this.singletons.snippetFactory = new SnippetFactory(knowledgeRepo);
413
408
  }
414
409
  return this.singletons.snippetFactory;
415
410
  });
@@ -450,6 +445,15 @@ export class ServiceContainer {
450
445
  return this.singletons.violationsStore;
451
446
  });
452
447
 
448
+ // Token Usage: 持久化 AI token 消耗
449
+ this.register('tokenUsageStore', () => {
450
+ if (!this.singletons.tokenUsageStore) {
451
+ const db = this.get('database').getDb();
452
+ this.singletons.tokenUsageStore = new TokenUsageStore(db);
453
+ }
454
+ return this.singletons.tokenUsageStore;
455
+ });
456
+
453
457
  // QualityScorer
454
458
  this.register('qualityScorer', () => {
455
459
  if (!this.singletons.qualityScorer) {
@@ -566,7 +570,7 @@ export class ServiceContainer {
566
570
 
567
571
  /**
568
572
  * 构建 ProjectGraph (v3.0 AST 结构图)
569
- * 应在 bootstrap 流程开始前调用一次
573
+ * 优先从磁盘缓存加载,支持 per-file hash 增量更新
570
574
  * @param {string} projectRoot 项目根目录
571
575
  * @param {object} [options] 传递给 ProjectGraph.build() 的选项
572
576
  * @returns {Promise<import('../core/ast/ProjectGraph.js').default|null>}
@@ -575,14 +579,70 @@ export class ServiceContainer {
575
579
  if (this.singletons.projectGraph) {
576
580
  return this.singletons.projectGraph;
577
581
  }
582
+
583
+ const cache = new GraphCache(projectRoot);
584
+ const startTime = Date.now();
585
+
578
586
  try {
587
+ // ── 尝试从缓存恢复 + 增量更新 ──
588
+ const cached = cache.load('project-graph');
589
+ if (cached?.data && cached.fileHashes) {
590
+ const graph = ProjectGraph.fromJSON(cached.data);
591
+ const currentFiles = this.#collectSourceFilePaths(projectRoot, options);
592
+ const oldHashes = cached.fileHashes || {};
593
+
594
+ // 计算差异:新增 / 变更 / 删除
595
+ const changedPaths = [];
596
+ const newHashes = {};
597
+ for (const fp of currentFiles) {
598
+ const rel = pathRelative(projectRoot, fp);
599
+ const h = cache.computeFileHash(fp);
600
+ newHashes[rel] = h;
601
+ if (!oldHashes[rel] || oldHashes[rel] !== h) {
602
+ changedPaths.push(fp);
603
+ }
604
+ }
605
+ const deletedPaths = Object.keys(oldHashes).filter(rel => !newHashes[rel]);
606
+
607
+ if (changedPaths.length === 0 && deletedPaths.length === 0) {
608
+ // 完全命中
609
+ this.singletons.projectGraph = graph;
610
+ this.logger.info(
611
+ `[ServiceContainer] ProjectGraph ⚡ 缓存命中 (${graph.getOverview().totalClasses} classes, ` +
612
+ `${Date.now() - startTime}ms)`
613
+ );
614
+ return graph;
615
+ }
616
+
617
+ // 增量更新
618
+ const diff = await graph.incrementalUpdate(changedPaths, deletedPaths, options);
619
+ this.singletons.projectGraph = graph;
620
+
621
+ // 写回缓存
622
+ cache.save('project-graph', graph.toJSON(), { fileHashes: newHashes });
623
+
624
+ const overview = graph.getOverview();
625
+ this.logger.info(
626
+ `[ServiceContainer] ProjectGraph 增量更新: +${diff.added} ~${diff.updated} -${diff.deleted} ` +
627
+ `(${overview.totalClasses} classes, ${Date.now() - startTime}ms)`
628
+ );
629
+ return graph;
630
+ }
631
+
632
+ // ── 无缓存,全量构建 ──
579
633
  const graph = await ProjectGraph.build(projectRoot, options);
580
634
  this.singletons.projectGraph = graph;
581
635
  const overview = graph.getOverview();
636
+
637
+ // 计算文件 hash 并写入缓存
638
+ const currentFiles = this.#collectSourceFilePaths(projectRoot, options);
639
+ const fileHashes = cache.computeFileHashes(currentFiles, projectRoot);
640
+ cache.save('project-graph', graph.toJSON(), { fileHashes });
641
+
582
642
  this.logger.info(
583
643
  `[ServiceContainer] ProjectGraph built: ${overview.totalClasses} classes, ` +
584
644
  `${overview.totalProtocols} protocols, ${overview.totalCategories} categories ` +
585
- `(${overview.buildTimeMs}ms)`
645
+ `(${overview.buildTimeMs}ms) — 缓存已写入`
586
646
  );
587
647
  return graph;
588
648
  } catch (err) {
@@ -590,6 +650,47 @@ export class ServiceContainer {
590
650
  return null;
591
651
  }
592
652
  }
653
+
654
+ /**
655
+ * 收集项目源码文件路径(用于 hash 计算)
656
+ * @param {string} projectRoot
657
+ * @param {object} options
658
+ * @returns {string[]}
659
+ */
660
+ #collectSourceFilePaths(projectRoot, options = {}) {
661
+ const DEFAULTS_EXT = { '.m': true, '.h': true, '.swift': true };
662
+ const extSet = new Set(options.extensions || Object.keys(DEFAULTS_EXT));
663
+ const excludePatterns = options.excludePatterns || [
664
+ 'Pods/', 'Carthage/', 'node_modules/', '.build/', 'build/',
665
+ 'DerivedData/', 'vendor/', '.git/', '__tests__/', 'Tests/',
666
+ ];
667
+ const maxFiles = options.maxFiles || 500;
668
+ const maxFileSizeBytes = options.maxFileSizeBytes || 500_000;
669
+ const results = [];
670
+
671
+ function walk(dir) {
672
+ if (results.length >= maxFiles) return;
673
+ let entries;
674
+ try { entries = readdirSync(dir, { withFileTypes: true }); } catch { return; }
675
+ for (const entry of entries) {
676
+ if (results.length >= maxFiles) return;
677
+ const fullPath = pathJoin(dir, entry.name);
678
+ const relativePath = pathRelative(projectRoot, fullPath);
679
+ if (excludePatterns.some(p => relativePath.includes(p))) continue;
680
+ if (entry.isDirectory()) {
681
+ walk(fullPath);
682
+ } else if (entry.isFile() && extSet.has(pathExtname(entry.name))) {
683
+ try {
684
+ const stat = statSync(fullPath);
685
+ if (stat.size <= maxFileSizeBytes) results.push(fullPath);
686
+ } catch { /* skip */ }
687
+ }
688
+ }
689
+ }
690
+
691
+ walk(projectRoot);
692
+ return results;
693
+ }
593
694
  }
594
695
 
595
696
  let containerInstance = null;