daedalus-cli 1.26.0 → 1.26.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.
package/dist/commands.js CHANGED
@@ -3,7 +3,7 @@ import fs from 'fs';
3
3
  import path from 'path';
4
4
  import pc from 'picocolors';
5
5
  import { executeToolCalls } from './tools/executor.js';
6
- import { discoverLocalServers } from './config/index.js';
6
+ import { discoverLocalServers, saveConfig } from './config/index.js';
7
7
  import { getSessionTodos } from './tools/builtin/todo.js';
8
8
  import { saveProfile } from './profile.js';
9
9
  import { extractAndSave } from './extraction.js';
@@ -811,16 +811,29 @@ Once you have finished making changes, I will automatically re-run the command t
811
811
  },
812
812
  {
813
813
  name: '/project',
814
- description: 'View or set project config settings',
814
+ description: 'View or set project config settings (.daedalusrc)',
815
815
  execute: async (args, _ctx) => {
816
816
  const rest = args.trim();
817
- const { loadProjectConfig, saveProjectConfig } = await import('./tools/builtin/project-config.js');
817
+ const { loadProjectConfig, saveProjectConfig, hasLocalConfig } = await import('./tools/builtin/project-config.js');
818
818
  if (!rest) {
819
819
  const cfg = loadProjectConfig(process.cwd());
820
- console.log(pc.bold('\n--- Project Config (.daedalus) ---'));
820
+ const isLocal = hasLocalConfig(process.cwd());
821
+ console.log(pc.bold(`\n--- Project Config (${isLocal ? '.daedalusrc' : 'global'}) ---`));
821
822
  console.log(JSON.stringify(cfg, null, 2));
822
823
  console.log(pc.bold('----------------------------------'));
823
824
  console.log(pc.gray('Use /project set <key> = <value> to update'));
825
+ console.log(pc.gray('Use /project init to create a .daedalusrc in this project'));
826
+ return;
827
+ }
828
+ if (rest === 'init') {
829
+ const localPath = path.join(process.cwd(), '.daedalusrc');
830
+ if (fs.existsSync(localPath)) {
831
+ console.log(pc.yellow('.daedalusrc already exists in this project'));
832
+ return;
833
+ }
834
+ const cfg = loadProjectConfig(process.cwd());
835
+ saveProjectConfig(cfg, true);
836
+ console.log(pc.green('Created .daedalusrc — project config is now local to this repo'));
824
837
  return;
825
838
  }
826
839
  if (rest.startsWith('set ')) {
@@ -837,7 +850,7 @@ Once you have finished making changes, I will automatically re-run the command t
837
850
  value = parts.slice(1).join(' ');
838
851
  }
839
852
  if (!key || !value) {
840
- console.log(pc.red('[WARN] Usage: /project set <key> = <value>'));
853
+ console.log(pc.red('Usage: /project set <key> = <value>'));
841
854
  }
842
855
  else {
843
856
  const cfg = loadProjectConfig(process.cwd());
@@ -849,12 +862,13 @@ Once you have finished making changes, I will automatically re-run the command t
849
862
  else if (!isNaN(Number(value)))
850
863
  parsedVal = Number(value);
851
864
  cfg[key] = parsedVal;
852
- saveProjectConfig(cfg);
853
- console.log(pc.green(`[OK] Set ${key} = ${value}`));
865
+ const isLocal = hasLocalConfig(process.cwd());
866
+ saveProjectConfig(cfg, isLocal);
867
+ console.log(pc.green(`Set ${key} = ${value} (${isLocal ? '.daedalusrc' : 'global'})`));
854
868
  }
855
869
  }
856
870
  else {
857
- console.log(pc.red('[WARN] Usage: /project | /project set <key> = <value>'));
871
+ console.log(pc.red(`Unknown subcommand: ${rest}. Try: /project, /project set <key> = <value>, /project init`));
858
872
  }
859
873
  }
860
874
  },
@@ -881,28 +895,54 @@ Once you have finished making changes, I will automatically re-run the command t
881
895
  });
882
896
  }
883
897
  console.log(pc.bold('---------------------\n'));
884
- console.log(pc.gray('Use `/session load <id>` to switch to a past session.'));
898
+ console.log(pc.gray('Use `/session load <id>` to resume a past session.'));
899
+ console.log(pc.gray('Use `/session search <query>` to search sessions.'));
885
900
  console.log(pc.gray('Use `/session new [title]` to start a new session.'));
886
901
  console.log(pc.gray('Use `/session rename <title>` to rename the current session.'));
887
902
  console.log(pc.gray('Use `/session delete <id>` to delete a session.'));
888
903
  return;
889
904
  }
