expxagents 0.21.0 → 0.21.2

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.
Files changed (96) hide show
  1. package/dist/cli/src/commands/create.js +2 -1
  2. package/dist/cli/src/commands/doctor.js +6 -4
  3. package/dist/cli/src/commands/init.js +11 -2
  4. package/dist/cli/src/commands/install.js +4 -2
  5. package/dist/cli/src/commands/list.js +5 -3
  6. package/dist/cli/src/commands/mcp.js +2 -1
  7. package/dist/cli/src/commands/onboarding.js +4 -2
  8. package/dist/cli/src/commands/outdated.js +2 -1
  9. package/dist/cli/src/commands/publish.js +2 -1
  10. package/dist/cli/src/commands/reorganize.js +2 -1
  11. package/dist/cli/src/commands/run.js +4 -2
  12. package/dist/cli/src/commands/sync-templates.js +2 -1
  13. package/dist/cli/src/commands/whoami.js +3 -2
  14. package/dist/cli/src/index.js +1 -12
  15. package/dist/cli/src/mcp/validate.js +2 -1
  16. package/dist/cli/src/utils/__tests__/command-prefix.test.d.ts +1 -0
  17. package/dist/cli/src/utils/__tests__/command-prefix.test.js +35 -0
  18. package/dist/cli/src/utils/__tests__/global-install.test.d.ts +1 -0
  19. package/dist/cli/src/utils/__tests__/global-install.test.js +25 -0
  20. package/dist/cli/src/utils/command-prefix.d.ts +5 -0
  21. package/dist/cli/src/utils/command-prefix.js +15 -0
  22. package/dist/cli/src/utils/ensure-server.js +55 -5
  23. package/dist/cli/src/utils/global-install.d.ts +6 -0
  24. package/dist/cli/src/utils/global-install.js +15 -0
  25. package/dist/cli/src/utils/version.d.ts +1 -0
  26. package/dist/cli/src/utils/version.js +12 -0
  27. package/node_modules/expxagents-knowledge/dist/config.d.ts +4 -0
  28. package/node_modules/expxagents-knowledge/dist/config.d.ts.map +1 -0
  29. package/node_modules/expxagents-knowledge/dist/config.js +23 -0
  30. package/node_modules/expxagents-knowledge/dist/config.js.map +1 -0
  31. package/node_modules/expxagents-knowledge/dist/db/connection.d.ts +6 -0
  32. package/node_modules/expxagents-knowledge/dist/db/connection.d.ts.map +1 -0
  33. package/node_modules/expxagents-knowledge/dist/db/connection.js +69 -0
  34. package/node_modules/expxagents-knowledge/dist/db/connection.js.map +1 -0
  35. package/node_modules/expxagents-knowledge/dist/db/migrations.d.ts +3 -0
  36. package/node_modules/expxagents-knowledge/dist/db/migrations.d.ts.map +1 -0
  37. package/node_modules/expxagents-knowledge/dist/db/migrations.js +46 -0
  38. package/node_modules/expxagents-knowledge/dist/db/migrations.js.map +1 -0
  39. package/node_modules/expxagents-knowledge/dist/db/schema.d.ts +3 -0
  40. package/node_modules/expxagents-knowledge/dist/db/schema.d.ts.map +1 -0
  41. package/node_modules/expxagents-knowledge/dist/db/schema.js +79 -0
  42. package/node_modules/expxagents-knowledge/dist/db/schema.js.map +1 -0
  43. package/node_modules/expxagents-knowledge/dist/index.d.ts +15 -0
  44. package/node_modules/expxagents-knowledge/dist/index.d.ts.map +1 -0
  45. package/node_modules/expxagents-knowledge/dist/index.js +15 -0
  46. package/node_modules/expxagents-knowledge/dist/index.js.map +1 -0
  47. package/node_modules/expxagents-knowledge/dist/ingest/chunker.d.ts +8 -0
  48. package/node_modules/expxagents-knowledge/dist/ingest/chunker.d.ts.map +1 -0
  49. package/node_modules/expxagents-knowledge/dist/ingest/chunker.js +139 -0
  50. package/node_modules/expxagents-knowledge/dist/ingest/chunker.js.map +1 -0
  51. package/node_modules/expxagents-knowledge/dist/ingest/document-loader.d.ts +4 -0
  52. package/node_modules/expxagents-knowledge/dist/ingest/document-loader.d.ts.map +1 -0
  53. package/node_modules/expxagents-knowledge/dist/ingest/document-loader.js +39 -0
  54. package/node_modules/expxagents-knowledge/dist/ingest/document-loader.js.map +1 -0
  55. package/node_modules/expxagents-knowledge/dist/ingest/embedder.d.ts +4 -0
  56. package/node_modules/expxagents-knowledge/dist/ingest/embedder.d.ts.map +1 -0
  57. package/node_modules/expxagents-knowledge/dist/ingest/embedder.js +25 -0
  58. package/node_modules/expxagents-knowledge/dist/ingest/embedder.js.map +1 -0
  59. package/node_modules/expxagents-knowledge/dist/ingest/entity-extractor.d.ts +21 -0
  60. package/node_modules/expxagents-knowledge/dist/ingest/entity-extractor.d.ts.map +1 -0
  61. package/node_modules/expxagents-knowledge/dist/ingest/entity-extractor.js +54 -0
  62. package/node_modules/expxagents-knowledge/dist/ingest/entity-extractor.js.map +1 -0
  63. package/node_modules/expxagents-knowledge/dist/ingest/extraction-queue.d.ts +16 -0
  64. package/node_modules/expxagents-knowledge/dist/ingest/extraction-queue.d.ts.map +1 -0
  65. package/node_modules/expxagents-knowledge/dist/ingest/extraction-queue.js +49 -0
  66. package/node_modules/expxagents-knowledge/dist/ingest/extraction-queue.js.map +1 -0
  67. package/node_modules/expxagents-knowledge/dist/ingest/pipeline.d.ts +27 -0
  68. package/node_modules/expxagents-knowledge/dist/ingest/pipeline.d.ts.map +1 -0
  69. package/node_modules/expxagents-knowledge/dist/ingest/pipeline.js +83 -0
  70. package/node_modules/expxagents-knowledge/dist/ingest/pipeline.js.map +1 -0
  71. package/node_modules/expxagents-knowledge/dist/query/graph-traversal.d.ts +41 -0
  72. package/node_modules/expxagents-knowledge/dist/query/graph-traversal.d.ts.map +1 -0
  73. package/node_modules/expxagents-knowledge/dist/query/graph-traversal.js +62 -0
  74. package/node_modules/expxagents-knowledge/dist/query/graph-traversal.js.map +1 -0
  75. package/node_modules/expxagents-knowledge/dist/query/knowledge-query.d.ts +31 -0
  76. package/node_modules/expxagents-knowledge/dist/query/knowledge-query.d.ts.map +1 -0
  77. package/node_modules/expxagents-knowledge/dist/query/knowledge-query.js +106 -0
  78. package/node_modules/expxagents-knowledge/dist/query/knowledge-query.js.map +1 -0
  79. package/node_modules/expxagents-knowledge/dist/query/vector-search.d.ts +26 -0
  80. package/node_modules/expxagents-knowledge/dist/query/vector-search.d.ts.map +1 -0
  81. package/node_modules/expxagents-knowledge/dist/query/vector-search.js +57 -0
  82. package/node_modules/expxagents-knowledge/dist/query/vector-search.js.map +1 -0
  83. package/node_modules/expxagents-knowledge/dist/sources/agent-output.d.ts +10 -0
  84. package/node_modules/expxagents-knowledge/dist/sources/agent-output.d.ts.map +1 -0
  85. package/node_modules/expxagents-knowledge/dist/sources/agent-output.js +29 -0
  86. package/node_modules/expxagents-knowledge/dist/sources/agent-output.js.map +1 -0
  87. package/node_modules/expxagents-knowledge/dist/sources/watcher.d.ts +6 -0
  88. package/node_modules/expxagents-knowledge/dist/sources/watcher.d.ts.map +1 -0
  89. package/node_modules/expxagents-knowledge/dist/sources/watcher.js +42 -0
  90. package/node_modules/expxagents-knowledge/dist/sources/watcher.js.map +1 -0
  91. package/node_modules/expxagents-knowledge/dist/types.d.ts +123 -0
  92. package/node_modules/expxagents-knowledge/dist/types.d.ts.map +1 -0
  93. package/node_modules/expxagents-knowledge/dist/types.js +2 -0
  94. package/node_modules/expxagents-knowledge/dist/types.js.map +1 -0
  95. package/node_modules/expxagents-knowledge/package.json +7 -0
  96. package/package.json +8 -2
