nexarch 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,11 +1,11 @@
1
1
  import { arch, homedir, hostname, platform, release, type as osType, userInfo } from "os";
2
- import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
2
+ import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from "fs";
3
3
  import { join } from "path";
4
4
  import process from "process";
5
5
  import { requireCredentials } from "../lib/credentials.js";
6
6
  import { fetchAgentRegistryOrThrow } from "../lib/agent-registry.js";
7
7
  import { callMcpTool, mcpInitialize, mcpListTools } from "../lib/mcp.js";
8
- const CLI_VERSION = "0.5.2";
8
+ const CLI_VERSION = "0.5.4";
9
9
  const AGENT_ENTITY_TYPE = "agent";
10
10
  const TECH_COMPONENT_ENTITY_TYPE = "technology_component";
11
11
  function parseFlag(args, flag) {
@@ -47,6 +47,32 @@ function getRuntimeMode() {
47
47
  return "ci";
48
48
  return "interactive";
49
49
  }
50
+ function detectLikelyApplicationInCwd() {
51
+ const cwd = process.cwd();
52
+ const files = [
53
+ "package.json",
54
+ "pyproject.toml",
55
+ "requirements.txt",
56
+ "go.mod",
57
+ "Cargo.toml",
58
+ "Gemfile",
59
+ "composer.json",
60
+ "pom.xml",
61
+ "build.gradle",
62
+ "build.gradle.kts",
63
+ ];
64
+ const evidence = files.filter((file) => existsSync(join(cwd, file)));
65
+ try {
66
+ const entries = readdirSync(cwd);
67
+ if (entries.some((name) => name.toLowerCase().endsWith(".csproj"))) {
68
+ evidence.push("*.csproj");
69
+ }
70
+ }
71
+ catch {
72
+ // non-fatal; ignore
73
+ }
74
+ return { detected: evidence.length > 0, evidence };
75
+ }
50
76
  function getSanitizedHostname(redactHostname) {
51
77
  const raw = hostname() || "unknown-host";
52
78
  return redactHostname ? "redacted" : raw;
@@ -245,6 +271,7 @@ function injectGenericAgentConfig(registry) {
245
271
  export async function initAgent(args) {
246
272
  const asJson = parseFlag(args, "--json");
247
273
  const strict = parseFlag(args, "--strict");
274
+ const fromSetup = parseFlag(args, "--from-setup");
248
275
  const redactHostname = parseFlag(args, "--redact-hostname");
249
276
  const explicitAgentId = parseOptionValue(args, "--agent-id");
250
277
  const bindToExternalKey = parseOptionValue(args, "--bind-to-external-key");
@@ -287,13 +314,23 @@ export async function initAgent(args) {
287
314
  });
288
315
  const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {}, { companyId: selectedCompanyId });
289
316
  const policies = parseToolText(policiesRaw);
317
+ const onboardingReady = Boolean(policies.policyBundleHash);
290
318
  checks.push({
291
319
  name: "governance.bootstrap",
292
- ok: Boolean(policies.policyBundleHash),
320
+ ok: onboardingReady,
293
321
  detail: policies.policyBundleHash
294
- ? `policyBundleHash=${policies.policyBundleHash.slice(0, 12)}… policyCount=${policies.policyCount ?? 0}`
322
+ ? `policyBundleHash=${policies.policyBundleHash.slice(0, 12)}…`
295
323
  : "missing policyBundleHash",
296
324
  });
325
+ if (!onboardingReady) {
326
+ const onboardingMessage = "Company onboarding is incomplete (missing policy bootstrap). Complete onboarding before running init-agent or init-project.";
327
+ if (asJson) {
328
+ process.stdout.write(`${JSON.stringify({ ok: false, error: "COMPANY_ONBOARDING_INCOMPLETE", message: onboardingMessage }, null, 2)}\n`);
329
+ process.exitCode = 1;
330
+ return;
331
+ }
332
+ throw new Error(onboardingMessage);
333
+ }
297
334
  const contractRaw = await callMcpTool("nexarch_get_ingest_contract", {}, { companyId: selectedCompanyId });
298
335
  const contract = parseToolText(contractRaw);
299
336
  checks.push({
@@ -718,6 +755,7 @@ export async function initAgent(args) {
718
755
  });
719
756
  }
720
757
  const allPassed = checks.every((c) => c.ok);
758
+ const appDetection = detectLikelyApplicationInCwd();
721
759
  const identifyPrompt = [
722
760
  "Please identify your runtime so the workspace can register richer metadata.",
723
761
  "Return ONLY the completed command below with real values (no markdown):",
@@ -751,6 +789,17 @@ export async function initAgent(args) {
751
789
  detail: "Prompt the coding agent to self-identify and submit via nexarch agent identify",
752
790
  prompt: identifyPrompt,
753
791
  submitCommandTemplate: `nexarch agent identify --agent-id \"${agentId}\" --provider \"<provider>\" --model \"<model>\" --client \"<client>\" --framework \"<openclaw|n8n|m365-agent-framework|other>\" --session-id \"<session-id>\" --tool-version \"<tool-version>\" --capabilities \"<capability1,capability2>\" --notes \"<optional notes>\" --json`,
792
+ initProjectSuggestion: appDetection.detected
793
+ ? {
794
+ shouldPromptUser: true,
795
+ reason: `Detected likely project manifests in cwd: ${appDetection.evidence.join(", ")}`,
796
+ userPrompt: "I found a likely application in this directory. Do you want me to run init-project next?",
797
+ suggestedCommand: "npx nexarch@latest init-project --dir . --json",
798
+ }
799
+ : {
800
+ shouldPromptUser: false,
801
+ reason: "No common application manifest files detected in current directory.",
802
+ },
754
803
  },
755
804
  companyId: creds.companyId,
756
805
  registry: { version: registry.release.version, registryVersion: registry.registryVersion, publishedAt: registry.release.publishedAt },
@@ -784,9 +833,11 @@ export async function initAgent(args) {
784
833
  }
785
834
  const hasInjectedAgentInstructions = agentConfigResults.some((r) => r.status === "injected" || r.status === "updated");
786
835
  if (hasInjectedAgentInstructions) {
787
- console.log("\n➡ Next step: in your coding agent, run:");
788
- console.log("\nrun npx nexarch@latest init-agent\n");
789
- console.log(" (Registration instructions were already written into your agent config file.)");
836
+ if (!fromSetup) {
837
+ console.log("\n➡ Next step: in your coding agent, run:");
838
+ console.log("\nrun npx nexarch@latest init-agent\n");
839
+ console.log(" (Registration instructions were already written into your agent config file.)");
840
+ }
790
841
  }
791
842
  else {
792
843
  console.log("\n➡ Next step (required): complete agent identity in your coding agent");
@@ -5,7 +5,6 @@ import { basename, join, relative, resolve as resolvePath } from "node:path";
5
5
  import { homedir } from "node:os";
6
6
  import { requireCredentials } from "../lib/credentials.js";
7
7
  import { callMcpTool } from "../lib/mcp.js";
8
- import { CURATED_AGENT_ICON_NAMES } from "../lib/application-icons.js";
9
8
  // ─── Helpers ─────────────────────────────────────────────────────────────────
10
9
  function parseFlag(args, flag) {
11
10
  return args.includes(flag);
@@ -817,6 +816,15 @@ export async function initProject(args) {
817
816
  const policiesRaw = await callMcpTool("nexarch_get_applied_policies", {}, mcpOpts);
818
817
  const policies = parseToolText(policiesRaw);
819
818
  const policyBundleHash = policies.policyBundleHash ?? null;
819
+ if (!policyBundleHash) {
820
+ const message = "Company onboarding is incomplete (missing policy bootstrap). Complete onboarding before running init-project.";
821
+ if (asJson) {
822
+ process.stdout.write(`${JSON.stringify({ ok: false, error: "COMPANY_ONBOARDING_INCOMPLETE", message }, null, 2)}\n`);
823
+ process.exitCode = 1;
824
+ return;
825
+ }
826
+ throw new Error(message);
827
+ }
820
828
  const nowIso = new Date().toISOString();
821
829
  const repoRef = repoRefOverride ?? detectedRepo?.rawRef ?? dir;
822
830
  const repoPath = repoPathOverride ?? dir;
@@ -988,7 +996,6 @@ export async function initProject(args) {
988
996
  const ecosystemLabel = detectedEcosystems.length > 0
989
997
  ? detectedEcosystems.join(", ")
990
998
  : "unknown";
991
- const curatedIconList = CURATED_AGENT_ICON_NAMES.join(", ");
992
999
  const manifestHint = detectedEcosystems.includes("nodejs")
993
1000
  ? "package.json"
994
1001
  : detectedEcosystems.includes("python")
@@ -1022,9 +1029,9 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
1022
1029
  → --entity-type technology_component --subtype tech_library
1023
1030
 
1024
1031
  ICON ASSIGNMENT (applications only):
1025
- • Pick one icon from this curated set for each application/sub-app.
1032
+ • Pick one icon from the full Lucide icon set for each application/sub-app.
1026
1033
  • If confidence is low, skip --icon rather than guessing.
1027
- Allowed icons: ${curatedIconList}
1034
+ Use kebab-case icon names (example: server, workflow, shield-check).
1028
1035
 
1029
1036
  For each sub-package, run update-entity to register it:
1030
1037
 
@@ -1034,7 +1041,7 @@ ${subPackages.map((sp) => ` • ${sp.name} (${sp.relativePath})`).join("\n")
1034
1041
  --subtype "<chosen subtype>" \\
1035
1042
  --name "<human readable name>" \\
1036
1043
  --description "<what this package does and its role in the project>" \\
1037
- --icon "<curated icon>" # applications only
1044
+ --icon "<lucide icon>" # applications only
1038
1045
 
1039
1046
  Then register it as a resolvable alias so future scans don't re-surface it as a candidate:
1040
1047
 
@@ -1158,8 +1165,7 @@ ${subPkgSection}${gapCheckSection}`;
1158
1165
  instructions: buildEnrichmentInstructions(),
1159
1166
  iconHints: {
1160
1167
  provider: "lucide",
1161
- curated: CURATED_AGENT_ICON_NAMES,
1162
- note: "Use curated list for agent-selected enrichment icons; omit icon when low confidence.",
1168
+ note: "Use any Lucide icon name for agent-selected enrichment icons; omit icon when low confidence.",
1163
1169
  },
1164
1170
  projectEntity: {
1165
1171
  externalKey: projectExternalKey,
@@ -59,6 +59,9 @@ export async function setup(args) {
59
59
  console.log("\nDone. Restart your MCP client for the changes to take effect.");
60
60
  }
61
61
  console.log("\nRegistering this runtime as a Nexarch agent…\n");
62
- await initAgent(args.includes("--strict") ? args : [...args, "--strict"]);
62
+ const initAgentArgs = args.includes("--strict") ? [...args] : [...args, "--strict"];
63
+ if (!initAgentArgs.includes("--from-setup"))
64
+ initAgentArgs.push("--from-setup");
65
+ await initAgent(initAgentArgs);
63
66
  console.log("\nSetup complete: MCP client config + agent registration are now in place.");
64
67
  }
@@ -1,7 +1,6 @@
1
1
  import process from "process";
2
2
  import { requireCredentials } from "../lib/credentials.js";
3
3
  import { callMcpTool } from "../lib/mcp.js";
4
- import { CURATED_AGENT_ICON_SET } from "../lib/application-icons.js";
5
4
  function parseFlag(args, flag) {
6
5
  return args.includes(flag);
7
6
  }
@@ -52,10 +51,6 @@ export async function updateEntity(args) {
52
51
  const policyContext = policyBundleHash
53
52
  ? { policyBundleHash, alignmentSummary: { score: 1, violations: [], waivers: [] } }
54
53
  : undefined;
55
- if (iconName && !CURATED_AGENT_ICON_SET.has(iconName)) {
56
- console.error(`error: --icon must be one of the curated agent icons (got '${iconName}')`);
57
- process.exit(1);
58
- }
59
54
  const entity = {
60
55
  externalKey,
61
56
  entityTypeCode,
package/dist/index.js CHANGED
@@ -87,7 +87,7 @@ Usage:
87
87
  --description <text>
88
88
  --entity-type <code> (default: application)
89
89
  --subtype <code>
90
- --icon <lucide-name> (curated set; for agent enrichment)
90
+ --icon <lucide-name> (full Lucide set; for agent enrichment)
91
91
  --json
92
92
  nexarch add-relationship
93
93
  Add a relationship between two existing graph entities.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "nexarch",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Your architecture workspace for AI delivery.",
5
5
  "keywords": [
6
6
  "nexarch",