905
+ if (subcommand === 'search') {
906
+ if (!subcommandArg) {
907
+ console.log(pc.red('Usage: /session search <query>'));
908
+ return;
909
+ }
910
+ const query = subcommandArg.toLowerCase();
911
+ const sessions = ctx.sessionManager.getSessionsForProject();
912
+ const matches = sessions.filter(s => s.title.toLowerCase().includes(query) ||
913
+ s.id.toLowerCase().includes(query));
914
+ if (matches.length === 0) {
915
+ console.log(pc.yellow(`No sessions matching "${subcommandArg}"`));
916
+ }
917
+ else {
918
+ console.log(pc.bold(`\n--- Matching Sessions (${matches.length}) ---`));
919
+ matches.forEach(s => {
920
+ const currentTag = s.id === ctx.sessionManager.sessionId ? pc.green(' (current)') : '';
921
+ const dateStr = new Date(s.updated_at).toLocaleString();
922
+ console.log(` • ${pc.cyan(s.id)}${currentTag}`);
923
+ console.log(` Title: ${pc.white(s.title)}`);
924
+ console.log(` Updated: ${pc.dim(dateStr)}`);
925
+ });
926
+ console.log(pc.bold('----------------------------------\n'));
927
+ }
928
+ return;
929
+ }
890
930
  if (subcommand === 'load') {
891
931
  if (!subcommandArg) {
892
- console.log(pc.red('[WARN] Usage: /session load <session-id>'));
932
+ console.log(pc.red('Usage: /session load <session-id>'));
893
933
  return;
894
934
  }
895
935
  const sessions = ctx.sessionManager.getSessionsForProject();
896
936
  const found = sessions.find(s => s.id === subcommandArg || s.id.startsWith(subcommandArg));
897
937
  if (!found) {
898
- console.log(pc.red(`[WARN] Session "${subcommandArg}" not found.`));
938
+ console.log(pc.red(`Session "${subcommandArg}" not found.`));
899
939
  return;
900
940
  }
901
941
  const currentTodos = getSessionTodos(ctx.toolContext.sessionId);
902
942
  ctx.sessionManager.saveSessionState(ctx.messages, ctx.activeFiles, currentTodos);
903
943
  const loaded = ctx.sessionManager.startSession(found.id, found.title);
904
944
  ctx.initializeSessionState(loaded);
905
- console.log(pc.green(`[OK] Loaded session: ${pc.bold(found.id)} ("${found.title}")`));
945
+ console.log(pc.green(`Loaded session: ${pc.bold(found.id)} ("${found.title}")`));
906
946
  return;
907
947
  }
908
948
  if (subcommand === 'new') {
@@ -911,38 +951,38 @@ Once you have finished making changes, I will automatically re-run the command t
911
951
  const title = subcommandArg || `Session on ${new Date().toLocaleDateString()}`;
912
952
  const loaded = ctx.sessionManager.startSession(undefined, title);
913
953
  ctx.initializeSessionState(loaded);
914
- console.log(pc.green(`[OK] Started new session: ${pc.bold(loaded.sessionId)}`));
954
+ console.log(pc.green(`Started new session: ${pc.bold(loaded.sessionId)}`));
915
955
  return;
916
956
  }
917
957
  if (subcommand === 'rename') {
918
958
  if (!subcommandArg) {
919
- console.log(pc.red('[WARN] Usage: /session rename <new-title>'));
959
+ console.log(pc.red('Usage: /session rename <new-title>'));
920
960
  return;
921
961
  }
922
962
  ctx.sessionManager.updateSessionTitle(subcommandArg);
923
- console.log(pc.green(`[OK] Session renamed to: "${subcommandArg}"`));
963
+ console.log(pc.green(`Session renamed to: "${subcommandArg}"`));
924
964
  return;
925
965
  }
926
966
  if (subcommand === 'delete') {
927
967
  if (!subcommandArg) {
928
- console.log(pc.red('[WARN] Usage: /session delete <session-id>'));
968
+ console.log(pc.red('Usage: /session delete <session-id>'));
929
969
  return;
930
970
  }
931
971
  if (subcommandArg === ctx.sessionManager.sessionId) {
932
- console.log(pc.red('[WARN] Cannot delete the current active session.'));
972
+ console.log(pc.red('Cannot delete the current active session.'));
933
973
  return;
934
974
  }
935
975
  const sessions = ctx.sessionManager.getSessionsForProject();
936
976
  const found = sessions.find(s => s.id === subcommandArg || s.id.startsWith(subcommandArg));
937
977
  if (!found) {
938
- console.log(pc.red(`[WARN] Session "${subcommandArg}" not found.`));
978
+ console.log(pc.red(`Session "${subcommandArg}" not found.`));
939
979
  return;
940
980
  }
941
981
  ctx.sessionManager.deleteSession(found.id);
942
- console.log(pc.green(`[OK] Deleted session: ${pc.bold(found.id)}`));
982
+ console.log(pc.green(`Deleted session: ${pc.bold(found.id)}`));
943
983
  return;
944
984
  }
