@moda-ai/cli 1.36.0 → 1.38.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-7qkhy41j.js → cli-3kcy61fr.js} +112 -444
- package/dist/cli-c5c6f0yw.js +128 -0
- package/dist/{cli-scjxgaba.js → cli-k38j2fpq.js} +2 -2
- package/dist/{cli-yz99t7n8.js → cli-kegd748v.js} +70 -3
- package/dist/{cli-drabjmaj.js → cli-q79sq80a.js} +1 -35
- package/dist/cli-ssfq84k5.js +393 -0
- package/dist/cli.js +211 -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-hwfe7gs8.js} +203 -210
- package/dist/prompt-source-mcp.js +3 -2
- package/dist/{provision-d242y9qd.js → provision-64c2n9aw.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-3kcy61fr.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,26 @@ 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-k38j2fpq.js";
|
|
35
38
|
import {
|
|
39
|
+
ConnectCodeError,
|
|
40
|
+
connectWithCode,
|
|
41
|
+
loadAuthSession,
|
|
36
42
|
loadToken,
|
|
37
43
|
login
|
|
38
|
-
} from "./cli-
|
|
44
|
+
} from "./cli-kegd748v.js";
|
|
39
45
|
import {
|
|
40
46
|
CliAuthError,
|
|
41
47
|
CliInputError,
|
|
@@ -43,154 +49,47 @@ import {
|
|
|
43
49
|
createCommandContext,
|
|
44
50
|
resolveIngestUrl,
|
|
45
51
|
resolveModaBaseUrl
|
|
46
|
-
} from "./cli-
|
|
52
|
+
} from "./cli-q79sq80a.js";
|
|
53
|
+
import"./cli-0v6na3yp.js";
|
|
47
54
|
|
|
48
55
|
// src/init/index.ts
|
|
49
|
-
import { existsSync as
|
|
50
|
-
import { dirname as
|
|
56
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync4, readdirSync as readdirSync3, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
57
|
+
import { dirname as dirname3, join as join5 } from "node:path";
|
|
51
58
|
import { checkbox, confirm, select } from "@inquirer/prompts";
|
|
52
59
|
|
|
53
60
|
// 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
|
-
}
|
|
61
|
+
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync } from "node:fs";
|
|
62
|
+
import { dirname, join as join2 } from "node:path";
|
|
164
63
|
|
|
165
64
|
// src/init/sdk-catalog.ts
|
|
166
65
|
import {
|
|
167
66
|
copyFileSync,
|
|
168
|
-
existsSync
|
|
169
|
-
mkdirSync
|
|
170
|
-
readdirSync
|
|
67
|
+
existsSync,
|
|
68
|
+
mkdirSync,
|
|
69
|
+
readdirSync,
|
|
171
70
|
rmSync,
|
|
172
71
|
statSync
|
|
173
72
|
} from "node:fs";
|
|
174
|
-
import { join
|
|
73
|
+
import { join, relative, resolve } from "node:path";
|
|
175
74
|
function materializeSdkCatalog(cwd, runId) {
|
|
176
75
|
if (!/^[a-z0-9-]+$/.test(runId)) {
|
|
177
76
|
throw new Error(`Invalid SDK catalog run id: ${JSON.stringify(runId).slice(0, 80)}`);
|
|
178
77
|
}
|
|
179
78
|
const sourceDir = bundledSkillsDir();
|
|
180
|
-
const indexPath =
|
|
181
|
-
if (!
|
|
79
|
+
const indexPath = join(sourceDir, "index.json");
|
|
80
|
+
if (!existsSync(sourceDir) || !existsSync(indexPath)) {
|
|
182
81
|
throw missingBundleError(sourceDir);
|
|
183
82
|
}
|
|
184
83
|
const index = readBundledIndex();
|
|
185
84
|
if (!index) {
|
|
186
85
|
throw missingBundleError(sourceDir);
|
|
187
86
|
}
|
|
188
|
-
const dir =
|
|
87
|
+
const dir = join(cwd, ".moda", "tmp", `sdk-catalog-${runId}`);
|
|
189
88
|
cleanupSdkCatalog(dir);
|
|
190
89
|
copyDirectoryRecursive(sourceDir, dir);
|
|
191
90
|
return {
|
|
192
91
|
dir,
|
|
193
|
-
relativeDir:
|
|
92
|
+
relativeDir: relative(cwd, dir).replace(/\\/g, "/"),
|
|
194
93
|
index
|
|
195
94
|
};
|
|
196
95
|
}
|
|
@@ -216,10 +115,10 @@ function renderCatalogListing(index) {
|
|
|
216
115
|
`);
|
|
217
116
|
}
|
|
218
117
|
function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
219
|
-
|
|
220
|
-
for (const entry of
|
|
221
|
-
const source =
|
|
222
|
-
const target =
|
|
118
|
+
mkdirSync(targetDir, { recursive: true });
|
|
119
|
+
for (const entry of readdirSync(sourceDir, { withFileTypes: true })) {
|
|
120
|
+
const source = join(sourceDir, entry.name);
|
|
121
|
+
const target = join(targetDir, entry.name);
|
|
223
122
|
if (entry.isDirectory()) {
|
|
224
123
|
copyDirectoryRecursive(source, target);
|
|
225
124
|
} else if (entry.isFile()) {
|
|
@@ -230,9 +129,9 @@ function copyDirectoryRecursive(sourceDir, targetDir) {
|
|
|
230
129
|
function missingBundleError(sourceDir) {
|
|
231
130
|
let detail = "is absent";
|
|
232
131
|
try {
|
|
233
|
-
if (
|
|
132
|
+
if (existsSync(sourceDir) && !statSync(sourceDir).isDirectory()) {
|
|
234
133
|
detail = "is not a directory";
|
|
235
|
-
} else if (
|
|
134
|
+
} else if (existsSync(sourceDir)) {
|
|
236
135
|
detail = "has no readable index.json";
|
|
237
136
|
}
|
|
238
137
|
} catch {
|
|
@@ -245,10 +144,10 @@ function missingBundleError(sourceDir) {
|
|
|
245
144
|
var SDK_GUIDE_PATH = ".moda/SDK_INTEGRATION.md";
|
|
246
145
|
function writeSdkIntegrationGuide(cwd = process.cwd()) {
|
|
247
146
|
const relativePath = SDK_GUIDE_PATH;
|
|
248
|
-
const absolutePath =
|
|
147
|
+
const absolutePath = join2(cwd, relativePath);
|
|
249
148
|
const content = renderSdkIntegrationGuideContent();
|
|
250
|
-
|
|
251
|
-
|
|
149
|
+
mkdirSync2(dirname(absolutePath), { recursive: true });
|
|
150
|
+
writeFileSync(absolutePath, content);
|
|
252
151
|
return relativePath;
|
|
253
152
|
}
|
|
254
153
|
function renderSdkIntegrationGuideContent() {
|
|
@@ -367,11 +266,11 @@ function readRuntimeSummary(cwd) {
|
|
|
367
266
|
providers: [],
|
|
368
267
|
runtimeAgentNames: []
|
|
369
268
|
};
|
|
370
|
-
const path =
|
|
371
|
-
if (!
|
|
269
|
+
const path = join2(cwd, ".moda", "harness.json");
|
|
270
|
+
if (!existsSync2(path))
|
|
372
271
|
return empty;
|
|
373
272
|
try {
|
|
374
|
-
const parsed = JSON.parse(
|
|
273
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
375
274
|
if (!isRecord(parsed))
|
|
376
275
|
return empty;
|
|
377
276
|
const runtimeAgents = Array.isArray(parsed.runtimeAgents) ? parsed.runtimeAgents : [];
|
|
@@ -415,22 +314,22 @@ function isRecord(value) {
|
|
|
415
314
|
import { randomBytes as randomBytes2 } from "node:crypto";
|
|
416
315
|
import {
|
|
417
316
|
closeSync,
|
|
418
|
-
existsSync as
|
|
419
|
-
mkdirSync as
|
|
317
|
+
existsSync as existsSync4,
|
|
318
|
+
mkdirSync as mkdirSync3,
|
|
420
319
|
openSync,
|
|
421
|
-
readFileSync as
|
|
320
|
+
readFileSync as readFileSync3,
|
|
422
321
|
readSync,
|
|
423
|
-
readdirSync as
|
|
322
|
+
readdirSync as readdirSync2,
|
|
424
323
|
rmSync as rmSync2,
|
|
425
324
|
statSync as statSync2,
|
|
426
|
-
writeFileSync as
|
|
325
|
+
writeFileSync as writeFileSync2
|
|
427
326
|
} from "node:fs";
|
|
428
|
-
import { dirname as
|
|
327
|
+
import { dirname as dirname2, join as join4, relative as relative2, resolve as resolve2 } from "node:path";
|
|
429
328
|
import { spawnSync } from "node:child_process";
|
|
430
329
|
|
|
431
330
|
// src/init/sdk-protocol.ts
|
|
432
|
-
import { existsSync as
|
|
433
|
-
import { join as
|
|
331
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "node:fs";
|
|
332
|
+
import { join as join3 } from "node:path";
|
|
434
333
|
import { z } from "zod";
|
|
435
334
|
var SDK_RESULT_SCHEMA_VERSION = "moda.sdk_integration_result.v1";
|
|
436
335
|
var SDK_RESULT_JSON_BEGIN = "MODA_SDK_RESULT_JSON_BEGIN";
|
|
@@ -522,10 +421,10 @@ var SdkAgentResultSchema = z.object({
|
|
|
522
421
|
});
|
|
523
422
|
function extractSdkAgentResult(opts) {
|
|
524
423
|
const diagnostics = [];
|
|
525
|
-
const resultPath =
|
|
526
|
-
if (
|
|
424
|
+
const resultPath = join3(opts.cwd, SDK_RESULT_FILE);
|
|
425
|
+
if (existsSync3(resultPath)) {
|
|
527
426
|
try {
|
|
528
|
-
const parsed = parseCandidate(
|
|
427
|
+
const parsed = parseCandidate(readFileSync2(resultPath, "utf8"));
|
|
529
428
|
if (parsed.result)
|
|
530
429
|
return { result: parsed.result, source: "file" };
|
|
531
430
|
diagnostics.push(`result file: ${parsed.detail}`);
|
|
@@ -585,6 +484,12 @@ var MAX_TIMEOUT_MS = 600000;
|
|
|
585
484
|
function generateVerificationId() {
|
|
586
485
|
return `moda-verify-${randomBytes(16).toString("hex")}`;
|
|
587
486
|
}
|
|
487
|
+
function isModaVerificationId(value) {
|
|
488
|
+
return /^moda-verify-[0-9a-f]{32}$/.test(value);
|
|
489
|
+
}
|
|
490
|
+
function isAcceptedVerificationId(value) {
|
|
491
|
+
return isModaVerificationId(value) || /^onb_[A-Za-z0-9]{16}$/.test(value);
|
|
492
|
+
}
|
|
588
493
|
async function verifyExactTrace(opts) {
|
|
589
494
|
const now = opts.now ?? Date.now;
|
|
590
495
|
const sleep = opts.sleep ?? ((ms) => new Promise((resolve2) => setTimeout(resolve2, ms)));
|
|
@@ -776,7 +681,7 @@ async function runSdkIntegration(options = {}) {
|
|
|
776
681
|
const verify = options.verify ?? verifyExactTrace;
|
|
777
682
|
const maxRepairAttempts = resolveMaxRepairAttempts(options.maxRepairAttempts);
|
|
778
683
|
const runId = randomBytes2(4).toString("hex");
|
|
779
|
-
const verificationId = generateVerificationId();
|
|
684
|
+
const verificationId = options.verificationId ?? generateVerificationId();
|
|
780
685
|
let catalog;
|
|
781
686
|
let gitBaseline = null;
|
|
782
687
|
let runStartedAt = Date.now();
|
|
@@ -1162,7 +1067,7 @@ function scanForSecrets(cwd, paths, apiKey) {
|
|
|
1162
1067
|
const absPath = resolve2(cwd, relPath);
|
|
1163
1068
|
if (!absPath.startsWith(resolvedCwd + "/") && absPath !== resolvedCwd)
|
|
1164
1069
|
continue;
|
|
1165
|
-
if (!
|
|
1070
|
+
if (!existsSync4(absPath))
|
|
1166
1071
|
continue;
|
|
1167
1072
|
let stat;
|
|
1168
1073
|
try {
|
|
@@ -1174,7 +1079,7 @@ function scanForSecrets(cwd, paths, apiKey) {
|
|
|
1174
1079
|
continue;
|
|
1175
1080
|
let content;
|
|
1176
1081
|
try {
|
|
1177
|
-
content = stat.size > MAX_SCAN_FILE_BYTES ? readFilePrefix(absPath, MAX_SCAN_FILE_BYTES) :
|
|
1082
|
+
content = stat.size > MAX_SCAN_FILE_BYTES ? readFilePrefix(absPath, MAX_SCAN_FILE_BYTES) : readFileSync3(absPath, "utf8");
|
|
1178
1083
|
} catch {
|
|
1179
1084
|
continue;
|
|
1180
1085
|
}
|
|
@@ -1210,7 +1115,7 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1210
1115
|
const addFile = (path) => {
|
|
1211
1116
|
if (files.length >= MAX_SCANNED_FILES || !isInsideRepo(path))
|
|
1212
1117
|
return;
|
|
1213
|
-
const relPath =
|
|
1118
|
+
const relPath = relative2(resolvedCwd, path).replace(/\\/g, "/");
|
|
1214
1119
|
if (!relPath || seenFiles.has(relPath))
|
|
1215
1120
|
return;
|
|
1216
1121
|
seenFiles.add(relPath);
|
|
@@ -1221,13 +1126,13 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1221
1126
|
return;
|
|
1222
1127
|
let entries;
|
|
1223
1128
|
try {
|
|
1224
|
-
entries =
|
|
1129
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
1225
1130
|
} catch {
|
|
1226
1131
|
return;
|
|
1227
1132
|
}
|
|
1228
1133
|
for (const entry of entries) {
|
|
1229
1134
|
if (entry.isFile() && (entry.name === ".env" || entry.name.startsWith(".env."))) {
|
|
1230
|
-
addFile(
|
|
1135
|
+
addFile(join4(dir, entry.name));
|
|
1231
1136
|
}
|
|
1232
1137
|
}
|
|
1233
1138
|
};
|
|
@@ -1240,14 +1145,14 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1240
1145
|
visitedDirectories += 1;
|
|
1241
1146
|
let entries;
|
|
1242
1147
|
try {
|
|
1243
|
-
entries =
|
|
1148
|
+
entries = readdirSync2(dir, { withFileTypes: true });
|
|
1244
1149
|
} catch {
|
|
1245
1150
|
continue;
|
|
1246
1151
|
}
|
|
1247
1152
|
for (const entry of entries) {
|
|
1248
1153
|
if (files.length >= MAX_SCANNED_FILES)
|
|
1249
1154
|
break;
|
|
1250
|
-
const path =
|
|
1155
|
+
const path = join4(dir, entry.name);
|
|
1251
1156
|
if (entry.isDirectory())
|
|
1252
1157
|
stack.push(path);
|
|
1253
1158
|
else if (entry.isFile())
|
|
@@ -1263,8 +1168,8 @@ function collectSecretScanFiles(cwd, paths) {
|
|
|
1263
1168
|
const absPath = resolve2(cwd, relPath);
|
|
1264
1169
|
if (!isInsideRepo(absPath))
|
|
1265
1170
|
continue;
|
|
1266
|
-
addEnvFiles(
|
|
1267
|
-
if (!
|
|
1171
|
+
addEnvFiles(dirname2(absPath));
|
|
1172
|
+
if (!existsSync4(absPath))
|
|
1268
1173
|
continue;
|
|
1269
1174
|
let stat;
|
|
1270
1175
|
try {
|
|
@@ -1353,9 +1258,9 @@ function renderProtocolRetryPrompt(catalogDir) {
|
|
|
1353
1258
|
`);
|
|
1354
1259
|
}
|
|
1355
1260
|
function removeAgentResultFile(cwd) {
|
|
1356
|
-
const path =
|
|
1261
|
+
const path = join4(cwd, SDK_RESULT_FILE);
|
|
1357
1262
|
try {
|
|
1358
|
-
if (
|
|
1263
|
+
if (existsSync4(path))
|
|
1359
1264
|
rmSync2(path);
|
|
1360
1265
|
} catch {}
|
|
1361
1266
|
}
|
|
@@ -1469,9 +1374,9 @@ function composeFinalDetail(status, detail, verification, apiKey, guardsDegraded
|
|
|
1469
1374
|
return combined ? redactSecretText(combined, apiKey) : undefined;
|
|
1470
1375
|
}
|
|
1471
1376
|
function writeFinalOutcomeArtifact(cwd, payload) {
|
|
1472
|
-
const path =
|
|
1473
|
-
|
|
1474
|
-
|
|
1377
|
+
const path = join4(cwd, SDK_RESULT_FILE);
|
|
1378
|
+
mkdirSync3(dirname2(path), { recursive: true });
|
|
1379
|
+
writeFileSync2(path, `${JSON.stringify({
|
|
1475
1380
|
schema_version: OUTCOME_SCHEMA_VERSION,
|
|
1476
1381
|
status: payload.status,
|
|
1477
1382
|
verification_id: payload.verification_id,
|
|
@@ -1681,13 +1586,38 @@ function visibleLength(value) {
|
|
|
1681
1586
|
|
|
1682
1587
|
// src/init/index.ts
|
|
1683
1588
|
var HARNESS_PULL_RECOVERY = "moda harness pull --sync";
|
|
1589
|
+
var HARNESS_SKIP_NEXT = {
|
|
1590
|
+
unavailable: "retry",
|
|
1591
|
+
lost: "pull",
|
|
1592
|
+
unfetched: "pull",
|
|
1593
|
+
failed: "retry"
|
|
1594
|
+
};
|
|
1595
|
+
var HARNESS_SKIP_DETAILS = {
|
|
1596
|
+
unavailable: "hosted analysis unavailable",
|
|
1597
|
+
lost: "lost track of the hosted run",
|
|
1598
|
+
unfetched: "could not fetch the finished run",
|
|
1599
|
+
failed: "hosted run failed"
|
|
1600
|
+
};
|
|
1601
|
+
var HARNESS_SKIP_PLAN = {
|
|
1602
|
+
unavailable: "unavailable",
|
|
1603
|
+
lost: "status unknown",
|
|
1604
|
+
unfetched: "finished but not fetched",
|
|
1605
|
+
failed: "failed"
|
|
1606
|
+
};
|
|
1607
|
+
var HARNESS_SKIP_EVENT = {
|
|
1608
|
+
unavailable: "unavailable",
|
|
1609
|
+
lost: "could not be followed",
|
|
1610
|
+
unfetched: "finished but could not be fetched",
|
|
1611
|
+
failed: "failed on Moda"
|
|
1612
|
+
};
|
|
1684
1613
|
var LANE_EVENT_THROTTLE_MS = 2000;
|
|
1685
1614
|
async function runInit(flags, options = {}) {
|
|
1686
1615
|
const explicitYes = flags.yes === true || flags.yes === "true" || flags.y === true || flags.y === "true";
|
|
1687
1616
|
const json = flags.json === true || flags.json === "true";
|
|
1688
1617
|
const agent = flags.agent === true || flags.agent === "true";
|
|
1689
1618
|
const human = !json && !agent;
|
|
1690
|
-
const
|
|
1619
|
+
const noPrompts = flags.non_interactive === true || flags.non_interactive === "true";
|
|
1620
|
+
const yes = explicitYes || json || agent || noPrompts;
|
|
1691
1621
|
const phases = [];
|
|
1692
1622
|
const log = (message) => {
|
|
1693
1623
|
if (human)
|
|
@@ -1716,8 +1646,34 @@ async function runInit(flags, options = {}) {
|
|
|
1716
1646
|
const profile = stringFlag(flags.profile);
|
|
1717
1647
|
log(renderInitStep(1, 5, "Sign in", "No environment variable files are written. File changes are listed before completion."));
|
|
1718
1648
|
let token = loadToken({ profile });
|
|
1649
|
+
const connectCode = stringFlag(flagValue(flags, "connect-code", "connect_code"));
|
|
1650
|
+
let codeTenantId;
|
|
1651
|
+
if (connectCode) {
|
|
1652
|
+
const previousEmail = token ? loadAuthSession({ profile })?.user_email : undefined;
|
|
1653
|
+
try {
|
|
1654
|
+
const connected = await connectWithCode(connectCode, {
|
|
1655
|
+
profile,
|
|
1656
|
+
tenantId: stringFlag(flagValue(flags, "tenant-id", "tenant_id"))
|
|
1657
|
+
});
|
|
1658
|
+
token = loadToken({ profile });
|
|
1659
|
+
codeTenantId = connected.tenantId;
|
|
1660
|
+
const email = connected.user.email || "your Moda account";
|
|
1661
|
+
const replaced = previousEmail && previousEmail !== connected.user.email ? ` (replacing the CLI session for ${previousEmail})` : "";
|
|
1662
|
+
log(renderInitNote(`Signed in as ${email} with the one-time code${replaced}`));
|
|
1663
|
+
emit("auth", `Signed in as ${email} with the one-time code${replaced}.`);
|
|
1664
|
+
} catch (error) {
|
|
1665
|
+
if (!(error instanceof ConnectCodeError))
|
|
1666
|
+
throw error;
|
|
1667
|
+
if (error.reason === "workspace_mismatch") {
|
|
1668
|
+
throw new CliInputError(error.message, "Copy the command again from the Moda onboarding page, or drop --tenant-id.");
|
|
1669
|
+
}
|
|
1670
|
+
const next = token ? `Continuing with your existing CLI session${previousEmail ? ` (${previousEmail})` : ""}.` : "Signing in through the browser instead.";
|
|
1671
|
+
warn(renderInitNote(`${error.message} ${next}`, "warning"));
|
|
1672
|
+
emit("auth", `${error.message} ${next}`, "warning");
|
|
1673
|
+
}
|
|
1674
|
+
}
|
|
1719
1675
|
if (!token) {
|
|
1720
|
-
if (json || agent) {
|
|
1676
|
+
if (json || agent || noPrompts) {
|
|
1721
1677
|
throw new CliAuthError("Browser authentication is required before non-interactive init can continue.", {
|
|
1722
1678
|
reason: "no_stored_credentials",
|
|
1723
1679
|
login_command: ["moda", "auth", "login"],
|
|
@@ -1725,7 +1681,8 @@ async function runInit(flags, options = {}) {
|
|
|
1725
1681
|
}, "Run `moda auth login`, then re-run `moda init`.");
|
|
1726
1682
|
}
|
|
1727
1683
|
let action = "login";
|
|
1728
|
-
|
|
1684
|
+
const fromDashboard = Boolean(connectCode || stringFlag(flagValue(flags, "tenant-id", "tenant_id")) || stringFlag(flagValue(flags, "verification-id", "verification_id")));
|
|
1685
|
+
if (!yes && !fromDashboard) {
|
|
1729
1686
|
action = await select({
|
|
1730
1687
|
message: "Get started",
|
|
1731
1688
|
choices: [
|
|
@@ -1746,11 +1703,18 @@ async function runInit(flags, options = {}) {
|
|
|
1746
1703
|
log(renderInitStep(2, 5, "Workspace"));
|
|
1747
1704
|
emit("workspace", "Selecting workspace and creating API key.");
|
|
1748
1705
|
const tenantIdFlag = stringFlag(flagValue(flags, "tenant-id", "tenant_id"));
|
|
1706
|
+
if (codeTenantId && tenantIdFlag && tenantIdFlag !== codeTenantId) {
|
|
1707
|
+
throw new CliInputError("The sign-in code is for a different workspace than --tenant-id.", "Copy the command again from the Moda onboarding page, or drop --tenant-id.");
|
|
1708
|
+
}
|
|
1709
|
+
const verificationId = stringFlag(flagValue(flags, "verification-id", "verification_id"));
|
|
1710
|
+
if (verificationId !== undefined && !isAcceptedVerificationId(verificationId)) {
|
|
1711
|
+
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.");
|
|
1712
|
+
}
|
|
1749
1713
|
const tenantIdEnv = stringFlag(process.env.MODA_TENANT_ID);
|
|
1750
1714
|
const { apiKey, tenantId: selectedTenantId, tenantName } = await selectTenantAndCreateKey({
|
|
1751
|
-
tenantId: tenantIdFlag ?? tenantIdEnv,
|
|
1752
|
-
tenantIdSource: tenantIdFlag ? "flag" : tenantIdEnv ? "env" : undefined,
|
|
1753
|
-
nonInteractive: json || agent,
|
|
1715
|
+
tenantId: tenantIdFlag ?? codeTenantId ?? tenantIdEnv,
|
|
1716
|
+
tenantIdSource: tenantIdFlag || codeTenantId ? "flag" : tenantIdEnv ? "env" : undefined,
|
|
1717
|
+
nonInteractive: json || agent || noPrompts,
|
|
1754
1718
|
silent: !human,
|
|
1755
1719
|
resumeCommand: ["moda", "init", ...resumeFlagsForInit(flags)],
|
|
1756
1720
|
profile
|
|
@@ -1854,6 +1818,8 @@ async function runInit(flags, options = {}) {
|
|
|
1854
1818
|
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
1819
|
const rootDir = process.cwd();
|
|
1856
1820
|
const allowLocalFallback = !isTruthyFlag(flagValue(flags, "remote"));
|
|
1821
|
+
const launchLocalAnalyst = human && !noPrompts || stringFlag(flags.analyst) !== undefined;
|
|
1822
|
+
const HARNESS_RETRY = "moda harness analyze --remote --summary --experimental";
|
|
1857
1823
|
const nestedHarnessFlags = (extra) => commandFlags({
|
|
1858
1824
|
summary: true,
|
|
1859
1825
|
yes: true,
|
|
@@ -1886,6 +1852,23 @@ async function runInit(flags, options = {}) {
|
|
|
1886
1852
|
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
1853
|
let harnessPhase = { id: "harness", title: "Harness discovery", status: "completed" };
|
|
1888
1854
|
let harnessDeferred = false;
|
|
1855
|
+
let harnessSkipped = false;
|
|
1856
|
+
let harnessSkip = "unavailable";
|
|
1857
|
+
const skipHarness = (error, kind = "unavailable") => {
|
|
1858
|
+
harnessSkipped = true;
|
|
1859
|
+
harnessDeferred = true;
|
|
1860
|
+
harnessSkip = kind;
|
|
1861
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
1862
|
+
const next = HARNESS_SKIP_NEXT[kind] === "pull" ? HARNESS_PULL_RECOVERY : HARNESS_RETRY;
|
|
1863
|
+
harnessPhase = {
|
|
1864
|
+
id: "harness",
|
|
1865
|
+
title: "Harness discovery",
|
|
1866
|
+
status: "warning",
|
|
1867
|
+
details: `skipped: ${HARNESS_SKIP_DETAILS[kind]}`
|
|
1868
|
+
};
|
|
1869
|
+
markApplied(setupPlan, "harness", `Hosted analysis ${HARNESS_SKIP_PLAN[kind]}; run \`${next}\` later.`);
|
|
1870
|
+
emit("harness", `Hosted harness discovery ${HARNESS_SKIP_EVENT[kind]} (${reason}); skipped. Continue with \`${next}\`.`, "warning");
|
|
1871
|
+
};
|
|
1889
1872
|
const approveAndSyncHarness = async () => {
|
|
1890
1873
|
const graphSignals = readHarnessQualitySignals(rootDir);
|
|
1891
1874
|
const repoSignals = detectRepoLlmSignals(rootDir);
|
|
@@ -1896,7 +1879,7 @@ async function runInit(flags, options = {}) {
|
|
|
1896
1879
|
} : undefined;
|
|
1897
1880
|
if (signals && harnessLooksIncomplete(signals)) {
|
|
1898
1881
|
const names = [...signals.frameworks, ...signals.providers].join(", ");
|
|
1899
|
-
const retryCommand = remote ? "moda harness analyze --remote --summary" : "moda harness analyze --analyst=claude|cursor|codex --summary";
|
|
1882
|
+
const retryCommand = remote ? "moda harness analyze --remote --summary --experimental" : "moda harness analyze --analyst=claude|cursor|codex --summary";
|
|
1900
1883
|
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
1884
|
warn(` Retry with \`${retryCommand}\`, then \`moda harness approve --yes && moda harness sync --from-report\`.`);
|
|
1902
1885
|
emit("harness", "Harness discovery found 0 agents despite LLM framework signals; sync withheld.", "warning");
|
|
@@ -2003,12 +1986,16 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2003
1986
|
} catch (error) {
|
|
2004
1987
|
if (!allowLocalFallback)
|
|
2005
1988
|
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
1989
|
remote = false;
|
|
1990
|
+
if (launchLocalAnalyst) {
|
|
1991
|
+
warn(renderInitNote(`Server-side harness discovery unavailable (${error instanceof Error ? error.message : String(error)}); falling back to local analyst.`, "warning"));
|
|
1992
|
+
emit("harness", "Server-side discovery failed; falling back to local analyst.", "warning");
|
|
1993
|
+
} else {
|
|
1994
|
+
skipHarness(error);
|
|
1995
|
+
}
|
|
2009
1996
|
}
|
|
2010
1997
|
}
|
|
2011
|
-
if (!remote) {
|
|
1998
|
+
if (!remote && !harnessSkipped) {
|
|
2012
1999
|
await finishLocalAnalyze();
|
|
2013
2000
|
}
|
|
2014
2001
|
emit("apply", "Applying approved setup changes.");
|
|
@@ -2068,6 +2055,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2068
2055
|
const verificationBaseUrl = resolveModaBaseUrl({ profile });
|
|
2069
2056
|
sdkIntegration = await runSdkIntegration({
|
|
2070
2057
|
apiKey,
|
|
2058
|
+
verificationId,
|
|
2071
2059
|
ingestUrl: resolveIngestUrl({ profile }),
|
|
2072
2060
|
fetchAudit: (id) => fetchAuditWithApiKey(id, apiKey, verificationBaseUrl),
|
|
2073
2061
|
onProgress: (message) => {
|
|
@@ -2093,17 +2081,21 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2093
2081
|
if (remote && pollPromise && remoteRunId) {
|
|
2094
2082
|
const final = await pollPromise;
|
|
2095
2083
|
process.removeListener("SIGINT", onRemoteSigint);
|
|
2096
|
-
const fallBackToLocal = async (error) => {
|
|
2084
|
+
const fallBackToLocal = async (error, kind) => {
|
|
2097
2085
|
board?.fail("harness", "hosted analysis failed");
|
|
2098
2086
|
board?.stop();
|
|
2099
2087
|
if (!allowLocalFallback)
|
|
2100
2088
|
throw error;
|
|
2089
|
+
if (!launchLocalAnalyst) {
|
|
2090
|
+
skipHarness(error, kind);
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2101
2093
|
warn(renderInitNote(`Server-side harness discovery failed (${error instanceof Error ? error.message : String(error)}); falling back to local analyst.`, "warning"));
|
|
2102
2094
|
emit("harness", "Server-side discovery failed; falling back to local analyst.", "warning");
|
|
2103
2095
|
await finishLocalAnalyze();
|
|
2104
2096
|
};
|
|
2105
2097
|
if (pollError !== undefined) {
|
|
2106
|
-
await fallBackToLocal(pollError);
|
|
2098
|
+
await fallBackToLocal(pollError, "lost");
|
|
2107
2099
|
} else if (!final || final.status !== "completed" && final.status !== "failed") {
|
|
2108
2100
|
harnessDeferred = true;
|
|
2109
2101
|
harnessPhase = { id: "harness", title: "Harness discovery", status: "warning", details: "still running on Moda" };
|
|
@@ -2123,7 +2115,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2123
2115
|
flags: nestedHarnessFlags({ "run-id": remoteRunId })
|
|
2124
2116
|
}));
|
|
2125
2117
|
} catch (error) {
|
|
2126
|
-
await fallBackToLocal(error);
|
|
2118
|
+
await fallBackToLocal(error, isRecoverableRemoteRun(final) ? "unfetched" : "failed");
|
|
2127
2119
|
}
|
|
2128
2120
|
if (pullResult) {
|
|
2129
2121
|
if (pullResult.exitCode !== 0) {
|
|
@@ -2175,7 +2167,7 @@ Harness analysis keeps running on Moda. Resume anytime with \`${HARNESS_PULL_REC
|
|
|
2175
2167
|
log(renderInitComplete({
|
|
2176
2168
|
tenantName,
|
|
2177
2169
|
dashboardUrl: `${resolveModaBaseUrl({ profile })}/dashboard`,
|
|
2178
|
-
harness: harnessDeferred ? { state: "pending" } : { state: "mapped", ...readHarnessGraphCounts() },
|
|
2170
|
+
harness: harnessSkipped ? { state: "skipped", reason: harnessSkip, next: HARNESS_SKIP_NEXT[harnessSkip] === "pull" ? HARNESS_PULL_RECOVERY : HARNESS_RETRY } : harnessDeferred ? { state: "pending" } : { state: "mapped", ...readHarnessGraphCounts() },
|
|
2179
2171
|
filesChanged: changedFiles(setupPlan),
|
|
2180
2172
|
skipped: setupPlan.filter((item) => item.status === "skipped" || item.status === "manual"),
|
|
2181
2173
|
telemetrySeen
|
|
@@ -2359,9 +2351,9 @@ var GENERATED_WORKFLOW_CLI = `@moda-ai/cli@${VERSION}`;
|
|
|
2359
2351
|
var GENERATED_WORKFLOW_NPX = `npx -y --package=${GENERATED_WORKFLOW_CLI} moda`;
|
|
2360
2352
|
function writeCiCdWorkflow(cwd = process.cwd()) {
|
|
2361
2353
|
const relativePath = ".github/workflows/moda-validation.yml";
|
|
2362
|
-
const path =
|
|
2363
|
-
|
|
2364
|
-
|
|
2354
|
+
const path = join5(cwd, relativePath);
|
|
2355
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2356
|
+
writeFileSync3(path, `${[
|
|
2365
2357
|
"name: Moda Validation",
|
|
2366
2358
|
"",
|
|
2367
2359
|
"on:",
|
|
@@ -2394,9 +2386,9 @@ function writeCiCdWorkflow(cwd = process.cwd()) {
|
|
|
2394
2386
|
}
|
|
2395
2387
|
function writeAutosyncWorkflow(cwd = process.cwd()) {
|
|
2396
2388
|
const relativePath = ".github/workflows/moda-autosync.yml";
|
|
2397
|
-
const path =
|
|
2398
|
-
|
|
2399
|
-
|
|
2389
|
+
const path = join5(cwd, relativePath);
|
|
2390
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2391
|
+
writeFileSync3(path, `${[
|
|
2400
2392
|
"name: Moda Autosync",
|
|
2401
2393
|
"",
|
|
2402
2394
|
"on:",
|
|
@@ -2435,8 +2427,8 @@ function writeAutosyncWorkflow(cwd = process.cwd()) {
|
|
|
2435
2427
|
}
|
|
2436
2428
|
function writeHarnessRescanWorkflow(cwd = process.cwd(), paths) {
|
|
2437
2429
|
const relativePath = ".github/workflows/moda-harness-rescan.yml";
|
|
2438
|
-
const path =
|
|
2439
|
-
|
|
2430
|
+
const path = join5(cwd, relativePath);
|
|
2431
|
+
mkdirSync4(dirname3(path), { recursive: true });
|
|
2440
2432
|
const pathScope = paths !== undefined && paths.length > 0 ? [
|
|
2441
2433
|
" # Only pushes touching these paths trigger a rescan.",
|
|
2442
2434
|
" paths:",
|
|
@@ -2446,7 +2438,7 @@ function writeHarnessRescanWorkflow(cwd = process.cwd(), paths) {
|
|
|
2446
2438
|
" # paths:",
|
|
2447
2439
|
" # - 'src/agents/**'"
|
|
2448
2440
|
];
|
|
2449
|
-
|
|
2441
|
+
writeFileSync3(path, `${[
|
|
2450
2442
|
"name: Moda Harness Rescan",
|
|
2451
2443
|
"",
|
|
2452
2444
|
"on:",
|
|
@@ -2492,11 +2484,11 @@ function writeHarnessRescanWorkflow(cwd = process.cwd(), paths) {
|
|
|
2492
2484
|
return relativePath;
|
|
2493
2485
|
}
|
|
2494
2486
|
function printHarnessDetectionSummary(cwd = process.cwd()) {
|
|
2495
|
-
const manifestPath =
|
|
2496
|
-
if (!
|
|
2487
|
+
const manifestPath = join5(cwd, ".moda", "harness.json");
|
|
2488
|
+
if (!existsSync5(manifestPath))
|
|
2497
2489
|
return;
|
|
2498
2490
|
try {
|
|
2499
|
-
const graph = JSON.parse(
|
|
2491
|
+
const graph = JSON.parse(readFileSync4(manifestPath, "utf8"));
|
|
2500
2492
|
const frameworkNames = (graph.agentFrameworks ?? []).map((framework) => typeof framework.name === "string" ? framework.name : undefined).filter((name) => Boolean(name));
|
|
2501
2493
|
const providerNames = (graph.modelProviders ?? []).map((provider) => typeof provider.name === "string" ? provider.name : undefined).filter((name) => Boolean(name));
|
|
2502
2494
|
console.log(` Detected harness: ${(graph.runtimeAgents ?? []).length} runtime agent(s), ` + `${(graph.artifacts ?? []).length} artifact(s), ${(graph.relationships ?? []).length} relationship(s)`);
|
|
@@ -2517,20 +2509,20 @@ function signalsFromGraph(graph) {
|
|
|
2517
2509
|
};
|
|
2518
2510
|
}
|
|
2519
2511
|
function readHarnessQualitySignals(cwd = process.cwd()) {
|
|
2520
|
-
const reportPath =
|
|
2521
|
-
if (
|
|
2512
|
+
const reportPath = join5(cwd, ".moda", "harness-report.json");
|
|
2513
|
+
if (existsSync5(reportPath)) {
|
|
2522
2514
|
try {
|
|
2523
|
-
const report = JSON.parse(
|
|
2515
|
+
const report = JSON.parse(readFileSync4(reportPath, "utf8"));
|
|
2524
2516
|
if (report.graph && typeof report.graph === "object") {
|
|
2525
2517
|
return signalsFromGraph(report.graph);
|
|
2526
2518
|
}
|
|
2527
2519
|
} catch {}
|
|
2528
2520
|
}
|
|
2529
|
-
const manifestPath =
|
|
2530
|
-
if (!
|
|
2521
|
+
const manifestPath = join5(cwd, ".moda", "harness.json");
|
|
2522
|
+
if (!existsSync5(manifestPath))
|
|
2531
2523
|
return;
|
|
2532
2524
|
try {
|
|
2533
|
-
return signalsFromGraph(JSON.parse(
|
|
2525
|
+
return signalsFromGraph(JSON.parse(readFileSync4(manifestPath, "utf8")));
|
|
2534
2526
|
} catch {
|
|
2535
2527
|
return;
|
|
2536
2528
|
}
|
|
@@ -2648,7 +2640,7 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2648
2640
|
};
|
|
2649
2641
|
const scanDir = (dir) => {
|
|
2650
2642
|
try {
|
|
2651
|
-
const pkg = JSON.parse(
|
|
2643
|
+
const pkg = JSON.parse(readFileSync4(join5(dir, "package.json"), "utf8"));
|
|
2652
2644
|
for (const dep of [
|
|
2653
2645
|
...Object.keys(pkg.dependencies ?? {}),
|
|
2654
2646
|
...Object.keys(pkg.devDependencies ?? {}),
|
|
@@ -2659,7 +2651,7 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2659
2651
|
} catch {}
|
|
2660
2652
|
for (const file of ["requirements.txt", "pyproject.toml"]) {
|
|
2661
2653
|
try {
|
|
2662
|
-
const text =
|
|
2654
|
+
const text = readFileSync4(join5(dir, file), "utf8");
|
|
2663
2655
|
for (const name of pythonRequirementNames(text, file)) {
|
|
2664
2656
|
record(pyDependencySignal(name));
|
|
2665
2657
|
}
|
|
@@ -2674,7 +2666,7 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2674
2666
|
scanDir(dir);
|
|
2675
2667
|
let entries;
|
|
2676
2668
|
try {
|
|
2677
|
-
entries =
|
|
2669
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
2678
2670
|
} catch {
|
|
2679
2671
|
return;
|
|
2680
2672
|
}
|
|
@@ -2683,18 +2675,18 @@ function detectRepoLlmSignals(cwd = process.cwd()) {
|
|
|
2683
2675
|
continue;
|
|
2684
2676
|
if (REPO_SIGNAL_SKIPPED_DIRS.has(entry.name))
|
|
2685
2677
|
continue;
|
|
2686
|
-
walk(
|
|
2678
|
+
walk(join5(dir, entry.name), depth + 1);
|
|
2687
2679
|
}
|
|
2688
2680
|
};
|
|
2689
2681
|
walk(cwd, 0);
|
|
2690
2682
|
return { frameworks: [...frameworks].sort(), providers: [...providers].sort() };
|
|
2691
2683
|
}
|
|
2692
2684
|
function readHarnessGraphCounts(cwd = process.cwd()) {
|
|
2693
|
-
const manifestPath =
|
|
2694
|
-
if (!
|
|
2685
|
+
const manifestPath = join5(cwd, ".moda", "harness.json");
|
|
2686
|
+
if (!existsSync5(manifestPath))
|
|
2695
2687
|
return {};
|
|
2696
2688
|
try {
|
|
2697
|
-
const graph = JSON.parse(
|
|
2689
|
+
const graph = JSON.parse(readFileSync4(manifestPath, "utf8"));
|
|
2698
2690
|
return {
|
|
2699
2691
|
agents: Array.isArray(graph.runtimeAgents) ? graph.runtimeAgents.length : 0,
|
|
2700
2692
|
artifacts: Array.isArray(graph.artifacts) ? graph.artifacts.length : 0
|
|
@@ -2713,6 +2705,7 @@ function resumeFlagsForInit(flags) {
|
|
|
2713
2705
|
const passthrough = [
|
|
2714
2706
|
"profile",
|
|
2715
2707
|
"tenant-id",
|
|
2708
|
+
"verification-id",
|
|
2716
2709
|
"offline",
|
|
2717
2710
|
"analyst",
|
|
2718
2711
|
"sdk-integrate",
|