@gluecharm-lab/easyspecs-cli 0.0.13 → 0.0.14
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 +89 -68
- package/commands.md +4 -1
- package/dist/main.cjs +628 -214
- package/dist/main.cjs.map +4 -4
- package/package.json +2 -2
package/dist/main.cjs
CHANGED
|
@@ -3000,7 +3000,7 @@ var require_compile = __commonJS({
|
|
|
3000
3000
|
const schOrFunc = root.refs[ref];
|
|
3001
3001
|
if (schOrFunc)
|
|
3002
3002
|
return schOrFunc;
|
|
3003
|
-
let _sch =
|
|
3003
|
+
let _sch = resolve17.call(this, root, ref);
|
|
3004
3004
|
if (_sch === void 0) {
|
|
3005
3005
|
const schema = (_a = root.localRefs) === null || _a === void 0 ? void 0 : _a[ref];
|
|
3006
3006
|
const { schemaId } = this.opts;
|
|
@@ -3027,7 +3027,7 @@ var require_compile = __commonJS({
|
|
|
3027
3027
|
function sameSchemaEnv(s1, s2) {
|
|
3028
3028
|
return s1.schema === s2.schema && s1.root === s2.root && s1.baseId === s2.baseId;
|
|
3029
3029
|
}
|
|
3030
|
-
function
|
|
3030
|
+
function resolve17(root, ref) {
|
|
3031
3031
|
let sch;
|
|
3032
3032
|
while (typeof (sch = this.refs[ref]) == "string")
|
|
3033
3033
|
ref = sch;
|
|
@@ -3242,8 +3242,8 @@ var require_utils = __commonJS({
|
|
|
3242
3242
|
}
|
|
3243
3243
|
return ind;
|
|
3244
3244
|
}
|
|
3245
|
-
function removeDotSegments(
|
|
3246
|
-
let input =
|
|
3245
|
+
function removeDotSegments(path47) {
|
|
3246
|
+
let input = path47;
|
|
3247
3247
|
const output = [];
|
|
3248
3248
|
let nextSlash = -1;
|
|
3249
3249
|
let len = 0;
|
|
@@ -3442,8 +3442,8 @@ var require_schemes = __commonJS({
|
|
|
3442
3442
|
wsComponent.secure = void 0;
|
|
3443
3443
|
}
|
|
3444
3444
|
if (wsComponent.resourceName) {
|
|
3445
|
-
const [
|
|
3446
|
-
wsComponent.path =
|
|
3445
|
+
const [path47, query] = wsComponent.resourceName.split("?");
|
|
3446
|
+
wsComponent.path = path47 && path47 !== "/" ? path47 : void 0;
|
|
3447
3447
|
wsComponent.query = query;
|
|
3448
3448
|
wsComponent.resourceName = void 0;
|
|
3449
3449
|
}
|
|
@@ -3602,55 +3602,55 @@ var require_fast_uri = __commonJS({
|
|
|
3602
3602
|
}
|
|
3603
3603
|
return uri;
|
|
3604
3604
|
}
|
|
3605
|
-
function
|
|
3605
|
+
function resolve17(baseURI, relativeURI, options) {
|
|
3606
3606
|
const schemelessOptions = options ? Object.assign({ scheme: "null" }, options) : { scheme: "null" };
|
|
3607
3607
|
const resolved = resolveComponent(parse(baseURI, schemelessOptions), parse(relativeURI, schemelessOptions), schemelessOptions, true);
|
|
3608
3608
|
schemelessOptions.skipEscape = true;
|
|
3609
3609
|
return serialize(resolved, schemelessOptions);
|
|
3610
3610
|
}
|
|
3611
|
-
function resolveComponent(base,
|
|
3611
|
+
function resolveComponent(base, relative13, options, skipNormalization) {
|
|
3612
3612
|
const target = {};
|
|
3613
3613
|
if (!skipNormalization) {
|
|
3614
3614
|
base = parse(serialize(base, options), options);
|
|
3615
|
-
|
|
3615
|
+
relative13 = parse(serialize(relative13, options), options);
|
|
3616
3616
|
}
|
|
3617
3617
|
options = options || {};
|
|
3618
|
-
if (!options.tolerant &&
|
|
3619
|
-
target.scheme =
|
|
3620
|
-
target.userinfo =
|
|
3621
|
-
target.host =
|
|
3622
|
-
target.port =
|
|
3623
|
-
target.path = removeDotSegments(
|
|
3624
|
-
target.query =
|
|
3618
|
+
if (!options.tolerant && relative13.scheme) {
|
|
3619
|
+
target.scheme = relative13.scheme;
|
|
3620
|
+
target.userinfo = relative13.userinfo;
|
|
3621
|
+
target.host = relative13.host;
|
|
3622
|
+
target.port = relative13.port;
|
|
3623
|
+
target.path = removeDotSegments(relative13.path || "");
|
|
3624
|
+
target.query = relative13.query;
|
|
3625
3625
|
} else {
|
|
3626
|
-
if (
|
|
3627
|
-
target.userinfo =
|
|
3628
|
-
target.host =
|
|
3629
|
-
target.port =
|
|
3630
|
-
target.path = removeDotSegments(
|
|
3631
|
-
target.query =
|
|
3626
|
+
if (relative13.userinfo !== void 0 || relative13.host !== void 0 || relative13.port !== void 0) {
|
|
3627
|
+
target.userinfo = relative13.userinfo;
|
|
3628
|
+
target.host = relative13.host;
|
|
3629
|
+
target.port = relative13.port;
|
|
3630
|
+
target.path = removeDotSegments(relative13.path || "");
|
|
3631
|
+
target.query = relative13.query;
|
|
3632
3632
|
} else {
|
|
3633
|
-
if (!
|
|
3633
|
+
if (!relative13.path) {
|
|
3634
3634
|
target.path = base.path;
|
|
3635
|
-
if (
|
|
3636
|
-
target.query =
|
|
3635
|
+
if (relative13.query !== void 0) {
|
|
3636
|
+
target.query = relative13.query;
|
|
3637
3637
|
} else {
|
|
3638
3638
|
target.query = base.query;
|
|
3639
3639
|
}
|
|
3640
3640
|
} else {
|
|
3641
|
-
if (
|
|
3642
|
-
target.path = removeDotSegments(
|
|
3641
|
+
if (relative13.path[0] === "/") {
|
|
3642
|
+
target.path = removeDotSegments(relative13.path);
|
|
3643
3643
|
} else {
|
|
3644
3644
|
if ((base.userinfo !== void 0 || base.host !== void 0 || base.port !== void 0) && !base.path) {
|
|
3645
|
-
target.path = "/" +
|
|
3645
|
+
target.path = "/" + relative13.path;
|
|
3646
3646
|
} else if (!base.path) {
|
|
3647
|
-
target.path =
|
|
3647
|
+
target.path = relative13.path;
|
|
3648
3648
|
} else {
|
|
3649
|
-
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) +
|
|
3649
|
+
target.path = base.path.slice(0, base.path.lastIndexOf("/") + 1) + relative13.path;
|
|
3650
3650
|
}
|
|
3651
3651
|
target.path = removeDotSegments(target.path);
|
|
3652
3652
|
}
|
|
3653
|
-
target.query =
|
|
3653
|
+
target.query = relative13.query;
|
|
3654
3654
|
}
|
|
3655
3655
|
target.userinfo = base.userinfo;
|
|
3656
3656
|
target.host = base.host;
|
|
@@ -3658,7 +3658,7 @@ var require_fast_uri = __commonJS({
|
|
|
3658
3658
|
}
|
|
3659
3659
|
target.scheme = base.scheme;
|
|
3660
3660
|
}
|
|
3661
|
-
target.fragment =
|
|
3661
|
+
target.fragment = relative13.fragment;
|
|
3662
3662
|
return target;
|
|
3663
3663
|
}
|
|
3664
3664
|
function equal(uriA, uriB, options) {
|
|
@@ -3829,7 +3829,7 @@ var require_fast_uri = __commonJS({
|
|
|
3829
3829
|
var fastUri = {
|
|
3830
3830
|
SCHEMES,
|
|
3831
3831
|
normalize: normalize6,
|
|
3832
|
-
resolve:
|
|
3832
|
+
resolve: resolve17,
|
|
3833
3833
|
resolveComponent,
|
|
3834
3834
|
equal,
|
|
3835
3835
|
serialize,
|
|
@@ -7629,12 +7629,12 @@ var require_dist = __commonJS({
|
|
|
7629
7629
|
throw new Error(`Unknown format "${name}"`);
|
|
7630
7630
|
return f;
|
|
7631
7631
|
};
|
|
7632
|
-
function addFormats2(ajv2, list,
|
|
7632
|
+
function addFormats2(ajv2, list, fs47, exportName) {
|
|
7633
7633
|
var _a;
|
|
7634
7634
|
var _b;
|
|
7635
7635
|
(_a = (_b = ajv2.opts.code).formats) !== null && _a !== void 0 ? _a : _b.formats = (0, codegen_1._)`require("ajv-formats/dist/formats").${exportName}`;
|
|
7636
7636
|
for (const f of list)
|
|
7637
|
-
ajv2.addFormat(f,
|
|
7637
|
+
ajv2.addFormat(f, fs47[f]);
|
|
7638
7638
|
}
|
|
7639
7639
|
module2.exports = exports2 = formatsPlugin;
|
|
7640
7640
|
Object.defineProperty(exports2, "__esModule", { value: true });
|
|
@@ -7643,8 +7643,8 @@ var require_dist = __commonJS({
|
|
|
7643
7643
|
});
|
|
7644
7644
|
|
|
7645
7645
|
// src/cli/main.ts
|
|
7646
|
-
var
|
|
7647
|
-
var
|
|
7646
|
+
var fs46 = __toESM(require("node:fs"));
|
|
7647
|
+
var path46 = __toESM(require("node:path"));
|
|
7648
7648
|
|
|
7649
7649
|
// src/cli/argv.ts
|
|
7650
7650
|
function isGlobalFlag(s) {
|
|
@@ -8597,6 +8597,22 @@ function ensureEasyspecsConfig(repoRoot, opts) {
|
|
|
8597
8597
|
atomicWriteFile(configPath, body);
|
|
8598
8598
|
return readEasyspecsConfig(repoRoot);
|
|
8599
8599
|
}
|
|
8600
|
+
function syncDefaultGitRemoteUrlIfEmpty(repoRoot, cfg, opts) {
|
|
8601
|
+
const current = (cfg.easyspecs?.defaultGitRemoteUrl ?? "").trim();
|
|
8602
|
+
if (current.length > 0) {
|
|
8603
|
+
return cfg;
|
|
8604
|
+
}
|
|
8605
|
+
const configPath = easyspecsConfigPath(repoRoot);
|
|
8606
|
+
if (!fs6.existsSync(configPath)) {
|
|
8607
|
+
return cfg;
|
|
8608
|
+
}
|
|
8609
|
+
const gitRoot = findGitRepoRoot(repoRoot) ?? repoRoot;
|
|
8610
|
+
const url = runGit(gitRoot, ["remote", "get-url", "origin"])?.trim();
|
|
8611
|
+
if (!url) {
|
|
8612
|
+
return cfg;
|
|
8613
|
+
}
|
|
8614
|
+
return updateEasyspecsConfig(repoRoot, { easyspecs: { defaultGitRemoteUrl: url } }, opts);
|
|
8615
|
+
}
|
|
8600
8616
|
function updateEasyspecsConfig(repoRoot, patch, opts) {
|
|
8601
8617
|
const configPath = easyspecsConfigPath(repoRoot);
|
|
8602
8618
|
const base = fs6.existsSync(configPath) ? readEasyspecsConfig(repoRoot) : buildFreshEasyspecsConfigFromLegacy(repoRoot, opts?.warnMigration);
|
|
@@ -8964,6 +8980,7 @@ function loadSrsDiscoveryIdMapFromContextDir(contextDir2, log) {
|
|
|
8964
8980
|
}
|
|
8965
8981
|
|
|
8966
8982
|
// src/analysis/contextSrsDiscoveryUpload.ts
|
|
8983
|
+
var UPLOAD_TARGET_FILENAME = "easyspecs-upload-target.json";
|
|
8967
8984
|
var ADAPTIVE_B_INITIAL = 100;
|
|
8968
8985
|
var ADAPTIVE_B_MIN = 1;
|
|
8969
8986
|
var ADAPTIVE_B_MAX = 500;
|
|
@@ -9197,7 +9214,7 @@ function stringifyForSrs13Debug(value) {
|
|
|
9197
9214
|
return truncateForSrs13DebugLog(String(value));
|
|
9198
9215
|
}
|
|
9199
9216
|
}
|
|
9200
|
-
function logSrs13HttpDebug(log,
|
|
9217
|
+
function logSrs13HttpDebug(log, path47, method, requestBody, error) {
|
|
9201
9218
|
if (!log) {
|
|
9202
9219
|
return;
|
|
9203
9220
|
}
|
|
@@ -9207,7 +9224,7 @@ function logSrs13HttpDebug(log, path45, method, requestBody, error) {
|
|
|
9207
9224
|
} catch {
|
|
9208
9225
|
bodyBytes = 0;
|
|
9209
9226
|
}
|
|
9210
|
-
log(`[upload] debug ${method} ${
|
|
9227
|
+
log(`[upload] debug ${method} ${path47} \u2014 request body ~${bodyBytes} bytes (JSON below):`);
|
|
9211
9228
|
log(stringifyForSrs13Debug(requestBody));
|
|
9212
9229
|
const raw = httpApiResponseBody(error);
|
|
9213
9230
|
const status = error && typeof error === "object" && "status" in error ? Number(error.status) : void 0;
|
|
@@ -9311,18 +9328,18 @@ async function runParallelSinglesIntoAccum(requestJson, applicationId, paths, on
|
|
|
9311
9328
|
const recentWaveMs = [];
|
|
9312
9329
|
let idx = 0;
|
|
9313
9330
|
const runOne = async (absPath) => {
|
|
9314
|
-
const
|
|
9331
|
+
const basename13 = path12.basename(absPath);
|
|
9315
9332
|
let content;
|
|
9316
9333
|
try {
|
|
9317
9334
|
content = fs11.readFileSync(absPath, "utf8");
|
|
9318
9335
|
} catch (e) {
|
|
9319
9336
|
const msg = errorMessage(e);
|
|
9320
|
-
logSrs13Failure(log, `read file file=${
|
|
9337
|
+
logSrs13Failure(log, `read file file=${basename13}`, msg);
|
|
9321
9338
|
accum.failed.push({ path: absPath, message: msg });
|
|
9322
9339
|
return;
|
|
9323
9340
|
}
|
|
9324
9341
|
const existingId = resolveExistingId(absPath);
|
|
9325
|
-
const payload = buildSrsDiscoverySavePayload(applicationId,
|
|
9342
|
+
const payload = buildSrsDiscoverySavePayload(applicationId, basename13, content, existingId);
|
|
9326
9343
|
try {
|
|
9327
9344
|
await postSingleCreate(requestJson, payload, log);
|
|
9328
9345
|
accum.succeeded.push(absPath);
|
|
@@ -9336,7 +9353,7 @@ async function runParallelSinglesIntoAccum(requestJson, applicationId, paths, on
|
|
|
9336
9353
|
throw e;
|
|
9337
9354
|
}
|
|
9338
9355
|
const msg = errorMessage(e);
|
|
9339
|
-
logSrs13Failure(log, `POST /api/content/srs_discovery file=${
|
|
9356
|
+
logSrs13Failure(log, `POST /api/content/srs_discovery file=${basename13}`, msg);
|
|
9340
9357
|
accum.failed.push({ path: absPath, message: msg });
|
|
9341
9358
|
}
|
|
9342
9359
|
};
|
|
@@ -9418,18 +9435,18 @@ async function executeContextSrsDiscoveryUploadPhase(filePaths, phaseOpts, accum
|
|
|
9418
9435
|
const items = [];
|
|
9419
9436
|
const chunkPaths = [];
|
|
9420
9437
|
for (const absPath of chunk) {
|
|
9421
|
-
const
|
|
9438
|
+
const basename13 = path12.basename(absPath);
|
|
9422
9439
|
let content;
|
|
9423
9440
|
try {
|
|
9424
9441
|
content = fs11.readFileSync(absPath, "utf8");
|
|
9425
9442
|
} catch (e) {
|
|
9426
9443
|
const msg = errorMessage(e);
|
|
9427
|
-
logSrs13Failure(log, `read file file=${
|
|
9444
|
+
logSrs13Failure(log, `read file file=${basename13}`, msg);
|
|
9428
9445
|
accum.failed.push({ path: absPath, message: msg });
|
|
9429
9446
|
continue;
|
|
9430
9447
|
}
|
|
9431
9448
|
const existingId = resolveExistingId(absPath);
|
|
9432
|
-
items.push(buildSrsDiscoverySavePayloadForBatch(applicationId,
|
|
9449
|
+
items.push(buildSrsDiscoverySavePayloadForBatch(applicationId, basename13, content, existingId));
|
|
9433
9450
|
chunkPaths.push(absPath);
|
|
9434
9451
|
}
|
|
9435
9452
|
if (items.length === 0) {
|
|
@@ -9836,9 +9853,9 @@ var ANALYSIS_STATIC_CONTEXT_OUTPUTS = [
|
|
|
9836
9853
|
"tech-stack-list.json"
|
|
9837
9854
|
];
|
|
9838
9855
|
function discoverDynamicAnalysisTestSteps(contextDir2) {
|
|
9839
|
-
const staticOutputs = ANALYSIS_STATIC_CONTEXT_OUTPUTS.map((
|
|
9840
|
-
basename:
|
|
9841
|
-
exists: nonEmptyContextFile(path14.join(contextDir2,
|
|
9856
|
+
const staticOutputs = ANALYSIS_STATIC_CONTEXT_OUTPUTS.map((basename13) => ({
|
|
9857
|
+
basename: basename13,
|
|
9858
|
+
exists: nonEmptyContextFile(path14.join(contextDir2, basename13))
|
|
9842
9859
|
}));
|
|
9843
9860
|
const featuresPath = path14.join(contextDir2, "features-list.json");
|
|
9844
9861
|
const featuresData = readJson(featuresPath);
|
|
@@ -10009,8 +10026,8 @@ function aceCurationPath(contextDir2, agentStem, runId) {
|
|
|
10009
10026
|
function aceConsolidatedSessionsJsonlPath(contextDir2) {
|
|
10010
10027
|
return path15.join(aceLearningsRoot(contextDir2), ACE_CONSOLIDATED_SESSIONS_JSONL);
|
|
10011
10028
|
}
|
|
10012
|
-
function opencodeAceSchemaPath(worktreeRoot,
|
|
10013
|
-
return path15.join(worktreeRoot, ".opencode", "schemas", "ace",
|
|
10029
|
+
function opencodeAceSchemaPath(worktreeRoot, basename13) {
|
|
10030
|
+
return path15.join(worktreeRoot, ".opencode", "schemas", "ace", basename13);
|
|
10014
10031
|
}
|
|
10015
10032
|
|
|
10016
10033
|
// src/analysis/aceJsonValidate.ts
|
|
@@ -10824,9 +10841,9 @@ function runOpenCodeAgent(cwd, args, options) {
|
|
|
10824
10841
|
log?.(`[AgentCode] command: ${formatCliCommandForLog(cmd, args)}`);
|
|
10825
10842
|
log?.(`[AgentCode] cwd: ${JSON.stringify(cwd)}`);
|
|
10826
10843
|
log?.(`[AgentCode] argv: ${JSON.stringify(args)}`);
|
|
10827
|
-
return new Promise((
|
|
10844
|
+
return new Promise((resolve17) => {
|
|
10828
10845
|
if (sig?.aborted) {
|
|
10829
|
-
|
|
10846
|
+
resolve17({ ok: false, message: "Stopped by user.", cancelled: true });
|
|
10830
10847
|
return;
|
|
10831
10848
|
}
|
|
10832
10849
|
const spawnEnv = options?.childEnv && Object.keys(options.childEnv).length > 0 ? { ...process.env, ...options.childEnv } : process.env;
|
|
@@ -10896,7 +10913,7 @@ ${truncateForDiag(outBody, DIAG_STDOUT_MAX)}`);
|
|
|
10896
10913
|
if (diag) {
|
|
10897
10914
|
finishDiag(diag.label, diag.code, diag.dumpStreams);
|
|
10898
10915
|
}
|
|
10899
|
-
|
|
10916
|
+
resolve17(result);
|
|
10900
10917
|
};
|
|
10901
10918
|
let onAbort;
|
|
10902
10919
|
const clearAbortHandler = () => {
|
|
@@ -12958,16 +12975,16 @@ function outputPaths(step, worktreeRoot, listTarget) {
|
|
|
12958
12975
|
if (!fe) {
|
|
12959
12976
|
throw new Error("listUseCases requires listTarget.featureCode");
|
|
12960
12977
|
}
|
|
12961
|
-
const
|
|
12962
|
-
return { absolute: path24.join(ctx,
|
|
12978
|
+
const basename13 = `${fe}-use-cases-list.json`;
|
|
12979
|
+
return { absolute: path24.join(ctx, basename13), basename: basename13 };
|
|
12963
12980
|
}
|
|
12964
12981
|
case "reviewUseCasesList": {
|
|
12965
12982
|
const fe = listTarget?.featureCode;
|
|
12966
12983
|
if (!fe) {
|
|
12967
12984
|
throw new Error("reviewUseCasesList requires listTarget.featureCode");
|
|
12968
12985
|
}
|
|
12969
|
-
const
|
|
12970
|
-
return { absolute: path24.join(ctx,
|
|
12986
|
+
const basename13 = `${fe}-use-cases-list.json`;
|
|
12987
|
+
return { absolute: path24.join(ctx, basename13), basename: basename13 };
|
|
12971
12988
|
}
|
|
12972
12989
|
case "listScenarios": {
|
|
12973
12990
|
const fe = listTarget?.featureCode;
|
|
@@ -12975,8 +12992,8 @@ function outputPaths(step, worktreeRoot, listTarget) {
|
|
|
12975
12992
|
if (!fe || !uc) {
|
|
12976
12993
|
throw new Error("listScenarios requires listTarget.featureCode and useCaseCode");
|
|
12977
12994
|
}
|
|
12978
|
-
const
|
|
12979
|
-
return { absolute: path24.join(ctx,
|
|
12995
|
+
const basename13 = `${fe}_${uc}-scenarios-list.json`;
|
|
12996
|
+
return { absolute: path24.join(ctx, basename13), basename: basename13 };
|
|
12980
12997
|
}
|
|
12981
12998
|
case "reviewScenariosList": {
|
|
12982
12999
|
const fe = listTarget?.featureCode;
|
|
@@ -12984,24 +13001,24 @@ function outputPaths(step, worktreeRoot, listTarget) {
|
|
|
12984
13001
|
if (!fe || !uc) {
|
|
12985
13002
|
throw new Error("reviewScenariosList requires listTarget.featureCode and useCaseCode");
|
|
12986
13003
|
}
|
|
12987
|
-
const
|
|
12988
|
-
return { absolute: path24.join(ctx,
|
|
13004
|
+
const basename13 = `${fe}_${uc}-scenarios-list.json`;
|
|
13005
|
+
return { absolute: path24.join(ctx, basename13), basename: basename13 };
|
|
12989
13006
|
}
|
|
12990
13007
|
case "listEntityFields": {
|
|
12991
13008
|
const dm = listTarget?.entityCode;
|
|
12992
13009
|
if (!dm) {
|
|
12993
13010
|
throw new Error("listEntityFields requires listTarget.entityCode");
|
|
12994
13011
|
}
|
|
12995
|
-
const
|
|
12996
|
-
return { absolute: path24.join(ctx,
|
|
13012
|
+
const basename13 = `${dm}-fields-list.json`;
|
|
13013
|
+
return { absolute: path24.join(ctx, basename13), basename: basename13 };
|
|
12997
13014
|
}
|
|
12998
13015
|
case "reviewEntityFieldsList": {
|
|
12999
13016
|
const dm = listTarget?.entityCode;
|
|
13000
13017
|
if (!dm) {
|
|
13001
13018
|
throw new Error("reviewEntityFieldsList requires listTarget.entityCode");
|
|
13002
13019
|
}
|
|
13003
|
-
const
|
|
13004
|
-
return { absolute: path24.join(ctx,
|
|
13020
|
+
const basename13 = `${dm}-fields-list.json`;
|
|
13021
|
+
return { absolute: path24.join(ctx, basename13), basename: basename13 };
|
|
13005
13022
|
}
|
|
13006
13023
|
default: {
|
|
13007
13024
|
const _u = step;
|
|
@@ -15136,91 +15153,91 @@ function mdItem(id, parentIds, t) {
|
|
|
15136
15153
|
};
|
|
15137
15154
|
}
|
|
15138
15155
|
function featureDetailTarget(contextDir2, code, name, slug) {
|
|
15139
|
-
const
|
|
15156
|
+
const basename13 = `${code}-${slug}.md`;
|
|
15140
15157
|
return {
|
|
15141
15158
|
openCodeAgentStem: "agent-md-feature-detail",
|
|
15142
15159
|
agentId: "ctx-md-feature-detail",
|
|
15143
15160
|
displayName: "Feature detail",
|
|
15144
|
-
outputBasename:
|
|
15161
|
+
outputBasename: basename13,
|
|
15145
15162
|
taskDescription: `Document feature **${code}** (**${name}**, slug **${slug}**) per \`.gluecharm/context/features-list.json\`: scope, behaviour, main dependencies, entry points, and links to code under the worktree. Write exactly one markdown file at the output path; cite substantive claims with file and line (or range).`,
|
|
15146
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15163
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15147
15164
|
};
|
|
15148
15165
|
}
|
|
15149
15166
|
function viewDetailTarget(row2, contextDir2) {
|
|
15150
|
-
const
|
|
15167
|
+
const basename13 = `${row2.code}-${row2.slug}.md`;
|
|
15151
15168
|
return {
|
|
15152
15169
|
openCodeAgentStem: "agent-md-view-detail",
|
|
15153
15170
|
agentId: "ctx-md-view-detail",
|
|
15154
15171
|
displayName: "View detail",
|
|
15155
|
-
outputBasename:
|
|
15172
|
+
outputBasename: basename13,
|
|
15156
15173
|
taskDescription: `Describe view **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/experiences-list.json\`: purpose, layout, controls, navigation, data shown; ground in components and routes.`,
|
|
15157
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15174
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15158
15175
|
};
|
|
15159
15176
|
}
|
|
15160
15177
|
function interactionDetailTarget(viewCode, ix, contextDir2) {
|
|
15161
|
-
const
|
|
15178
|
+
const basename13 = `${viewCode}_${ix.code}-${ix.slug}.md`;
|
|
15162
15179
|
return {
|
|
15163
15180
|
openCodeAgentStem: "agent-md-interaction-detail",
|
|
15164
15181
|
agentId: "ctx-md-interaction-detail",
|
|
15165
15182
|
displayName: "Interaction detail",
|
|
15166
|
-
outputBasename:
|
|
15183
|
+
outputBasename: basename13,
|
|
15167
15184
|
taskDescription: `Document interaction **${ix.code}** (**${ix.name}**) on view **${viewCode}** per \`.gluecharm/context/experiences-list.json\`.`,
|
|
15168
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15185
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15169
15186
|
};
|
|
15170
15187
|
}
|
|
15171
15188
|
function serviceDetailTarget(row2, contextDir2) {
|
|
15172
|
-
const
|
|
15189
|
+
const basename13 = `${row2.code}-${row2.slug}.md`;
|
|
15173
15190
|
return {
|
|
15174
15191
|
openCodeAgentStem: "agent-md-service-detail",
|
|
15175
15192
|
agentId: "ctx-md-service-detail",
|
|
15176
15193
|
displayName: "Service detail",
|
|
15177
|
-
outputBasename:
|
|
15194
|
+
outputBasename: basename13,
|
|
15178
15195
|
taskDescription: `Describe service **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/services-list.json\`: responsibilities, consumers, errors, integration points.`,
|
|
15179
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15196
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15180
15197
|
};
|
|
15181
15198
|
}
|
|
15182
15199
|
function methodDetailTarget(svc, m, contextDir2) {
|
|
15183
|
-
const
|
|
15200
|
+
const basename13 = `${svc.code}_${m.code}-${m.slug}.md`;
|
|
15184
15201
|
return {
|
|
15185
15202
|
openCodeAgentStem: "agent-md-method-detail",
|
|
15186
15203
|
agentId: "ctx-md-method-detail",
|
|
15187
15204
|
displayName: "Method detail",
|
|
15188
|
-
outputBasename:
|
|
15205
|
+
outputBasename: basename13,
|
|
15189
15206
|
taskDescription: `Document method **${m.code}** (**${m.name}**) on service **${svc.code}** per \`.gluecharm/context/services-list.json\`.`,
|
|
15190
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15207
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15191
15208
|
};
|
|
15192
15209
|
}
|
|
15193
15210
|
function entityDetailTarget(dmCode, ename, slug, contextDir2) {
|
|
15194
|
-
const
|
|
15211
|
+
const basename13 = `${dmCode}-${slug}.md`;
|
|
15195
15212
|
return {
|
|
15196
15213
|
openCodeAgentStem: "agent-md-entity-detail",
|
|
15197
15214
|
agentId: "ctx-md-entity-detail",
|
|
15198
15215
|
displayName: "Entity detail",
|
|
15199
|
-
outputBasename:
|
|
15216
|
+
outputBasename: basename13,
|
|
15200
15217
|
taskDescription: `Describe entity **${dmCode}** (**${ename}**, slug **${slug}**) per \`.gluecharm/context/data-model-list.json\`: lifecycle, invariants, storage, ORM/schema mapping.`,
|
|
15201
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15218
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15202
15219
|
};
|
|
15203
15220
|
}
|
|
15204
15221
|
function fieldDetailTarget(dmCode, fdCode, fname, fSlug, contextDir2) {
|
|
15205
|
-
const
|
|
15222
|
+
const basename13 = `${dmCode}_${fdCode}-${fSlug}.md`;
|
|
15206
15223
|
return {
|
|
15207
15224
|
openCodeAgentStem: "agent-md-field-detail",
|
|
15208
15225
|
agentId: "ctx-md-field-detail",
|
|
15209
15226
|
displayName: "Field detail",
|
|
15210
|
-
outputBasename:
|
|
15227
|
+
outputBasename: basename13,
|
|
15211
15228
|
taskDescription: `Document field **${fdCode}** (**${fname}**) on entity **${dmCode}** per \`.gluecharm/context/${dmCode}-fields-list.json\`. Cite types and constraints with file and line.`,
|
|
15212
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15229
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15213
15230
|
};
|
|
15214
15231
|
}
|
|
15215
15232
|
function toolDetailTarget(row2, contextDir2) {
|
|
15216
|
-
const
|
|
15233
|
+
const basename13 = `${row2.code}-${row2.slug}.md`;
|
|
15217
15234
|
return {
|
|
15218
15235
|
openCodeAgentStem: "agent-md-tool-detail",
|
|
15219
15236
|
agentId: "ctx-md-tool-detail",
|
|
15220
15237
|
displayName: "Tool detail",
|
|
15221
|
-
outputBasename:
|
|
15238
|
+
outputBasename: basename13,
|
|
15222
15239
|
taskDescription: `Describe tool **${row2.code}** (**${row2.name}**, slug **${row2.slug}**) per \`.gluecharm/context/tech-stack-list.json\`: role, version hints, configuration, boundaries.`,
|
|
15223
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15240
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15224
15241
|
};
|
|
15225
15242
|
}
|
|
15226
15243
|
function useCaseDetailTarget(feCode, ucCode, ucName, ucSlug, contextDir2) {
|
|
@@ -15237,14 +15254,14 @@ Follow bundled agent **agent-md-use-case-detail**: include **## Data inputs and
|
|
|
15237
15254
|
};
|
|
15238
15255
|
}
|
|
15239
15256
|
function scenarioDetailTarget(feCode, ucCode, scCode, scName, contextDir2) {
|
|
15240
|
-
const
|
|
15257
|
+
const basename13 = `${feCode}_${ucCode}_${scCode}.md`;
|
|
15241
15258
|
return {
|
|
15242
15259
|
openCodeAgentStem: "agent-md-scenario-detail",
|
|
15243
15260
|
agentId: "ctx-md-scenario-detail",
|
|
15244
15261
|
displayName: "Scenario detail",
|
|
15245
|
-
outputBasename:
|
|
15262
|
+
outputBasename: basename13,
|
|
15246
15263
|
taskDescription: `Document scenario **${scCode}** (**${scName}**) for **${feCode}** / **${ucCode}** per \`.gluecharm/context/${feCode}_${ucCode}-scenarios-list.json\`. Cite steps with file and line where possible.`,
|
|
15247
|
-
exists: fs31.existsSync(path29.join(contextDir2,
|
|
15264
|
+
exists: fs31.existsSync(path29.join(contextDir2, basename13)) && fs31.statSync(path29.join(contextDir2, basename13)).size > 0
|
|
15248
15265
|
};
|
|
15249
15266
|
}
|
|
15250
15267
|
function parentsDone(item, byId) {
|
|
@@ -15470,15 +15487,15 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
|
|
|
15470
15487
|
return;
|
|
15471
15488
|
}
|
|
15472
15489
|
if (completed.kind === "markdown") {
|
|
15473
|
-
const
|
|
15490
|
+
const basename13 = completed.payload.outputBasename;
|
|
15474
15491
|
const mdId = completed.id;
|
|
15475
|
-
const feFromFeatureMd =
|
|
15492
|
+
const feFromFeatureMd = basename13.match(/^(FE-\d+)-[^/]+\.md$/);
|
|
15476
15493
|
if (feFromFeatureMd) {
|
|
15477
15494
|
const fe = feFromFeatureMd[1];
|
|
15478
15495
|
add(coordItem(`coord:uc:${fe}`, [mdId], "listUseCases", { featureCode: fe }));
|
|
15479
15496
|
return;
|
|
15480
15497
|
}
|
|
15481
|
-
const xpFromView =
|
|
15498
|
+
const xpFromView = basename13.match(/^(XP-\d+)-[^/]+\.md$/);
|
|
15482
15499
|
if (xpFromView) {
|
|
15483
15500
|
const xp = xpFromView[1];
|
|
15484
15501
|
const row2 = discoverExperienceTreeRows(contextDir2).find((r) => r.code === xp);
|
|
@@ -15490,7 +15507,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
|
|
|
15490
15507
|
}
|
|
15491
15508
|
return;
|
|
15492
15509
|
}
|
|
15493
|
-
const svFromSvc =
|
|
15510
|
+
const svFromSvc = basename13.match(/^(SV-\d+)-[^/]+\.md$/);
|
|
15494
15511
|
if (svFromSvc) {
|
|
15495
15512
|
const sv = svFromSvc[1];
|
|
15496
15513
|
const srow = discoverServiceTreeRows(contextDir2).find((r) => r.code === sv);
|
|
@@ -15502,7 +15519,7 @@ function expandAfterSuccess(completed, contextDir2, byId, fifo) {
|
|
|
15502
15519
|
}
|
|
15503
15520
|
return;
|
|
15504
15521
|
}
|
|
15505
|
-
const dmFromEnt =
|
|
15522
|
+
const dmFromEnt = basename13.match(/^(DM-\d+)-[^/]+\.md$/);
|
|
15506
15523
|
if (dmFromEnt) {
|
|
15507
15524
|
const dm = dmFromEnt[1];
|
|
15508
15525
|
add(coordItem(`coord:ef:${dm}`, [mdId], "listEntityFields", { entityCode: dm }));
|
|
@@ -15668,8 +15685,8 @@ async function drainArtefactWorkPool(p) {
|
|
|
15668
15685
|
const artefactRunId = readArtefactRunSnapshot(storageContext)?.runId ?? "unknown-run";
|
|
15669
15686
|
let active = 0;
|
|
15670
15687
|
let wake;
|
|
15671
|
-
const waitTurn = () => new Promise((
|
|
15672
|
-
wake =
|
|
15688
|
+
const waitTurn = () => new Promise((resolve17) => {
|
|
15689
|
+
wake = resolve17;
|
|
15673
15690
|
});
|
|
15674
15691
|
const persist = async () => {
|
|
15675
15692
|
const items = {};
|
|
@@ -15997,7 +16014,7 @@ function macroSleep(ms, signal) {
|
|
|
15997
16014
|
if (signal.aborted) {
|
|
15998
16015
|
return Promise.reject(new DOMException("The operation was aborted.", "AbortError"));
|
|
15999
16016
|
}
|
|
16000
|
-
return new Promise((
|
|
16017
|
+
return new Promise((resolve17, reject) => {
|
|
16001
16018
|
const onAbort = () => {
|
|
16002
16019
|
clearTimeout(t);
|
|
16003
16020
|
signal.removeEventListener("abort", onAbort);
|
|
@@ -16005,7 +16022,7 @@ function macroSleep(ms, signal) {
|
|
|
16005
16022
|
};
|
|
16006
16023
|
const t = setTimeout(() => {
|
|
16007
16024
|
signal.removeEventListener("abort", onAbort);
|
|
16008
|
-
|
|
16025
|
+
resolve17();
|
|
16009
16026
|
}, ms);
|
|
16010
16027
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
16011
16028
|
});
|
|
@@ -16373,8 +16390,8 @@ async function runMacroAnalysisOrchestration(deps) {
|
|
|
16373
16390
|
}
|
|
16374
16391
|
|
|
16375
16392
|
// src/analysis/macroHeadlessHost.ts
|
|
16376
|
-
var
|
|
16377
|
-
var
|
|
16393
|
+
var fs41 = __toESM(require("node:fs"));
|
|
16394
|
+
var path40 = __toESM(require("node:path"));
|
|
16378
16395
|
|
|
16379
16396
|
// src/stores/analysisPipelineStore.ts
|
|
16380
16397
|
var STORAGE_KEY2 = "easyspecs.analysis.pipelineRun.v1";
|
|
@@ -16540,8 +16557,8 @@ function expectedFeatureDetailBasenameFromRow(row2) {
|
|
|
16540
16557
|
}
|
|
16541
16558
|
return `${code}-${slug}.md`;
|
|
16542
16559
|
}
|
|
16543
|
-
function ctxPath(contextDir2,
|
|
16544
|
-
return path31.join(contextDir2,
|
|
16560
|
+
function ctxPath(contextDir2, basename13) {
|
|
16561
|
+
return path31.join(contextDir2, basename13);
|
|
16545
16562
|
}
|
|
16546
16563
|
function pushTarget(targets, stem, outputBasename, taskDescription, contextDir2) {
|
|
16547
16564
|
const meta = STEM_TO_AGENT[stem];
|
|
@@ -16570,11 +16587,11 @@ function discoverDetailMarkdownGroups(contextDir2) {
|
|
|
16570
16587
|
continue;
|
|
16571
16588
|
}
|
|
16572
16589
|
const name = safeStr3(row2.name) || code;
|
|
16573
|
-
const
|
|
16590
|
+
const basename13 = `${code}-${slug}.md`;
|
|
16574
16591
|
pushTarget(
|
|
16575
16592
|
featureTargets,
|
|
16576
16593
|
"agent-md-feature-detail",
|
|
16577
|
-
|
|
16594
|
+
basename13,
|
|
16578
16595
|
`Document feature **${code}** (**${name}**, slug **${slug}**) per \`.gluecharm/context/features-list.json\`: scope, behaviour, main dependencies, entry points, and links to code under the worktree. Write exactly one markdown file at the output path; cite substantive claims with file and line (or range).`,
|
|
16579
16596
|
contextDir2
|
|
16580
16597
|
);
|
|
@@ -17762,8 +17779,8 @@ function formatAjvErrors6(errors) {
|
|
|
17762
17779
|
});
|
|
17763
17780
|
}
|
|
17764
17781
|
var ajv = new import__6.default({ allErrors: true, strict: false });
|
|
17765
|
-
function compileSchema(
|
|
17766
|
-
const schemaPath = path36.join(schemasDir(),
|
|
17782
|
+
function compileSchema(basename13) {
|
|
17783
|
+
const schemaPath = path36.join(schemasDir(), basename13);
|
|
17767
17784
|
const schemaRaw = stripUtf8Bom5(fs37.readFileSync(schemaPath, "utf-8"));
|
|
17768
17785
|
const schema = JSON.parse(schemaRaw);
|
|
17769
17786
|
return ajv.compile(schema);
|
|
@@ -19024,6 +19041,31 @@ async function runReferenceCoverageExecutionReport(p) {
|
|
|
19024
19041
|
return { ok: true, outputAbsolutePath: outAbs };
|
|
19025
19042
|
}
|
|
19026
19043
|
|
|
19044
|
+
// src/gluecharm/minimalGluecharmLayout.ts
|
|
19045
|
+
var fs40 = __toESM(require("node:fs"));
|
|
19046
|
+
var path39 = __toESM(require("node:path"));
|
|
19047
|
+
var MINIMAL_GLUECHARM_RELATIVE_DIRS = [
|
|
19048
|
+
[".gluecharm", "docs", "srs"],
|
|
19049
|
+
[".gluecharm", "content"],
|
|
19050
|
+
[".gluecharm", "logs"],
|
|
19051
|
+
[".gluecharm", "context"]
|
|
19052
|
+
];
|
|
19053
|
+
function ensureMinimalGluecharmLayoutNode(repoRootAbs) {
|
|
19054
|
+
const root = path39.resolve(repoRootAbs);
|
|
19055
|
+
for (const segments of MINIMAL_GLUECHARM_RELATIVE_DIRS) {
|
|
19056
|
+
const dir = path39.join(root, ...segments);
|
|
19057
|
+
try {
|
|
19058
|
+
fs40.mkdirSync(dir, { recursive: true });
|
|
19059
|
+
} catch (e) {
|
|
19060
|
+
const err = e;
|
|
19061
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
19062
|
+
const code = err.code ? ` (${err.code})` : "";
|
|
19063
|
+
return { ok: false, error: `Could not create directory ${dir}${code}: ${msg}` };
|
|
19064
|
+
}
|
|
19065
|
+
}
|
|
19066
|
+
return { ok: true };
|
|
19067
|
+
}
|
|
19068
|
+
|
|
19027
19069
|
// src/analysis/macroHeadlessHost.ts
|
|
19028
19070
|
function buildMacroOrchestrationDepsHeadless(input) {
|
|
19029
19071
|
const { storageContext, repoRoot, agentsDirFs, buildOpenCodeOptions, log, signal, macroConfig } = input;
|
|
@@ -19042,13 +19084,13 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19042
19084
|
},
|
|
19043
19085
|
runPrepareAnalysisWorktree: async (resume) => {
|
|
19044
19086
|
if (resume) {
|
|
19045
|
-
if (adHocWorktree &&
|
|
19087
|
+
if (adHocWorktree && fs41.existsSync(path40.join(adHocWorktree.path, ".git"))) {
|
|
19046
19088
|
return { ok: true };
|
|
19047
19089
|
}
|
|
19048
19090
|
const snap = readAnalysisWorkspaceSnapshot(storageContext);
|
|
19049
19091
|
const wtPath = snap?.adHocWorktreePath?.trim();
|
|
19050
19092
|
const repo = snap?.adHocRepositoryRoot?.trim() || repoRoot;
|
|
19051
|
-
if (wtPath &&
|
|
19093
|
+
if (wtPath && fs41.existsSync(path40.join(wtPath, ".git"))) {
|
|
19052
19094
|
adHocWorktree = attachWorktreeHandle(wtPath, repo);
|
|
19053
19095
|
macroSourceBranch = snap?.adHocSourceBranchAtCreation;
|
|
19054
19096
|
macroFinalize = () => {
|
|
@@ -19113,10 +19155,11 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19113
19155
|
return { ok: false, error: "No analysis checkout." };
|
|
19114
19156
|
}
|
|
19115
19157
|
if (resume) {
|
|
19116
|
-
const
|
|
19117
|
-
if (!
|
|
19118
|
-
return { ok: false, error:
|
|
19158
|
+
const layout = ensureMinimalGluecharmLayoutNode(ar);
|
|
19159
|
+
if (!layout.ok) {
|
|
19160
|
+
return { ok: false, error: layout.error };
|
|
19119
19161
|
}
|
|
19162
|
+
const ctxDir = path40.join(ar, ".gluecharm", "context");
|
|
19120
19163
|
const snap = readArtefactRunSnapshot(storageContext);
|
|
19121
19164
|
const rows = listMissingArtefacts(ctxDir, ar, snap);
|
|
19122
19165
|
if (rows.length === 0) {
|
|
@@ -19130,7 +19173,7 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19130
19173
|
storageContext,
|
|
19131
19174
|
repositoryRoot: repoRoot,
|
|
19132
19175
|
worktreeRoot: ar,
|
|
19133
|
-
workspaceLabel:
|
|
19176
|
+
workspaceLabel: path40.basename(ar),
|
|
19134
19177
|
oc: {
|
|
19135
19178
|
...oc,
|
|
19136
19179
|
aceEnabled: getAceAnalysisEnabledForCheckout(ar),
|
|
@@ -19158,7 +19201,7 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19158
19201
|
}
|
|
19159
19202
|
try {
|
|
19160
19203
|
await startPipelineRun(storageContext, repoRoot);
|
|
19161
|
-
const folderName =
|
|
19204
|
+
const folderName = path40.basename(repoRoot);
|
|
19162
19205
|
const oc = buildOpenCodeOptions(handle.path);
|
|
19163
19206
|
const result = await runContextArtefactPipelineDrainFromPreparedWorktree(
|
|
19164
19207
|
storageContext,
|
|
@@ -19215,7 +19258,7 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19215
19258
|
if (!ar) {
|
|
19216
19259
|
return 0;
|
|
19217
19260
|
}
|
|
19218
|
-
const ctxDir =
|
|
19261
|
+
const ctxDir = path40.join(ar, ".gluecharm", "context");
|
|
19219
19262
|
const snap = readArtefactRunSnapshot(storageContext);
|
|
19220
19263
|
return listMissingArtefacts(ctxDir, ar, snap).length;
|
|
19221
19264
|
},
|
|
@@ -19224,10 +19267,11 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19224
19267
|
if (!ar) {
|
|
19225
19268
|
return { ok: false, message: "No analysis worktree." };
|
|
19226
19269
|
}
|
|
19227
|
-
const
|
|
19228
|
-
if (!
|
|
19229
|
-
return { ok: false, message:
|
|
19270
|
+
const layout = ensureMinimalGluecharmLayoutNode(ar);
|
|
19271
|
+
if (!layout.ok) {
|
|
19272
|
+
return { ok: false, message: layout.error };
|
|
19230
19273
|
}
|
|
19274
|
+
const contextDir2 = path40.join(ar, ".gluecharm", "context");
|
|
19231
19275
|
log(`[setup] macro \u2014 reference coverage (worktree) \u2014 ${ar}`);
|
|
19232
19276
|
const res = runCoverageReferenceValidation({
|
|
19233
19277
|
repositoryRootAbs: ar,
|
|
@@ -19276,6 +19320,10 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19276
19320
|
if (!ar) {
|
|
19277
19321
|
return { ok: false, message: "No analysis worktree." };
|
|
19278
19322
|
}
|
|
19323
|
+
const execLayout = ensureMinimalGluecharmLayoutNode(ar);
|
|
19324
|
+
if (!execLayout.ok) {
|
|
19325
|
+
return { ok: false, message: execLayout.error };
|
|
19326
|
+
}
|
|
19279
19327
|
log(`[setup] macro \u2014 reference coverage execution report \u2014 ${(/* @__PURE__ */ new Date()).toISOString()}`);
|
|
19280
19328
|
const res = await runReferenceCoverageExecutionReport({
|
|
19281
19329
|
repositoryRootAbs: ar,
|
|
@@ -19283,7 +19331,7 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19283
19331
|
diagnosticLog: log
|
|
19284
19332
|
});
|
|
19285
19333
|
if (res.ok) {
|
|
19286
|
-
return { ok: true, message: `Report: ${
|
|
19334
|
+
return { ok: true, message: `Report: ${path40.basename(res.outputAbsolutePath)}` };
|
|
19287
19335
|
}
|
|
19288
19336
|
if (res.cancelled) {
|
|
19289
19337
|
return { ok: false, cancelled: true, message: "Report generation stopped." };
|
|
@@ -19295,10 +19343,11 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19295
19343
|
if (!snap?.adHocWorktreePath) {
|
|
19296
19344
|
return { ok: false, message: "No analysis worktree for index assembly." };
|
|
19297
19345
|
}
|
|
19298
|
-
const
|
|
19299
|
-
if (!
|
|
19300
|
-
return { ok: false, message:
|
|
19346
|
+
const idxLayout = ensureMinimalGluecharmLayoutNode(snap.adHocWorktreePath);
|
|
19347
|
+
if (!idxLayout.ok) {
|
|
19348
|
+
return { ok: false, message: idxLayout.error };
|
|
19301
19349
|
}
|
|
19350
|
+
const contextDir2 = path40.join(snap.adHocWorktreePath, ".gluecharm", "context");
|
|
19302
19351
|
try {
|
|
19303
19352
|
writeIndexApplicationContext(contextDir2, void 0, {
|
|
19304
19353
|
sourceBranchAtWorktreeCreation: snap.adHocSourceBranchAtCreation
|
|
@@ -19317,10 +19366,14 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19317
19366
|
return input.runBackendSyncImpl();
|
|
19318
19367
|
}
|
|
19319
19368
|
const snap = readAnalysisWorkspaceSnapshot(storageContext);
|
|
19320
|
-
|
|
19321
|
-
if (!wtContextDir) {
|
|
19369
|
+
if (!snap?.adHocWorktreePath) {
|
|
19322
19370
|
return { ok: false, message: "No worktree context to upload." };
|
|
19323
19371
|
}
|
|
19372
|
+
const syncLayout = ensureMinimalGluecharmLayoutNode(snap.adHocWorktreePath);
|
|
19373
|
+
if (!syncLayout.ok) {
|
|
19374
|
+
return { ok: false, message: syncLayout.error };
|
|
19375
|
+
}
|
|
19376
|
+
const wtContextDir = path40.join(snap.adHocWorktreePath, ".gluecharm", "context");
|
|
19324
19377
|
let applicationId;
|
|
19325
19378
|
try {
|
|
19326
19379
|
applicationId = getEasyspecsProjectIdFromRepoConfig(readEasyspecsConfig(repoRoot))?.trim();
|
|
@@ -19336,8 +19389,8 @@ function buildMacroOrchestrationDepsHeadless(input) {
|
|
|
19336
19389
|
}
|
|
19337
19390
|
|
|
19338
19391
|
// src/analysis/coordinationDuplicatesDiagnosis.ts
|
|
19339
|
-
var
|
|
19340
|
-
var
|
|
19392
|
+
var fs42 = __toESM(require("fs"));
|
|
19393
|
+
var path41 = __toESM(require("path"));
|
|
19341
19394
|
var import__7 = __toESM(require__());
|
|
19342
19395
|
var COORDINATION_DUPLICATES_REPORT_BASENAME = "coordination-duplicates-report.json";
|
|
19343
19396
|
var COORDINATION_LIST_SCAN_ENTRIES = [
|
|
@@ -19362,30 +19415,30 @@ var RE_MD_SV = /^SV-\d+-.+\.md$/i;
|
|
|
19362
19415
|
var RE_MD_DM_FD = /^DM-\d+_FD-\d+-.+\.md$/i;
|
|
19363
19416
|
var RE_MD_DM = /^DM-\d+-.+\.md$/i;
|
|
19364
19417
|
var RE_MD_TS = /^TS-\d+-.+\.md$/i;
|
|
19365
|
-
function looksLikeCoordinationDetailMarkdownBasename(
|
|
19366
|
-
if (!
|
|
19418
|
+
function looksLikeCoordinationDetailMarkdownBasename(basename13) {
|
|
19419
|
+
if (!basename13 || basename13 !== path41.basename(basename13) || !/\.md$/i.test(basename13)) {
|
|
19367
19420
|
return false;
|
|
19368
19421
|
}
|
|
19369
|
-
if (STAPLE_CONTEXT_MARKDOWN_BASENAMES.has(
|
|
19422
|
+
if (STAPLE_CONTEXT_MARKDOWN_BASENAMES.has(basename13)) {
|
|
19370
19423
|
return false;
|
|
19371
19424
|
}
|
|
19372
|
-
return RE_MD_FE_UC_SC.test(
|
|
19425
|
+
return RE_MD_FE_UC_SC.test(basename13) || RE_MD_FE_UC_SLUG.test(basename13) || RE_MD_FE_UC_PLAIN.test(basename13) || RE_MD_FE_FEATURE.test(basename13) || RE_MD_XP_BH.test(basename13) || RE_MD_XP_VIEW.test(basename13) || RE_MD_SV_ME.test(basename13) || RE_MD_SV.test(basename13) || RE_MD_DM_FD.test(basename13) || RE_MD_DM.test(basename13) || RE_MD_TS.test(basename13);
|
|
19373
19426
|
}
|
|
19374
19427
|
function loadRawFeatureRows(contextDirAbs) {
|
|
19375
|
-
const p =
|
|
19376
|
-
if (!
|
|
19428
|
+
const p = path41.join(contextDirAbs, "features-list.json");
|
|
19429
|
+
if (!fs42.existsSync(p)) {
|
|
19377
19430
|
return [];
|
|
19378
19431
|
}
|
|
19379
19432
|
try {
|
|
19380
|
-
const raw = stripUtf8Bom6(
|
|
19433
|
+
const raw = stripUtf8Bom6(fs42.readFileSync(p, "utf-8"));
|
|
19381
19434
|
const doc = JSON.parse(raw);
|
|
19382
19435
|
return Array.isArray(doc.features) ? doc.features : [];
|
|
19383
19436
|
} catch {
|
|
19384
19437
|
return [];
|
|
19385
19438
|
}
|
|
19386
19439
|
}
|
|
19387
|
-
function hintForOrphanFeatureMarkdown(
|
|
19388
|
-
const m = /^FE-(\d+)-(.+)\.md$/i.exec(
|
|
19440
|
+
function hintForOrphanFeatureMarkdown(basename13, featureRows) {
|
|
19441
|
+
const m = /^FE-(\d+)-(.+)\.md$/i.exec(basename13);
|
|
19389
19442
|
if (!m) {
|
|
19390
19443
|
return void 0;
|
|
19391
19444
|
}
|
|
@@ -19397,8 +19450,8 @@ function hintForOrphanFeatureMarkdown(basename12, featureRows) {
|
|
|
19397
19450
|
continue;
|
|
19398
19451
|
}
|
|
19399
19452
|
const expected = expectedFeatureDetailBasenameFromRow(row2);
|
|
19400
|
-
if (expected && expected !==
|
|
19401
|
-
return `features-list row ${code} currently implies detail file ${expected} (slug/name changed \u2014 ${
|
|
19453
|
+
if (expected && expected !== basename13) {
|
|
19454
|
+
return `features-list row ${code} currently implies detail file ${expected} (slug/name changed \u2014 ${basename13} may be stale).`;
|
|
19402
19455
|
}
|
|
19403
19456
|
if (!expected) {
|
|
19404
19457
|
return `features-list row ${code} has no resolvable slug for a detail markdown basename.`;
|
|
@@ -19412,7 +19465,7 @@ function findOrphanCoordinationMarkdown(contextDirAbs) {
|
|
|
19412
19465
|
const featureRows = loadRawFeatureRows(contextDirAbs);
|
|
19413
19466
|
let dirents;
|
|
19414
19467
|
try {
|
|
19415
|
-
dirents =
|
|
19468
|
+
dirents = fs42.readdirSync(contextDirAbs, { withFileTypes: true });
|
|
19416
19469
|
} catch {
|
|
19417
19470
|
return [];
|
|
19418
19471
|
}
|
|
@@ -19665,14 +19718,14 @@ function buildCoordinationDuplicatesReport(input) {
|
|
|
19665
19718
|
const lists = [];
|
|
19666
19719
|
const duplicateGroups = [];
|
|
19667
19720
|
for (const entry of COORDINATION_LIST_SCAN_ENTRIES) {
|
|
19668
|
-
const filePath =
|
|
19669
|
-
if (!
|
|
19721
|
+
const filePath = path41.join(input.contextDirAbsolute, entry.basename);
|
|
19722
|
+
if (!fs42.existsSync(filePath)) {
|
|
19670
19723
|
lists.push({ basename: entry.basename, status: "missing" });
|
|
19671
19724
|
continue;
|
|
19672
19725
|
}
|
|
19673
19726
|
let raw;
|
|
19674
19727
|
try {
|
|
19675
|
-
raw = stripUtf8Bom6(
|
|
19728
|
+
raw = stripUtf8Bom6(fs42.readFileSync(filePath, "utf-8"));
|
|
19676
19729
|
} catch (e) {
|
|
19677
19730
|
lists.push({
|
|
19678
19731
|
basename: entry.basename,
|
|
@@ -19720,8 +19773,8 @@ var validateReportCompiled;
|
|
|
19720
19773
|
function validateReportData(data) {
|
|
19721
19774
|
if (!validateReportCompiled) {
|
|
19722
19775
|
const ajv2 = new import__7.default({ allErrors: true, strict: false });
|
|
19723
|
-
const schemaPath =
|
|
19724
|
-
const schemaRaw = stripUtf8Bom6(
|
|
19776
|
+
const schemaPath = path41.join(resolveContextListSchemasDir(), "coordination-duplicates-report.schema.json");
|
|
19777
|
+
const schemaRaw = stripUtf8Bom6(fs42.readFileSync(schemaPath, "utf-8"));
|
|
19725
19778
|
validateReportCompiled = ajv2.compile(JSON.parse(schemaRaw));
|
|
19726
19779
|
}
|
|
19727
19780
|
if (validateReportCompiled(data)) {
|
|
@@ -19739,20 +19792,20 @@ function runCoordinationDuplicatesDiagnosis(input) {
|
|
|
19739
19792
|
if (!v.ok) {
|
|
19740
19793
|
return { ok: false, message: `Report validation failed: ${v.errors.join("; ")}` };
|
|
19741
19794
|
}
|
|
19742
|
-
const outPath =
|
|
19795
|
+
const outPath = path41.join(input.contextDirAbsolute, COORDINATION_DUPLICATES_REPORT_BASENAME);
|
|
19743
19796
|
const payload = `${JSON.stringify(report, null, 2)}
|
|
19744
19797
|
`;
|
|
19745
19798
|
const tmp = `${outPath}.tmp.${process.pid}`;
|
|
19746
19799
|
try {
|
|
19747
|
-
|
|
19800
|
+
fs42.writeFileSync(tmp, payload, "utf-8");
|
|
19748
19801
|
} catch (e) {
|
|
19749
19802
|
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
19750
19803
|
}
|
|
19751
19804
|
try {
|
|
19752
|
-
|
|
19805
|
+
fs42.renameSync(tmp, outPath);
|
|
19753
19806
|
} catch (e) {
|
|
19754
19807
|
try {
|
|
19755
|
-
|
|
19808
|
+
fs42.unlinkSync(tmp);
|
|
19756
19809
|
} catch {
|
|
19757
19810
|
}
|
|
19758
19811
|
return { ok: false, message: e instanceof Error ? e.message : String(e) };
|
|
@@ -19785,6 +19838,247 @@ function runCoordinationDuplicatesDiagnosis(input) {
|
|
|
19785
19838
|
};
|
|
19786
19839
|
}
|
|
19787
19840
|
|
|
19841
|
+
// src/analysis/contextSrsDiscoveryDownload.ts
|
|
19842
|
+
var fs43 = __toESM(require("node:fs"));
|
|
19843
|
+
var path42 = __toESM(require("node:path"));
|
|
19844
|
+
var SRS_DISCOVERY_BATCH_GET_CHUNK_SIZE = 200;
|
|
19845
|
+
function isRecord6(v) {
|
|
19846
|
+
return Boolean(v) && typeof v === "object" && !Array.isArray(v);
|
|
19847
|
+
}
|
|
19848
|
+
function extractSrsDiscoveryIdsFromApplicationProperties(properties) {
|
|
19849
|
+
const raw = properties.srs_discovery;
|
|
19850
|
+
if (!Array.isArray(raw)) {
|
|
19851
|
+
return [];
|
|
19852
|
+
}
|
|
19853
|
+
const out = [];
|
|
19854
|
+
for (const item of raw) {
|
|
19855
|
+
if (typeof item === "string") {
|
|
19856
|
+
const t = item.trim();
|
|
19857
|
+
if (t.length > 0) {
|
|
19858
|
+
out.push(t);
|
|
19859
|
+
}
|
|
19860
|
+
} else if (isRecord6(item)) {
|
|
19861
|
+
const id = typeof item.id === "string" ? item.id.trim() : "";
|
|
19862
|
+
if (id.length > 0) {
|
|
19863
|
+
out.push(id);
|
|
19864
|
+
}
|
|
19865
|
+
}
|
|
19866
|
+
}
|
|
19867
|
+
return [...new Set(out)];
|
|
19868
|
+
}
|
|
19869
|
+
async function fetchApplicationPropertiesForDownload(requestJson, applicationId) {
|
|
19870
|
+
const p = `/api/content/application/${encodeURIComponent(applicationId)}`;
|
|
19871
|
+
const body = await requestJson(p, { method: "GET", timeoutMs: 6e4 });
|
|
19872
|
+
const full = parseApplicationPropertiesFromContentGet(body);
|
|
19873
|
+
if (!full || full.id !== applicationId) {
|
|
19874
|
+
throw new Error("Application not found or unexpected response shape.");
|
|
19875
|
+
}
|
|
19876
|
+
return full.properties;
|
|
19877
|
+
}
|
|
19878
|
+
function parseBatchGetSrsDiscoveryEntities(res) {
|
|
19879
|
+
assertBasicResponseSuccess(res);
|
|
19880
|
+
const root = isRecord6(res) ? res.data : void 0;
|
|
19881
|
+
if (root === void 0 || root === null) {
|
|
19882
|
+
throw new Error("Batch get response missing data.");
|
|
19883
|
+
}
|
|
19884
|
+
if (isRecord6(root) && Array.isArray(root.entities)) {
|
|
19885
|
+
return root.entities.filter(isRecord6);
|
|
19886
|
+
}
|
|
19887
|
+
if (Array.isArray(root)) {
|
|
19888
|
+
return root.filter(isRecord6);
|
|
19889
|
+
}
|
|
19890
|
+
if (isRecord6(root) && Array.isArray(root.nodes)) {
|
|
19891
|
+
return root.nodes.filter(isRecord6);
|
|
19892
|
+
}
|
|
19893
|
+
throw new Error("Unexpected batch get response shape (expected data.entities).");
|
|
19894
|
+
}
|
|
19895
|
+
function entityBag(record) {
|
|
19896
|
+
const nested = record.properties;
|
|
19897
|
+
if (isRecord6(nested)) {
|
|
19898
|
+
return nested;
|
|
19899
|
+
}
|
|
19900
|
+
return record;
|
|
19901
|
+
}
|
|
19902
|
+
function extractSrsDiscoveryEntityId(record) {
|
|
19903
|
+
const direct = typeof record.id === "string" ? record.id.trim() : "";
|
|
19904
|
+
if (direct.length > 0) {
|
|
19905
|
+
return direct;
|
|
19906
|
+
}
|
|
19907
|
+
const bag = entityBag(record);
|
|
19908
|
+
const id = typeof bag.id === "string" ? bag.id.trim() : "";
|
|
19909
|
+
return id.length > 0 ? id : void 0;
|
|
19910
|
+
}
|
|
19911
|
+
function extractSrsDiscoveryEntityName(record) {
|
|
19912
|
+
const bag = entityBag(record);
|
|
19913
|
+
const n = bag.name;
|
|
19914
|
+
return typeof n === "string" ? n.trim() : "";
|
|
19915
|
+
}
|
|
19916
|
+
function extractSrsDiscoveryEntityBody(record) {
|
|
19917
|
+
const bag = entityBag(record);
|
|
19918
|
+
const ld = bag.long_description;
|
|
19919
|
+
return typeof ld === "string" ? ld : "";
|
|
19920
|
+
}
|
|
19921
|
+
function resolveSafeContextOutputPath(contextDirAbs, nameRaw) {
|
|
19922
|
+
const norm = nameRaw.replace(/\\/g, "/").trim();
|
|
19923
|
+
if (!norm || norm.includes("\0")) {
|
|
19924
|
+
return null;
|
|
19925
|
+
}
|
|
19926
|
+
const trimmed = norm.replace(/^\/+/, "");
|
|
19927
|
+
const segments = trimmed.split("/");
|
|
19928
|
+
for (const seg of segments) {
|
|
19929
|
+
if (seg.length === 0 || seg === "." || seg === "..") {
|
|
19930
|
+
return null;
|
|
19931
|
+
}
|
|
19932
|
+
}
|
|
19933
|
+
const resolved = path42.resolve(contextDirAbs, ...segments);
|
|
19934
|
+
const rel = path42.relative(contextDirAbs, resolved);
|
|
19935
|
+
if (rel.startsWith("..") || path42.isAbsolute(rel)) {
|
|
19936
|
+
return null;
|
|
19937
|
+
}
|
|
19938
|
+
return resolved;
|
|
19939
|
+
}
|
|
19940
|
+
function chunkIds(ids, size) {
|
|
19941
|
+
const out = [];
|
|
19942
|
+
for (let i = 0; i < ids.length; i += size) {
|
|
19943
|
+
out.push(ids.slice(i, i + size));
|
|
19944
|
+
}
|
|
19945
|
+
return out;
|
|
19946
|
+
}
|
|
19947
|
+
function clearContextDirectoryForCloudReplace(contextDirAbs) {
|
|
19948
|
+
if (!fs43.existsSync(contextDirAbs) || !fs43.statSync(contextDirAbs).isDirectory()) {
|
|
19949
|
+
return { filesRemoved: 0 };
|
|
19950
|
+
}
|
|
19951
|
+
const preserveAbs = path42.resolve(contextDirAbs, UPLOAD_TARGET_FILENAME);
|
|
19952
|
+
const preserveSet = /* @__PURE__ */ new Set();
|
|
19953
|
+
if (fs43.existsSync(preserveAbs) && fs43.statSync(preserveAbs).isFile()) {
|
|
19954
|
+
preserveSet.add(preserveAbs);
|
|
19955
|
+
}
|
|
19956
|
+
let filesRemoved = 0;
|
|
19957
|
+
const walkRm = (dir) => {
|
|
19958
|
+
let entries;
|
|
19959
|
+
try {
|
|
19960
|
+
entries = fs43.readdirSync(dir, { withFileTypes: true });
|
|
19961
|
+
} catch {
|
|
19962
|
+
return;
|
|
19963
|
+
}
|
|
19964
|
+
for (const e of entries) {
|
|
19965
|
+
const full = path42.join(dir, e.name);
|
|
19966
|
+
if (e.isDirectory()) {
|
|
19967
|
+
walkRm(full);
|
|
19968
|
+
try {
|
|
19969
|
+
fs43.rmdirSync(full);
|
|
19970
|
+
} catch {
|
|
19971
|
+
}
|
|
19972
|
+
} else if (e.isFile()) {
|
|
19973
|
+
const abs = path42.resolve(full);
|
|
19974
|
+
if (preserveSet.has(abs)) {
|
|
19975
|
+
continue;
|
|
19976
|
+
}
|
|
19977
|
+
try {
|
|
19978
|
+
fs43.unlinkSync(abs);
|
|
19979
|
+
filesRemoved += 1;
|
|
19980
|
+
} catch {
|
|
19981
|
+
}
|
|
19982
|
+
}
|
|
19983
|
+
}
|
|
19984
|
+
};
|
|
19985
|
+
walkRm(contextDirAbs);
|
|
19986
|
+
return { filesRemoved };
|
|
19987
|
+
}
|
|
19988
|
+
var BATCH_GET_PATH = "/api/batch/content/srs_discovery/get";
|
|
19989
|
+
async function runContextSrsDiscoveryDownload(opts) {
|
|
19990
|
+
const chunkSize = opts.batchChunkSize ?? SRS_DISCOVERY_BATCH_GET_CHUNK_SIZE;
|
|
19991
|
+
const timeoutMs = opts.timeoutMs ?? SRS_DISCOVERY_BATCH_SAVE_TIMEOUT_MS;
|
|
19992
|
+
const log = opts.log;
|
|
19993
|
+
const props = await fetchApplicationPropertiesForDownload(opts.requestJson, opts.applicationId);
|
|
19994
|
+
const ids = extractSrsDiscoveryIdsFromApplicationProperties(props);
|
|
19995
|
+
let localFilesRemoved = 0;
|
|
19996
|
+
if (opts.replaceFromCloud) {
|
|
19997
|
+
const clr = clearContextDirectoryForCloudReplace(opts.contextDirAbs);
|
|
19998
|
+
localFilesRemoved = clr.filesRemoved;
|
|
19999
|
+
log?.(`[download] removed ${String(localFilesRemoved)} local file(s) before cloud fetch (--replace-from-cloud).`);
|
|
20000
|
+
}
|
|
20001
|
+
if (ids.length === 0) {
|
|
20002
|
+
return {
|
|
20003
|
+
downloaded: 0,
|
|
20004
|
+
skipped: 0,
|
|
20005
|
+
failed: [],
|
|
20006
|
+
localFilesRemoved,
|
|
20007
|
+
succeededIds: {}
|
|
20008
|
+
};
|
|
20009
|
+
}
|
|
20010
|
+
const entityById = /* @__PURE__ */ new Map();
|
|
20011
|
+
const batches = chunkIds(ids, chunkSize);
|
|
20012
|
+
for (const chunk of batches) {
|
|
20013
|
+
const body = {
|
|
20014
|
+
ids: chunk,
|
|
20015
|
+
content_schema_prefix: SRS_DISCOVERY_CONTENT_SCHEMA_PREFIX
|
|
20016
|
+
};
|
|
20017
|
+
const req = {
|
|
20018
|
+
method: "POST",
|
|
20019
|
+
body,
|
|
20020
|
+
timeoutMs
|
|
20021
|
+
};
|
|
20022
|
+
const res = await opts.requestJson(BATCH_GET_PATH, req);
|
|
20023
|
+
const entities = parseBatchGetSrsDiscoveryEntities(res);
|
|
20024
|
+
for (const ent of entities) {
|
|
20025
|
+
const rowId = extractSrsDiscoveryEntityId(ent);
|
|
20026
|
+
if (rowId) {
|
|
20027
|
+
entityById.set(rowId, ent);
|
|
20028
|
+
}
|
|
20029
|
+
}
|
|
20030
|
+
}
|
|
20031
|
+
const failed = [];
|
|
20032
|
+
let downloaded = 0;
|
|
20033
|
+
let skipped = 0;
|
|
20034
|
+
const succeededIds = {};
|
|
20035
|
+
for (const id of ids) {
|
|
20036
|
+
const ent = entityById.get(id);
|
|
20037
|
+
if (!ent) {
|
|
20038
|
+
failed.push({ id, message: "Not returned by batch get." });
|
|
20039
|
+
continue;
|
|
20040
|
+
}
|
|
20041
|
+
const name = extractSrsDiscoveryEntityName(ent);
|
|
20042
|
+
if (!name) {
|
|
20043
|
+
failed.push({ id, message: "Missing name on srs_discovery node." });
|
|
20044
|
+
continue;
|
|
20045
|
+
}
|
|
20046
|
+
if (name === UPLOAD_TARGET_FILENAME || path42.basename(name) === UPLOAD_TARGET_FILENAME) {
|
|
20047
|
+
skipped += 1;
|
|
20048
|
+
log?.(`[download] skip ${name} (upload target row).`);
|
|
20049
|
+
continue;
|
|
20050
|
+
}
|
|
20051
|
+
const bodyText = extractSrsDiscoveryEntityBody(ent);
|
|
20052
|
+
const outAbs = resolveSafeContextOutputPath(opts.contextDirAbs, name);
|
|
20053
|
+
if (!outAbs) {
|
|
20054
|
+
failed.push({ id, name, message: "Unsafe or invalid name for local path." });
|
|
20055
|
+
continue;
|
|
20056
|
+
}
|
|
20057
|
+
fs43.mkdirSync(path42.dirname(outAbs), { recursive: true });
|
|
20058
|
+
const exists = fs43.existsSync(outAbs);
|
|
20059
|
+
if (exists && !opts.force && !opts.replaceFromCloud) {
|
|
20060
|
+
skipped += 1;
|
|
20061
|
+
log?.(`[download] skip existing ${name} (use --force to overwrite).`);
|
|
20062
|
+
continue;
|
|
20063
|
+
}
|
|
20064
|
+
try {
|
|
20065
|
+
fs43.writeFileSync(outAbs, bodyText, "utf8");
|
|
20066
|
+
downloaded += 1;
|
|
20067
|
+
succeededIds[outAbs] = id;
|
|
20068
|
+
} catch (e) {
|
|
20069
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
20070
|
+
failed.push({ id, name, message: msg });
|
|
20071
|
+
}
|
|
20072
|
+
}
|
|
20073
|
+
if (Object.keys(succeededIds).length > 0) {
|
|
20074
|
+
const mergeRes = mergeUploadIdsIntoIndexOnDisk(opts.contextDirAbs, succeededIds);
|
|
20075
|
+
if (!mergeRes.ok) {
|
|
20076
|
+
log?.(`[download] merge ids into index failed: ${mergeRes.message}`);
|
|
20077
|
+
}
|
|
20078
|
+
}
|
|
20079
|
+
return { downloaded, skipped, failed, localFilesRemoved, succeededIds };
|
|
20080
|
+
}
|
|
20081
|
+
|
|
19788
20082
|
// src/auth/authApi.ts
|
|
19789
20083
|
var API_TIMEOUT_MS = 1e4;
|
|
19790
20084
|
async function fetchWithTimeout(url, init, fetchImpl) {
|
|
@@ -19843,12 +20137,12 @@ function toFetchErrorMessage(e) {
|
|
|
19843
20137
|
|
|
19844
20138
|
// src/auth/gluecharmContentNegotiation.ts
|
|
19845
20139
|
var GLUECHARM_WS_LEGACY_JSON = "application/vnd.gluecharm.v1.ws-legacy+json";
|
|
19846
|
-
function pathWithoutQuery(
|
|
19847
|
-
const q =
|
|
19848
|
-
return q === -1 ?
|
|
20140
|
+
function pathWithoutQuery(path47) {
|
|
20141
|
+
const q = path47.indexOf("?");
|
|
20142
|
+
return q === -1 ? path47 : path47.slice(0, q);
|
|
19849
20143
|
}
|
|
19850
|
-
function isGluecharmContentApiPath(
|
|
19851
|
-
const p = pathWithoutQuery(
|
|
20144
|
+
function isGluecharmContentApiPath(path47) {
|
|
20145
|
+
const p = pathWithoutQuery(path47);
|
|
19852
20146
|
return p.startsWith("/api/content/") || p.startsWith("/api/batch/content/");
|
|
19853
20147
|
}
|
|
19854
20148
|
function gluecharmContentHeaders(method) {
|
|
@@ -19886,7 +20180,7 @@ async function fetchWithTimeout2(url, init, fetchImpl, timeoutMs, externalSignal
|
|
|
19886
20180
|
}
|
|
19887
20181
|
function createAuthenticatedRequestJson(deps) {
|
|
19888
20182
|
const fetchImpl = deps.fetchImpl ?? fetch;
|
|
19889
|
-
async function requestJson(
|
|
20183
|
+
async function requestJson(path47, options = {}) {
|
|
19890
20184
|
const base = deps.getApiBaseUrl();
|
|
19891
20185
|
if (!base) {
|
|
19892
20186
|
const err = { status: 0, message: "easyspecs.apiBaseUrl is not configured." };
|
|
@@ -19894,7 +20188,7 @@ function createAuthenticatedRequestJson(deps) {
|
|
|
19894
20188
|
}
|
|
19895
20189
|
const method = options.method ?? "GET";
|
|
19896
20190
|
const headers = { ...options.headers };
|
|
19897
|
-
if (isGluecharmContentApiPath(
|
|
20191
|
+
if (isGluecharmContentApiPath(path47)) {
|
|
19898
20192
|
Object.assign(headers, gluecharmContentHeaders(method));
|
|
19899
20193
|
} else {
|
|
19900
20194
|
if (!headers.Accept) {
|
|
@@ -19908,7 +20202,7 @@ function createAuthenticatedRequestJson(deps) {
|
|
|
19908
20202
|
if (options.withAuth !== false && access) {
|
|
19909
20203
|
headers.Authorization = `Bearer ${access}`;
|
|
19910
20204
|
}
|
|
19911
|
-
const url = `${base}${
|
|
20205
|
+
const url = `${base}${path47}`;
|
|
19912
20206
|
const timeoutMs = options.timeoutMs ?? API_TIMEOUT_MS;
|
|
19913
20207
|
const response = await fetchWithTimeout2(
|
|
19914
20208
|
url,
|
|
@@ -19929,7 +20223,7 @@ function createAuthenticatedRequestJson(deps) {
|
|
|
19929
20223
|
if (shouldRetryUnauthorized) {
|
|
19930
20224
|
const refreshed = await deps.refreshSession();
|
|
19931
20225
|
if (refreshed) {
|
|
19932
|
-
return requestJson(
|
|
20226
|
+
return requestJson(path47, { ...options, retryOnUnauthorized: false });
|
|
19933
20227
|
}
|
|
19934
20228
|
}
|
|
19935
20229
|
const fallback = payload == null ? `${response.statusText || "HTTP error"} (response body empty or not JSON)` : "Request failed.";
|
|
@@ -19939,15 +20233,15 @@ function createAuthenticatedRequestJson(deps) {
|
|
|
19939
20233
|
}
|
|
19940
20234
|
|
|
19941
20235
|
// src/cli/cliSession.ts
|
|
19942
|
-
var
|
|
20236
|
+
var fs44 = __toESM(require("node:fs"));
|
|
19943
20237
|
var os2 = __toESM(require("node:os"));
|
|
19944
|
-
var
|
|
20238
|
+
var path43 = __toESM(require("node:path"));
|
|
19945
20239
|
function defaultSessionPath() {
|
|
19946
|
-
return
|
|
20240
|
+
return path43.join(os2.homedir(), ".easyspecs", "cli-session.json");
|
|
19947
20241
|
}
|
|
19948
20242
|
var configSessionPath;
|
|
19949
20243
|
function setCliSessionPathFromConfig(absPath) {
|
|
19950
|
-
configSessionPath = absPath?.trim() ?
|
|
20244
|
+
configSessionPath = absPath?.trim() ? path43.resolve(absPath) : void 0;
|
|
19951
20245
|
}
|
|
19952
20246
|
function applyCliSessionPathFromRepoConfig(repoRoot, cfg) {
|
|
19953
20247
|
const raw = cfg.easyspecs?.cliSessionPath?.trim();
|
|
@@ -19955,7 +20249,7 @@ function applyCliSessionPathFromRepoConfig(repoRoot, cfg) {
|
|
|
19955
20249
|
setCliSessionPathFromConfig(void 0);
|
|
19956
20250
|
return;
|
|
19957
20251
|
}
|
|
19958
|
-
const abs =
|
|
20252
|
+
const abs = path43.isAbsolute(raw) ? raw : path43.join(repoRoot, raw);
|
|
19959
20253
|
setCliSessionPathFromConfig(abs);
|
|
19960
20254
|
}
|
|
19961
20255
|
function normalizeCliSessionPathForConfig(repoRoot, raw) {
|
|
@@ -19963,15 +20257,15 @@ function normalizeCliSessionPathForConfig(repoRoot, raw) {
|
|
|
19963
20257
|
if (!t) {
|
|
19964
20258
|
return "";
|
|
19965
20259
|
}
|
|
19966
|
-
const resolvedRepo =
|
|
19967
|
-
const abs =
|
|
19968
|
-
const rel =
|
|
20260
|
+
const resolvedRepo = path43.resolve(repoRoot);
|
|
20261
|
+
const abs = path43.isAbsolute(t) ? path43.normalize(t) : path43.resolve(resolvedRepo, t);
|
|
20262
|
+
const rel = path43.relative(resolvedRepo, abs);
|
|
19969
20263
|
if (rel === "") {
|
|
19970
20264
|
return abs;
|
|
19971
20265
|
}
|
|
19972
|
-
const underRepo = !rel.startsWith("..") && !
|
|
20266
|
+
const underRepo = !rel.startsWith("..") && !path43.isAbsolute(rel);
|
|
19973
20267
|
if (underRepo) {
|
|
19974
|
-
return rel.split(
|
|
20268
|
+
return rel.split(path43.sep).join("/");
|
|
19975
20269
|
}
|
|
19976
20270
|
return abs;
|
|
19977
20271
|
}
|
|
@@ -19984,10 +20278,10 @@ function effectiveCliSessionPath() {
|
|
|
19984
20278
|
function readCliSession() {
|
|
19985
20279
|
const p = effectiveCliSessionPath();
|
|
19986
20280
|
try {
|
|
19987
|
-
if (!
|
|
20281
|
+
if (!fs44.existsSync(p)) {
|
|
19988
20282
|
return void 0;
|
|
19989
20283
|
}
|
|
19990
|
-
const j = JSON.parse(
|
|
20284
|
+
const j = JSON.parse(fs44.readFileSync(p, "utf8"));
|
|
19991
20285
|
const apiBaseUrl = typeof j.apiBaseUrl === "string" ? j.apiBaseUrl.trim() : "";
|
|
19992
20286
|
const accessToken = typeof j.accessToken === "string" ? j.accessToken : "";
|
|
19993
20287
|
const refreshToken = typeof j.refreshToken === "string" ? j.refreshToken : "";
|
|
@@ -20001,33 +20295,33 @@ function readCliSession() {
|
|
|
20001
20295
|
}
|
|
20002
20296
|
function writeCliSession(s) {
|
|
20003
20297
|
const p = effectiveCliSessionPath();
|
|
20004
|
-
|
|
20005
|
-
|
|
20298
|
+
fs44.mkdirSync(path43.dirname(p), { recursive: true });
|
|
20299
|
+
fs44.writeFileSync(p, `${JSON.stringify(s, null, 2)}
|
|
20006
20300
|
`, "utf8");
|
|
20007
20301
|
}
|
|
20008
20302
|
function clearCliSession() {
|
|
20009
20303
|
const p = effectiveCliSessionPath();
|
|
20010
20304
|
try {
|
|
20011
|
-
|
|
20305
|
+
fs44.unlinkSync(p);
|
|
20012
20306
|
} catch {
|
|
20013
20307
|
}
|
|
20014
20308
|
}
|
|
20015
20309
|
|
|
20016
20310
|
// src/analysis/acePendingTraces.ts
|
|
20017
|
-
var
|
|
20018
|
-
var
|
|
20311
|
+
var fs45 = __toESM(require("fs"));
|
|
20312
|
+
var path44 = __toESM(require("path"));
|
|
20019
20313
|
function normalizeAceTraceRelativePath(rel) {
|
|
20020
20314
|
return rel.split(/[/\\]/).join("/");
|
|
20021
20315
|
}
|
|
20022
20316
|
function readCompletedTraceRelativePaths(contextDir2) {
|
|
20023
20317
|
const set = /* @__PURE__ */ new Set();
|
|
20024
20318
|
const jsonlPath = aceConsolidatedSessionsJsonlPath(contextDir2);
|
|
20025
|
-
if (!
|
|
20319
|
+
if (!fs45.existsSync(jsonlPath)) {
|
|
20026
20320
|
return set;
|
|
20027
20321
|
}
|
|
20028
20322
|
let raw;
|
|
20029
20323
|
try {
|
|
20030
|
-
raw =
|
|
20324
|
+
raw = fs45.readFileSync(jsonlPath, "utf8");
|
|
20031
20325
|
} catch {
|
|
20032
20326
|
return set;
|
|
20033
20327
|
}
|
|
@@ -20058,14 +20352,14 @@ function readCompletedTraceRelativePaths(contextDir2) {
|
|
|
20058
20352
|
}
|
|
20059
20353
|
function listPendingAceTraceFiles(contextDir2, worktreeRoot) {
|
|
20060
20354
|
const traceSchema = opencodeAceSchemaPath(worktreeRoot, ACE_SCHEMA_TRACE);
|
|
20061
|
-
if (!
|
|
20355
|
+
if (!fs45.existsSync(traceSchema)) {
|
|
20062
20356
|
return [];
|
|
20063
20357
|
}
|
|
20064
20358
|
const completed = readCompletedTraceRelativePaths(contextDir2);
|
|
20065
20359
|
const allAbs = listAceTraceFiles(contextDir2);
|
|
20066
20360
|
const pending = [];
|
|
20067
20361
|
for (const abs of allAbs) {
|
|
20068
|
-
const rel = normalizeAceTraceRelativePath(
|
|
20362
|
+
const rel = normalizeAceTraceRelativePath(path44.relative(contextDir2, abs));
|
|
20069
20363
|
const v = validateAceJsonFile(abs, traceSchema);
|
|
20070
20364
|
if (!v.ok) {
|
|
20071
20365
|
continue;
|
|
@@ -20080,7 +20374,7 @@ function listPendingAceTraceFiles(contextDir2, worktreeRoot) {
|
|
|
20080
20374
|
}
|
|
20081
20375
|
|
|
20082
20376
|
// src/analysis/aceAutoLearnPool.ts
|
|
20083
|
-
var
|
|
20377
|
+
var path45 = __toESM(require("path"));
|
|
20084
20378
|
function clampConcurrency2(n) {
|
|
20085
20379
|
if (!Number.isFinite(n)) {
|
|
20086
20380
|
return 30;
|
|
@@ -20094,8 +20388,8 @@ async function runAceAutoLearnPool(p) {
|
|
|
20094
20388
|
const fifo = [...p.traceAbsolutePaths];
|
|
20095
20389
|
let active = 0;
|
|
20096
20390
|
let wake;
|
|
20097
|
-
const waitTurn = () => new Promise((
|
|
20098
|
-
wake =
|
|
20391
|
+
const waitTurn = () => new Promise((resolve17) => {
|
|
20392
|
+
wake = resolve17;
|
|
20099
20393
|
});
|
|
20100
20394
|
const pump = () => {
|
|
20101
20395
|
wake?.();
|
|
@@ -20115,7 +20409,7 @@ async function runAceAutoLearnPool(p) {
|
|
|
20115
20409
|
abortSignal.addEventListener("abort", onPipelineAbort, { once: true });
|
|
20116
20410
|
}
|
|
20117
20411
|
}
|
|
20118
|
-
const traceRel = (abs) =>
|
|
20412
|
+
const traceRel = (abs) => path45.relative(contextDir2, abs).split(path45.sep).join("/");
|
|
20119
20413
|
const runOne = async (traceAbs) => {
|
|
20120
20414
|
if (abortSignal?.aborted) {
|
|
20121
20415
|
active -= 1;
|
|
@@ -20564,7 +20858,7 @@ function formatCliStderrLine(line, useAnsi) {
|
|
|
20564
20858
|
}
|
|
20565
20859
|
|
|
20566
20860
|
// src/cli/main.ts
|
|
20567
|
-
var PKG_VERSION = "0.0.
|
|
20861
|
+
var PKG_VERSION = "0.0.14";
|
|
20568
20862
|
function isEasyspecsConfigReadError(e) {
|
|
20569
20863
|
return e instanceof EasyspecsConfigInvalidJsonError || e instanceof EasyspecsConfigSchemaError;
|
|
20570
20864
|
}
|
|
@@ -20623,6 +20917,7 @@ Commands:
|
|
|
20623
20917
|
diagnose coverage-report --root workspace|worktree [--worktree <path>]
|
|
20624
20918
|
diagnose missing-artefacts --root workspace|worktree [--worktree <path>]
|
|
20625
20919
|
diagnose zero-reference [--worktree <path>]
|
|
20920
|
+
download context [--force] [--replace-from-cloud]
|
|
20626
20921
|
upload context | upload republish
|
|
20627
20922
|
ace clear | ace learn | ace auto-learn [--worktree <path>]
|
|
20628
20923
|
config init [--overwrite]
|
|
@@ -20643,23 +20938,23 @@ function resolveAnalysisRoot(repoRoot, rootKind, worktreePath) {
|
|
|
20643
20938
|
return repoRoot;
|
|
20644
20939
|
}
|
|
20645
20940
|
const wt = worktreePath?.trim();
|
|
20646
|
-
if (wt &&
|
|
20647
|
-
return
|
|
20941
|
+
if (wt && fs46.existsSync(path46.join(wt, ".git"))) {
|
|
20942
|
+
return path46.resolve(wt);
|
|
20648
20943
|
}
|
|
20649
20944
|
throw new Error("worktree mode requires --worktree <path> to an existing analysis checkout.");
|
|
20650
20945
|
}
|
|
20651
20946
|
function resolveAdHocCheckoutRoot(_repoRoot, storage, worktreeFlag) {
|
|
20652
20947
|
const w = worktreeFlag?.trim();
|
|
20653
20948
|
if (w) {
|
|
20654
|
-
const abs =
|
|
20655
|
-
if (
|
|
20949
|
+
const abs = path46.resolve(w);
|
|
20950
|
+
if (fs46.existsSync(path46.join(abs, ".git"))) {
|
|
20656
20951
|
return abs;
|
|
20657
20952
|
}
|
|
20658
20953
|
throw new Error(`Invalid --worktree (not a git checkout): ${abs}`);
|
|
20659
20954
|
}
|
|
20660
20955
|
const snap = readAnalysisWorkspaceSnapshot(storage);
|
|
20661
20956
|
const p = snap?.adHocWorktreePath?.trim();
|
|
20662
|
-
if (p &&
|
|
20957
|
+
if (p && fs46.existsSync(path46.join(p, ".git"))) {
|
|
20663
20958
|
return p;
|
|
20664
20959
|
}
|
|
20665
20960
|
throw new Error("No analysis checkout: run `easyspecs-cli run synthesis` first or pass `--worktree <path>`.");
|
|
@@ -20686,7 +20981,7 @@ async function runResumeRemediationPool(storage, repoRoot, analysisRoot, merged,
|
|
|
20686
20981
|
requireOpenCode(merged, flags);
|
|
20687
20982
|
const agentsDir = resolveOpenCodeAgentsDir(repoRoot, repoConfig);
|
|
20688
20983
|
assertAgentsDirExists(agentsDir);
|
|
20689
|
-
const ctxDir =
|
|
20984
|
+
const ctxDir = path46.join(analysisRoot, ".gluecharm", "context");
|
|
20690
20985
|
const snap = readArtefactRunSnapshot(storage);
|
|
20691
20986
|
const rows = listMissingArtefacts(ctxDir, analysisRoot, snap);
|
|
20692
20987
|
if (rows.length === 0) {
|
|
@@ -20704,7 +20999,7 @@ async function runResumeRemediationPool(storage, repoRoot, analysisRoot, merged,
|
|
|
20704
20999
|
storageContext: storage,
|
|
20705
21000
|
repositoryRoot: repoRoot,
|
|
20706
21001
|
worktreeRoot: analysisRoot,
|
|
20707
|
-
workspaceLabel:
|
|
21002
|
+
workspaceLabel: path46.basename(analysisRoot),
|
|
20708
21003
|
oc,
|
|
20709
21004
|
log: (line) => logErr(flags, line),
|
|
20710
21005
|
abortSignal: void 0,
|
|
@@ -20823,17 +21118,17 @@ async function main() {
|
|
|
20823
21118
|
{ easyspecs: { defaultGitRemoteUrl: url } },
|
|
20824
21119
|
{ warnMigration: (m) => logErr(flags, `[EasySpecs] ${m}`) }
|
|
20825
21120
|
);
|
|
20826
|
-
const
|
|
21121
|
+
const path47 = easyspecsConfigPath(repoRoot);
|
|
20827
21122
|
if (flags.json) {
|
|
20828
21123
|
printJsonLine({
|
|
20829
21124
|
command: "config set-git-remote",
|
|
20830
21125
|
durationMs: Date.now() - t0,
|
|
20831
21126
|
ok: true,
|
|
20832
|
-
path:
|
|
21127
|
+
path: path47,
|
|
20833
21128
|
defaultGitRemoteUrl: cfg.easyspecs?.defaultGitRemoteUrl ?? ""
|
|
20834
21129
|
});
|
|
20835
21130
|
} else {
|
|
20836
|
-
console.log(`Updated ${
|
|
21131
|
+
console.log(`Updated ${path47} \u2014 easyspecs.defaultGitRemoteUrl`);
|
|
20837
21132
|
}
|
|
20838
21133
|
process.exit(ExitCode.ok);
|
|
20839
21134
|
} catch (e) {
|
|
@@ -20851,6 +21146,9 @@ async function main() {
|
|
|
20851
21146
|
allowCreate: allowCreateConfig,
|
|
20852
21147
|
warnMigration: (m) => logErr(flags, `[EasySpecs] ${m}`)
|
|
20853
21148
|
});
|
|
21149
|
+
repoConfig = syncDefaultGitRemoteUrlIfEmpty(repoRoot, repoConfig, {
|
|
21150
|
+
warnMigration: (m) => logErr(flags, `[EasySpecs] ${m}`)
|
|
21151
|
+
});
|
|
20854
21152
|
} catch (e) {
|
|
20855
21153
|
if (isEasyspecsConfigReadError(e)) {
|
|
20856
21154
|
console.error(e.message);
|
|
@@ -20861,7 +21159,7 @@ async function main() {
|
|
|
20861
21159
|
applyCliSessionPathFromRepoConfig(repoRoot, repoConfig);
|
|
20862
21160
|
if (flags.sessionPath?.trim()) {
|
|
20863
21161
|
const sp = flags.sessionPath.trim();
|
|
20864
|
-
const abs =
|
|
21162
|
+
const abs = path46.isAbsolute(sp) ? path46.normalize(sp) : path46.resolve(repoRoot, sp);
|
|
20865
21163
|
setCliSessionPathFromConfig(abs);
|
|
20866
21164
|
}
|
|
20867
21165
|
const apiResolved = initApiBaseUrlForCli(repoRoot, flags, repoConfig);
|
|
@@ -20882,6 +21180,12 @@ async function main() {
|
|
|
20882
21180
|
process.exit(code);
|
|
20883
21181
|
throw new Error("unreachable");
|
|
20884
21182
|
};
|
|
21183
|
+
const requireMinimalGluecharmLayoutAt = (absRoot) => {
|
|
21184
|
+
const r = ensureMinimalGluecharmLayoutNode(absRoot);
|
|
21185
|
+
if (!r.ok) {
|
|
21186
|
+
finish(ExitCode.misconfiguration, { ok: false, error: r.error });
|
|
21187
|
+
}
|
|
21188
|
+
};
|
|
20885
21189
|
const pos = positionals;
|
|
20886
21190
|
try {
|
|
20887
21191
|
resetCliDiagnosticPhaseState();
|
|
@@ -20895,7 +21199,7 @@ async function main() {
|
|
|
20895
21199
|
process.exit(ExitCode.usage);
|
|
20896
21200
|
}
|
|
20897
21201
|
const agentsDir = resolveOpenCodeAgentsDir(repoRoot, repoConfig);
|
|
20898
|
-
const agentsOk =
|
|
21202
|
+
const agentsOk = fs46.existsSync(agentsDir);
|
|
20899
21203
|
const oc = getOpenCodeReadiness({
|
|
20900
21204
|
executable: merged.openCodeExecutable,
|
|
20901
21205
|
skipCredentialsCheck: merged.openCodeSkipCredentialsCheck,
|
|
@@ -21070,7 +21374,7 @@ async function main() {
|
|
|
21070
21374
|
const result = await runContextArtefactPipelineAsync(
|
|
21071
21375
|
storage,
|
|
21072
21376
|
repoRoot,
|
|
21073
|
-
|
|
21377
|
+
path46.basename(repoRoot),
|
|
21074
21378
|
agentsDir,
|
|
21075
21379
|
{
|
|
21076
21380
|
...merged.pipelineOpenCode
|
|
@@ -21124,7 +21428,8 @@ async function main() {
|
|
|
21124
21428
|
const worktree = wtExplicit ?? wtFromRoot;
|
|
21125
21429
|
if (sub === "reference-coverage") {
|
|
21126
21430
|
const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
|
|
21127
|
-
|
|
21431
|
+
requireMinimalGluecharmLayoutAt(rootAbs);
|
|
21432
|
+
const contextDir2 = path46.join(rootAbs, ".gluecharm", "context");
|
|
21128
21433
|
const res = runCoverageReferenceValidation({
|
|
21129
21434
|
repositoryRootAbs: rootAbs,
|
|
21130
21435
|
contextDirAbs: contextDir2,
|
|
@@ -21146,7 +21451,8 @@ async function main() {
|
|
|
21146
21451
|
}
|
|
21147
21452
|
if (sub === "coordination-duplicates") {
|
|
21148
21453
|
const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
|
|
21149
|
-
|
|
21454
|
+
requireMinimalGluecharmLayoutAt(rootAbs);
|
|
21455
|
+
const contextDir2 = path46.join(rootAbs, ".gluecharm", "context");
|
|
21150
21456
|
const res = runCoordinationDuplicatesDiagnosis({
|
|
21151
21457
|
contextDirAbsolute: contextDir2,
|
|
21152
21458
|
sourceRoot: rootKind === "worktree" ? "worktree" : "workspace"
|
|
@@ -21168,6 +21474,7 @@ async function main() {
|
|
|
21168
21474
|
}
|
|
21169
21475
|
if (sub === "coverage-report") {
|
|
21170
21476
|
const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
|
|
21477
|
+
requireMinimalGluecharmLayoutAt(rootAbs);
|
|
21171
21478
|
const rep = await runReferenceCoverageExecutionReport({
|
|
21172
21479
|
repositoryRootAbs: rootAbs,
|
|
21173
21480
|
diagnosticLog: (line) => logErr(flags, line)
|
|
@@ -21180,7 +21487,8 @@ async function main() {
|
|
|
21180
21487
|
}
|
|
21181
21488
|
if (sub === "missing-artefacts") {
|
|
21182
21489
|
const rootAbs = resolveAnalysisRoot(repoRoot, rootKind, worktree);
|
|
21183
|
-
|
|
21490
|
+
requireMinimalGluecharmLayoutAt(rootAbs);
|
|
21491
|
+
const ctxDir = path46.join(rootAbs, ".gluecharm", "context");
|
|
21184
21492
|
const storage = createFileBackedWorkspaceState(repoRoot);
|
|
21185
21493
|
const snap = readArtefactRunSnapshot(storage);
|
|
21186
21494
|
const rows = listMissingArtefacts(ctxDir, rootAbs, snap);
|
|
@@ -21201,6 +21509,7 @@ async function main() {
|
|
|
21201
21509
|
const storage = createFileBackedWorkspaceState(repoRoot);
|
|
21202
21510
|
const { worktree: wtAce } = parseWorktreeFlag(pos.slice(2));
|
|
21203
21511
|
const analysisRoot = resolveAdHocCheckoutRoot(repoRoot, storage, wtAce);
|
|
21512
|
+
requireMinimalGluecharmLayoutAt(analysisRoot);
|
|
21204
21513
|
const cov = readNonReferencedFilesFromRepositoryRoot(analysisRoot);
|
|
21205
21514
|
if (!cov.ok) {
|
|
21206
21515
|
finish(ExitCode.validation, { ok: false, error: cov.error });
|
|
@@ -21260,6 +21569,7 @@ async function main() {
|
|
|
21260
21569
|
cloudContextAnalyzedAt: cloudCachedAt
|
|
21261
21570
|
});
|
|
21262
21571
|
}
|
|
21572
|
+
requireMinimalGluecharmLayoutAt(repoRoot);
|
|
21263
21573
|
requireOpenCode(merged, flags);
|
|
21264
21574
|
const wantsUpload = positionals.includes("--upload");
|
|
21265
21575
|
const uploadSession = wantsUpload ? readCliSession() : void 0;
|
|
@@ -21276,8 +21586,8 @@ async function main() {
|
|
|
21276
21586
|
const runBackendSyncImpl = wantsUpload && uploadSession ? async () => {
|
|
21277
21587
|
const s = uploadSession;
|
|
21278
21588
|
const snap = readAnalysisWorkspaceSnapshot(storage);
|
|
21279
|
-
const wsContextDir =
|
|
21280
|
-
const wtContextDir = snap?.adHocWorktreePath &&
|
|
21589
|
+
const wsContextDir = path46.join(repoRoot, ".gluecharm", "context");
|
|
21590
|
+
const wtContextDir = snap?.adHocWorktreePath && fs46.existsSync(path46.join(snap.adHocWorktreePath, ".gluecharm", "context")) ? path46.join(snap.adHocWorktreePath, ".gluecharm", "context") : "";
|
|
21281
21591
|
if (!wtContextDir) {
|
|
21282
21592
|
return { ok: false, message: "No worktree context to upload." };
|
|
21283
21593
|
}
|
|
@@ -21343,6 +21653,104 @@ async function main() {
|
|
|
21343
21653
|
totalElapsedMs: res.totalElapsedMs
|
|
21344
21654
|
});
|
|
21345
21655
|
}
|
|
21656
|
+
if (pos[0] === "download" && pos[1] === "context") {
|
|
21657
|
+
const sessRaw = readCliSession();
|
|
21658
|
+
if (sessRaw === void 0) {
|
|
21659
|
+
finish(ExitCode.auth, {
|
|
21660
|
+
ok: false,
|
|
21661
|
+
error: "auth login first (`easyspecs-cli auth login --email \u2026 --password \u2026`, or `--ci` with easyspecs.auth.ciLogin in .easyspecs/config.json)"
|
|
21662
|
+
});
|
|
21663
|
+
}
|
|
21664
|
+
let force = false;
|
|
21665
|
+
let replaceFromCloud = false;
|
|
21666
|
+
for (const a of pos.slice(2)) {
|
|
21667
|
+
if (a === "--force") {
|
|
21668
|
+
force = true;
|
|
21669
|
+
continue;
|
|
21670
|
+
}
|
|
21671
|
+
if (a === "--replace-from-cloud") {
|
|
21672
|
+
replaceFromCloud = true;
|
|
21673
|
+
continue;
|
|
21674
|
+
}
|
|
21675
|
+
finish(ExitCode.usage, { ok: false, error: `unknown download context flag: ${a}` });
|
|
21676
|
+
}
|
|
21677
|
+
requireMinimalGluecharmLayoutAt(repoRoot);
|
|
21678
|
+
const ctxDir = path46.resolve(path46.join(repoRoot, ".gluecharm", "context"));
|
|
21679
|
+
const gluecharmParent = path46.dirname(ctxDir);
|
|
21680
|
+
if (path46.basename(gluecharmParent) === ".gluecharm" && path46.basename(ctxDir) === "context") {
|
|
21681
|
+
requireMinimalGluecharmLayoutAt(path46.dirname(gluecharmParent));
|
|
21682
|
+
}
|
|
21683
|
+
const appIdRaw = getEasyspecsProjectIdFromRepoConfig(repoConfig)?.trim();
|
|
21684
|
+
if (!appIdRaw) {
|
|
21685
|
+
finish(ExitCode.misconfiguration, {
|
|
21686
|
+
ok: false,
|
|
21687
|
+
error: "Missing easyspecs.easyspecsProjectId in .easyspecs/config.json."
|
|
21688
|
+
});
|
|
21689
|
+
}
|
|
21690
|
+
const applicationId = appIdRaw;
|
|
21691
|
+
const sess = sessRaw;
|
|
21692
|
+
let access = sess.accessToken;
|
|
21693
|
+
const refresh = sess.refreshToken;
|
|
21694
|
+
const requestJson = createAuthenticatedRequestJson({
|
|
21695
|
+
getApiBaseUrl: () => sess.apiBaseUrl,
|
|
21696
|
+
getAccessToken: () => access,
|
|
21697
|
+
getRefreshToken: () => refresh,
|
|
21698
|
+
refreshSession: async () => {
|
|
21699
|
+
try {
|
|
21700
|
+
const r = await refreshTokenWithApi(sess.apiBaseUrl, fetch, refresh);
|
|
21701
|
+
access = r.accessToken;
|
|
21702
|
+
writeCliSession({
|
|
21703
|
+
apiBaseUrl: sess.apiBaseUrl,
|
|
21704
|
+
accessToken: r.accessToken,
|
|
21705
|
+
refreshToken: r.refreshToken
|
|
21706
|
+
});
|
|
21707
|
+
return true;
|
|
21708
|
+
} catch {
|
|
21709
|
+
return false;
|
|
21710
|
+
}
|
|
21711
|
+
}
|
|
21712
|
+
});
|
|
21713
|
+
try {
|
|
21714
|
+
const result = await runContextSrsDiscoveryDownload({
|
|
21715
|
+
requestJson,
|
|
21716
|
+
applicationId,
|
|
21717
|
+
contextDirAbs: ctxDir,
|
|
21718
|
+
force,
|
|
21719
|
+
replaceFromCloud,
|
|
21720
|
+
log: (line) => logErr(flags, line)
|
|
21721
|
+
});
|
|
21722
|
+
const failCount = result.failed.length;
|
|
21723
|
+
const cmdOk = failCount === 0;
|
|
21724
|
+
if (!flags.json) {
|
|
21725
|
+
logErr(
|
|
21726
|
+
flags,
|
|
21727
|
+
`[download] downloaded=${String(result.downloaded)} skipped=${String(result.skipped)} failed=${String(failCount)} localRemoved=${String(result.localFilesRemoved)}`
|
|
21728
|
+
);
|
|
21729
|
+
if (failCount > 0) {
|
|
21730
|
+
for (const f of result.failed.slice(0, 12)) {
|
|
21731
|
+
logErr(
|
|
21732
|
+
flags,
|
|
21733
|
+
`[download] failed ${f.name ?? "?"} (${f.id ?? "?"}): ${f.message}`
|
|
21734
|
+
);
|
|
21735
|
+
}
|
|
21736
|
+
if (result.failed.length > 12) {
|
|
21737
|
+
logErr(flags, `[download] \u2026 and ${String(result.failed.length - 12)} more`);
|
|
21738
|
+
}
|
|
21739
|
+
}
|
|
21740
|
+
}
|
|
21741
|
+
finish(cmdOk ? ExitCode.ok : ExitCode.upload, {
|
|
21742
|
+
ok: cmdOk,
|
|
21743
|
+
downloaded: result.downloaded,
|
|
21744
|
+
skipped: result.skipped,
|
|
21745
|
+
failed: failCount,
|
|
21746
|
+
localRemoved: result.localFilesRemoved,
|
|
21747
|
+
...flags.verbose && failCount > 0 ? { failures: result.failed } : {}
|
|
21748
|
+
});
|
|
21749
|
+
} catch (e) {
|
|
21750
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
21751
|
+
finish(ExitCode.internal, { ok: false, error: msg });
|
|
21752
|
+
}
|
|
21753
|
+
}
|
|
21346
21754
|
if (pos[0] === "upload" && (pos[1] === "context" || pos[1] === "republish")) {
|
|
21347
21755
|
const sessRaw = readCliSession();
|
|
21348
21756
|
if (sessRaw === void 0) {
|
|
@@ -21352,16 +21760,17 @@ async function main() {
|
|
|
21352
21760
|
});
|
|
21353
21761
|
}
|
|
21354
21762
|
const sess = sessRaw;
|
|
21355
|
-
|
|
21763
|
+
requireMinimalGluecharmLayoutAt(repoRoot);
|
|
21764
|
+
let ctxDir = path46.join(repoRoot, ".gluecharm", "context");
|
|
21356
21765
|
if (pos[1] === "republish") {
|
|
21357
21766
|
const fromCfg = repoConfig.easyspecs?.upload?.contextDirectory?.trim();
|
|
21358
|
-
const resolvedOverride = fromCfg && fromCfg.length > 0 ?
|
|
21359
|
-
if (resolvedOverride &&
|
|
21767
|
+
const resolvedOverride = fromCfg && fromCfg.length > 0 ? path46.isAbsolute(fromCfg) ? path46.normalize(fromCfg) : path46.resolve(repoRoot, fromCfg) : "";
|
|
21768
|
+
if (resolvedOverride && fs46.existsSync(path46.join(resolvedOverride, ".."))) {
|
|
21360
21769
|
ctxDir = resolvedOverride;
|
|
21361
21770
|
} else {
|
|
21362
21771
|
const storage = createFileBackedWorkspaceState(repoRoot);
|
|
21363
21772
|
const snap = readAnalysisWorkspaceSnapshot(storage);
|
|
21364
|
-
const wt = snap?.adHocWorktreePath &&
|
|
21773
|
+
const wt = snap?.adHocWorktreePath && fs46.existsSync(path46.join(snap.adHocWorktreePath, ".gluecharm", "context")) ? path46.join(snap.adHocWorktreePath, ".gluecharm", "context") : "";
|
|
21365
21774
|
if (!wt) {
|
|
21366
21775
|
finish(ExitCode.misconfiguration, {
|
|
21367
21776
|
ok: false,
|
|
@@ -21371,6 +21780,11 @@ async function main() {
|
|
|
21371
21780
|
ctxDir = wt;
|
|
21372
21781
|
}
|
|
21373
21782
|
}
|
|
21783
|
+
const ctxResolved = path46.resolve(ctxDir);
|
|
21784
|
+
const gluecharmParent = path46.dirname(ctxResolved);
|
|
21785
|
+
if (path46.basename(gluecharmParent) === ".gluecharm" && path46.basename(ctxResolved) === "context") {
|
|
21786
|
+
requireMinimalGluecharmLayoutAt(path46.dirname(gluecharmParent));
|
|
21787
|
+
}
|
|
21374
21788
|
const appIdRaw = getEasyspecsProjectIdFromRepoConfig(repoConfig)?.trim();
|
|
21375
21789
|
if (!appIdRaw) {
|
|
21376
21790
|
finish(ExitCode.misconfiguration, {
|
|
@@ -21473,18 +21887,18 @@ async function main() {
|
|
|
21473
21887
|
finish(failed ? ExitCode.upload : ExitCode.ok, primary);
|
|
21474
21888
|
}
|
|
21475
21889
|
if (pos[0] === "ace" && pos[1] === "clear") {
|
|
21476
|
-
const learnings =
|
|
21477
|
-
if (!
|
|
21890
|
+
const learnings = path46.join(repoRoot, ".gluecharm", "context", "learnings");
|
|
21891
|
+
if (!fs46.existsSync(learnings)) {
|
|
21478
21892
|
finish(ExitCode.ok, { ok: true, message: "nothing to clear" });
|
|
21479
21893
|
}
|
|
21480
|
-
|
|
21894
|
+
fs46.rmSync(learnings, { recursive: true, force: true });
|
|
21481
21895
|
finish(ExitCode.ok, { ok: true, message: `cleared ${learnings}` });
|
|
21482
21896
|
}
|
|
21483
21897
|
if (pos[0] === "ace" && pos[1] === "learn") {
|
|
21484
21898
|
requireOpenCode(merged, flags);
|
|
21485
21899
|
const { worktree } = parseWorktreeFlag(pos.slice(2));
|
|
21486
|
-
const root = worktree &&
|
|
21487
|
-
const contextDir2 =
|
|
21900
|
+
const root = worktree && fs46.existsSync(path46.join(worktree, ".opencode", "schemas", "ace")) ? worktree : repoRoot;
|
|
21901
|
+
const contextDir2 = path46.join(root, ".gluecharm", "context");
|
|
21488
21902
|
const traces = listAceTraceFiles(contextDir2);
|
|
21489
21903
|
if (traces.length === 0) {
|
|
21490
21904
|
finish(ExitCode.ok, { ok: true, message: "no traces", traceCount: 0 });
|
|
@@ -21508,8 +21922,8 @@ async function main() {
|
|
|
21508
21922
|
if (pos[0] === "ace" && pos[1] === "auto-learn") {
|
|
21509
21923
|
requireOpenCode(merged, flags);
|
|
21510
21924
|
const { worktree } = parseWorktreeFlag(pos.slice(2));
|
|
21511
|
-
const root = worktree &&
|
|
21512
|
-
const contextDir2 =
|
|
21925
|
+
const root = worktree && fs46.existsSync(path46.join(worktree, ".git")) ? worktree : repoRoot;
|
|
21926
|
+
const contextDir2 = path46.join(root, ".gluecharm", "context");
|
|
21513
21927
|
const pending = listPendingAceTraceFiles(contextDir2, root);
|
|
21514
21928
|
if (pending.length === 0) {
|
|
21515
21929
|
finish(ExitCode.ok, { ok: true, pending: 0 });
|