nexarch 0.12.0 → 0.12.3
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/dist/commands/check-in.js +16 -4
- package/dist/commands/command-claim.js +4 -0
- package/dist/commands/init-agent.js +51 -33
- package/dist/commands/init-project.js +4 -4
- package/dist/commands/register-runtime.js +77 -0
- package/dist/commands/setup.js +2 -2
- package/dist/index.js +11 -0
- package/dist/lib/application-context.js +51 -0
- package/package.json +1 -1
|
@@ -5,6 +5,7 @@ import { homedir } from "os";
|
|
|
5
5
|
import { saveCheckInLease } from "../lib/check-in-lease.js";
|
|
6
6
|
import { requireCredentials } from "../lib/credentials.js";
|
|
7
7
|
import { callMcpTool } from "../lib/mcp.js";
|
|
8
|
+
import { inferAccessibleApplicationRefs } from "../lib/application-context.js";
|
|
8
9
|
function parseFlag(args, flag) {
|
|
9
10
|
return args.includes(flag);
|
|
10
11
|
}
|
|
@@ -36,6 +37,7 @@ function loadIdentity() {
|
|
|
36
37
|
export async function checkIn(args) {
|
|
37
38
|
const asJson = parseFlag(args, "--json");
|
|
38
39
|
const agentKeyArg = parseOptionValue(args, "--agent-ref") ?? parseOptionValue(args, "--agent-key");
|
|
40
|
+
const applicationRefArg = parseOptionValue(args, "--application-ref");
|
|
39
41
|
const creds = requireCredentials();
|
|
40
42
|
const identity = loadIdentity();
|
|
41
43
|
const agentKey = agentKeyArg ?? identity.agentKey;
|
|
@@ -48,9 +50,16 @@ export async function checkIn(args) {
|
|
|
48
50
|
}
|
|
49
51
|
return;
|
|
50
52
|
}
|
|
53
|
+
const applicationContext = await inferAccessibleApplicationRefs({
|
|
54
|
+
companyId: creds.companyId,
|
|
55
|
+
explicitApplicationRefs: applicationRefArg ? [applicationRefArg] : undefined,
|
|
56
|
+
});
|
|
51
57
|
const raw = await callMcpTool("nexarch_check_in", {
|
|
52
58
|
agentRef: agentKey,
|
|
53
59
|
agentKey,
|
|
60
|
+
applicationContext: applicationContext.accessibleApplicationRefs.length > 0
|
|
61
|
+
? { accessibleApplicationRefs: applicationContext.accessibleApplicationRefs }
|
|
62
|
+
: undefined,
|
|
54
63
|
companyId: creds.companyId,
|
|
55
64
|
}, { companyId: creds.companyId });
|
|
56
65
|
const result = parseToolText(raw);
|
|
@@ -74,6 +83,7 @@ export async function checkIn(args) {
|
|
|
74
83
|
if (result.leaseExpiresAt) {
|
|
75
84
|
console.log(`Check-in lease active until ${new Date(result.leaseExpiresAt).toLocaleString()}.`);
|
|
76
85
|
}
|
|
86
|
+
console.log(`Application context: ${applicationContext.detail}`);
|
|
77
87
|
for (const warning of result.warnings ?? []) {
|
|
78
88
|
if (warning.message)
|
|
79
89
|
console.log(`Warning: ${warning.message}`);
|
|
@@ -91,7 +101,7 @@ export async function checkIn(args) {
|
|
|
91
101
|
}
|
|
92
102
|
for (const cmd of commands) {
|
|
93
103
|
const claimable = cmd.claimable ? "yes" : "no";
|
|
94
|
-
const reason = cmd.claimReason ?? (cmd.claimable ? "application_access_confirmed" : "
|
|
104
|
+
const reason = cmd.claimReason ?? (cmd.claimable ? "application_access_confirmed" : "application_context_required_for_claim");
|
|
95
105
|
console.log(`\n- ID : ${cmd.id}`);
|
|
96
106
|
console.log(` Type : ${cmd.command_type}`);
|
|
97
107
|
console.log(` Target : ${cmd.target_entity_key ?? "(none)"}`);
|
|
@@ -101,12 +111,14 @@ export async function checkIn(args) {
|
|
|
101
111
|
console.log(` Type ver : ${cmd.command_type_version}`);
|
|
102
112
|
if (cmd.resolved_runtime_handler)
|
|
103
113
|
console.log(` Runtime : ${cmd.resolved_runtime_handler}`);
|
|
104
|
-
if (cmd.
|
|
105
|
-
console.log(`
|
|
106
|
-
}
|
|
114
|
+
if (cmd.claimGuidance)
|
|
115
|
+
console.log(` Guidance : ${cmd.claimGuidance}`);
|
|
116
|
+
console.log(` Claim cmd : npx nexarch command-claim --id "${cmd.id}"${cmd.target_entity_key ? ` --application-ref "${cmd.target_entity_key}"` : ""}`);
|
|
107
117
|
}
|
|
108
118
|
console.log("\nTo validate an application target is accessible in Nexarch before claiming:");
|
|
109
119
|
console.log(" npx nexarch policy-controls --entity <application:key> --json");
|
|
120
|
+
console.log("If no application context was inferred, re-run with:");
|
|
121
|
+
console.log(" npx nexarch check-in --application-ref <application:key>");
|
|
110
122
|
}
|
|
111
123
|
else {
|
|
112
124
|
console.log("No pending application-target commands found.");
|
|
@@ -37,6 +37,7 @@ export async function commandClaim(args) {
|
|
|
37
37
|
const asJson = parseFlag(args, "--json");
|
|
38
38
|
const id = parseOptionValue(args, "--id");
|
|
39
39
|
const agentKeyArg = parseOptionValue(args, "--agent-ref") ?? parseOptionValue(args, "--agent-key");
|
|
40
|
+
const applicationEntityRef = parseOptionValue(args, "--application-ref");
|
|
40
41
|
if (!id) {
|
|
41
42
|
console.error("error: --id <commandId> is required");
|
|
42
43
|
process.exit(1);
|
|
@@ -54,6 +55,7 @@ export async function commandClaim(args) {
|
|
|
54
55
|
agentRef: agentKey,
|
|
55
56
|
agentKey,
|
|
56
57
|
leaseToken: lease?.leaseToken,
|
|
58
|
+
applicationEntityRef: applicationEntityRef ?? undefined,
|
|
57
59
|
companyId: creds.companyId,
|
|
58
60
|
}, { companyId: creds.companyId });
|
|
59
61
|
const result = parseToolText(raw);
|
|
@@ -72,6 +74,8 @@ export async function commandClaim(args) {
|
|
|
72
74
|
console.log(`Command ${id} was not claimed.`);
|
|
73
75
|
if (result.reason)
|
|
74
76
|
console.log(`Reason: ${result.reason}`);
|
|
77
|
+
if (!applicationEntityRef)
|
|
78
|
+
console.log("Tip: re-run with --application-ref <application:key>.");
|
|
75
79
|
return;
|
|
76
80
|
}
|
|
77
81
|
const cmd = result.command;
|
|
@@ -337,29 +337,28 @@ function canonicalTargetKey(filePath) {
|
|
|
337
337
|
const abs = resolve(filePath);
|
|
338
338
|
return process.platform === "win32" || process.platform === "darwin" ? abs.toLowerCase() : abs;
|
|
339
339
|
}
|
|
340
|
-
function injectAgentConfigs(registry) {
|
|
340
|
+
function injectAgentConfigs(registry, runtimeCode) {
|
|
341
341
|
const templateByCode = new Map(registry.instructionTemplates.map((t) => [t.code, t]));
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
342
|
+
const sortedTargets = [...registry.instructionTargets]
|
|
343
|
+
.filter((target) => target.matchMode === "exact")
|
|
344
|
+
.sort((a, b) => a.sortOrder - b.sortOrder || a.filePathPattern.localeCompare(b.filePathPattern));
|
|
345
|
+
const candidateTargets = runtimeCode
|
|
346
|
+
? sortedTargets.filter((target) => target.runtimeCode === runtimeCode)
|
|
347
|
+
: sortedTargets;
|
|
348
|
+
const applyToTarget = (target) => {
|
|
348
349
|
const template = templateByCode.get(target.templateCode);
|
|
349
350
|
if (!template)
|
|
350
|
-
|
|
351
|
+
return null;
|
|
351
352
|
const filePath = join(process.cwd(), target.filePathPattern);
|
|
352
|
-
const targetKey = canonicalTargetKey(filePath);
|
|
353
|
-
if (seenTargets.has(targetKey))
|
|
354
|
-
continue;
|
|
355
|
-
seenTargets.add(targetKey);
|
|
356
|
-
if (!existsSync(filePath))
|
|
357
|
-
continue;
|
|
358
|
-
const existing = readFileSync(filePath, "utf8");
|
|
359
353
|
const sectionBody = template.body.trim();
|
|
360
354
|
const sectionHeading = target.sectionHeading ?? "## Nexarch Agent Registration";
|
|
361
355
|
const sectionMarker = target.sectionMarker ?? sectionHeading;
|
|
362
356
|
const managedBody = wrapManagedSection("agent-registration", sectionBody);
|
|
357
|
+
if (!existsSync(filePath)) {
|
|
358
|
+
writeFileSync(filePath, `${managedBody}\n`, "utf8");
|
|
359
|
+
return { path: filePath, status: "injected" };
|
|
360
|
+
}
|
|
361
|
+
const existing = readFileSync(filePath, "utf8");
|
|
363
362
|
if (target.insertionMode === "replace_section") {
|
|
364
363
|
let replaced = replaceManagedSection(existing, "agent-registration", sectionBody);
|
|
365
364
|
if (replaced === existing) {
|
|
@@ -372,29 +371,46 @@ function injectAgentConfigs(registry) {
|
|
|
372
371
|
}
|
|
373
372
|
if (replaced !== existing) {
|
|
374
373
|
writeFileSync(filePath, replaced, "utf8");
|
|
375
|
-
|
|
374
|
+
return { path: filePath, status: "updated" };
|
|
376
375
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
}
|
|
380
|
-
else {
|
|
381
|
-
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
382
|
-
writeFileSync(filePath, existing + separator + managedBody + "\n", "utf8");
|
|
383
|
-
results.push({ path: filePath, status: "injected" });
|
|
376
|
+
if (existing.includes(managedBody) || existing.includes(sectionBody)) {
|
|
377
|
+
return { path: filePath, status: "already_present" };
|
|
384
378
|
}
|
|
385
|
-
|
|
379
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
380
|
+
writeFileSync(filePath, existing + separator + managedBody + "\n", "utf8");
|
|
381
|
+
return { path: filePath, status: "injected" };
|
|
386
382
|
}
|
|
387
383
|
if (existing.includes(managedBody) || existing.includes(sectionBody)) {
|
|
388
|
-
|
|
389
|
-
}
|
|
390
|
-
else {
|
|
391
|
-
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
392
|
-
const next = existing + separator + managedBody + "\n";
|
|
393
|
-
writeFileSync(filePath, next, "utf8");
|
|
394
|
-
results.push({ path: filePath, status: "injected" });
|
|
384
|
+
return { path: filePath, status: "already_present" };
|
|
395
385
|
}
|
|
386
|
+
const separator = existing.endsWith("\n") ? "" : "\n";
|
|
387
|
+
const next = existing + separator + managedBody + "\n";
|
|
388
|
+
writeFileSync(filePath, next, "utf8");
|
|
389
|
+
return { path: filePath, status: "injected" };
|
|
390
|
+
};
|
|
391
|
+
const seenTargets = new Set();
|
|
392
|
+
const existingMatches = [];
|
|
393
|
+
for (const target of candidateTargets) {
|
|
394
|
+
const filePath = join(process.cwd(), target.filePathPattern);
|
|
395
|
+
const targetKey = canonicalTargetKey(filePath);
|
|
396
|
+
if (seenTargets.has(targetKey))
|
|
397
|
+
continue;
|
|
398
|
+
seenTargets.add(targetKey);
|
|
399
|
+
if (existsSync(filePath))
|
|
400
|
+
existingMatches.push(target);
|
|
401
|
+
}
|
|
402
|
+
if (existingMatches.length > 0) {
|
|
403
|
+
const results = existingMatches
|
|
404
|
+
.map((target) => applyToTarget(target))
|
|
405
|
+
.filter((result) => Boolean(result));
|
|
406
|
+
if (results.length > 0)
|
|
407
|
+
return results;
|
|
408
|
+
}
|
|
409
|
+
if (runtimeCode && candidateTargets.length > 0) {
|
|
410
|
+
const created = applyToTarget(candidateTargets[0]);
|
|
411
|
+
return created ? [created] : [];
|
|
396
412
|
}
|
|
397
|
-
return
|
|
413
|
+
return [];
|
|
398
414
|
}
|
|
399
415
|
function injectTrustAttestationBlock(path, attestation) {
|
|
400
416
|
if (!attestation.token || !attestation.payload)
|
|
@@ -1017,6 +1033,7 @@ export async function initAgent(args) {
|
|
|
1017
1033
|
detail: "skipped",
|
|
1018
1034
|
missingRequired: [],
|
|
1019
1035
|
};
|
|
1036
|
+
let selectedClient = clientArg ?? null;
|
|
1020
1037
|
if (registration.ok) {
|
|
1021
1038
|
let provider = providerArg;
|
|
1022
1039
|
let model = modelArg;
|
|
@@ -1026,6 +1043,7 @@ export async function initAgent(args) {
|
|
|
1026
1043
|
model = model ?? await promptForValue("Model id (e.g. claude-sonnet-4-6): ", true);
|
|
1027
1044
|
client = client ?? await promptForValue("Client (e.g. claude-code/cursor/codex-cli): ", true);
|
|
1028
1045
|
}
|
|
1046
|
+
selectedClient = client ?? null;
|
|
1029
1047
|
const missingRequired = [
|
|
1030
1048
|
!provider ? "provider" : null,
|
|
1031
1049
|
!model ? "model" : null,
|
|
@@ -1110,7 +1128,7 @@ export async function initAgent(args) {
|
|
|
1110
1128
|
catch {
|
|
1111
1129
|
// non-fatal
|
|
1112
1130
|
}
|
|
1113
|
-
let existingInstructionTargets = injectAgentConfigs(registry);
|
|
1131
|
+
let existingInstructionTargets = injectAgentConfigs(registry, selectedClient);
|
|
1114
1132
|
if (existingInstructionTargets.length === 0) {
|
|
1115
1133
|
existingInstructionTargets = injectGenericAgentConfig(registry);
|
|
1116
1134
|
}
|
|
@@ -221,7 +221,7 @@ function choosePreferredRepositoryRef(candidates) {
|
|
|
221
221
|
});
|
|
222
222
|
return scored[0]?.rawRef ?? null;
|
|
223
223
|
}
|
|
224
|
-
function detectSourceRepository(dir) {
|
|
224
|
+
export function detectSourceRepository(dir) {
|
|
225
225
|
let rawRef = null;
|
|
226
226
|
let vcsType = "unknown";
|
|
227
227
|
// Prefer git when present, and prefer hosted remotes over local-only URLs.
|
|
@@ -869,7 +869,7 @@ function structuralRelForSubPackage(sp, projectExternalKey) {
|
|
|
869
869
|
return { type: "part_of", from: sp.externalKey, to: projectExternalKey };
|
|
870
870
|
return null;
|
|
871
871
|
}
|
|
872
|
-
function scanProject(dir) {
|
|
872
|
+
export function scanProject(dir) {
|
|
873
873
|
const names = new Set();
|
|
874
874
|
let projectName = basename(dir);
|
|
875
875
|
const subPackages = [];
|
|
@@ -995,7 +995,7 @@ function pickRelationshipType(toEntityTypeCode, toEntitySubtypeCode, _fromEntity
|
|
|
995
995
|
return "depends_on";
|
|
996
996
|
}
|
|
997
997
|
}
|
|
998
|
-
function normalizeToken(value) {
|
|
998
|
+
export function normalizeToken(value) {
|
|
999
999
|
return value.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
1000
1000
|
}
|
|
1001
1001
|
function extractHost(input) {
|
|
@@ -1008,7 +1008,7 @@ function extractHost(input) {
|
|
|
1008
1008
|
return null;
|
|
1009
1009
|
}
|
|
1010
1010
|
}
|
|
1011
|
-
function scoreApplicationCandidate(app, projectName, repoUrl) {
|
|
1011
|
+
export function scoreApplicationCandidate(app, projectName, repoUrl) {
|
|
1012
1012
|
const entityRef = app.entityRef ?? app.externalKey ?? null;
|
|
1013
1013
|
if (!entityRef)
|
|
1014
1014
|
return null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import process from "process";
|
|
2
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
3
|
+
import { arch, hostname, platform, release, type as osType, homedir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { requireCredentials } from "../lib/credentials.js";
|
|
6
|
+
import { callMcpTool } from "../lib/mcp.js";
|
|
7
|
+
import { inferAccessibleApplicationRefs } from "../lib/application-context.js";
|
|
8
|
+
function parseFlag(args, flag) {
|
|
9
|
+
return args.includes(flag);
|
|
10
|
+
}
|
|
11
|
+
function parseOptionValue(args, option) {
|
|
12
|
+
const idx = args.indexOf(option);
|
|
13
|
+
if (idx === -1)
|
|
14
|
+
return null;
|
|
15
|
+
const value = args[idx + 1];
|
|
16
|
+
if (!value || value.startsWith("--"))
|
|
17
|
+
return null;
|
|
18
|
+
return value;
|
|
19
|
+
}
|
|
20
|
+
function parseToolText(result) {
|
|
21
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
22
|
+
return JSON.parse(text);
|
|
23
|
+
}
|
|
24
|
+
function loadIdentity() {
|
|
25
|
+
const identityPath = join(homedir(), ".nexarch", "identity.json");
|
|
26
|
+
if (!existsSync(identityPath))
|
|
27
|
+
return { agentKey: null };
|
|
28
|
+
try {
|
|
29
|
+
const data = JSON.parse(readFileSync(identityPath, "utf8"));
|
|
30
|
+
return { agentKey: data.agentKey ?? null };
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return { agentKey: null };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function registerRuntime(args) {
|
|
37
|
+
const asJson = parseFlag(args, "--json");
|
|
38
|
+
const applicationRefArg = parseOptionValue(args, "--application-ref");
|
|
39
|
+
const client = parseOptionValue(args, "--client") ?? "nexarch-cli";
|
|
40
|
+
const version = parseOptionValue(args, "--version") ?? null;
|
|
41
|
+
const mode = process.env.CI ? "ci" : "interactive";
|
|
42
|
+
const creds = requireCredentials();
|
|
43
|
+
const identity = loadIdentity();
|
|
44
|
+
if (!identity.agentKey)
|
|
45
|
+
throw new Error("No agent key found. Run nexarch init-agent first.");
|
|
46
|
+
const applicationContext = await inferAccessibleApplicationRefs({
|
|
47
|
+
companyId: creds.companyId,
|
|
48
|
+
explicitApplicationRefs: applicationRefArg ? [applicationRefArg] : undefined,
|
|
49
|
+
});
|
|
50
|
+
const raw = await callMcpTool("nexarch_register_agent_runtime", {
|
|
51
|
+
agentRef: identity.agentKey,
|
|
52
|
+
runtime: {
|
|
53
|
+
client,
|
|
54
|
+
...(version ? { version } : {}),
|
|
55
|
+
mode,
|
|
56
|
+
nodeVersion: process.version,
|
|
57
|
+
},
|
|
58
|
+
host: {
|
|
59
|
+
hostname: hostname(),
|
|
60
|
+
osPlatform: platform(),
|
|
61
|
+
osType: osType(),
|
|
62
|
+
osRelease: release(),
|
|
63
|
+
arch: arch(),
|
|
64
|
+
},
|
|
65
|
+
applicationContext: applicationContext.accessibleApplicationRefs.length > 0
|
|
66
|
+
? { accessibleApplicationRefs: applicationContext.accessibleApplicationRefs }
|
|
67
|
+
: undefined,
|
|
68
|
+
companyId: creds.companyId,
|
|
69
|
+
}, { companyId: creds.companyId });
|
|
70
|
+
const result = parseToolText(raw);
|
|
71
|
+
if (asJson) {
|
|
72
|
+
process.stdout.write(`${JSON.stringify(result)}\n`);
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
console.log(`Registered runtime for ${identity.agentKey}.`);
|
|
76
|
+
console.log(`Application context: ${applicationContext.detail}`);
|
|
77
|
+
}
|
package/dist/commands/setup.js
CHANGED
|
@@ -67,9 +67,9 @@ export async function setup(args) {
|
|
|
67
67
|
if (clients.length > 0) {
|
|
68
68
|
const names = clients.map((c) => c.name);
|
|
69
69
|
const listed = names.length === 1 ? names[0] : `${names.slice(0, -1).join(", ")} and ${names[names.length - 1]}`;
|
|
70
|
-
console.log(`\nSetup complete. Open a new ${listed} session
|
|
70
|
+
console.log(`\nSetup complete. Open a new ${listed} session — your Nexarch registration is already in place. If you want the agent to look for pending work, ask it to check in.`);
|
|
71
71
|
}
|
|
72
72
|
else {
|
|
73
|
-
console.log(
|
|
73
|
+
console.log("\nSetup complete. Once you have configured an MCP client (see above), open a new session — your Nexarch registration is already in place. If you want the agent to look for pending work, ask it to check in.");
|
|
74
74
|
}
|
|
75
75
|
}
|
package/dist/index.js
CHANGED
|
@@ -25,6 +25,7 @@ import { policyAuditResults } from "./commands/policy-audit-results.js";
|
|
|
25
25
|
import { appliedPolicies } from "./commands/applied-policies.js";
|
|
26
26
|
import { governanceSummary } from "./commands/governance-summary.js";
|
|
27
27
|
import { proposalsStart } from "./commands/proposals-start.js";
|
|
28
|
+
import { registerRuntime } from "./commands/register-runtime.js";
|
|
28
29
|
const [, , command, ...args] = process.argv;
|
|
29
30
|
const commands = {
|
|
30
31
|
login,
|
|
@@ -43,6 +44,7 @@ const commands = {
|
|
|
43
44
|
"resolve-names": resolveNames,
|
|
44
45
|
"list-entities": listEntities,
|
|
45
46
|
"list-relationships": listRelationships,
|
|
47
|
+
"register-runtime": registerRuntime,
|
|
46
48
|
"check-in": checkIn,
|
|
47
49
|
"command-done": commandDone,
|
|
48
50
|
"command-fail": commandFail,
|
|
@@ -180,12 +182,20 @@ Usage:
|
|
|
180
182
|
--to <toExternalKey>
|
|
181
183
|
--limit <1-500>
|
|
182
184
|
--json
|
|
185
|
+
nexarch register-runtime
|
|
186
|
+
Register or refresh runtime + optional application context
|
|
187
|
+
without performing check-in.
|
|
188
|
+
Options: --application-ref <entityRef>
|
|
189
|
+
--client <name>
|
|
190
|
+
--version <semver>
|
|
191
|
+
--json
|
|
183
192
|
nexarch check-in Preview pending application-target commands (no auto-claim)
|
|
184
193
|
and report draft/proposed applications needing review so the
|
|
185
194
|
agent can prompt the user to explore and instantiate them.
|
|
186
195
|
Use command-claim to explicitly claim a specific command.
|
|
187
196
|
Scope is resolved server-side from active company context.
|
|
188
197
|
Options: --agent-key <key> override stored agent key
|
|
198
|
+
--application-ref <entityRef> narrow preview scope
|
|
189
199
|
--json JSON output includes draftApplications[] and proposedApplications[]
|
|
190
200
|
nexarch proposals start
|
|
191
201
|
Start a new application workspace from a proposed NexArch app.
|
|
@@ -205,6 +215,7 @@ Usage:
|
|
|
205
215
|
Explicitly claim a pending command by ID.
|
|
206
216
|
Options: --id <commandId> (required)
|
|
207
217
|
--agent-key <key> override stored agent key
|
|
218
|
+
--application-ref <entityRef> required for application-target commands
|
|
208
219
|
--json
|
|
209
220
|
nexarch command-done
|
|
210
221
|
Mark a claimed command as completed.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { resolve as resolvePath } from "node:path";
|
|
2
|
+
import { callMcpTool } from "./mcp.js";
|
|
3
|
+
import { detectSourceRepository, scanProject, scoreApplicationCandidate } from "../commands/init-project.js";
|
|
4
|
+
function parseToolText(result) {
|
|
5
|
+
const text = result.content?.[0]?.text ?? "{}";
|
|
6
|
+
return JSON.parse(text);
|
|
7
|
+
}
|
|
8
|
+
export async function inferAccessibleApplicationRefs(params) {
|
|
9
|
+
const explicit = Array.from(new Set((params.explicitApplicationRefs ?? []).map((value) => value.trim()).filter(Boolean)));
|
|
10
|
+
if (explicit.length > 0) {
|
|
11
|
+
return {
|
|
12
|
+
accessibleApplicationRefs: explicit,
|
|
13
|
+
source: "explicit",
|
|
14
|
+
detail: `Using explicit application context (${explicit.join(", ")}).`,
|
|
15
|
+
repoUrl: detectSourceRepository(params.dir ?? process.cwd())?.url ?? null,
|
|
16
|
+
projectNames: [],
|
|
17
|
+
candidates: [],
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
const dir = resolvePath(params.dir ?? process.cwd());
|
|
21
|
+
const repoUrl = detectSourceRepository(dir)?.url ?? null;
|
|
22
|
+
const scan = scanProject(dir);
|
|
23
|
+
const projectNames = Array.from(new Set([scan.projectName, ...scan.subPackages.map((sp) => sp.name)])).filter(Boolean);
|
|
24
|
+
const appsRaw = await callMcpTool("nexarch_list_entities", { entityTypeCode: "application", status: "active", limit: 500, companyId: params.companyId }, { companyId: params.companyId });
|
|
25
|
+
const appsData = parseToolText(appsRaw);
|
|
26
|
+
const apps = (appsData.entities ?? []).filter((entity) => (entity.entityRef ?? entity.externalKey));
|
|
27
|
+
const candidateMap = new Map();
|
|
28
|
+
for (const projectName of projectNames) {
|
|
29
|
+
for (const app of apps) {
|
|
30
|
+
const match = scoreApplicationCandidate(app, projectName, repoUrl);
|
|
31
|
+
if (!match)
|
|
32
|
+
continue;
|
|
33
|
+
const existing = candidateMap.get(match.entityRef);
|
|
34
|
+
if (!existing || match.score > existing.score) {
|
|
35
|
+
candidateMap.set(match.entityRef, match);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
const candidates = Array.from(candidateMap.values()).sort((a, b) => b.score - a.score);
|
|
40
|
+
const accessibleApplicationRefs = candidates.filter((candidate) => candidate.score >= 0.85).map((candidate) => candidate.entityRef);
|
|
41
|
+
return {
|
|
42
|
+
accessibleApplicationRefs,
|
|
43
|
+
source: accessibleApplicationRefs.length > 0 ? "inferred" : "none",
|
|
44
|
+
detail: accessibleApplicationRefs.length > 0
|
|
45
|
+
? `Inferred application context from current folder (${accessibleApplicationRefs.join(", ")}).`
|
|
46
|
+
: "No high-confidence application context inferred from current folder.",
|
|
47
|
+
repoUrl,
|
|
48
|
+
projectNames,
|
|
49
|
+
candidates,
|
|
50
|
+
};
|
|
51
|
+
}
|