@nepopsx/cli 0.0.3 → 0.0.5

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 (168) hide show
  1. package/dist/agents/configuration.d.ts +9 -0
  2. package/dist/agents/configuration.d.ts.map +1 -0
  3. package/dist/agents/configuration.js +57 -0
  4. package/dist/agents/configuration.js.map +1 -0
  5. package/dist/agents/remote-config.d.ts +30 -0
  6. package/dist/agents/remote-config.d.ts.map +1 -0
  7. package/dist/agents/remote-config.js +102 -0
  8. package/dist/agents/remote-config.js.map +1 -0
  9. package/dist/commands/activate.d.ts.map +1 -1
  10. package/dist/commands/activate.js +4 -3
  11. package/dist/commands/activate.js.map +1 -1
  12. package/dist/commands/decode.js +2 -2
  13. package/dist/commands/decode.js.map +1 -1
  14. package/dist/commands/doctor.js +16 -16
  15. package/dist/commands/doctor.js.map +1 -1
  16. package/dist/commands/init.d.ts.map +1 -1
  17. package/dist/commands/init.js +353 -18
  18. package/dist/commands/init.js.map +1 -1
  19. package/dist/commands/install.d.ts +1 -0
  20. package/dist/commands/install.d.ts.map +1 -1
  21. package/dist/commands/install.js +253 -25
  22. package/dist/commands/install.js.map +1 -1
  23. package/dist/commands/login.d.ts +6 -0
  24. package/dist/commands/login.d.ts.map +1 -0
  25. package/dist/commands/login.js +108 -0
  26. package/dist/commands/login.js.map +1 -0
  27. package/dist/commands/push-learnings.d.ts +14 -0
  28. package/dist/commands/push-learnings.d.ts.map +1 -0
  29. package/dist/commands/push-learnings.js +186 -0
  30. package/dist/commands/push-learnings.js.map +1 -0
  31. package/dist/commands/scan.d.ts +2 -0
  32. package/dist/commands/scan.d.ts.map +1 -0
  33. package/dist/commands/scan.js +520 -0
  34. package/dist/commands/scan.js.map +1 -0
  35. package/dist/commands/sync.d.ts +2 -0
  36. package/dist/commands/sync.d.ts.map +1 -1
  37. package/dist/commands/sync.js +203 -234
  38. package/dist/commands/sync.js.map +1 -1
  39. package/dist/config/api-config.d.ts.map +1 -1
  40. package/dist/config/api-config.js +4 -0
  41. package/dist/config/api-config.js.map +1 -1
  42. package/dist/constants.d.ts +7 -0
  43. package/dist/constants.d.ts.map +1 -0
  44. package/dist/constants.js +7 -0
  45. package/dist/constants.js.map +1 -0
  46. package/dist/customs/instructions.d.ts +3 -0
  47. package/dist/customs/instructions.d.ts.map +1 -0
  48. package/dist/customs/instructions.js +51 -0
  49. package/dist/customs/instructions.js.map +1 -0
  50. package/dist/enrichment/confirm.d.ts +17 -0
  51. package/dist/enrichment/confirm.d.ts.map +1 -0
  52. package/dist/enrichment/confirm.js +44 -0
  53. package/dist/enrichment/confirm.js.map +1 -0
  54. package/dist/enrichment/state.d.ts +8 -0
  55. package/dist/enrichment/state.d.ts.map +1 -0
  56. package/dist/enrichment/state.js +107 -0
  57. package/dist/enrichment/state.js.map +1 -0
  58. package/dist/enrichment/types.d.ts +13 -0
  59. package/dist/enrichment/types.d.ts.map +1 -0
  60. package/dist/enrichment/types.js +2 -0
  61. package/dist/enrichment/types.js.map +1 -0
  62. package/dist/enrichment/workflow.d.ts +35 -0
  63. package/dist/enrichment/workflow.d.ts.map +1 -0
  64. package/dist/enrichment/workflow.js +305 -0
  65. package/dist/enrichment/workflow.js.map +1 -0
  66. package/dist/generator/builtin-templates.d.ts +2 -1
  67. package/dist/generator/builtin-templates.d.ts.map +1 -1
  68. package/dist/generator/builtin-templates.js +161 -2
  69. package/dist/generator/builtin-templates.js.map +1 -1
  70. package/dist/generator/package-renderer.d.ts +29 -0
  71. package/dist/generator/package-renderer.d.ts.map +1 -0
  72. package/dist/generator/package-renderer.js +123 -0
  73. package/dist/generator/package-renderer.js.map +1 -0
  74. package/dist/generator/render.d.ts +14 -63
  75. package/dist/generator/render.d.ts.map +1 -1
  76. package/dist/generator/render.js +30 -176
  77. package/dist/generator/render.js.map +1 -1
  78. package/dist/index.js +42 -2
  79. package/dist/index.js.map +1 -1
  80. package/dist/licensing/installer.d.ts +51 -0
  81. package/dist/licensing/installer.d.ts.map +1 -0
  82. package/dist/licensing/installer.js +112 -0
  83. package/dist/licensing/installer.js.map +1 -0
  84. package/dist/licensing/license-manager.d.ts +6 -0
  85. package/dist/licensing/license-manager.d.ts.map +1 -1
  86. package/dist/licensing/license-manager.js +12 -35
  87. package/dist/licensing/license-manager.js.map +1 -1
  88. package/dist/licensing/template-fetch.d.ts +3 -2
  89. package/dist/licensing/template-fetch.d.ts.map +1 -1
  90. package/dist/licensing/template-fetch.js +10 -41
  91. package/dist/licensing/template-fetch.js.map +1 -1
  92. package/dist/scan/__tests__/context-gatherer.test.d.ts +2 -0
  93. package/dist/scan/__tests__/context-gatherer.test.d.ts.map +1 -0
  94. package/dist/scan/__tests__/context-gatherer.test.js +111 -0
  95. package/dist/scan/__tests__/context-gatherer.test.js.map +1 -0
  96. package/dist/scan/__tests__/merge.test.d.ts +2 -0
  97. package/dist/scan/__tests__/merge.test.d.ts.map +1 -0
  98. package/dist/scan/__tests__/merge.test.js +163 -0
  99. package/dist/scan/__tests__/merge.test.js.map +1 -0
  100. package/dist/scan/auto-bootstrap.d.ts +3 -0
  101. package/dist/scan/auto-bootstrap.d.ts.map +1 -0
  102. package/dist/scan/auto-bootstrap.js +30 -0
  103. package/dist/scan/auto-bootstrap.js.map +1 -0
  104. package/dist/scan/config.d.ts +24 -0
  105. package/dist/scan/config.d.ts.map +1 -0
  106. package/dist/scan/config.js +77 -0
  107. package/dist/scan/config.js.map +1 -0
  108. package/dist/scan/context/gatherer.d.ts +13 -0
  109. package/dist/scan/context/gatherer.d.ts.map +1 -0
  110. package/dist/scan/context/gatherer.js +97 -0
  111. package/dist/scan/context/gatherer.js.map +1 -0
  112. package/dist/scan/context/gitignore.d.ts +9 -0
  113. package/dist/scan/context/gitignore.d.ts.map +1 -0
  114. package/dist/scan/context/gitignore.js +50 -0
  115. package/dist/scan/context/gitignore.js.map +1 -0
  116. package/dist/scan/context/patterns.d.ts +14 -0
  117. package/dist/scan/context/patterns.d.ts.map +1 -0
  118. package/dist/scan/context/patterns.js +159 -0
  119. package/dist/scan/context/patterns.js.map +1 -0
  120. package/dist/scan/customs/writer.d.ts +6 -0
  121. package/dist/scan/customs/writer.d.ts.map +1 -0
  122. package/dist/scan/customs/writer.js +149 -0
  123. package/dist/scan/customs/writer.js.map +1 -0
  124. package/dist/scan/llm/anthropic.d.ts +11 -0
  125. package/dist/scan/llm/anthropic.d.ts.map +1 -0
  126. package/dist/scan/llm/anthropic.js +89 -0
  127. package/dist/scan/llm/anthropic.js.map +1 -0
  128. package/dist/scan/llm/factory.d.ts +4 -0
  129. package/dist/scan/llm/factory.d.ts.map +1 -0
  130. package/dist/scan/llm/factory.js +20 -0
  131. package/dist/scan/llm/factory.js.map +1 -0
  132. package/dist/scan/llm/ollama.d.ts +11 -0
  133. package/dist/scan/llm/ollama.d.ts.map +1 -0
  134. package/dist/scan/llm/ollama.js +64 -0
  135. package/dist/scan/llm/ollama.js.map +1 -0
  136. package/dist/scan/llm/openai.d.ts +12 -0
  137. package/dist/scan/llm/openai.d.ts.map +1 -0
  138. package/dist/scan/llm/openai.js +78 -0
  139. package/dist/scan/llm/openai.js.map +1 -0
  140. package/dist/scan/llm/types.d.ts +23 -0
  141. package/dist/scan/llm/types.d.ts.map +1 -0
  142. package/dist/scan/llm/types.js +3 -0
  143. package/dist/scan/llm/types.js.map +1 -0
  144. package/dist/scan/merge/diff-display.d.ts +11 -0
  145. package/dist/scan/merge/diff-display.d.ts.map +1 -0
  146. package/dist/scan/merge/diff-display.js +72 -0
  147. package/dist/scan/merge/diff-display.js.map +1 -0
  148. package/dist/scan/prompt/agent.d.ts +23 -0
  149. package/dist/scan/prompt/agent.d.ts.map +1 -0
  150. package/dist/scan/prompt/agent.js +95 -0
  151. package/dist/scan/prompt/agent.js.map +1 -0
  152. package/dist/scan/prompt/builder.d.ts +16 -0
  153. package/dist/scan/prompt/builder.d.ts.map +1 -0
  154. package/dist/scan/prompt/builder.js +64 -0
  155. package/dist/scan/prompt/builder.js.map +1 -0
  156. package/dist/scan/prompt/schema.d.ts +7 -0
  157. package/dist/scan/prompt/schema.d.ts.map +1 -0
  158. package/dist/scan/prompt/schema.js +52 -0
  159. package/dist/scan/prompt/schema.js.map +1 -0
  160. package/dist/security/scanner.d.ts +3 -40
  161. package/dist/security/scanner.d.ts.map +1 -1
  162. package/dist/security/scanner.js +3 -169
  163. package/dist/security/scanner.js.map +1 -1
  164. package/dist/utils/fetch.d.ts +9 -0
  165. package/dist/utils/fetch.d.ts.map +1 -0
  166. package/dist/utils/fetch.js +18 -0
  167. package/dist/utils/fetch.js.map +1 -0
  168. package/package.json +15 -12
