@moda-ai/cli 1.36.0 → 1.37.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.
- package/README.md +2 -0
- package/dist/{cli-0p6n391h.js → cli-07tv75te.js} +21 -6
- package/dist/cli-0v6na3yp.js +35 -0
- package/dist/cli-c5c6f0yw.js +128 -0
- package/dist/{cli-7qkhy41j.js → cli-ert1sbbt.js} +112 -444
- package/dist/{cli-drabjmaj.js → cli-q79sq80a.js} +1 -35
- package/dist/{cli-scjxgaba.js → cli-qgf2cz2v.js} +2 -2
- package/dist/cli-ssfq84k5.js +393 -0
- package/dist/{cli-yz99t7n8.js → cli-strngwn3.js} +4 -2
- package/dist/cli.js +209 -10
- package/dist/{harness-github-actions-tk17mnge.js → harness-github-actions-qz5d22rz.js} +3 -2
- package/dist/{harness-heasma1f.js → harness-n2xgzz31.js} +3 -2
- package/dist/{index-pf2hvadv.js → index-vmmfya6b.js} +167 -207
- package/dist/prompt-source-mcp.js +3 -2
- package/dist/{provision-d242y9qd.js → provision-spwn5t41.js} +4 -3
- package/dist/skills-bundled-mdmhzfsr.js +14 -0
- package/package.json +1 -1
- package/skills/integration/index.json +3 -3
- package/skills/integration/integration-python-langchain/EXAMPLE.md +1 -1
- package/skills/integration/integration-python-langchain/SKILL.md +44 -11
- package/skills/moda-cli/SKILL.md +2 -0
|
@@ -1,16 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
-
SKILL_SOURCE_INTEGRATION_BUNDLED,
|
|
3
2
|
buildDoctorReport,
|
|
4
|
-
claudeSkillToCursorRule,
|
|
5
|
-
detectAgents,
|
|
6
3
|
initPrompts,
|
|
7
|
-
installRules,
|
|
8
|
-
packageAssetPath,
|
|
9
|
-
readSkillsManifest,
|
|
10
4
|
runPromptSync,
|
|
11
|
-
runSkillSync
|
|
12
|
-
|
|
13
|
-
} from "./cli-7qkhy41j.js";
|
|
5
|
+
runSkillSync
|
|
6
|
+
} from "./cli-ert1sbbt.js";
|
|
14
7
|
import {
|
|
15
8
|
codingAgentDisplayName,
|
|
16
9
|
describeCodingAgentEvent,
|
|
@@ -18,6 +11,7 @@ import {
|
|
|
18
11
|
describeSyncTenantLabel,
|
|
19
12
|
detectAvailableCodingAgents,
|
|
20
13
|
isDeterministicLocalAnalyst,
|
|
14
|
+
isRecoverableRemoteRun,
|
|
21
15
|
pollRemoteAnalyzeUntilDone,
|
|
22
16
|
renderInitBanner,
|
|
23
17
|
renderInitComplete,
|
|
@@ -28,14 +22,23 @@ import {
|
|
|
28
22
|
runHarnessCommand,
|
|
29
23
|
startRemoteAnalyze,
|
|
30
24
|
stripAnsi
|
|
31
|
-
} from "./cli-
|
|
25
|
+
} from "./cli-07tv75te.js";
|
|
26
|
+
import {
|
|
27
|
+
bundledSkillsDir,
|
|
28
|
+
installIntegrationSkill,
|
|
29
|
+
readBundledIndex
|
|
30
|
+
} from "./cli-c5c6f0yw.js";
|
|
31
|
+
import {
|
|
32
|
+
detectAgents,
|
|
33
|
+
installRules
|
|
34
|
+
} from "./cli-ssfq84k5.js";
|
|
32
35
|
import {
|
|
33
36
|
selectTenantAndCreateKey
|
|
34
|
-
} from "./cli-
|
|
37
|
+
} from "./cli-qgf2cz2v.js";
|
|
35
38
|
import {
|
|
36
39
|
loadToken,
|
|
37
40
|
login
|
|
38
|
-
} from "./cli-
|
|
41
|
+
} from "./cli-strngwn3.js";
|
|
39
42
|
import {
|
|
40
43
|
CliAuthError,
|
|
41
44
|
CliInputError,
|
|
@@ -43,154 +46,47 @@ import {
|
|
|
43
46
|
createCommandContext,
|
|
44
47
|
resolveIngestUrl,
|
|
45
48
|
resolveModaBaseUrl
|
|
46
|
-
} from "./cli-
|
|
49
|
+
} from "./cli-q79sq80a.js";
|
|
50
|
+
import"./cli-0v6na3yp.js";
|
|
47
51
|
|
|
48
52
|
// src/init/index.ts
|
|
49
|
-
import { existsSync as
|
|
50
|
-
import { dirname as
|
|
53
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
54
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
51
55
|
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
52
56
|
|
|
53
57
|
// src/init/sdk-guide.ts
|
|
54
|
-
import { existsSync as
|
|
55
|
-
import { dirname
|
|
56
|
-
|
|
57
|
-
// src/skills-bundled.ts
|
|
58
|
-
import { createHash } from "node:crypto";
|
|
59
|
-
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
60
|
-
import { dirname, join, relative } from "node:path";
|
|
61
|
-
var BUNDLED_SKILLS_DIR = packageAssetPath("skills", "integration");
|
|
62
|
-
function bundledSkillsDir() {
|
|
63
|
-
return process.env.MODA_BUNDLED_SKILLS_DIR || BUNDLED_SKILLS_DIR;
|
|
64
|
-
}
|
|
65
|
-
function readBundledIndex() {
|
|
66
|
-
const indexPath = join(bundledSkillsDir(), "index.json");
|
|
67
|
-
if (!existsSync(indexPath))
|
|
68
|
-
return null;
|
|
69
|
-
try {
|
|
70
|
-
const parsed = JSON.parse(readFileSync(indexPath, "utf8"));
|
|
71
|
-
if (!Array.isArray(parsed.skills))
|
|
72
|
-
return null;
|
|
73
|
-
return parsed;
|
|
74
|
-
} catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
function installIntegrationSkill(skillId, cwd = process.cwd()) {
|
|
79
|
-
if (!/^[a-z0-9-]+$/.test(skillId)) {
|
|
80
|
-
throw new Error(`Invalid integration skill id: ${JSON.stringify(skillId).slice(0, 80)}`);
|
|
81
|
-
}
|
|
82
|
-
const sourceDir = join(bundledSkillsDir(), skillId);
|
|
83
|
-
if (!existsSync(join(sourceDir, "SKILL.md"))) {
|
|
84
|
-
const bundled = readBundledIndex()?.skills.map((skill) => skill.id) ?? [];
|
|
85
|
-
throw new Error(`Integration skill '${skillId}' is not bundled with this CLI.${bundled.length > 0 ? ` Bundled skills: ${bundled.join(", ")}` : ""}`);
|
|
86
|
-
}
|
|
87
|
-
const files = listFilesRecursive(sourceDir).sort();
|
|
88
|
-
const plannedWrites = [];
|
|
89
|
-
for (const file of files) {
|
|
90
|
-
const relPath = relative(sourceDir, file).replace(/\\/g, "/");
|
|
91
|
-
plannedWrites.push({
|
|
92
|
-
targetRelPath: `.claude/skills/${skillId}/${relPath}`,
|
|
93
|
-
targetPath: join(cwd, ".claude", "skills", skillId, relPath),
|
|
94
|
-
content: readFileSync(file, "utf8")
|
|
95
|
-
});
|
|
96
|
-
}
|
|
97
|
-
const writeCursor = detectAgents().includes("Cursor") || existsSync(join(cwd, ".cursor"));
|
|
98
|
-
if (writeCursor) {
|
|
99
|
-
const skillMd = readFileSync(join(sourceDir, "SKILL.md"), "utf8");
|
|
100
|
-
plannedWrites.push({
|
|
101
|
-
targetRelPath: `.cursor/rules/${skillId}.mdc`,
|
|
102
|
-
targetPath: join(cwd, ".cursor", "rules", `${skillId}.mdc`),
|
|
103
|
-
content: claudeSkillToCursorRule(skillMd)
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
const ownedPaths = new Set((readSkillsManifest(cwd)?.records ?? []).flatMap((record) => record.paths));
|
|
107
|
-
const collisions = plannedWrites.filter((write) => existsSync(write.targetPath) && !ownedPaths.has(write.targetRelPath));
|
|
108
|
-
if (collisions.length > 0) {
|
|
109
|
-
throw new Error(`Refusing to overwrite ${collisions.length} existing file(s) not managed by Moda: ` + `${collisions.map((write) => write.targetRelPath).join(", ")}. ` + "Move or remove them, then re-run to install the bundled integration skill.");
|
|
110
|
-
}
|
|
111
|
-
const paths = [];
|
|
112
|
-
let wroteAny = false;
|
|
113
|
-
for (const write of plannedWrites) {
|
|
114
|
-
if (writeIfChanged(write.targetPath, write.content))
|
|
115
|
-
wroteAny = true;
|
|
116
|
-
paths.push(write.targetRelPath);
|
|
117
|
-
}
|
|
118
|
-
upsertSkillManifestRecord(cwd, {
|
|
119
|
-
skillId,
|
|
120
|
-
contentHash: hashSkillDir(sourceDir, files),
|
|
121
|
-
source: SKILL_SOURCE_INTEGRATION_BUNDLED,
|
|
122
|
-
paths
|
|
123
|
-
});
|
|
124
|
-
return { skillId, status: wroteAny ? "installed" : "unchanged", paths };
|
|
125
|
-
}
|
|
126
|
-
function writeIfChanged(path, content) {
|
|
127
|
-
if (existsSync(path)) {
|
|
128
|
-
try {
|
|
129
|
-
if (readFileSync(path, "utf8") === content)
|
|
130
|
-
return false;
|
|
131
|
-
} catch {}
|
|
132
|
-
}
|
|
133
|
-
mkdirSync(dirname(path), { recursive: true });
|
|
134
|
-
writeFileSync(path, content);
|
|
135
|
-
return true;
|
|
136
|
-
}
|
|
137
|
-
function listFilesRecursive(dir) {
|
|
138
|
-
const files = [];
|
|
139
|
-
const stack = [dir];
|
|
140
|
-
while (stack.length > 0) {
|
|
141
|
-
const current = stack.pop();
|
|
142
|
-
if (!current)
|
|
143
|
-
continue;
|
|
144
|
-
for (const entry of readdirSync(current, { withFileTypes: true })) {
|
|
145
|
-
const path = join(current, entry.name);
|
|
146
|
-
if (entry.isDirectory())
|
|
147
|
-
stack.push(path);
|
|
148
|
-
else if (entry.isFile())
|
|
149
|
-
files.push(path);
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return files;
|
|
153
|
-
}
|
|
154
|
-
function hashSkillDir(sourceDir, files) {
|
|
155
|
-
const hash = createHash("sha256");
|
|
156
|
-
for (const file of files) {
|
|
157
|
-
hash.update(relative(sourceDir, file).replace(/\\/g, "/"));
|
|
158
|
-
hash.update("\x00");
|
|
159
|
-
hash.update(readFileSync(file));
|
|
160
|
-
hash.update("\x00");
|
|
161
|
-
}
|
|
162
|
-
return hash.digest("hex");
|
|
163
|
-
}
|
|
58
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "node:fs";
|
|
59
|
+
import { dirname, join as join2 } from "node:path";
|
|
164
60
|
|
|
165
61
|
// src/init/sdk-catalog.ts
|
|
166
62
|
import {
|
|
167
63
|
copyFileSync,
|
|
168
|
-
existsSync
|
|
169
|
-
mkdirSync
|
|
170
|
-
readdirSync
|
|
64
|
+
existsSync,
|
|
65
|
+
mkdirSync,
|
|
66
|
+
readdirSync,
|
|
171
67
|
rmSync,
|
|
172
68
|
statSync
|
|
173
69
|
} from "node:fs";
|
|
174
|
-
import { join
|
|
70
|
+
import { join, relative, resolve } from "node:path";
|
|
175
71
|
function materializeSdkCatalog(cwd, runId) {
|
|
176
72
|
if (!/^[a-z0-9-]+$/.test(runId)) {
|
|
177
73
|
throw new Error(`Invalid SDK catalog run id: ${JSON.stringify(runId).slice(0, 80)}`);
|
|
178
74
|
}
|
|
179
75
|
const sourceDir = bundledSkillsDir();
|
|
180
|
-
const indexPath =
|
|
181
|
-
if (!
|
|
76
|
+
const indexPath = join(sourceDir, "index.json");
|
|
77
|
+
if (!existsSync(sourceDir) || !existsSync(indexPath)) {
|
|
182
78
|
throw missingBundleError(sourceDir);
|
|
183
79
|
}
|
|
184
80
|
const index = readBundledIndex();
|
|
185
81
|
if (!index) {
|
|
186
82
|
throw missingBundleError(sourceDir);
|
|
187
83
|
}
|
|
188
|
-
const dir =
|
|
84
|
+
const dir = join(cwd, ".moda", "tmp", `sdk-catalog-${runId}`);
|
|
189
85
|
cleanupSdkCatalog(dir);
|
|
190
86
|
copyDirectoryRecursive(sourceDir, dir);
|
|
191
87
|
return {
|
|
192
88
|
dir,
|
|
193
|
-
relativeDir:
|
|
89
|
+
relativeDir: relative(cwd, dir).replace(/\\/g, "/"),
|
|
194
90
|
index
|
|
195
91
|
};
|
|
196
92
|
}
|
|
@@ -216,10 +112,10 @@ function renderCatalogListing(index) {
|
|
|
216
112
|
`);
|
|
217
113
|
}
|
|
218
114
|
function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
219
|
-
|
|
220
|
-
for (const entry of
|
|
221
|
-
const source =
|
|
222
|
-
const target =
|
|
115
|
+
mkdirSync(targetDir, { recursive: true });
|
|
116
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
117
|
+
const source = join(sourceDir, entry.name);
|
|
118
|
+
const target = join(targetDir, entry.name);
|
|
223
119
|
if (entry.isDirectory()) {
|
|
224
120
|
copyDirectoryRecursive(source, target);
|
|
225
121
|
} else if (entry.isFile()) {
|
|
@@ -230,9 +126,9 @@ function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
|
230
126
|
function missingBundleError(sourceDir) {
|
|
231
127
|
let detail = "is absent";
|
|
232
128
|
try {
|
|
233
|
-
if (
|
|
129
|
+
if (existsSync(sourceDir) && !statSync(sourceDir).isDirectory()) {
|
|
234
130
|
detail = "is not a directory";
|
|
235
|
-
} else if (
|
|
131
|
+
} else if (existsSync(sourceDir)) {
|
|
236
132
|
detail = "has no readable index.json";
|
|
237
133
|
}
|
|
238
134
|
} catch {
|
|
@@ -245,10 +141,10 @@ function missingBundleError(sourceDir) {
|
|
|
245
141
|
var SDK_GUIDE_PATH = ".moda/SDK_INTEGRATION.md";
|
|
246
142
|
function writeSdkIntegrationGuide(cwd = process.cwd()) {
|
|
247
143
|
const relativePath = SDK_GUIDE_PATH;
|
|
248
|
-
const absolutePath =
|
|
144
|
+
const absolutePath = join2(cwd, relativePath);
|
|
249
145
|
const content = renderSdkIntegrationGuideContent();
|
|
250
|
-
|
|
251
|
-
|
|
146
|
+
mkdirSync2(dirname(absolutePath), { recursive: true });
|
|
147
|
+
writeFileSync(absolutePath, content);
|
|
252
148
|
return relativePath;
|
|
253
149
|
}
|
|
254
150
|
function renderSdkIntegrationGuideContent() {
|
|
@@ -367,11 +263,11 @@ function readRuntimeSummary(cwd) {
|
|
|
367
263
|
providers: [],
|
|
368
264
|
runtimeAgentNames: []
|
|
369
265
|
};
|
|
370
|
-
const path =
|
|
371
|
-
if (!
|
|
266
|
+
const path = join2(cwd, ".moda", "harness.json");
|
|
267
|
+
if (!existsSync2(path))
|
|
372
268
|
return empty;
|
|
373
269
|
try {
|
|
374
|
-
const parsed = JSON.parse(
|
|
270
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
375
271
|
if (!isRecord(parsed))
|
|
376
272
|
return empty;
|
|
377
273
|
const runtimeAgents = Array.isArray(parsed.runtimeAgents) ? parsed.runtimeAgents : [];
|
|
@@ -415,22 +311,22 @@ function isRecord(value) {
|
|
|
415
311
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
416
312
|
import {
|
|
417
313
|
closeSync,
|
|
418
|
-
existsSync as
|
|
419
|
-
mkdirSync as
|
|
314
|
+
existsSync as existsSync4,
|
|
315
|
+
mkdirSync as mkdirSync3,
|
|
420
316
|
openSync,
|
|
421
|
-
readFileSync as
|
|
317
|
+
readFileSync as readFileSync3,
|
|
422
318
|
readSync,
|
|
423
|
-
readdirSync as
|
|
319
|
+
readdirSync as readdirSync2,
|
|
424
320
|
rmSync as rmSync2,
|
|
425
321
|
statSync as statSync2,
|
|
426
|
-
writeFileSync as
|
|
322
|
+
writeFileSync as writeFileSync2
|
|
427
323
|
} from "node:fs";
|
|
428
|
-
import { dirname as
|
|
324
|
+
import { dirname as dirname2, join as join4, relative as relative2, resolve as resolve2 } from "node:path";
|
|
429
325
|
import { spawnSync } from "node:child_process";
|
|
430
326
|
|
|
431
327
|
// src/init/sdk-protocol.ts
|
|
432
|
-
import { existsSync as
|
|
433
|
-
import { join as
|
|
328
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
329
|
+
import { join as join3 } from "node:path";
|
|
434
330
|
import { z } from "zod";
|
|
435
331
|
var SDK_RESULT_SCHEMA_VERSION = "moda.sdk_integration_result.v1";
|
|
436
332
|
var SDK_RESULT_JSON_BEGIN = "MODA_SDK_RESULT_JSON_BEGIN";
|
|
@@ -522,10 +418,10 @@ var SdkAgentResultSchema = z.object({
|
|
|
522
418
|
});
|
|
523
419
|
function extractSdkAgentResult(opts) {
|
|
524
420
|
const diagnostics = [];
|
|
525
|
-
const resultPath =
|
|
526
|
-
if (
|
|
421
|
+
const resultPath = join3(opts.cwd, SDK_RESULT_FILE);
|
|
422
|
+
if (existsSync3(resultPath)) {
|
|
527
423
|
try {
|
|
528
|
-
const parsed = parseCandidate(
|
|
424
|
+
const parsed = parseCandidate(readFileSync2(resultPath, "utf8"));
|
|
529
425
|
if (parsed.result)
|
|
530
426
|
return { result: parsed.result, source: "file" };
|
|
531
427
|
diagnostics.push(`result file: ${parsed.detail}`);
|
|
@@ -585,6 +481,12 @@ var MAX_TIMEOUT_MS = 600000;
|
|
|
585
481
|
function generateVerificationId() {
|
|
586
482
|
return `moda-verify-${randomBytes(16).toString("hex")}`;
|
|
587
483
|
}
|
|
484
|
+
function isModaVerificationId(value) {
|
|
485
|
+
return /^moda-verify-[0-9a-f]{32}$/.test(value);
|
|
486
|
+
}
|
|
487
|
+
function isAcceptedVerificationId(value) {
|
|
488
|
+
return isModaVerificationId(value) || /^onb_[A-Za-z0-9]{16}$/.test(value);
|
|
489
|
+
}
|
|
588
490
|
async function verifyExactTrace(opts) {
|
|
589
491
|
const now = opts.now ?? Date.now;
|
|
590
492
|
const sleep = opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
@@ -776,7 +678,7 @@ async function runSdkIntegration(options = {}) {
|
|
|
776
678
|
const verify = options.verify ?? verifyExactTrace;
|
|
777
679
|
const maxRepairAttempts = resolveMaxRepairAttempts(options.maxRepairAttempts);
|
|
778
680
|
const runId = randomBytes2(4).toString("hex");
|
|
779
|
-
const verificationId = generateVerificationId();
|
|
681
|
+
const verificationId = options.verificationId ?? generateVerificationId();
|
|
780
682
|
let catalog;
|
|
781
683
|
let gitBaseline = null;
|
|
782
684
|
let runStartedAt = Date.now();
|
|
@@ -1162,7 +1064,7 @@ function scanForSecrets(cwd, paths, apiKey) {
|
|
|
1162
1064
|
const absPath = resolve2(cwd, relPath);
|
|
1163
1065
|
if (!absPath.startsWith(resolvedCwd + "/") && absPath !== resolvedCwd)
|
|
1164
1066
|
continue;
|
|
1165
|
-
if (!
|
|
1067
|
+
if (!existsSync4(absPath))
|
|
1166
1068
|
continue;
|
|
1167
1069
|
let stat;
|
|
1168
1070
|
try {
|
|
@@ -1174,7 +1076,7 @@ function scanForSecrets(cwd, paths, apiKey) {
|
|
|
1174
1076
|
continue;
|
|
1175
1077
|
let content;
|
|
1176
1078
|
try {
|
|
1177
|
-
content = stat.size > MAX_SCAN_FILE_BYTES ? readFilePrefix(absPath, MAX_SCAN_FILE_BYTES) :
|
|
1079
|
+
content = stat.size > MAX_SCAN_FILE_BYTES ? readFilePrefix(absPath, MAX_SCAN_FILE_BYTES) : readFileSync3(absPath, "utf8");
|
|
1178
1080
|
} catch {
|
|
1179
1081
|
continue;
|
|
1180
1082
|
}
|
|
@@ -1210,7 +1112,7 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1210
1112
|
const addFile = (path) => {
|
|
1211
1113
|
if (files.length >= MAX_SCANNED_FILES || !isInsideRepo(path))
|
|
1212
1114
|
return;
|
|
1213
|
-
const relPath =
|
|
1115
|
+
const relPath = relative2(resolvedCwd, path).replace(/\\/g, "/");
|
|
1214
1116
|
if (!relPath || seenFiles.has(relPath))
|
|
1215
1117
|
return;
|
|
1216
1118
|
seenFiles.add(relPath);
|
|
@@ -1221,13 +1123,13 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1221
1123
|
return;
|
|
1222
1124
|
let entries;
|
|
1223
1125
|
try {
|
|
1224
|
-
entries =
|
|
1126
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
1225
1127
|
} catch {
|
|
1226
1128
|
return;
|
|
1227
1129
|
}
|
|
1228
1130
|
for (const entry of entries) {
|
|
1229
1131
|
if (entry.isFile() && (entry.name === ".env" || entry.name.startsWith(".env."))) {
|
|
1230
|
-
addFile(
|
|
1132
|
+
addFile(join4(dir, entry.name));
|
|
1231
1133
|
}
|
|
1232
1134
|
}
|
|
1233
1135
|
};
|
|
@@ -1240,14 +1142,14 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1240
1142
|
visitedDirectories += 1;
|
|
1241
1143
|
let entries;
|
|
1242
1144
|
try {
|
|
1243
|
-
entries =
|
|
1145
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
1244
1146
|
} catch {
|
|
1245
1147
|
continue;
|
|
1246
1148
|
}
|
|
1247
1149
|
for (const entry of entries) {
|
|
1248
1150
|
if (files.length >= MAX_SCANNED_FILES)
|
|
1249
1151
|
break;
|
|
1250
|
-
const path =
|
|
1152
|
+
const path = join4(dir, entry.name);
|
|
1251
1153
|
if (entry.isDirectory())
|
|
1252
1154
|
stack.push(path);
|
|
1253
1155
|
else if (entry.isFile())
|
|
@@ -1263,8 +1165,8 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1263
1165
|
const absPath = resolve2(cwd, relPath);
|
|
1264
1166
|
if (!isInsideRepo(absPath))
|
|
1265
1167
|
continue;
|
|
1266
|
-
addEnvFiles(
|
|
1267
|
-
if (!
|
|
1168
|
+
addEnvFiles(dirname2(absPath));
|
|
1169
|
+
if (!existsSync4(absPath))
|
|
1268
1170
|
continue;
|
|
1269
1171
|
let stat;
|
|
1270
1172
|
try {
|
|
@@ -1353,9 +1255,9 @@ function renderProtocolRetryPrompt(catalogDir) {
|
|
|
1353
1255
|
`);
|
|
1354
1256
|
}
|
|
1355
1257
|
function removeAgentResultFile(cwd) {
|
|
1356
|
-
const path =
|
|
1258
|
+
const path = join4(cwd, SDK_RESULT_FILE);
|
|
1357
1259
|
try {
|
|
1358
|
-
if (
|
|
1260
|
+
if (existsSync4(path))
|
|
1359
1261
|
rmSync2(path);
|
|
1360
1262
|
} catch {}
|
|
1361
1263
|
}
|
|
@@ -1469,9 +1371,9 @@ function composeFinalDetail(status, detail, verification, apiKey, guardsDegraded
|
|
|
1469
1371
|
return combined ? redactSecretText(combined, apiKey) : undefined;
|
|
1470
1372
|
}
|
|
1471
1373
|
function writeFinalOutcomeArtifact(cwd, payload) {
|
|
1472
|
-
const path =
|
|
1473
|
-
|
|
1474
|
-
|
|
1374
|
+
const path = join4(cwd, SDK_RESULT_FILE);
|
|
1375
|
+
mkdirSync3(dirname2(path), { recursive: true });
|
|
1376
|
+
writeFileSync2(path, `${JSON.stringify({
|
|
1475
1377
|
schema_version: OUTCOME_SCHEMA_VERSION,
|
|
1476
1378
|
status: payload.status,
|
|
1477
1379
|
verification_id: payload.verification_id,
|
|
@@ -1681,13 +1583,38 @@ function visibleLength(value) {
|
|
|
1681
1583
|
|
|
1682
1584
|
// src/init/index.ts
|
|
1683
1585
|
var HARNESS_PULL_RECOVERY = "moda harness pull --sync";
|
|
1586
|
+
var HARNESS_SKIP_NEXT = {
|
|
1587
|
+
unavailable: "retry",
|
|
1588
|
+
lost: "pull",
|
|
1589
|
+
unfetched: "pull",
|
|
1590
|
+
failed: "retry"
|
|
1591
|
+
};
|
|
1592
|
+
var HARNESS_SKIP_DETAILS = {
|
|
1593
|
+
unavailable: "hosted analysis unavailable",
|
|
1594
|
+
lost: "lost track of the hosted run",
|
|
1595
|
+
unfetched: "could not fetch the finished run",
|
|
1596
|
+
failed: "hosted run failed"
|
|
1597
|
+
};
|
|
1598
|
+
var HARNESS_SKIP_PLAN = {
|
|
1599
|
+
unavailable: "unavailable",
|
|
1600
|
+
lost: "status unknown",
|
|
1601
|
+
unfetched: "finished but not fetched",
|
|
1602
|
+
failed: "failed"
|
|
1603
|
+
};
|
|
1604
|
+
var HARNESS_SKIP_EVENT = {
|
|
1605
|
+
unavailable: "unavailable",
|
|
1606
|
+
lost: "could not be followed",
|
|
1607
|
+
unfetched: "finished but could not be fetched",
|
|
1608
|
+
failed: "failed on Moda"
|
|
1609
|
+
};
|
|
1684
1610
|
var LANE_EVENT_THROTTLE_MS = 2000;
|
|
1685
1611
|
async function runInit(flags, options = {}) {
|
|
1686
1612
|
const explicitYes = flags.yes === true || flags.yes === "true" || flags.y === true || flags.y === "true";
|
|
1687
1613
|
const json = flags.json === true || flags.json === "true";
|
|
1688
1614
|
const agent = flags.agent === true || flags.agent === "true";
|
|
1689
1615
|
const human = !json && !agent;
|
|
1690
|
-
const
|
|
1616
|
+
const noPrompts = flags.non_interactive === true || flags.non_interactive === "true";
|
|
1617
|
+
const yes = explicitYes || json || agent || noPrompts;
|
|
1691
1618
|
const phases = [];
|
|
1692
1619
|
const log = (message) => {
|
|
1693
1620
|
if (human)
|
|
@@ -1717,7 +1644,7 @@ async function runInit(flags, options = {}) {
|
|
|
1717
1644
|
log(renderInitStep(1, 5, "Sign in", "No environment variable files are written. File changes are listed before completion."));
|
|
1718
1645
|
let token = loadToken({ profile });
|
|
1719
1646
|
if (!token) {
|
|
1720
|
-
if (json || agent) {
|
|
1647
|
+
if (json || agent || noPrompts) {
|
|
1721
1648
|
throw new CliAuthError("Browser authentication is required before non-interactive init can continue.", {
|
|
1722
1649
|
reason: "no_stored_credentials",
|
|
1723
1650
|
login_command: ["moda", "auth", "login"],
|
|
@@ -1746,11 +1673,15 @@ async function runInit(flags, options = {}) {
|
|
|
1746
1673
|
log(renderInitStep(2, 5, "Workspace"));
|
|
1747
1674
|
emit("workspace", "Selecting workspace and creating API key.");
|
|
1748
1675
|
const tenantIdFlag = stringFlag(flagValue(flags, "tenant-id", "tenant_id"));
|
|
1676
|
+
const verificationId = stringFlag(flagValue(flags, "verification-id", "verification_id"));
|
|
1677
|
+
if (verificationId !== undefined && !isAcceptedVerificationId(verificationId)) {
|
|
1678
|
+
throw new CliInputError(`Invalid --verification-id ${JSON.stringify(verificationId).slice(0, 80)}`, "Pass the onb_ id shown in the Moda onboarding page, or omit the flag.");
|
|
1679
|
+
}
|
|
1749
1680
|
const tenantIdEnv = stringFlag(process.env.MODA_TENANT_ID);
|
|
1750
1681
|
const { apiKey, tenantId: selectedTenantId, tenantName } = await selectTenantAndCreateKey({
|
|
1751
1682
|
tenantId: tenantIdFlag ?? tenantIdEnv,
|
|
1752
1683
|
tenantIdSource: tenantIdFlag ? "flag" : tenantIdEnv ? "env" : undefined,
|
|
1753
|
-
nonInteractive: json || agent,
|
|
1684
|
+
nonInteractive: json || agent || noPrompts,
|
|
1754
1685
|
silent: !human,
|
|
1755
1686
|
resumeCommand: ["moda", "init", ...resumeFlagsForInit(flags)],
|
|
1756
1687
|
profile
|
|
@@ -1854,6 +1785,8 @@ async function runInit(flags, options = {}) {
|
|
|
1854
1785
|
log(renderInitStep(4, 5, "Set up", remote ? "Harness analysis runs on Moda while local setup continues. No further input is needed." : "Runs unattended. No further input is needed."));
|
|
1855
1786
|
const rootDir = process.cwd();
|
|
1856
1787
|
const allowLocalFallback = !isTruthyFlag(flagValue(flags, "remote"));
|
|
1788
|
+
const launchLocalAnalyst = human && !noPrompts || stringFlag(flags.analyst) !== undefined;
|
|
1789
|
+
const HARNESS_RETRY = "moda harness analyze --remote --summary --experimental";
|
|
1857
1790
|
const nestedHarnessFlags = (extra) => commandFlags({
|
|
1858
1791
|
summary: true,
|
|
1859
1792
|
yes: true,
|
|
@@ -1886,6 +1819,23 @@ async function runInit(flags, options = {}) {
|
|
|
1886
1819
|
const validationFailure = () => new CliInputError("Moda Analyst produced a harness report that did not pass validation.", "Review .moda/harness-report-validation.json, then rerun `moda init --local --analyst=claude|cursor|codex` or `moda harness analyze --analyst=local-scan --summary`.");
|
|
1887
1820
|
let harnessPhase = { id: "harness", title: "Harness discovery", status: "completed" };
|
|
1888
1821
|
let harnessDeferred = false;
|
|
1822
|
+
let harnessSkipped = false;
|
|
1823
|
+
let harnessSkip = "unavailable";
|
|
1824
|
+
const skipHarness = (error, kind = "unavailable") => {
|
|
1825
|
+
harnessSkipped = true;
|
|
1826
|
+
harnessDeferred = true;
|
|
1827
|
+
harnessSkip = kind;
|
|
1828
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1829
|
+
const next = HARNESS_SKIP_NEXT[kind] === "pull" ? HARNESS_PULL_RECOVERY : HARNESS_RETRY;
|
|
1830
|
+
harnessPhase = {
|
|
1831
|
+
id: "harness",
|
|
1832
|
+
title: "Harness discovery",
|
|
1833
|
+
status: "warning",
|
|
1834
|
+
details: `skipped: ${HARNESS_SKIP_DETAILS[kind]}`
|
|
1835
|
+
};
|
|
1836
|
+
markApplied(setupPlan, "harness", `Hosted analysis ${HARNESS_SKIP_PLAN[kind]}; run \`${next}\` later.`);
|
|
1837
|
+
emit("harness", `Hosted harness discovery ${HARNESS_SKIP_EVENT[kind]} (${reason}); skipped. Continue with \`${next}\`.`, "warning");
|
|
1838
|
+
};
|
|
1889
1839
|
const approveAndSyncHarness = async () => {
|
|
1890
1840
|
const graphSignals = readHarnessQualitySignals(rootDir);
|
|
1891
1841
|
const repoSignals = detectRepoLlmSignals(rootDir);
|
|
@@ -1896,7 +1846,7 @@ async function runInit(flags, options = {}) {
|
|
|
1896
1846
|
} : undefined;
|
|
1897
1847
|
if (signals && harnessLooksIncomplete(signals)) {
|
|
1898
1848
|
const names = [...signals.frameworks, ...signals.providers].join(", ");
|
|
1899
|
-
const retryCommand = remote ? "moda harness analyze --remote --summary" : "moda harness analyze --analyst=claude|cursor|codex --summary";
|
|
1849
|
+
const retryCommand = remote ? "moda harness analyze --remote --summary --experimental" : "moda harness analyze --analyst=claude|cursor|codex --summary";
|
|
1900
1850
|
warn(renderInitNote(`Harness discovery found 0 runtime agents, but this repo has LLM framework signals (${names}). ` + "Discovery likely failed, so the result was NOT approved or synced.", "warning"));
|
|
1901
1851
|
warn(` Retry with \`${retryCommand}\`, then \`moda harness approve --yes && moda harness sync --from-report\`.`);
|
|
1902
1852
|
emit("harness", "Harness discovery found 0 agents despite LLM framework signals; sync withheld.", "warning");
|
|
@@ -2003,12 +1953,16 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2003
1953
|
} catch (error) {
|
|
2004
1954
|
if (!allowLocalFallback)
|
|
2005
1955
|
throw error;
|
|
2006
|
-
warn(renderInitNote(`Server-side harness discovery unavailable (${error instanceof Error ? error.message : String(error)}); falling back to local analyst.`, "warning"));
|
|
2007
|
-
emit("harness", "Server-side discovery failed; falling back to local analyst.", "warning");
|
|
2008
1956
|
remote = false;
|
|
1957
|
+
if (launchLocalAnalyst) {
|
|
1958
|
+
warn(renderInitNote(`Server-side harness discovery unavailable (${error instanceof Error ? error.message : String(error)}); falling back to local analyst.`, "warning"));
|
|
1959
|
+
emit("harness", "Server-side discovery failed; falling back to local analyst.", "warning");
|
|
1960
|
+
} else {
|
|
1961
|
+
skipHarness(error);
|
|
1962
|
+
}
|
|
2009
1963
|
}
|
|
2010
1964
|
}
|
|
2011
|
-
if (!remote) {
|
|
1965
|
+
if (!remote && !harnessSkipped) {
|
|
2012
1966
|
await finishLocalAnalyze();
|
|
2013
1967
|
}
|
|
2014
1968
|
emit("apply", "Applying approved setup changes.");
|
|
@@ -2068,6 +2022,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2068
2022
|
const verificationBaseUrl = resolveModaBaseUrl({ profile });
|
|
2069
2023
|
sdkIntegration = await runSdkIntegration({
|
|
2070
2024
|
apiKey,
|
|
2025
|
+
verificationId,
|
|
2071
2026
|
ingestUrl: resolveIngestUrl({ profile }),
|
|
2072
2027
|
fetchAudit: (id) => fetchAuditWithApiKey(id, apiKey, verificationBaseUrl),
|
|
2073
2028
|
onProgress: (message) => {
|
|
@@ -2093,17 +2048,21 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2093
2048
|
if (remote && pollPromise && remoteRunId) {
|
|
2094
2049
|
const final = await pollPromise;
|
|
2095
2050
|
process.removeListener("SIGINT", onRemoteSigint);
|
|
2096
|
-
const fallBackToLocal = async (error) => {
|
|
2051
|
+
const fallBackToLocal = async (error, kind) => {
|
|
2097
2052
|
board?.fail("harness", "hosted analysis failed");
|
|
2098
2053
|
board?.stop();
|
|
2099
2054
|
if (!allowLocalFallback)
|
|
2100
2055
|
throw error;
|
|
2056
|
+
if (!launchLocalAnalyst) {
|
|
2057
|
+
skipHarness(error, kind);
|
|
2058
|
+
return;
|
|
2059
|
+
}
|
|
2101
2060
|
warn(renderInitNote(`Server-side harness discovery failed (${error instanceof Error ? error.message : String(error)}); falling back to local analyst.`, "warning"));
|
|
2102
2061
|
emit("harness", "Server-side discovery failed; falling back to local analyst.", "warning");
|
|
2103
2062
|
await finishLocalAnalyze();
|
|
2104
2063
|
};
|
|
2105
2064
|
if (pollError !== undefined) {
|
|
2106
|
-
await fallBackToLocal(pollError);
|
|
2065
|
+
await fallBackToLocal(pollError, "lost");
|
|
2107
2066
|
} else if (!final || final.status !== "completed" && final.status !== "failed") {
|
|
2108
2067
|
harnessDeferred = true;
|
|
2109
2068
|
harnessPhase = { id: "harness", title: "Harness discovery", status: "warning", details: "still running on Moda" };
|
|
@@ -2123,7 +2082,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2123
2082
|
flags: nestedHarnessFlags({ "run-id": remoteRunId })
|
|
2124
2083
|
}));
|
|
2125
2084
|
} catch (error) {
|
|
2126
|
-
await fallBackToLocal(error);
|
|
2085
|
+
await fallBackToLocal(error, isRecoverableRemoteRun(final) ? "unfetched" : "failed");
|
|
2127
2086
|
}
|
|
2128
2087
|
if (pullResult) {
|
|
2129
2088
|
if (pullResult.exitCode !== 0) {
|
|
@@ -2175,7 +2134,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2175
2134
|
log(renderInitComplete({
|
|
2176
2135
|
tenantName,
|
|
2177
2136
|
dashboardUrl: `${resolveModaBaseUrl({ profile })}/dashboard`,
|
|
2178
|
-
harness: harnessDeferred ? { state: "pending" } : { state: "mapped", ...readHarnessGraphCounts() },
|
|
2137
|
+
harness: harnessSkipped ? { state: "skipped", reason: harnessSkip, next: HARNESS_SKIP_NEXT[harnessSkip] === "pull" ? HARNESS_PULL_RECOVERY : HARNESS_RETRY } : harnessDeferred ? { state: "pending" } : { state: "mapped", ...readHarnessGraphCounts() },
|
|
2179
2138
|
filesChanged: changedFiles(setupPlan),
|
|
2180
2139
|
skipped: setupPlan.filter((item) => item.status === "skipped" || item.status === "manual"),
|
|
2181
2140
|
telemetrySeen
|
|
@@ -2359,9 +2318,9 @@ var GENERATED_WORKFLOW_CLI = `@moda-ai/cli@${VERSION}`;
|
|
|
2359
2318
|
var GENERATED_WORKFLOW_NPX = `npx -y --package=${GENERATED_WORKFLOW_CLI} moda`;
|
|
2360
2319
|
function writeCiCdWorkflow(cwd = process.cwd()) {
|
|
2361
2320
|
const relativePath = ".github/workflows/moda-validation.yml";
|
|
2362
|
-
const path =
|
|
2363
|
-
|
|
2364
|
-
|
|
2321
|
+
const path = join5(cwd, relativePath);
|
|
2322
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2323
|
+
writeFileSync3(path, `${[
|
|
2365
2324
|
"name: Moda Validation",
|
|
2366
2325
|
"",
|
|
2367
2326
|
"on:",
|
|
@@ -2394,9 +2353,9 @@ function writeCiCdWorkflow(cwd = process.cwd()) {
|
|
|
2394
2353
|
}
|
|
2395
2354
|
function writeAutosyncWorkflow(cwd = process.cwd()) {
|
|
2396
2355
|
const relativePath = ".github/workflows/moda-autosync.yml";
|
|
2397
|
-
const path =
|
|
2398
|
-
|
|
2399
|
-
|
|
2356
|
+
const path = join5(cwd, relativePath);
|
|
2357
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2358
|
+
writeFileSync3(path, `${[
|
|
2400
2359
|
"name: Moda Autosync",
|
|
2401
2360
|
"",
|
|
2402
2361
|
"on:",
|
|
@@ -2435,8 +2394,8 @@ function writeAutosyncWorkflow(cwd = process.cwd()) {
|
|
|
2435
2394
|
}
|
|
2436
2395
|
function writeHarnessRescanWorkflow(cwd = process.cwd(), paths) {
|
|
2437
2396
|
const relativePath = ".github/workflows/moda-harness-rescan.yml";
|
|
2438
|
-
const path =
|
|
2439
|
-
|
|
2397
|
+
const path = join5(cwd, relativePath);
|
|
2398
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2440
2399
|
const pathScope = paths !== undefined && paths.length > 0 ? [
|
|
2441
2400
|
" # Only pushes touching these paths trigger a rescan.",
|
|
2442
2401
|
" paths:",
|
|
@@ -2446,7 +2405,7 @@ function writeHarnessRescanWorkflow(cwd = process.cwd(), paths) {
|
|
|
2446
2405
|
" # paths:",
|
|
2447
2406
|
" # - 'src/agents/**'"
|
|
2448
2407
|
];
|
|
2449
|
-
|
|
2408
|
+
writeFileSync3(path, `${[
|
|
2450
2409
|
"name: Moda Harness Rescan",
|
|
2451
2410
|
"",
|
|
2452
2411
|
"on:",
|
|
@@ -2492,11 +2451,11 @@ function writeHarnessRescanWorkflow(cwd = process.cwd(), paths) {
|
|
|
2492
2451
|
return relativePath;
|
|
2493
2452
|
}
|
|
2494
2453
|
function printHarnessDetectionSummary(cwd = process.cwd()) {
|
|
2495
|
-
const manifestPath =
|
|
2496
|
-
if (!
|
|
2454
|
+
const manifestPath = join5(cwd, ".moda", "harness.json");
|
|
2455
|
+
if (!existsSync5(manifestPath))
|
|
2497
2456
|
return;
|
|
2498
2457
|
try {
|
|
2499
|
-
const graph = JSON.parse(
|
|
2458
|
+
const graph = JSON.parse(readFileSync4(manifestPath, "utf8"));
|
|
2500
2459
|
const frameworkNames = (graph.agentFrameworks ?? []).map((framework) => typeof framework.name === "string" ? framework.name : undefined).filter((name) => Boolean(name));
|
|
2501
2460
|
const providerNames = (graph.modelProviders ?? []).map((provider) => typeof provider.name === "string" ? provider.name : undefined).filter((name) => Boolean(name));
|
|
2502
2461
|
console.log(` Detected harness: ${(graph.runtimeAgents ?? []).length} runtime agent(s), ` + `${(graph.artifacts ?? []).length} artifact(s), ${(graph.relationships ?? []).length} relationship(s)`);
|
|
@@ -2517,20 +2476,20 @@ function signalsFromGraph(graph) {
|
|
|
2517
2476
|
};
|
|
2518
2477
|
}
|
|
2519
2478
|
function readHarnessQualitySignals(cwd = process.cwd()) {
|
|
2520
|
-
const reportPath =
|
|
2521
|
-
if (
|
|
2479
|
+
const reportPath = join5(cwd, ".moda", "harness-report.json");
|
|
2480
|
+
if (existsSync5(reportPath)) {
|
|
2522
2481
|
try {
|
|
2523
|
-
const report = JSON.parse(
|
|
2482
|
+
const report = JSON.parse(readFileSync4(reportPath, "utf8"));
|
|
2524
2483
|
if (report.graph && typeof report.graph === "object") {
|
|
2525
2484
|
return signalsFromGraph(report.graph);
|
|
2526
2485
|
}
|
|
2527
2486
|
} catch {}
|
|
2528
2487
|
}
|
|
2529
|
-
const manifestPath =
|
|
2530
|
-
if (!
|
|
2488
|
+
const manifestPath = join5(cwd, ".moda", "harness.json");
|
|
2489
|
+
if (!existsSync5(manifestPath))
|
|
2531
2490
|
return;
|
|
2532
2491
|
try {
|
|
2533
|
-
return signalsFromGraph(JSON.parse(
|
|
2492
|
+
return signalsFromGraph(JSON.parse(readFileSync4(manifestPath, "utf8")));
|
|
2534
2493
|
} catch {
|
|
2535
2494
|
return;
|
|
2536
2495
|
}
|
|
@@ -2648,7 +2607,7 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2648
2607
|
};
|
|
2649
2608
|
const scanDir = (dir) => {
|
|
2650
2609
|
try {
|
|
2651
|
-
const pkg = JSON.parse(
|
|
2610
|
+
const pkg = JSON.parse(readFileSync4(join5(dir, "package.json"), "utf8"));
|
|
2652
2611
|
for (const dep of [
|
|
2653
2612
|
...Object.keys(pkg.dependencies ?? {}),
|
|
2654
2613
|
...Object.keys(pkg.devDependencies ?? {}),
|
|
@@ -2659,7 +2618,7 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2659
2618
|
} catch {}
|
|
2660
2619
|
for (const file of ["requirements.txt", "pyproject.toml"]) {
|
|
2661
2620
|
try {
|
|
2662
|
-
const text =
|
|
2621
|
+
const text = readFileSync4(join5(dir, file), "utf8");
|
|
2663
2622
|
for (const name of pythonRequirementNames(text, file)) {
|
|
2664
2623
|
record(pyDependencySignal(name));
|
|
2665
2624
|
}
|
|
@@ -2674,7 +2633,7 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2674
2633
|
scanDir(dir);
|
|
2675
2634
|
let entries;
|
|
2676
2635
|
try {
|
|
2677
|
-
entries =
|
|
2636
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
2678
2637
|
} catch {
|
|
2679
2638
|
return;
|
|
2680
2639
|
}
|
|
@@ -2683,18 +2642,18 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2683
2642
|
continue;
|
|
2684
2643
|
if (REPO_SIGNAL_SKIPPED_DIRS.has(entry.name))
|
|
2685
2644
|
continue;
|
|
2686
|
-
walk(
|
|
2645
|
+
walk(join5(dir, entry.name), depth + 1);
|
|
2687
2646
|
}
|
|
2688
2647
|
};
|
|
2689
2648
|
walk(cwd, 0);
|
|
2690
2649
|
return { frameworks: [...frameworks].sort(), providers: [...providers].sort() };
|
|
2691
2650
|
}
|
|
2692
2651
|
function readHarnessGraphCounts(cwd = process.cwd()) {
|
|
2693
|
-
const manifestPath =
|
|
2694
|
-
if (!
|
|
2652
|
+
const manifestPath = join5(cwd, ".moda", "harness.json");
|
|
2653
|
+
if (!existsSync5(manifestPath))
|
|
2695
2654
|
return {};
|
|
2696
2655
|
try {
|
|
2697
|
-
const graph = JSON.parse(
|
|
2656
|
+
const graph = JSON.parse(readFileSync4(manifestPath, "utf8"));
|
|
2698
2657
|
return {
|
|
2699
2658
|
agents: Array.isArray(graph.runtimeAgents) ? graph.runtimeAgents.length : 0,
|
|
2700
2659
|
artifacts: Array.isArray(graph.artifacts) ? graph.artifacts.length : 0
|
|
@@ -2713,6 +2672,7 @@ function resumeFlagsForInit(flags) {
|
|
|
2713
2672
|
const passthrough = [
|
|
2714
2673
|
"profile",
|
|
2715
2674
|
"tenant-id",
|
|
2675
|
+
"verification-id",
|
|
2716
2676
|
"offline",
|
|
2717
2677
|
"analyst",
|
|
2718
2678
|
"sdk-integrate",
|