@nepopsx/cli 0.0.2 → 0.0.4

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 (106) hide show
  1. package/dist/commands/install.d.ts.map +1 -1
  2. package/dist/commands/install.js +113 -24
  3. package/dist/commands/install.js.map +1 -1
  4. package/dist/commands/login.d.ts +7 -0
  5. package/dist/commands/login.d.ts.map +1 -0
  6. package/dist/commands/login.js +108 -0
  7. package/dist/commands/login.js.map +1 -0
  8. package/dist/commands/scan.d.ts +2 -0
  9. package/dist/commands/scan.d.ts.map +1 -0
  10. package/dist/commands/scan.js +405 -0
  11. package/dist/commands/scan.js.map +1 -0
  12. package/dist/commands/sync.d.ts +2 -0
  13. package/dist/commands/sync.d.ts.map +1 -1
  14. package/dist/commands/sync.js +68 -120
  15. package/dist/commands/sync.js.map +1 -1
  16. package/dist/generator/builtin-templates.d.ts +2 -1
  17. package/dist/generator/builtin-templates.d.ts.map +1 -1
  18. package/dist/generator/builtin-templates.js +161 -2
  19. package/dist/generator/builtin-templates.js.map +1 -1
  20. package/dist/generator/package-renderer.d.ts +37 -0
  21. package/dist/generator/package-renderer.d.ts.map +1 -0
  22. package/dist/generator/package-renderer.js +143 -0
  23. package/dist/generator/package-renderer.js.map +1 -0
  24. package/dist/generator/render.d.ts +11 -54
  25. package/dist/generator/render.d.ts.map +1 -1
  26. package/dist/generator/render.js +18 -174
  27. package/dist/generator/render.js.map +1 -1
  28. package/dist/index.js +32 -1
  29. package/dist/index.js.map +1 -1
  30. package/dist/licensing/fingerprint.test.d.ts +2 -0
  31. package/dist/licensing/fingerprint.test.d.ts.map +1 -0
  32. package/dist/licensing/fingerprint.test.js +41 -0
  33. package/dist/licensing/fingerprint.test.js.map +1 -0
  34. package/dist/licensing/installer.d.ts +43 -0
  35. package/dist/licensing/installer.d.ts.map +1 -0
  36. package/dist/licensing/installer.js +98 -0
  37. package/dist/licensing/installer.js.map +1 -0
  38. package/dist/scan/__tests__/context-gatherer.test.d.ts +2 -0
  39. package/dist/scan/__tests__/context-gatherer.test.d.ts.map +1 -0
  40. package/dist/scan/__tests__/context-gatherer.test.js +111 -0
  41. package/dist/scan/__tests__/context-gatherer.test.js.map +1 -0
  42. package/dist/scan/__tests__/merge.test.d.ts +2 -0
  43. package/dist/scan/__tests__/merge.test.d.ts.map +1 -0
  44. package/dist/scan/__tests__/merge.test.js +163 -0
  45. package/dist/scan/__tests__/merge.test.js.map +1 -0
  46. package/dist/scan/config.d.ts +24 -0
  47. package/dist/scan/config.d.ts.map +1 -0
  48. package/dist/scan/config.js +77 -0
  49. package/dist/scan/config.js.map +1 -0
  50. package/dist/scan/context/gatherer.d.ts +13 -0
  51. package/dist/scan/context/gatherer.d.ts.map +1 -0
  52. package/dist/scan/context/gatherer.js +97 -0
  53. package/dist/scan/context/gatherer.js.map +1 -0
  54. package/dist/scan/context/gitignore.d.ts +9 -0
  55. package/dist/scan/context/gitignore.d.ts.map +1 -0
  56. package/dist/scan/context/gitignore.js +50 -0
  57. package/dist/scan/context/gitignore.js.map +1 -0
  58. package/dist/scan/context/patterns.d.ts +14 -0
  59. package/dist/scan/context/patterns.d.ts.map +1 -0
  60. package/dist/scan/context/patterns.js +159 -0
  61. package/dist/scan/context/patterns.js.map +1 -0
  62. package/dist/scan/customs/writer.d.ts +6 -0
  63. package/dist/scan/customs/writer.d.ts.map +1 -0
  64. package/dist/scan/customs/writer.js +149 -0
  65. package/dist/scan/customs/writer.js.map +1 -0
  66. package/dist/scan/llm/anthropic.d.ts +11 -0
  67. package/dist/scan/llm/anthropic.d.ts.map +1 -0
  68. package/dist/scan/llm/anthropic.js +98 -0
  69. package/dist/scan/llm/anthropic.js.map +1 -0
  70. package/dist/scan/llm/factory.d.ts +4 -0
  71. package/dist/scan/llm/factory.d.ts.map +1 -0
  72. package/dist/scan/llm/factory.js +20 -0
  73. package/dist/scan/llm/factory.js.map +1 -0
  74. package/dist/scan/llm/ollama.d.ts +11 -0
  75. package/dist/scan/llm/ollama.d.ts.map +1 -0
  76. package/dist/scan/llm/ollama.js +73 -0
  77. package/dist/scan/llm/ollama.js.map +1 -0
  78. package/dist/scan/llm/openai.d.ts +12 -0
  79. package/dist/scan/llm/openai.d.ts.map +1 -0
  80. package/dist/scan/llm/openai.js +87 -0
  81. package/dist/scan/llm/openai.js.map +1 -0
  82. package/dist/scan/llm/types.d.ts +23 -0
  83. package/dist/scan/llm/types.d.ts.map +1 -0
  84. package/dist/scan/llm/types.js +3 -0
  85. package/dist/scan/llm/types.js.map +1 -0
  86. package/dist/scan/merge/diff-display.d.ts +11 -0
  87. package/dist/scan/merge/diff-display.d.ts.map +1 -0
  88. package/dist/scan/merge/diff-display.js +72 -0
  89. package/dist/scan/merge/diff-display.js.map +1 -0
  90. package/dist/scan/prompt/agent.d.ts +23 -0
  91. package/dist/scan/prompt/agent.d.ts.map +1 -0
  92. package/dist/scan/prompt/agent.js +95 -0
  93. package/dist/scan/prompt/agent.js.map +1 -0
  94. package/dist/scan/prompt/builder.d.ts +16 -0
  95. package/dist/scan/prompt/builder.d.ts.map +1 -0
  96. package/dist/scan/prompt/builder.js +64 -0
  97. package/dist/scan/prompt/builder.js.map +1 -0
  98. package/dist/scan/prompt/schema.d.ts +7 -0
  99. package/dist/scan/prompt/schema.d.ts.map +1 -0
  100. package/dist/scan/prompt/schema.js +52 -0
  101. package/dist/scan/prompt/schema.js.map +1 -0
  102. package/dist/security/scanner.d.ts +3 -40
  103. package/dist/security/scanner.d.ts.map +1 -1
  104. package/dist/security/scanner.js +3 -169
  105. package/dist/security/scanner.js.map +1 -1
  106. package/package.json +15 -12
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fingerprint.test.js","sourceRoot":"","sources":["../../src/licensing/fingerprint.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAE5G,MAAM,WAAW,GAAG,cAAc,CAAC;AAEnC,MAAM,qBAAqB,GAAG;;;;;;;;;;;;;;CAc7B,CAAC;AAEF,IAAI,CAAC,wEAAwE,EAAE,GAAG,EAAE;IAClF,MAAM,QAAQ,GAAG,kBAAkB,CAAC,qBAAqB,EAAE,WAAW,CAAC,CAAC;IACxE,MAAM,UAAU,GAAG,eAAe,CAAC,WAAW,CAAC,CAAC;IAEhD,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;IAExD,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IAC1C,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,YAAY,EAAE,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC,CAAC;IAC9E,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC;IAErC,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,8DAA8D,CAAC,CAAC;AACzF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2EAA2E,EAAE,GAAG,EAAE;IACrF,MAAM,cAAc,GAAG;;;;;;;CAOxB,CAAC;IAEA,MAAM,QAAQ,GAAG,kBAAkB,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,QAAQ,CAAC,EAAE,eAAe,CAAC,WAAW,CAAC,CAAC,CAAC;AAC5E,CAAC,CAAC,CAAC"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Agent bundle installer — downloads, decrypts, verifies, and writes
3
+ * agent template files from the NEPOPSX API.
4
+ *
5
+ * Flow:
6
+ * 1. downloadBundle — POST /v1/templates/:agent/:version (auth: Bearer <key>)
7
+ * 2. decryptBundle — AES-256-GCM, key = SHA-256(licenseKey + ":nepopsx-template-key")
8
+ * 3. verifyBundleSha — SHA-256 of raw JSON must match server-provided sha256
9
+ * 4. writeAgentFiles — atomic write via temp + rename into .github/<relative-path>
10
+ */
11
+ import type { License } from "@nepopsx/core";
12
+ export interface EncryptedBundle {
13
+ payload: string;
14
+ iv: string;
15
+ tag: string;
16
+ agent: string;
17
+ version: string;
18
+ sha256: string;
19
+ }
20
+ export interface InstalledFile {
21
+ path: string;
22
+ }
23
+ /**
24
+ * Download an encrypted template bundle from the NEPOPSX API.
25
+ */
26
+ export declare function downloadBundle(cwd: string, license: License, agent: string, version: string): Promise<EncryptedBundle>;
27
+ /**
28
+ * Decrypt an AES-256-GCM bundle using the license key.
29
+ * Returns the raw JSON string (not yet parsed).
30
+ */
31
+ export declare function decryptBundle(bundle: EncryptedBundle, licenseKey: string): string;
32
+ /**
33
+ * Verify that the decrypted JSON matches the server's sha256.
34
+ * Throws if the digest does not match.
35
+ */
36
+ export declare function verifyBundleSha(decryptedJson: string, expectedSha: string): void;
37
+ /**
38
+ * Atomically write agent template files into .github/<relative-path>.
39
+ * Uses a tmp-file + rename pattern to avoid partial writes.
40
+ * Returns the list of written file paths.
41
+ */
42
+ export declare function writeAgentFiles(cwd: string, templates: Record<string, string>): InstalledFile[];
43
+ //# sourceMappingURL=installer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/licensing/installer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAM7C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAC;CACd;AAID;;GAEG;AACH,wBAAsB,cAAc,CAClC,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,OAAO,EAChB,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,GACd,OAAO,CAAC,eAAe,CAAC,CAiC1B;AAID;;;GAGG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,eAAe,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAcjF;AAID;;;GAGG;AACH,wBAAgB,eAAe,CAAC,aAAa,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,IAAI,CAOhF;AAID;;;;GAIG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAChC,aAAa,EAAE,CAmBjB"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Agent bundle installer — downloads, decrypts, verifies, and writes
3
+ * agent template files from the NEPOPSX API.
4
+ *
5
+ * Flow:
6
+ * 1. downloadBundle — POST /v1/templates/:agent/:version (auth: Bearer <key>)
7
+ * 2. decryptBundle — AES-256-GCM, key = SHA-256(licenseKey + ":nepopsx-template-key")
8
+ * 3. verifyBundleSha — SHA-256 of raw JSON must match server-provided sha256
9
+ * 4. writeAgentFiles — atomic write via temp + rename into .github/<relative-path>
10
+ */
11
+ import { createDecipheriv, createHash } from "node:crypto";
12
+ import { existsSync, mkdirSync, renameSync, writeFileSync } from "node:fs";
13
+ import { dirname, join } from "node:path";
14
+ import { resolveApiBase } from "../config/api-config.js";
15
+ const REQUEST_TIMEOUT_MS = 30_000;
16
+ // ─── Download ───────────────────────────────────────────────
17
+ /**
18
+ * Download an encrypted template bundle from the NEPOPSX API.
19
+ */
20
+ export async function downloadBundle(cwd, license, agent, version) {
21
+ const apiBase = resolveApiBase(cwd);
22
+ const url = `${apiBase}/templates/${encodeURIComponent(agent)}/${encodeURIComponent(version)}`;
23
+ const controller = new AbortController();
24
+ const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
25
+ let response;
26
+ try {
27
+ response = await fetch(url, {
28
+ method: "POST",
29
+ headers: {
30
+ Authorization: license.key,
31
+ "Content-Type": "application/json",
32
+ },
33
+ body: JSON.stringify({
34
+ machine_id: license.machine_id,
35
+ workspace: license.workspace ?? "",
36
+ }),
37
+ signal: controller.signal,
38
+ });
39
+ }
40
+ finally {
41
+ clearTimeout(timer);
42
+ }
43
+ if (!response.ok) {
44
+ const body = await response.text().catch(() => "");
45
+ throw new Error(`Bundle download failed (${response.status}): ${body || response.statusText}`);
46
+ }
47
+ return response.json();
48
+ }
49
+ // ─── Decrypt ────────────────────────────────────────────────
50
+ /**
51
+ * Decrypt an AES-256-GCM bundle using the license key.
52
+ * Returns the raw JSON string (not yet parsed).
53
+ */
54
+ export function decryptBundle(bundle, licenseKey) {
55
+ const key = createHash("sha256")
56
+ .update(`${licenseKey}:nepopsx-template-key`)
57
+ .digest();
58
+ const iv = Buffer.from(bundle.iv, "base64");
59
+ const tag = Buffer.from(bundle.tag, "base64");
60
+ const encrypted = Buffer.from(bundle.payload, "base64");
61
+ const decipher = createDecipheriv("aes-256-gcm", key, iv);
62
+ decipher.setAuthTag(tag);
63
+ const decrypted = Buffer.concat([decipher.update(encrypted), decipher.final()]);
64
+ return decrypted.toString("utf-8");
65
+ }
66
+ // ─── Verify ─────────────────────────────────────────────────
67
+ /**
68
+ * Verify that the decrypted JSON matches the server's sha256.
69
+ * Throws if the digest does not match.
70
+ */
71
+ export function verifyBundleSha(decryptedJson, expectedSha) {
72
+ const actual = createHash("sha256").update(decryptedJson, "utf-8").digest("hex");
73
+ if (actual !== expectedSha) {
74
+ throw new Error(`Bundle integrity check failed — sha256 mismatch\n expected: ${expectedSha}\n actual: ${actual}`);
75
+ }
76
+ }
77
+ // ─── Write ──────────────────────────────────────────────────
78
+ /**
79
+ * Atomically write agent template files into .github/<relative-path>.
80
+ * Uses a tmp-file + rename pattern to avoid partial writes.
81
+ * Returns the list of written file paths.
82
+ */
83
+ export function writeAgentFiles(cwd, templates) {
84
+ const written = [];
85
+ for (const [relativePath, content] of Object.entries(templates)) {
86
+ const dest = join(cwd, ".github", relativePath);
87
+ const destDir = dirname(dest);
88
+ const tmp = `${dest}.tmp`;
89
+ if (!existsSync(destDir)) {
90
+ mkdirSync(destDir, { recursive: true });
91
+ }
92
+ writeFileSync(tmp, content, "utf-8");
93
+ renameSync(tmp, dest);
94
+ written.push({ path: join(".github", relativePath) });
95
+ }
96
+ return written;
97
+ }
98
+ //# sourceMappingURL=installer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"installer.js","sourceRoot":"","sources":["../../src/licensing/installer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=context-gatherer.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-gatherer.test.d.ts","sourceRoot":"","sources":["../../../src/scan/__tests__/context-gatherer.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,111 @@
1
+ // @nepopsx/cli — Context gatherer tests
2
+ import { strict as assert } from 'node:assert';
3
+ import { describe, it, before, after } from 'node:test';
4
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from 'node:fs';
5
+ import { tmpdir } from 'node:os';
6
+ import { join } from 'node:path';
7
+ import { gatherContext } from '../context/gatherer.js';
8
+ import { loadIgnoreFilter } from '../context/gitignore.js';
9
+ function createFixture(dir, files) {
10
+ for (const [relPath, content] of Object.entries(files)) {
11
+ const absPath = join(dir, relPath);
12
+ mkdirSync(join(absPath, '..'), { recursive: true });
13
+ writeFileSync(absPath, content, 'utf-8');
14
+ }
15
+ }
16
+ describe('gatherContext', () => {
17
+ let tmpDir;
18
+ before(() => {
19
+ tmpDir = mkdtempSync(join(tmpdir(), 'nepopsx-test-'));
20
+ });
21
+ after(() => {
22
+ rmSync(tmpDir, { recursive: true, force: true });
23
+ });
24
+ it('stat-first budget enforcement: files exceeding budget are skipped', async () => {
25
+ const svcDir = join(tmpDir, 'budget-test');
26
+ mkdirSync(svcDir, { recursive: true });
27
+ // Create a package.json (small) and a large src file that nestjs patterns will pick up
28
+ const largeContent = 'x'.repeat(10_000);
29
+ createFixture(svcDir, {
30
+ 'package.json': JSON.stringify({ name: 'test', version: '1.0.0' }),
31
+ 'tsconfig.json': '{}',
32
+ 'src/app.module.ts': 'import { Module } from "@nestjs/common"; @Module({}) export class AppModule {}',
33
+ });
34
+ // A very large service file that nestjs tier2 patterns will pick up
35
+ mkdirSync(join(svcDir, 'src'), { recursive: true });
36
+ writeFileSync(join(svcDir, 'src', 'app.service.ts'), largeContent, 'utf-8');
37
+ // Budget of 200 tokens — src/app.service.ts (~2857 tokens) must be skipped
38
+ const result = await gatherContext(svcDir, 'nestjs', 200);
39
+ assert.ok(result.files.size > 0, 'at least one file included');
40
+ assert.ok(!result.files.has('src/app.service.ts'), 'large file excluded from budget');
41
+ assert.ok(result.skippedFiles.includes('src/app.service.ts'), 'large file listed in skippedFiles');
42
+ assert.ok(result.totalTokens <= 200, `total tokens within budget: ${result.totalTokens}`);
43
+ });
44
+ it('gitignore patterns are respected', async () => {
45
+ const svcDir = join(tmpDir, 'gitignore-test');
46
+ mkdirSync(svcDir, { recursive: true });
47
+ createFixture(svcDir, {
48
+ '.gitignore': 'node_modules/\ndist/\n*.log\n',
49
+ 'README.md': '# Test service',
50
+ 'package.json': '{"name":"test"}',
51
+ 'dist/index.js': 'compiled code',
52
+ 'node_modules/lodash/index.js': 'library code',
53
+ 'debug.log': 'log output',
54
+ });
55
+ const ignoreFilter = loadIgnoreFilter(svcDir);
56
+ const result = await gatherContext(svcDir, undefined, 50_000, ignoreFilter);
57
+ assert.ok(!result.files.has('dist/index.js'), 'dist/ is ignored');
58
+ assert.ok(!result.files.has('node_modules/lodash/index.js'), 'node_modules/ is ignored');
59
+ assert.ok(!result.files.has('debug.log'), '*.log is ignored');
60
+ assert.ok(result.files.has('README.md'), 'README.md is included');
61
+ });
62
+ it('empty service directory returns empty context', async () => {
63
+ const svcDir = join(tmpDir, 'empty-test');
64
+ mkdirSync(svcDir, { recursive: true });
65
+ const result = await gatherContext(svcDir, undefined, 50_000);
66
+ assert.equal(result.files.size, 0);
67
+ assert.equal(result.totalTokens, 0);
68
+ assert.equal(result.skippedFiles.length, 0);
69
+ });
70
+ it('framework patterns include framework-specific files', async () => {
71
+ const svcDir = join(tmpDir, 'nestjs-test');
72
+ mkdirSync(svcDir, { recursive: true });
73
+ createFixture(svcDir, {
74
+ 'package.json': '{"name":"api","dependencies":{"@nestjs/core":"^10"}}',
75
+ 'tsconfig.json': '{"compilerOptions":{}}',
76
+ 'src/app.module.ts': 'import { Module } from "@nestjs/common";',
77
+ 'src/main.ts': 'import { NestFactory } from "@nestjs/core";',
78
+ });
79
+ const result = await gatherContext(svcDir, 'nestjs', 50_000);
80
+ assert.ok(result.files.has('package.json'), 'package.json included');
81
+ assert.ok(result.files.has('src/app.module.ts'), 'app.module.ts included');
82
+ assert.ok(result.files.has('src/main.ts'), 'main.ts included');
83
+ });
84
+ });
85
+ describe('loadIgnoreFilter', () => {
86
+ let tmpDir;
87
+ before(() => {
88
+ tmpDir = mkdtempSync(join(tmpdir(), 'nepopsx-gitignore-test-'));
89
+ });
90
+ after(() => {
91
+ rmSync(tmpDir, { recursive: true, force: true });
92
+ });
93
+ it('default exclusions apply when no .gitignore exists', () => {
94
+ const svcDir = join(tmpDir, 'no-gitignore');
95
+ mkdirSync(svcDir, { recursive: true });
96
+ const filter = loadIgnoreFilter(svcDir);
97
+ assert.ok(filter('node_modules/index.js'), 'node_modules is excluded by default');
98
+ assert.ok(filter('dist/bundle.js'), 'dist is excluded by default');
99
+ assert.ok(!filter('src/app.ts'), 'src/app.ts is not excluded');
100
+ });
101
+ it('custom .gitignore patterns are applied', () => {
102
+ const svcDir = join(tmpDir, 'custom-gitignore');
103
+ mkdirSync(svcDir, { recursive: true });
104
+ writeFileSync(join(svcDir, '.gitignore'), 'secrets/\n*.env\n', 'utf-8');
105
+ const filter = loadIgnoreFilter(svcDir);
106
+ assert.ok(filter('secrets/api-key.txt'), 'secrets/ is excluded');
107
+ assert.ok(filter('prod.env'), '*.env is excluded');
108
+ assert.ok(!filter('src/index.ts'), 'src/index.ts is not excluded');
109
+ });
110
+ });
111
+ //# sourceMappingURL=context-gatherer.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-gatherer.test.js","sourceRoot":"","sources":["../../../src/scan/__tests__/context-gatherer.test.ts"],"names":[],"mappings":"AAAA,wCAAwC;AACxC,OAAO,EAAE,MAAM,IAAI,MAAM,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AACxE,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,SAAS,aAAa,CAAC,GAAW,EAAE,KAA6B;IAC/D,KAAK,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACvD,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC;QACnC,SAAS,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,aAAa,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;AACH,CAAC;AAED,QAAQ,CAAC,eAAe,EAAE,GAAG,EAAE;IAC7B,IAAI,MAAc,CAAC;IAEnB,MAAM,CAAC,GAAG,EAAE;QACV,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,eAAe,CAAC,CAAC,CAAC;IACxD,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,GAAG,EAAE;QACT,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;QACjF,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAC3C,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,uFAAuF;QACvF,MAAM,YAAY,GAAG,GAAG,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAExC,aAAa,CAAC,MAAM,EAAE;YACpB,cAAc,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;YAClE,eAAe,EAAE,IAAI;YACrB,mBAAmB,EAAE,gFAAgF;SACtG,CAAC,CAAC;QAEH,oEAAoE;QACpE,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACpD,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,KAAK,EAAE,gBAAgB,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,CAAC;QAE5E,2EAA2E;QAC3E,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,GAAG,CAAC,CAAC;QAE1D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,GAAG,CAAC,EAAE,4BAA4B,CAAC,CAAC;QAC/D,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,iCAAiC,CAAC,CAAC;QACtF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,mCAAmC,CAAC,CAAC;QACnG,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,WAAW,IAAI,GAAG,EAAE,+BAA+B,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;IAC5F,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,kCAAkC,EAAE,KAAK,IAAI,EAAE;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;QAC9C,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,aAAa,CAAC,MAAM,EAAE;YACpB,YAAY,EAAE,+BAA+B;YAC7C,WAAW,EAAE,gBAAgB;YAC7B,cAAc,EAAE,iBAAiB;YACjC,eAAe,EAAE,eAAe;YAChC,8BAA8B,EAAE,cAAc;YAC9C,WAAW,EAAE,YAAY;SAC1B,CAAC,CAAC;QAEH,MAAM,YAAY,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,YAAY,CAAC,CAAC;QAE5E,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,eAAe,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAClE,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,8BAA8B,CAAC,EAAE,0BAA0B,CAAC,CAAC;QACzF,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,kBAAkB,CAAC,CAAC;QAC9D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,uBAAuB,CAAC,CAAC;IACpE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;QAC1C,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAE9D,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACnC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;QAC3C,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,aAAa,CAAC,MAAM,EAAE;YACpB,cAAc,EAAE,sDAAsD;YACtE,eAAe,EAAE,wBAAwB;YACzC,mBAAmB,EAAE,0CAA0C;YAC/D,aAAa,EAAE,6CAA6C;SAC7D,CAAC,CAAC;QAEH,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAE7D,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,EAAE,uBAAuB,CAAC,CAAC;QACrE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE,wBAAwB,CAAC,CAAC;QAC3E,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;IAChC,IAAI,MAAc,CAAC;IAEnB,MAAM,CAAC,GAAG,EAAE;QACV,MAAM,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,yBAAyB,CAAC,CAAC,CAAC;IAClE,CAAC,CAAC,CAAC;IAEH,KAAK,CAAC,GAAG,EAAE;QACT,MAAM,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;QAC5C,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvC,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,uBAAuB,CAAC,EAAE,qCAAqC,CAAC,CAAC;QAClF,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,gBAAgB,CAAC,EAAE,6BAA6B,CAAC,CAAC;QACnE,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,YAAY,CAAC,EAAE,4BAA4B,CAAC,CAAC;IACjE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,MAAM,GAAG,IAAI,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;QAChD,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACvC,aAAa,CAAC,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC,EAAE,mBAAmB,EAAE,OAAO,CAAC,CAAC;QAExE,MAAM,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC;QAExC,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,qBAAqB,CAAC,EAAE,sBAAsB,CAAC,CAAC;QACjE,MAAM,CAAC,EAAE,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACnD,MAAM,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC,EAAE,8BAA8B,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=merge.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"merge.test.d.ts","sourceRoot":"","sources":["../../../src/scan/__tests__/merge.test.ts"],"names":[],"mappings":""}
@@ -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,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"}