@@ -2,6 +2,7 @@ import { spawn } from 'child_process';
2
2
  import path from 'path';
3
3
  import fs from 'fs';
4
4
  import { getCoreAsset } from '../utils/config.js';
5
+ import { getCommandPrefix } from '../utils/command-prefix.js';
5
6
  export async function createCommand(description) {
6
7
  const cwd = process.cwd();
7
8
  const architectPath = getCoreAsset('solution-architect.agent.md');
@@ -46,7 +47,7 @@ export async function createCommand(description) {
46
47
  });
47
48
  child.on('exit', (code) => {
48
49
  if (code === 0) {
49
- console.log('\nSquad created! Run `expxagents list` to see it.');
50
+ console.log(`\nSquad created! Run \`${getCommandPrefix()} list\` to see it.`);
50
51
  }
51
52
  else {
52
53
  console.error(`\nArchitect exited with code ${code}`);
@@ -2,8 +2,10 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { execSync } from 'child_process';
4
4
  import { getAssetsDir } from '../utils/config.js';
5
+ import { getCommandPrefix } from '../utils/command-prefix.js';
5
6
  export async function doctorCommand() {
6
7
  const cwd = process.cwd();
8
+ const cmd = getCommandPrefix();
7
9
  const checks = [];
8
10
  // Node.js version
9
11
  const nodeVersion = process.version;
@@ -44,7 +46,7 @@ export async function doctorCommand() {
44
46
  checks.push({
45
47
  name: 'MCP integrations',
46
48
  status: 'warn',
47
- message: 'No MCPs configured. Run: expxagents mcp setup <id>',
49
+ message: `No MCPs configured. Run: ${cmd} mcp setup <id>`,
48
50
  });
49
51
  }
50
52
  // Project structure
@@ -54,7 +56,7 @@ export async function doctorCommand() {
54
56
  checks.push({
55
57
  name: `Directory: ${dir}`,
56
58
  status: exists ? 'ok' : 'warn',
57
- message: exists ? 'exists' : 'missing — run `expxagents init`',
59
+ message: exists ? 'exists' : `missing — run \`${cmd} init\``,
58
60
  });
59
61
  }
60
62
  // .env file
@@ -62,7 +64,7 @@ export async function doctorCommand() {
62
64
  checks.push({
63
65
  name: '.env file',
64
66
  status: envExists ? 'ok' : 'warn',
65
- message: envExists ? 'exists' : 'missing — run `expxagents init`',
67
+ message: envExists ? 'exists' : `missing — run \`${cmd} init\``,
66
68
  });
67
69
  // Company profile
68
70
  const companyPath = path.join(cwd, '_expxagents', '_memory', 'company.md');
@@ -72,7 +74,7 @@ export async function doctorCommand() {
72
74
  checks.push({
73
75
  name: 'Company profile',
74
76
  status: configured ? 'ok' : 'warn',
75
- message: configured ? 'configured' : 'not configured — run `expxagents onboarding`',
77
+ message: configured ? 'configured' : `not configured — run \`${cmd} onboarding\``,
76
78
  });
77
79
  }
78
80
  // Squads count
@@ -3,6 +3,8 @@ import path from 'path';
3
3
  import crypto from 'crypto';
4
4
  import { execSync } from 'child_process';
5
5
  import { getTemplateDir, getAssetsDir } from '../utils/config.js';
6
+ import { tryGlobalInstall } from '../utils/global-install.js';
7
+ import { getVersion } from '../utils/version.js';
6
8
  function getDefaultTemplate(file) {
7
9
  if (file === 'company.md') {
8
10
  return `<!-- NOT CONFIGURED -->
@@ -408,6 +410,13 @@ BRIDGE_TIMEOUT_MS=300000
408
410
  fs.writeFileSync(gitignorePath, gitignoreEntries.join('\n') + '\n', 'utf-8');
409
411
  console.log(' Created .gitignore');
410
412
  }
413
+ // Try global install
414
+ const version = getVersion();
415
+ const globalInstalled = tryGlobalInstall(version);
416
+ const prefix = globalInstalled ? 'expxagents' : 'npx expxagents';
417
+ if (globalInstalled) {
418
+ console.log('\n ✓ CLI installed globally');
419
+ }
411
420
  // MCP Setup
412
421
  const mcpsDir = path.resolve('mcps');
413
422
  if (fs.existsSync(mcpsDir)) {
@@ -438,7 +447,7 @@ BRIDGE_TIMEOUT_MS=300000
438
447
  }
439
448
  }
440
449
  }
441
- console.log('\n Run: expxagents mcp setup <id> to configure additional MCPs\n');
450
+ console.log(`\n Run: ${prefix} mcp setup <id> to configure additional MCPs\n`);
442
451
  }
443
452
  // Install Python dependencies
444
453
  const pythonDeps = ['aiohttp', 'aiofiles', 'python-dotenv'];
@@ -461,7 +470,7 @@ BRIDGE_TIMEOUT_MS=300000
461
470
  console.log('\nUpdate complete! Assets refreshed to latest version.');
462
471
  }
463
472
  else {
464
- console.log('\nProject initialized! Run `expxagents onboarding` to configure your profile.');
473
+ console.log(`\nProject initialized! Run \`${prefix} onboarding\` to configure your profile.`);
465
474
  console.log('Use /expxagents in Claude Code to get started.');
466
475
  }
467
476
  }
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { getCommandPrefix } from '../utils/command-prefix.js';
3
4
  const SKILL_CATALOG = {
4
5
  'web-search': {
5
6
  description: 'Web search capability (built-in)',
@@ -34,6 +35,7 @@ const SKILL_CATALOG = {
34
35
  };
35
36
  export async function installCommand(skillName) {
36
37
  const cwd = process.cwd();
38
+ const cmd = getCommandPrefix();
37
39
  const skillsDir = path.join(cwd, 'skills');
38
40
  if (!skillName) {
39
41
  console.log('\n Available skills:\n');
@@ -41,13 +43,13 @@ export async function installCommand(skillName) {
41
43
  const installed = fs.existsSync(path.join(skillsDir, name));
42
44
  console.log(` ${installed ? '[installed]' : ' '} ${name} — ${info.description}`);
43
45
  }
44
- console.log('\n Usage: expxagents install <skill-name>\n');
46
+ console.log(`\n Usage: ${cmd} install <skill-name>\n`);
45
47
  return;
46
48
  }
47
49
  const skill = SKILL_CATALOG[skillName];
48
50
  if (!skill) {
49
51
  console.error(` Skill "${skillName}" not found in catalog.`);
50
- console.log(' Run `expxagents install` to see available skills.');
52
+ console.log(` Run \`${cmd} install\` to see available skills.`);
51
53
  process.exit(1);
52
54
  }
53
55
  const skillDir = path.join(skillsDir, skillName);
@@ -3,6 +3,7 @@ import path from 'path';
3
3
  import yaml from 'js-yaml';
4
4
  import { loadSkills } from '../../../core/skills-loader.js';
5
5
  import { readState } from '../../../core/state-manager.js';
6
+ import { getCommandPrefix } from '../utils/command-prefix.js';
6
7
  function walkSquads(squadsDir) {
7
8
  if (!fs.existsSync(squadsDir))
8
9
  return [];
@@ -70,6 +71,7 @@ function capitalize(s) {
70
71
  }
71
72
  export async function listCommand() {
72
73
  const cwd = process.cwd();
74
+ const cmd = getCommandPrefix();
73
75
  const squadsDir = path.join(cwd, 'squads');
74
76
  const skillsDir = path.join(cwd, 'skills');
75
77
  console.log('Squads:');
@@ -77,7 +79,7 @@ export async function listCommand() {
77
79
  if (fs.existsSync(squadsDir)) {
78
80
  const allSquads = walkSquads(squadsDir);
79
81
  if (allSquads.length === 0) {
80
- console.log(' No squads found. Run `expxagents create` to create one.\n');
82
+ console.log(` No squads found. Run \`${cmd} create\` to create one.\n`);
81
83
  }
82
84
  else {
83
85
  const hierarchical = allSquads
@@ -113,14 +115,14 @@ export async function listCommand() {
113
115
  }
114
116
  }
115
117
  else {
116
- console.log(' No squads directory. Run `expxagents init` first.\n');
118
+ console.log(` No squads directory. Run \`${cmd} init\` first.\n`);
117
119
  }
118
120
  // List skills (unchanged)
119
121
  console.log('Skills:');
120
122
  console.log('-------');
121
123
  const skills = loadSkills(skillsDir);
122
124
  if (skills.length === 0) {
123
- console.log(' No skills installed. Run `expxagents install <skill>` to add one.');
125
+ console.log(` No skills installed. Run \`${cmd} install <skill>\` to add one.`);
124
126
  }
125
127
  else {
126
128
  for (const skill of skills) {
@@ -7,6 +7,7 @@ import { detectMcp, detectExtension, resolvePlatformBinary } from '../mcp/detect
7
7
  import { writeMcpConfig, removeMcpConfig } from '../mcp/setup.js';
8
8
  import { getConfiguredMcpIds } from '../mcp/validate.js';
9
9
  import { getAssetsDir } from '../utils/config.js';
10
+ import { getCommandPrefix } from '../utils/command-prefix.js';
10
11
  function getMcpsDir() {
11
12
  const local = path.resolve('mcps');
12
13
  if (fs.existsSync(local))
@@ -54,7 +55,7 @@ export function mcpCommand() {
54
55
  }
55
56
  console.log('');
56
57
  }
57
- console.log('Run: expxagents mcp setup <id> to configure\n');
58
+ console.log(`Run: ${getCommandPrefix()} mcp setup <id> to configure\n`);
58
59
  });
59
60
  mcp
60
61
  .command('setup <ids...>')
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import readline from 'readline';
4
+ import { getCommandPrefix } from '../utils/command-prefix.js';
4
5
  function ask(rl, question) {
5
6
  return new Promise(resolve => rl.question(question, resolve));
6
7
  }
@@ -8,8 +9,9 @@ export async function onboardingCommand() {
8
9
  const cwd = process.cwd();
9
10
  const companyPath = path.join(cwd, '_expxagents', '_memory', 'company.md');
10
11
  const prefsPath = path.join(cwd, '_expxagents', '_memory', 'preferences.md');
12
+ const cmd = getCommandPrefix();
11
13
  if (!fs.existsSync(path.dirname(companyPath))) {
12
- console.error('Project not initialized. Run `expxagents init` first.');
14
+ console.error(`Project not initialized. Run \`${cmd} init\` first.`);
13
15
  process.exit(1);
14
16
  }
15
17
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
@@ -31,7 +33,7 @@ export async function onboardingCommand() {
31
33
  const companyContent = `# Company Profile\n\n## Company\n- **Name:** ${companyName}\n- **Website:** ${companyUrl}\n- **Sector:** ${sector}\n- **Description:** ${description}\n\n## Target Audience\n- **Primary:** ${audience}\n\n## Tone of Voice\n- **Style:** ${toneStyle}\n`;
32
34
  fs.writeFileSync(companyPath, companyContent, 'utf-8');
33
35
  console.log('\n Profile saved! You can edit it anytime at _expxagents/_memory/company.md');
34
- console.log(' Run `expxagents create` to design your first squad.\n');
36
+ console.log(` Run \`${cmd} create\` to design your first squad.\n`);
35
37
  }
36
38
  finally {
37
39
  rl.close();
@@ -3,6 +3,7 @@ import { resolve, join } from 'node:path';
3
3
  import { readdirSync, readFileSync, existsSync } from 'node:fs';
4
4
  import { RegistryClient, readCredentials } from '@expxagents/registry-client';
5
5
  import { load as loadYaml } from 'js-yaml';
6
+ import { getCommandPrefix } from '../utils/command-prefix.js';
6
7
  export function scanInstalledSquads(squadsDir) {
7
8
  const installed = [];
8
9
  if (!existsSync(squadsDir))
@@ -83,5 +84,5 @@ export const outdatedCommand = new Command('outdated')
83
84
  for (const o of outdated) {
84
85
  console.log(` ${o.fullName} ${o.current} \u2192 ${o.latest}`);
85
86
  }
86
- console.log(`\nRun \`expxagents update\` to update all.`);
87
+ console.log(`\nRun \`${getCommandPrefix()} update\` to update all.`);
87
88
  });
@@ -1,6 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import { resolve } from 'node:path';
3
3
  import { RegistryClient, readCredentials } from '@expxagents/registry-client';
4
+ import { getCommandPrefix } from '../utils/command-prefix.js';
4
5
  export const publishCommand = new Command('publish')
5
6
  .description('Publish a squad to the registry')
6
7
  .argument('[dir]', 'Squad directory', '.')
@@ -8,7 +9,7 @@ export const publishCommand = new Command('publish')
8
9
  .action(async (dir, options) => {
9
10
  const creds = readCredentials();
10
11
  if (!creds) {
11
- console.error('Not logged in. Run: expxagents login');
12
+ console.error(`Not logged in. Run: ${getCommandPrefix()} login`);
12
13
  process.exit(1);
13
14
  }
14
15
  const squadDir = resolve(dir);
@@ -2,6 +2,7 @@ import fs from 'fs';
2
2
  import path from 'path';
3
3
  import * as readline from 'readline';
4
4
  import yaml from 'js-yaml';
5
+ import { getCommandPrefix } from '../utils/command-prefix.js';
5
6
  // Walk squadsDir recursively, return all squad summaries
6
7
  function discoverAllSquads(squadsDir) {
7
8
  const result = [];
@@ -214,7 +215,7 @@ export async function reorganizeCommand() {
214
215
  const cwd = process.cwd();
215
216
  const squadsDir = path.join(cwd, 'squads');
216
217
  if (!fs.existsSync(squadsDir)) {
217
- console.error('No squads directory found. Run `expxagents init` first.');
218
+ console.error(`No squads directory found. Run \`${getCommandPrefix()} init\` first.`);
218
219
  process.exit(1);
219
220
  }
220
221
  console.log('Escaneando squads...\n');
@@ -5,6 +5,7 @@ import { loadSquad, findSquadDir } from '../../../core/squad-loader.js';
5
5
  import { createInitialState, readState, writeState, updateAgentStatus, updateStep, setHandoff, setSquadStatus, HANDOFF_DELAY_MS, } from '../../../core/state-manager.js';
6
6
  import { loadSkills } from '../../../core/skills-loader.js';
7
7
  import { getCoreAsset } from '../utils/config.js';
8
+ import { getCommandPrefix } from '../utils/command-prefix.js';
8
9
  import { runWithProvider } from '../runners/provider-runner.js';
9
10
  import { estimateCost, formatCost } from '../runners/cost-tracker.js';
10
11
  function delay(ms) {
@@ -135,8 +136,9 @@ export async function runCommand(name) {
135
136
  const squadsDir = path.join(cwd, 'squads');
136
137
  const squadDir = findSquadDir(squadsDir, name);
137
138
  const skillsDir = path.join(cwd, 'skills');
139
+ const cmd = getCommandPrefix();
138
140
  if (!squadDir) {
139
- console.error(`Squad "${name}" not found. Run \`expxagents list\` to see available squads.`);
141
+ console.error(`Squad "${name}" not found. Run \`${cmd} list\` to see available squads.`);
140
142
  process.exit(1);
141
143
  }
142
144
  let config;
@@ -155,7 +157,7 @@ export async function runCommand(name) {
155
157
  }
156
158
  const existingState = readState(squadDir);
157
159
  if (existingState && existingState.status === 'running') {
158
- console.error(`Squad "${name}" is already running. Use \`expxagents stop ${name}\` first.`);
160
+ console.error(`Squad "${name}" is already running. Use \`${cmd} stop ${name}\` first.`);
159
161
  process.exit(1);
160
162
  }
161
163
  const outputDir = getNextVersionDir(squadDir);
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
3
  import { findSquadDir, loadSquad } from '../../../core/squad-loader.js';
4
+ import { getCommandPrefix } from '../utils/command-prefix.js';
4
5
  /**
5
6
  * Find all .pen files in a squad's templates/ directory.
6
7
  */
@@ -65,7 +66,7 @@ export async function syncTemplatesCommand(squadFilter) {
65
66
  const cwd = process.cwd();
66
67
  const squadsDir = path.join(cwd, 'squads');
67
68
  if (!fs.existsSync(squadsDir)) {
68
- console.error('No squads/ directory found. Run `expxagents init` first.');
69
+ console.error(`No squads/ directory found. Run \`${getCommandPrefix()} init\` first.`);
69
70
  process.exit(1);
70
71
  }
71
72
  let squadDirs;
@@ -1,11 +1,12 @@
1
1
  import { Command } from 'commander';
2
2
  import { RegistryClient, readCredentials } from '@expxagents/registry-client';
3
+ import { getCommandPrefix } from '../utils/command-prefix.js';
3
4
  export const whoamiCommand = new Command('whoami')
4
5
  .description('Show current registry identity')
5
6
  .action(async () => {
6
7
  const creds = readCredentials();
7
8
  if (!creds) {
8
- console.error('Not logged in. Run: expxagents login');
9
+ console.error(`Not logged in. Run: ${getCommandPrefix()} login`);
9
10
  process.exit(1);
10
11
  }
11
12
  const client = new RegistryClient({ registryUrl: creds.registryUrl, apiKey: creds.apiKey });
@@ -17,7 +18,7 @@ export const whoamiCommand = new Command('whoami')
17
18
  console.log(` Registry: ${creds.registryUrl}`);
18
19
  }
19
20
  catch {
20
- console.error('\u2717 Invalid credentials. Run: expxagents login');
21
+ console.error(`\u2717 Invalid credentials. Run: ${getCommandPrefix()} login`);
21
22
  process.exit(1);
22
23
  }
23
24
  });
@@ -1,4 +1,3 @@
1
- import fs from 'fs';
2
1
  import { Command } from 'commander';
3
2
  import { initCommand } from './commands/init.js';
4
3
  import { createCommand } from './commands/create.js';
@@ -27,17 +26,7 @@ import { searchCommand } from './commands/search.js';
27
26
  import { infoCommand } from './commands/info.js';
28
27
  import { outdatedCommand } from './commands/outdated.js';
29
28
  import { updateCommand } from './commands/update.js';
30
- import { findPackageRoot } from './utils/config.js';
31
- function getVersion() {
32
- try {
33
- const pkgPath = `${findPackageRoot()}/package.json`;
34
- const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
35
- return pkg.version ?? '0.0.0';
36
- }
37
- catch {
38
- return '0.0.0';
39
- }
40
- }
29
+ import { getVersion } from './utils/version.js';
41
30
  const program = new Command();
42
31
  program
43
32
  .name('expxagents')
@@ -1,5 +1,6 @@
1
1
  import fs from 'fs';
2
2
  import path from 'path';
3
+ import { getCommandPrefix } from '../utils/command-prefix.js';
3
4
  export function getConfiguredMcpIds(projectDir) {
4
5
  const mcpPath = path.join(projectDir, '.mcp.json');
5
6
  if (!fs.existsSync(mcpPath))
@@ -18,6 +19,6 @@ export function validateSquadMcps(squadMcps, configuredMcps) {
18
19
  const missing = squadMcps.filter(m => !configuredMcps.includes(m));
19
20
  if (missing.length > 0) {
20
21
  throw new Error(`Squad requires MCPs not configured: ${missing.join(', ')}.\n` +
21
- `Run: expxagents mcp setup ${missing.join(' ')}`);
22
+ `Run: ${getCommandPrefix()} mcp setup ${missing.join(' ')}`);
22
23
  }
23
24
  }
@@ -0,0 +1,35 @@
1
+ import { describe, it, expect, afterEach } from 'vitest';
2
+ import { getCommandPrefix } from '../command-prefix.js';
3
+ describe('getCommandPrefix', () => {
4
+ const originalArgv = process.argv;
5
+ const originalEnv = { ...process.env };
6
+ afterEach(() => {
7
+ process.argv = originalArgv;
8
+ process.env = originalEnv;
9
+ });
10
+ it('returns "npx expxagents" when npm_execpath contains npx', () => {
11
+ process.env.npm_execpath = '/usr/local/lib/node_modules/npm/bin/npx-cli.js';
12
+ process.argv = [process.argv[0], '/usr/local/bin/expxagents'];
13
+ expect(getCommandPrefix()).toBe('npx expxagents');
14
+ });
15
+ it('returns "npx expxagents" when argv[1] contains _npx (fallback)', () => {
16
+ delete process.env.npm_execpath;
17
+ process.argv = [process.argv[0], '/home/user/.npm/_npx/abc123/bin/expxagents.js'];
18
+ expect(getCommandPrefix()).toBe('npx expxagents');
19
+ });
20
+ it('returns "expxagents" for global install on Unix', () => {
21
+ delete process.env.npm_execpath;
22
+ process.argv = [process.argv[0], '/usr/local/bin/expxagents'];
23
+ expect(getCommandPrefix()).toBe('expxagents');
24
+ });
25
+ it('returns "expxagents" for global install on Windows', () => {
26
+ delete process.env.npm_execpath;
27
+ process.argv = [process.argv[0], 'C:\\Users\\X\\AppData\\Roaming\\npm\\expxagents'];
28
+ expect(getCommandPrefix()).toBe('expxagents');
29
+ });
30
+ it('returns "expxagents" when both env and argv are empty', () => {
31
+ delete process.env.npm_execpath;
32
+ process.argv = [process.argv[0], ''];
33
+ expect(getCommandPrefix()).toBe('expxagents');
34
+ });
35
+ });
@@ -0,0 +1,25 @@
1
+ import { describe, it, expect, vi, afterEach } from 'vitest';
2
+ import { execSync } from 'child_process';
3
+ import { tryGlobalInstall } from '../global-install.js';
4
+ vi.mock('child_process', () => ({
5
+ execSync: vi.fn(),
6
+ }));
7
+ const mockExecSync = vi.mocked(execSync);
8
+ afterEach(() => {
9
+ vi.restoreAllMocks();
10
+ });
11
+ describe('tryGlobalInstall', () => {
12
+ it('returns true when npm install -g succeeds', () => {
13
+ mockExecSync.mockReturnValue(Buffer.from(''));
14
+ expect(tryGlobalInstall('0.20.1')).toBe(true);
15
+ expect(mockExecSync).toHaveBeenCalledWith('npm install -g expxagents@0.20.1', { stdio: 'pipe', timeout: 60000 });
16
+ });
17
+ it('returns false when npm install -g fails', () => {
18
+ mockExecSync.mockImplementation(() => { throw new Error('EACCES'); });
19
+ expect(tryGlobalInstall('0.20.1')).toBe(false);
20
+ });
21
+ it('never throws', () => {
22
+ mockExecSync.mockImplementation(() => { throw new Error('network error'); });
23
+ expect(() => tryGlobalInstall('1.0.0')).not.toThrow();
24
+ });
25
+ });
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Detects whether the CLI is running globally or via npx.
3
+ * Returns 'expxagents' for global, 'npx expxagents' for npx.
4
+ */
5
+ export declare function getCommandPrefix(): string;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Detects whether the CLI is running globally or via npx.
3
+ * Returns 'expxagents' for global, 'npx expxagents' for npx.
4
+ */
5
+ export function getCommandPrefix() {
6
+ const npmExecPath = process.env.npm_execpath ?? '';
7
+ if (npmExecPath.includes('npx')) {
8
+ return 'npx expxagents';
9
+ }
10
+ const execPath = process.argv[1] ?? '';
11
+ if (execPath.includes('_npx')) {
12
+ return 'npx expxagents';
13
+ }
14
+ return 'expxagents';
15
+ }
@@ -1,7 +1,53 @@
1
- import { spawn } from 'child_process';
1
+ import { execSync, spawn } from 'child_process';
2
2
  import fs from 'fs';
3
3
  import path from 'path';
4
4
  import { resolveServerPaths } from './server-paths.js';
5
+ const MAX_HEALTH_CHECK_ATTEMPTS = 30;
6
+ const HEALTH_CHECK_INTERVAL_MS = 500;
7
+ /**
8
+ * Kill the process listening on the given port (cross-platform).
9
+ */
10
+ function killProcessOnPort(port) {
11
+ if (process.platform === 'win32') {
12
+ // Find PID using netstat, then kill it
13
+ try {
14
+ const output = execSync(`netstat -ano | findstr :${port} | findstr LISTENING`, {
15
+ encoding: 'utf-8',
16
+ stdio: ['pipe', 'pipe', 'ignore'],
17
+ });
18
+ const pids = new Set(output
19
+ .split('\n')
20
+ .map((line) => line.trim().split(/\s+/).pop())
21
+ .filter((pid) => !!pid && pid !== '0'));
22
+ for (const pid of pids) {
23
+ execSync(`taskkill /PID ${pid} /F`, { stdio: 'ignore' });
24
+ }
25
+ }
26
+ catch {
27
+ throw new Error(`Could not stop existing server on port ${port}`);
28
+ }
29
+ }
30
+ else {
31
+ execSync(`lsof -ti:${port} | xargs kill -9`, { stdio: 'ignore' });
32
+ }
33
+ }
34
+ /**
35
+ * Poll the health endpoint until the server responds OK or max attempts reached.
36
+ */
37
+ async function waitForHealthy(port) {
38
+ for (let i = 0; i < MAX_HEALTH_CHECK_ATTEMPTS; i++) {
39
+ try {
40
+ const res = await fetch(`http://localhost:${port}/api/health`);
41
+ if (res.ok)
42
+ return true;
43
+ }
44
+ catch {
45
+ // Server not ready yet
46
+ }
47
+ await new Promise((resolve) => setTimeout(resolve, HEALTH_CHECK_INTERVAL_MS));
48
+ }
49
+ return false;
50
+ }
5
51
  /**
6
52
  * Ensure the production server is running on the given port.
7
53
  * If a dev server is detected (health OK but no dashboard), it is killed and replaced.
@@ -32,8 +78,7 @@ export async function ensureProductionServer(port) {
32
78
  // Server is running but not serving dashboard (dev mode) — kill it
33
79
  console.log('Detected dev server on port ' + port + ', restarting in production mode...');
34
80
  try {
35
- const { execSync } = await import('child_process');
36
- execSync(`lsof -ti:${port} | xargs kill -9`, { stdio: 'ignore' });
81
+ killProcessOnPort(port);
37
82
  // Wait for port to free up
38
83
  await new Promise((resolve) => setTimeout(resolve, 1000));
39
84
  }
@@ -71,6 +116,11 @@ export async function ensureProductionServer(port) {
71
116
  console.error(`Failed to start server: ${err.message}`);
72
117
  process.exit(1);
73
118
  });
74
- // Wait for server to start
75
- await new Promise((resolve) => setTimeout(resolve, 2000));
119
+ // Poll health endpoint until server is ready (up to 15 seconds)
120
+ const healthy = await waitForHealthy(port);
121
+ if (!healthy) {
122
+ console.error(`Server failed to start on port ${port} within ${(MAX_HEALTH_CHECK_ATTEMPTS * HEALTH_CHECK_INTERVAL_MS) / 1000} seconds.`);
123
+ console.error('Check the server logs above for errors.');
124
+ process.exit(1);
125
+ }
76
126
  }
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Attempts to install expxagents globally via npm.
3
+ * Returns true if successful, false otherwise.
4
+ * Never throws — failures are silent.
5
+ */
6
+ export declare function tryGlobalInstall(version: string): boolean;
@@ -0,0 +1,15 @@
1
+ import { execSync } from 'child_process';
2
+ /**
3
+ * Attempts to install expxagents globally via npm.
4
+ * Returns true if successful, false otherwise.
5
+ * Never throws — failures are silent.
6
+ */
7
+ export function tryGlobalInstall(version) {
8
+ try {
9
+ execSync(`npm install -g expxagents@${version}`, { stdio: 'pipe', timeout: 60000 });
10
+ return true;
11
+ }
12
+ catch {
13
+ return false;
14
+ }
15
+ }
@@ -0,0 +1 @@
1
+ export declare function getVersion(): string;
@@ -0,0 +1,12 @@
1
+ import fs from 'fs';
2
+ import { findPackageRoot } from './config.js';
3
+ export function getVersion() {
4
+ try {
5
+ const pkgPath = `${findPackageRoot()}/package.json`;
6
+ const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf-8'));
7
+ return pkg.version ?? '0.0.0';
8
+ }
9
+ catch {
10
+ return '0.0.0';
11
+ }
12
+ }
@@ -0,0 +1,4 @@
1
+ import type { KnowledgeConfig } from './types.js';
2
+ export declare const DEFAULT_CONFIG: KnowledgeConfig;
3
+ export declare function loadKnowledgeConfig(projectRoot: string, dataDir?: string): KnowledgeConfig;
4
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,eAAO,MAAM,cAAc,EAAE,eAQ5B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,eAAe,CAa1F"}
@@ -0,0 +1,23 @@
1
+ import path from 'node:path';
2
+ export const DEFAULT_CONFIG = {
3
+ enabled: true,
4
+ db_path: '',
5
+ embedder: { provider: 'auto', batch_size: 32 },
6
+ chunker: { max_tokens: 512, overlap: 0.2 },
7
+ graph: { enabled: true, extract_on_ingest: true, dedup_threshold: 0.85 },
8
+ auto_ingest: { agent_outputs: true, activity_logs: false, watch_paths: [] },
9
+ limits: { max_document_size_mb: 10, max_documents: 10000, max_entities: 50000 },
10
+ };
11
+ export function loadKnowledgeConfig(projectRoot, dataDir) {
12
+ const config = structuredClone(DEFAULT_CONFIG);
13
+ config.db_path = path.join(dataDir ?? path.join(projectRoot, '.expxagents', 'data'), 'knowledge.db');
14
+ // Env var overrides (take precedence over yaml)
15
+ if (process.env.KNOWLEDGE_ENABLED !== undefined) {
16
+ config.enabled = process.env.KNOWLEDGE_ENABLED !== 'false';
17
+ }
18
+ if (process.env.KNOWLEDGE_EMBEDDER_PROVIDER) {
19
+ config.embedder.provider = process.env.KNOWLEDGE_EMBEDDER_PROVIDER;
20
+ }
21
+ return config;
22
+ }
23
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AACA,OAAO,IAAI,MAAM,WAAW,CAAC;AAG7B,MAAM,CAAC,MAAM,cAAc,GAAoB;IAC7C,OAAO,EAAE,IAAI;IACb,OAAO,EAAE,EAAE;IACX,QAAQ,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,EAAE,EAAE;IAC9C,OAAO,EAAE,EAAE,UAAU,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE;IAC1C,KAAK,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,iBAAiB,EAAE,IAAI,EAAE,eAAe,EAAE,IAAI,EAAE;IACxE,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,EAAE;IAC3E,MAAM,EAAE,EAAE,oBAAoB,EAAE,EAAE,EAAE,aAAa,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE;CAChF,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,WAAmB,EAAE,OAAgB;IACvE,MAAM,MAAM,GAAG,eAAe,CAAC,cAAc,CAAC,CAAC;IAC/C,MAAM,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,aAAa,EAAE,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC;IAErG,gDAAgD;IAChD,IAAI,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,SAAS,EAAE,CAAC;QAChD,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,iBAAiB,KAAK,OAAO,CAAC;IAC7D,CAAC;IACD,IAAI,OAAO,CAAC,GAAG,CAAC,2BAA2B,EAAE,CAAC;QAC5C,MAAM,CAAC,QAAQ,CAAC,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,2BAAsE,CAAC;IAChH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC"}
@@ -0,0 +1,6 @@
1
+ import Database from 'better-sqlite3';
2
+ export declare function getKnowledgeDb(dbPath: string): Database.Database;
3
+ export declare function isVecLoaded(): boolean;
4
+ export declare function closeKnowledgeDb(): void;
5
+ export declare function createTestDb(): Database.Database;
6
+ //# sourceMappingURL=connection.d.ts.map