945
- console.log(pc.red(`[WARN] Unknown subcommand: ${subcommand}`));
985
+ console.log(pc.red(`Unknown subcommand: ${subcommand}. Try: list, search, load, new, rename, delete`));
946
986
  }
947
987
  },
948
988
  {
@@ -1551,6 +1591,53 @@ Once you have finished making changes, I will automatically re-run the command t
1551
1591
  }
1552
1592
  return;
1553
1593
  }
1594
+ case 'reconnect':
1595
+ case 'rc': {
1596
+ const { loadConfig } = await import('./config/index.js');
1597
+ const config = loadConfig();
1598
+ const mcpConfigs = Object.entries(config.tools.mcpServers)
1599
+ .filter(([_, s]) => s.enabled)
1600
+ .map(([name, s]) => ({
1601
+ name,
1602
+ transport: s.transport,
1603
+ command: s.command,
1604
+ args: s.args,
1605
+ url: s.url,
1606
+ headers: s.headers,
1607
+ enabled: s.enabled,
1608
+ }));
1609
+ const already = mcpRegistry.getConnectedServers();
1610
+ const newServers = mcpConfigs.filter(c => !already.includes(c.name));
1611
+ if (newServers.length === 0) {
1612
+ if (mcpConfigs.length === 0) {
1613
+ console.log(pc.yellow(' No enabled MCP servers configured. Install one with /mcp install'));
1614
+ }
1615
+ else {
1616
+ console.log(pc.dim(' All enabled MCP servers are already connected.'));
1617
+ }
1618
+ return;
1619
+ }
1620
+ mcpRegistry.setConfigs(mcpConfigs);
1621
+ const connected = [];
1622
+ const failed = [];
1623
+ for (const s of newServers) {
1624
+ try {
1625
+ await mcpRegistry.connectServer(s);
1626
+ connected.push(s.name);
1627
+ }
1628
+ catch (err) {
1629
+ failed.push(`${s.name} (${err.message})`);
1630
+ }
1631
+ }
1632
+ if (connected.length > 0) {
1633
+ const totalTools = mcpRegistry.getToolDefinitions().length;
1634
+ console.log(pc.green(` Connected: ${connected.join(', ')} (${totalTools} MCP tools total)`));
1635
+ }
1636
+ if (failed.length > 0) {
1637
+ console.log(pc.yellow(` Failed: ${failed.join(', ')}`));
1638
+ }
1639
+ return;
1640
+ }
1554
1641
  case 'enable':
1555
1642
  case 'e': {
1556
1643
  if (!rest) {
@@ -1579,6 +1666,7 @@ Once you have finished making changes, I will automatically re-run the command t
1579
1666
  console.log(` ${pc.cyan('/mcp list')} ${pc.dim('List installed servers')}`);
1580
1667
  console.log(` ${pc.cyan('/mcp remove <name>')} ${pc.dim('Remove an installed server')}`);
1581
1668
  console.log(` ${pc.cyan('/mcp info <name>')} ${pc.dim('Show server details')}`);
1669
+ console.log(` ${pc.cyan('/mcp reconnect')} ${pc.dim('Reconnect all enabled servers')}`);
1582
1670
  console.log(` ${pc.cyan('/mcp enable <name>')} ${pc.dim('Enable a disabled server')}`);
1583
1671
  console.log(` ${pc.cyan('/mcp disable <name>')} ${pc.dim('Disable a server without removing it')}`);
1584
1672
  console.log(`\n ${pc.bold('Zero-config starters (no API keys needed):')}`);
@@ -1593,6 +1681,112 @@ Once you have finished making changes, I will automatically re-run the command t
1593
1681
  }
1594
1682
  }
1595
1683
  },