@@ -0,0 +1,163 @@
1
+ // @nepopsx/cli — Merge logic tests (re-exported from core)
2
+ // These tests exercise the mergeWorkspaceConfig function via the scan pipeline.
3
+ import { strict as assert } from 'node:assert';
4
+ import { describe, it } from 'node:test';
5
+ import { mergeWorkspaceConfig } from '@nepopsx/core';
6
+ const BASE_CONFIG = {
7
+ version: 1,
8
+ workspace: { name: 'test-workspace' },
9
+ services: [
10
+ {
11
+ name: 'api',
12
+ path: './api',
13
+ language: 'typescript',
14
+ framework: 'nestjs',
15
+ },
16
+ {
17
+ name: 'web',
18
+ path: './web',
19
+ language: 'typescript',
20
+ framework: 'nextjs',
21
+ },
22
+ ],
23
+ };
24
+ describe('mergeWorkspaceConfig', () => {
25
+ it('empty field gets filled from scan result', () => {
26
+ const scan = {
27
+ services: [
28
+ {
29
+ service_name: 'api',
30
+ description: 'REST API service',
31
+ confidence: { description: 'high' },
32
+ },
33
+ ],
34
+ };
35
+ const { updated, changedFields } = mergeWorkspaceConfig(BASE_CONFIG, scan, null);
36
+ assert.equal(updated.services[0].description, 'REST API service');
37
+ assert.ok(changedFields.includes('services.api.description'));
38
+ });
39
+ it('user-provenance field gets suggestion, not overwrite', () => {
40
+ const scan = {
41
+ services: [
42
+ {
43
+ service_name: 'api',
44
+ description: 'LLM-suggested description',
45
+ confidence: { description: 'high' },
46
+ },
47
+ ],
48
+ };
49
+ const state = {
50
+ version: 1,
51
+ lastRunAt: new Date().toISOString(),
52
+ provider: 'openai',
53
+ model: 'gpt-4o',
54
+ fields: {
55
+ 'services.api.description': 'user',
56
+ },
57
+ };
58
+ const config = {
59
+ ...BASE_CONFIG,
60
+ services: [
61
+ { ...BASE_CONFIG.services[0], description: 'My custom description' },
62
+ BASE_CONFIG.services[1],
63
+ ],
64
+ };
65
+ const { updated, suggestions, changedFields } = mergeWorkspaceConfig(config, scan, state);
66
+ assert.equal(updated.services[0].description, 'My custom description');
67
+ assert.ok(!changedFields.includes('services.api.description'));
68
+ assert.ok(suggestions.some((s) => s.path === 'services.api.description'));
69
+ assert.equal(suggestions.find((s) => s.path === 'services.api.description')?.suggestedValue, 'LLM-suggested description');
70
+ });
71
+ it('scan-provenance field gets updated with new value', () => {
72
+ const scan = {
73
+ services: [
74
+ {
75
+ service_name: 'api',
76
+ description: 'Updated description from scan',
77
+ confidence: { description: 'high' },
78
+ },
79
+ ],
80
+ };
81
+ const state = {
82
+ version: 1,
83
+ lastRunAt: new Date().toISOString(),
84
+ provider: 'openai',
85
+ model: 'gpt-4o',
86
+ fields: {
87
+ 'services.api.description': 'scan',
88
+ },
89
+ };
90
+ const config = {
91
+ ...BASE_CONFIG,
92
+ services: [
93
+ { ...BASE_CONFIG.services[0], description: 'Previous scan description' },
94
+ BASE_CONFIG.services[1],
95
+ ],
96
+ };
97
+ const { updated, changedFields } = mergeWorkspaceConfig(config, scan, state);
98
+ assert.equal(updated.services[0].description, 'Updated description from scan');
99
+ assert.ok(changedFields.includes('services.api.description'));
100
+ });
101
+ it('commands are only filled when absent', () => {
102
+ const scan = {
103
+ services: [
104
+ {
105
+ service_name: 'api',
106
+ commands: { dev: 'pnpm dev', build: 'pnpm build', test: 'pnpm test' },
107
+ confidence: {},
108
+ },
109
+ ],
110
+ };
111
+ const config = {
112
+ ...BASE_CONFIG,
113
+ services: [
114
+ { ...BASE_CONFIG.services[0], commands: { dev: 'npm run dev' } },
115
+ BASE_CONFIG.services[1],
116
+ ],
117
+ };
118
+ const { updated, suggestions } = mergeWorkspaceConfig(config, scan, null);
119
+ // dev was set by user — should not overwrite
120
+ assert.equal(updated.services[0].commands?.dev, 'npm run dev');
121
+ // build was absent — should fill
122
+ assert.equal(updated.services[0].commands?.build, 'pnpm build');
123
+ // User-set dev should appear as suggestion
124
+ assert.ok(suggestions.some((s) => s.path.endsWith('commands.dev')));
125
+ });
126
+ it('database type-fallback applies when exactly one DB of that type exists', () => {
127
+ const config = {
128
+ ...BASE_CONFIG,
129
+ databases: [{ name: 'main-db', type: 'postgresql' }],
130
+ };
131
+ const scan = {
132
+ services: [{ service_name: 'api', confidence: {} }],
133
+ cross_service: {
134
+ databases: [{ name: 'renamed-pg', type: 'postgresql', description: 'Main database' }],
135
+ },
136
+ };
137
+ // When exactly one PostgreSQL db exists, type-fallback matches it
138
+ const { updated } = mergeWorkspaceConfig(config, scan, null);
139
+ // The existing DB should get the description merged
140
+ const mainDb = updated.databases?.find((d) => d.name === 'main-db');
141
+ assert.ok(mainDb, 'original DB preserved');
142
+ assert.equal(mainDb?.description, 'Main database');
143
+ });
144
+ it('database type-fallback rejected when two DBs share the same type', () => {
145
+ const config = {
146
+ ...BASE_CONFIG,
147
+ databases: [
148
+ { name: 'db-a', type: 'postgresql' },
149
+ { name: 'db-b', type: 'postgresql' },
150
+ ],
151
+ };
152
+ const scan = {
153
+ services: [{ service_name: 'api', confidence: {} }],
154
+ cross_service: {
155
+ databases: [{ name: 'some-pg', type: 'postgresql', description: 'ambiguous' }],
156
+ },
157
+ };
158
+ // Two PostgreSQL dbs — no type fallback, new DB should be added
159
+ const { updated } = mergeWorkspaceConfig(config, scan, null);
160
+ assert.ok(updated.databases?.length === 3, 'new DB added since no unique type match');
161
+ });
162
+ });
163
+ //# sourceMappingURL=merge.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.test.js","sourceRoot":"","sources":["../../../src/scan/__tests__/merge.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import { type License } from '@nepopsx/core';
2
+ export declare function ensureFreshWorkspaceScan(rootDir: string, license: License | null): Promise<boolean>;
3
+ //# sourceMappingURL=auto-bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-bootstrap.d.ts","sourceRoot":"","sources":["../../src/scan/auto-bootstrap.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,OAAO,EAAE,MAAM,eAAe,CAAC;AAczE,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,GAAG,IAAI,GACtB,OAAO,CAAC,OAAO,CAAC,CAwBlB"}
@@ -0,0 +1,30 @@
1
+ import chalk from 'chalk';
2
+ import { TIER_LIMITS, readScanState } from '@nepopsx/core';
3
+ import { scanCommand } from '../commands/scan.js';
4
+ const AUTO_SCAN_MAX_AGE_DAYS = 7;
5
+ function getScanAgeDays(lastRunAt) {
6
+ const lastRun = new Date(lastRunAt).getTime();
7
+ if (Number.isNaN(lastRun)) {
8
+ return Number.POSITIVE_INFINITY;
9
+ }
10
+ return (Date.now() - lastRun) / (1000 * 60 * 60 * 24);
11
+ }
12
+ export async function ensureFreshWorkspaceScan(rootDir, license) {
13
+ if (!license || !TIER_LIMITS[license.tier].scan_allowed) {
14
+ return false;
15
+ }
16
+ const scanState = readScanState(rootDir);
17
+ if (!scanState) {
18
+ console.log(chalk.yellow('\nNo scan state found. Running required bootstrap scan before backend sync...'));
19
+ await scanCommand({ yes: true, resume: true });
20
+ return true;
21
+ }
22
+ const ageDays = getScanAgeDays(scanState.lastRunAt);
23
+ if (ageDays <= AUTO_SCAN_MAX_AGE_DAYS) {
24
+ return false;
25
+ }
26
+ console.log(chalk.yellow(`\nScan state is stale (${Math.floor(ageDays)} day(s) old). Running required refresh scan before backend sync...`));
27
+ await scanCommand({ yes: true, resume: true });
28
+ return true;
29
+ }
30
+ //# sourceMappingURL=auto-bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-bootstrap.js","sourceRoot":"","sources":["../../src/scan/auto-bootstrap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,WAAW,EAAE,aAAa,EAAgB,MAAM,eAAe,CAAC;AACzE,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,MAAM,sBAAsB,GAAG,CAAC,CAAC;AAEjC,SAAS,cAAc,CAAC,SAAiB;IACvC,MAAM,OAAO,GAAG,IAAI,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE,CAAC;IAC9C,IAAI,MAAM,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,OAAO,MAAM,CAAC,iBAAiB,CAAC;IAClC,CAAC;IAED,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,OAAe,EACf,OAAuB;IAEvB,IAAI,CAAC,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;QACxD,OAAO,KAAK,CAAC;IACf,CAAC;IAED,MAAM,SAAS,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,CAAC,SAAS,EAAE,CAAC;QACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,+EAA+E,CAAC,CAAC,CAAC;QAC3G,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QAC/C,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,SAAS,CAAC,CAAC;IACpD,IAAI,OAAO,IAAI,sBAAsB,EAAE,CAAC;QACtC,OAAO,KAAK,CAAC;IACf,CAAC;IAED,OAAO,CAAC,GAAG,CACT,KAAK,CAAC,MAAM,CACV,0BAA0B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,oEAAoE,CAClH,CACF,CAAC;IACF,MAAM,WAAW,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,24 @@
1
+ import type { ProviderName } from './llm/types.js';
2
+ export interface ResolvedScanConfig {
3
+ provider: ProviderName;
4
+ model: string;
5
+ /** API key — resolved from env only, never persisted */
6
+ apiKey?: string;
7
+ /** Custom endpoint for ollama or compatible providers */
8
+ endpoint?: string;
9
+ /** Per-service token budget */
10
+ budget: number;
11
+ /** Total context budget for agent mode */
12
+ agentBudget: number;
13
+ /** Concurrent service analysis */
14
+ parallel: number;
15
+ customs: boolean;
16
+ dryRun: boolean;
17
+ verbose: boolean;
18
+ yes: boolean;
19
+ /** Service name filter (undefined = all services) */
20
+ services?: string[];
21
+ resume: boolean;
22
+ }
23
+ export declare function resolveScanConfig(flags: Record<string, unknown>, cwd?: string): Promise<ResolvedScanConfig>;
24
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/scan/config.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,YAAY,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,wDAAwD;IACxD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,yDAAyD;IACzD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,CAAC;IACf,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,kCAAkC;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,GAAG,EAAE,OAAO,CAAC;IACb,qDAAqD;IACrD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB,MAAM,EAAE,OAAO,CAAC;CACjB;AAkCD,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC9B,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,kBAAkB,CAAC,CA0D7B"}
@@ -0,0 +1,77 @@
1
+ // @nepopsx/cli — Provider config resolution for scan command
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ function readConfigFile(cwd) {
5
+ const configPath = join(cwd, '.nepopsx', 'config.json');
6
+ if (!existsSync(configPath))
7
+ return {};
8
+ try {
9
+ const raw = JSON.parse(readFileSync(configPath, 'utf-8'));
10
+ return (raw.scan ?? {});
11
+ }
12
+ catch {
13
+ return {};
14
+ }
15
+ }
16
+ const PROVIDER_DEFAULTS = {
17
+ openai: 'gpt-4o',
18
+ anthropic: 'claude-3-5-sonnet-20241022',
19
+ ollama: 'llama3.2',
20
+ compatible: 'gpt-4o',
21
+ };
22
+ function parseIntSafe(value, fallback) {
23
+ const n = parseInt(String(value ?? fallback), 10);
24
+ return isNaN(n) ? fallback : n;
25
+ }
26
+ export async function resolveScanConfig(flags, cwd = process.cwd()) {
27
+ const fileConf = readConfigFile(cwd);
28
+ // Resolve provider: CLI flags > env > config file > default (ollama)
29
+ const providerRaw = flags.provider ??
30
+ process.env.NEPOPSX_LLM_PROVIDER ??
31
+ fileConf.provider ??
32
+ 'ollama';
33
+ const provider = providerRaw;
34
+ // Resolve model: CLI flags > env > config file > provider default
35
+ const model = flags.model ??
36
+ process.env.NEPOPSX_LLM_MODEL ??
37
+ fileConf.model ??
38
+ PROVIDER_DEFAULTS[provider] ??
39
+ 'gpt-4o';
40
+ // Resolve API key from env only — never from config file (C3)
41
+ let apiKey;
42
+ if (provider === 'openai' || provider === 'compatible') {
43
+ apiKey =
44
+ flags.apiKey ??
45
+ process.env.OPENAI_API_KEY ??
46
+ process.env.NEPOPSX_LLM_API_KEY;
47
+ }
48
+ else if (provider === 'anthropic') {
49
+ apiKey =
50
+ flags.apiKey ??
51
+ process.env.ANTHROPIC_API_KEY ??
52
+ process.env.NEPOPSX_LLM_API_KEY;
53
+ }
54
+ const endpoint = flags.endpoint ?? fileConf.endpoint;
55
+ const budget = parseIntSafe(flags.budget ?? fileConf.budget, 30_000);
56
+ const agentBudget = parseIntSafe(flags.agentBudget ?? fileConf.agentBudget, 60_000);
57
+ const parallel = parseIntSafe(flags.parallel ?? fileConf.parallel, 1);
58
+ return {
59
+ provider,
60
+ model,
61
+ apiKey,
62
+ endpoint,
63
+ budget,
64
+ agentBudget,
65
+ parallel,
66
+ customs: Boolean(flags.customs),
67
+ dryRun: Boolean(flags.dryRun),
68
+ verbose: Boolean(flags.verbose),
69
+ yes: Boolean(flags.yes),
70
+ // Empty array (Commander default when --service not passed) means "all services"
71
+ services: Array.isArray(flags.service) && flags.service.length > 0
72
+ ? flags.service
73
+ : undefined,
74
+ resume: Boolean(flags.resume),
75
+ };
76
+ }
77
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/scan/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ export interface FileContext {
2
+ /** relativePath → content */
3
+ files: Map<string, string>;
4
+ totalTokens: number;
5
+ /** Files skipped due to budget overflow (for --verbose logging) */
6
+ skippedFiles: string[];
7
+ }
8
+ /**
9
+ * Gather high-signal files from a service directory, respecting a token budget.
10
+ * Uses stat-first estimation to avoid reading files that would exceed budget.
11
+ */
12
+ export declare function gatherContext(servicePath: string, framework: string | undefined, budget: number, ignoreFilter?: (path: string) => boolean): Promise<FileContext>;
13
+ //# sourceMappingURL=gatherer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gatherer.d.ts","sourceRoot":"","sources":["../../../src/scan/context/gatherer.ts"],"names":[],"mappings":"AAQA,MAAM,WAAW,WAAW;IAC1B,6BAA6B;IAC7B,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,mEAAmE;IACnE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AA0BD;;;GAGG;AACH,wBAAsB,aAAa,CACjC,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,MAAM,EAAE,MAAM,EACd,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,GACvC,OAAO,CAAC,WAAW,CAAC,CAuEtB"}
@@ -0,0 +1,97 @@
1
+ // @nepopsx/cli — Stat-first context gatherer with tiered budget enforcement
2
+ import { statSync } from 'node:fs';
3
+ import { readFile } from 'node:fs/promises';
4
+ import { join } from 'node:path';
5
+ import { glob } from 'glob';
6
+ import { getPatternsForFramework } from './patterns.js';
7
+ import { loadIgnoreFilter } from './gitignore.js';
8
+ /** Estimate tokens from file size before reading */
9
+ function estimateTokensFromSize(bytes) {
10
+ return Math.ceil(bytes / 3);
11
+ }
12
+ /** Count actual tokens after reading */
13
+ function countTokens(content) {
14
+ return Math.ceil(content.length / 3.5);
15
+ }
16
+ async function globSafe(pattern, cwd) {
17
+ try {
18
+ return await glob(pattern, { cwd, nodir: true, dot: false });
19
+ }
20
+ catch {
21
+ return [];
22
+ }
23
+ }
24
+ /**
25
+ * Gather high-signal files from a service directory, respecting a token budget.
26
+ * Uses stat-first estimation to avoid reading files that would exceed budget.
27
+ */
28
+ export async function gatherContext(servicePath, framework, budget, ignoreFilter) {
29
+ const filter = ignoreFilter ?? loadIgnoreFilter(servicePath);
30
+ const patterns = getPatternsForFramework(framework);
31
+ const files = new Map();
32
+ const skippedFiles = [];
33
+ let totalTokens = 0;
34
+ async function processPatterns(globs, maxFiles) {
35
+ // Collect all matching paths
36
+ const matchedPaths = new Set();
37
+ for (const pattern of globs) {
38
+ const matches = await globSafe(pattern, servicePath);
39
+ for (const m of matches) {
40
+ matchedPaths.add(m);
41
+ }
42
+ }
43
+ // Stat-first: estimate tokens for all matched files before reading any
44
+ const candidates = [];
45
+ for (const relPath of matchedPaths) {
46
+ if (files.has(relPath))
47
+ continue; // already included
48
+ if (filter(relPath))
49
+ continue; // gitignored
50
+ const absPath = join(servicePath, relPath);
51
+ try {
52
+ const stats = statSync(absPath);
53
+ if (!stats.isFile())
54
+ continue;
55
+ const estimatedTokens = estimateTokensFromSize(stats.size);
56
+ candidates.push({ absPath, relPath, estimatedTokens });
57
+ }
58
+ catch {
59
+ // File may have disappeared between glob and stat
60
+ }
61
+ }
62
+ // Sort by path depth ascending, then by estimated size ascending
63
+ candidates.sort((a, b) => {
64
+ const depthA = a.relPath.split('/').length;
65
+ const depthB = b.relPath.split('/').length;
66
+ return depthA !== depthB ? depthA - depthB : a.estimatedTokens - b.estimatedTokens;
67
+ });
68
+ // Apply maxFiles limit for tier3
69
+ const limited = maxFiles !== undefined ? candidates.slice(0, maxFiles) : candidates;
70
+ // Read files that fit within remaining budget
71
+ for (const candidate of limited) {
72
+ if (totalTokens + candidate.estimatedTokens > budget) {
73
+ skippedFiles.push(candidate.relPath);
74
+ continue;
75
+ }
76
+ try {
77
+ const content = await readFile(candidate.absPath, 'utf-8');
78
+ const actualTokens = countTokens(content);
79
+ if (totalTokens + actualTokens > budget) {
80
+ skippedFiles.push(candidate.relPath);
81
+ continue;
82
+ }
83
+ files.set(candidate.relPath, content);
84
+ totalTokens += actualTokens;
85
+ }
86
+ catch {
87
+ // Skip unreadable files
88
+ }
89
+ }
90
+ }
91
+ // Process tiers in priority order
92
+ await processPatterns(patterns.tier1);
93
+ await processPatterns(patterns.tier2);
94
+ await processPatterns(patterns.tier3, 3);
95
+ return { files, totalTokens, skippedFiles };
96
+ }
97
+ //# sourceMappingURL=gatherer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gatherer.js","sourceRoot":"","sources":["../../../src/scan/context/gatherer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Create a filter function for a service directory using .gitignore rules.
3
+ * Falls back to default exclusions if no .gitignore exists.
4
+ *
5
+ * @param serviceDir Absolute path to the service root directory
6
+ * @returns A function: (relativePath: string) => boolean — true if path should be ignored
7
+ */
8
+ export declare function loadIgnoreFilter(serviceDir: string): (path: string) => boolean;
9
+ //# sourceMappingURL=gitignore.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.d.ts","sourceRoot":"","sources":["../../../src/scan/context/gitignore.ts"],"names":[],"mappings":"AAqBA;;;;;;GAMG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAuB9E"}
@@ -0,0 +1,50 @@
1
+ // @nepopsx/cli — .gitignore parsing using the `ignore` package
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import ignore from 'ignore';
5
+ const DEFAULT_EXCLUDES = [
6
+ 'node_modules',
7
+ 'dist',
8
+ '.git',
9
+ '__pycache__',
10
+ 'vendor',
11
+ 'build',
12
+ '.next',
13
+ '.nuxt',
14
+ 'coverage',
15
+ 'target',
16
+ '.cache',
17
+ '.turbo',
18
+ '*.log',
19
+ ];
20
+ /**
21
+ * Create a filter function for a service directory using .gitignore rules.
22
+ * Falls back to default exclusions if no .gitignore exists.
23
+ *
24
+ * @param serviceDir Absolute path to the service root directory
25
+ * @returns A function: (relativePath: string) => boolean — true if path should be ignored
26
+ */
27
+ export function loadIgnoreFilter(serviceDir) {
28
+ const ig = ignore();
29
+ // Add default exclusions
30
+ ig.add(DEFAULT_EXCLUDES);
31
+ // Add .gitignore if it exists
32
+ const gitignorePath = join(serviceDir, '.gitignore');
33
+ if (existsSync(gitignorePath)) {
34
+ try {
35
+ const content = readFileSync(gitignorePath, 'utf-8');
36
+ ig.add(content);
37
+ }
38
+ catch {
39
+ // Silently ignore read errors
40
+ }
41
+ }
42
+ return (relPath) => {
43
+ // The `ignore` package requires paths without leading slashes
44
+ const normalized = relPath.startsWith('/') ? relPath.slice(1) : relPath;
45
+ if (!normalized)
46
+ return false;
47
+ return ig.ignores(normalized);
48
+ };
49
+ }
50
+ //# sourceMappingURL=gitignore.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"gitignore.js","sourceRoot":"","sources":["../../../src/scan/context/gitignore.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAC/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,MAAM,MAAM,QAAQ,CAAC;AAE5B,MAAM,gBAAgB,GAAG;IACvB,cAAc;IACd,MAAM;IACN,MAAM;IACN,aAAa;IACb,QAAQ;IACR,OAAO;IACP,OAAO;IACP,OAAO;IACP,UAAU;IACV,QAAQ;IACR,QAAQ;IACR,QAAQ;IACR,OAAO;CACR,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,UAAU,gBAAgB,CAAC,UAAkB;IACjD,MAAM,EAAE,GAAG,MAAM,EAAE,CAAC;IAEpB,yBAAyB;IACzB,EAAE,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAEzB,8BAA8B;IAC9B,MAAM,aAAa,GAAG,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC;IACrD,IAAI,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;QAC9B,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,YAAY,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC;YACrD,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QAClB,CAAC;QAAC,MAAM,CAAC;YACP,8BAA8B;QAChC,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAe,EAAE,EAAE;QACzB,8DAA8D;QAC9D,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC;QACxE,IAAI,CAAC,UAAU;YAAE,OAAO,KAAK,CAAC;QAC9B,OAAO,EAAE,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAChC,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ export interface TieredPatterns {
2
+ /** Always include — entry points, config files, manifests */
3
+ tier1: string[];
4
+ /** Include if budget allows — key modules, route definitions */
5
+ tier2: string[];
6
+ /** Sample if budget allows — representative implementation files (max 3) */
7
+ tier3: string[];
8
+ }
9
+ /**
10
+ * Get tiered file patterns for a given framework.
11
+ * The returned patterns are merged with the base patterns.
12
+ */
13
+ export declare function getPatternsForFramework(framework: string | undefined): TieredPatterns;
14
+ //# sourceMappingURL=patterns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"patterns.d.ts","sourceRoot":"","sources":["../../../src/scan/context/patterns.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,cAAc;IAC7B,6DAA6D;IAC7D,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,gEAAgE;IAChE,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,4EAA4E;IAC5E,KAAK,EAAE,MAAM,EAAE,CAAC;CACjB;AAoJD;;;GAGG;AACH,wBAAgB,uBAAuB,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,cAAc,CASrF"}