1684
+ {
1685
+ name: '/onboard',
1686
+ description: 'First-time setup — discover local models, configure, and test',
1687
+ execute: async (_args, ctx) => {
1688
+ const config = ctx.config;
1689
+ console.log(pc.bold(pc.cyan('\n╔══════════════════════════════════════╗')));
1690
+ console.log(pc.bold(pc.cyan('║ Daedalus Onboarding ║')));
1691
+ console.log(pc.bold(pc.cyan('╚══════════════════════════════════════╝')));
1692
+ console.log();
1693
+ console.log('Daedalus runs AI models locally on your machine.');
1694
+ console.log('First, I need to know which model server to use.');
1695
+ console.log();
1696
+ // Step 1: Discover local model servers
1697
+ console.log(pc.bold('🔍 Scanning for local model servers...'));
1698
+ const discovered = await discoverLocalServers();
1699
+ let chosenEndpoint = '';
1700
+ let chosenModel = '';
1701
+ if (discovered.length > 0) {
1702
+ console.log(pc.green(`\n Found ${discovered.length} running server(s):\n`));
1703
+ for (let i = 0; i < discovered.length; i++) {
1704
+ const s = discovered[i];
1705
+ console.log(` ${i + 1}. ${pc.cyan(s.name)} at ${s.endpoint}`);
1706
+ for (const m of s.models.slice(0, 3)) {
1707
+ console.log(` - ${m}`);
1708
+ }
1709
+ if (s.models.length > 3) {
1710
+ console.log(pc.gray(` ... and ${s.models.length - 3} more`));
1711
+ }
1712
+ }
1713
+ console.log();
1714
+ const serverChoice = await ctx.askLine(`Select a server (1-${discovered.length}) or press Enter to add manually: `);
1715
+ const idx = parseInt(serverChoice) - 1;
1716
+ if (idx >= 0 && idx < discovered.length) {
1717
+ const server = discovered[idx];
1718
+ chosenEndpoint = server.endpoint;
1719
+ if (server.models.length === 1) {
1720
+ chosenModel = server.models[0];
1721
+ }
1722
+ else {
1723
+ console.log(`\nModels on ${pc.cyan(server.name)}:`);
1724
+ for (let i = 0; i < server.models.length; i++) {
1725
+ console.log(` ${i + 1}. ${server.models[i]}`);
1726
+ }
1727
+ const modelChoice = await ctx.askLine(`Select a model (1-${server.models.length}): `);
1728
+ const midx = parseInt(modelChoice) - 1;
1729
+ if (midx >= 0 && midx < server.models.length) {
1730
+ chosenModel = server.models[midx];
1731
+ }
1732
+ }
1733
+ }
1734
+ }
1735
+ if (!chosenEndpoint) {
1736
+ console.log(`\nEnter your model server details manually.`);
1737
+ chosenEndpoint = await ctx.askLine('API endpoint (e.g. http://localhost:1234/v1): ');
1738
+ if (!chosenEndpoint)
1739
+ chosenEndpoint = 'http://localhost:1234/v1';
1740
+ chosenModel = await ctx.askLine('Model name (e.g. qwen2.5-coder-7b-instruct): ');
1741
+ if (!chosenModel)
1742
+ chosenModel = 'auto';
1743
+ }
1744
+ if (!chosenModel)
1745
+ chosenModel = 'auto';
1746
+ // Step 2: Add to config
1747
+ const entry = {
1748
+ name: chosenModel,
1749
+ endpoint: chosenEndpoint,
1750
+ model: chosenModel,
1751
+ priority: 1,
1752
+ enabled: true,
1753
+ };
1754
+ // Replace any existing chain or add to it
1755
+ config.router.chain = [entry, ...config.router.chain.filter((e) => e.endpoint !== chosenEndpoint)];
1756
+ saveConfig(config);
1757
+ console.log(pc.green(`\n✓ Added model "${pc.bold(chosenModel)}" at ${chosenEndpoint}`));
1758
+ // Step 3: Test the model
1759
+ const testPrompt = await ctx.askLine('\nRun a quick test? (Y/n): ');
1760
+ if (testPrompt.toLowerCase() !== 'n') {
1761
+ console.log(pc.dim('\nSending test request...'));
1762
+ try {
1763
+ const start = Date.now();
1764
+ const testMessages = [
1765
+ { role: 'system', content: 'You are a helpful assistant. Respond in 1-2 sentences.' },
1766
+ { role: 'user', content: 'Say hello and confirm you are working.' },
1767
+ ];
1768
+ const testRouter = ctx.router;
1769
+ const completion = await testRouter.chat.completions.create({
1770
+ model: chosenModel,
1771
+ messages: testMessages,
1772
+ temperature: 0.1,
1773
+ });
1774
+ const elapsed = Date.now() - start;
1775
+ const text = completion.choices?.[0]?.message?.content || '(no response)';
1776
+ console.log(pc.green(`\n✓ Response received in ${elapsed}ms:`));
1777
+ console.log(` ${pc.white(text)}`);
1778
+ }
1779
+ catch (err) {
1780
+ console.log(pc.yellow(`\n⚠ Test failed: ${err.message}`));
1781
+ console.log(' The model is configured but may need troubleshooting.');
1782
+ console.log(` Check ${pc.cyan(ctx.configDir + '/config.json')} and verify the endpoint.`);
1783
+ }
1784
+ }
1785
+ console.log(pc.green(`\n✓ Onboarding complete! Configuration saved to:`));
1786
+ console.log(` ${pc.cyan(ctx.configDir + '/config.json')}`);
1787
+ console.log(`\nType ${pc.cyan('?')} to see all available commands, or just start typing.`);
1788
+ }
1789
+ },
1596
1790
  {
1597
1791
  name: 'exit',
1598
1792
  aliases: ['/exit', '/quit', 'quit'],