@hive-org/cli 0.0.6 → 0.0.8

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 (51) hide show
  1. package/dist/agent/analysis.js +78 -0
  2. package/dist/agent/app.js +32 -0
  3. package/dist/agent/chat-prompt.js +63 -0
  4. package/dist/agent/components/AsciiTicker.js +81 -0
  5. package/dist/agent/components/HoneycombBoot.js +270 -0
  6. package/dist/agent/components/Spinner.js +37 -0
  7. package/dist/agent/config.js +52 -0
  8. package/dist/agent/edit-section.js +59 -0
  9. package/dist/agent/fetch-rules.js +21 -0
  10. package/dist/agent/helpers.js +22 -0
  11. package/dist/agent/hooks/useAgent.js +270 -0
  12. package/{templates/memory-prompt.ts → dist/agent/memory-prompt.js} +17 -32
  13. package/dist/agent/model.js +63 -0
  14. package/dist/agent/objects.js +1 -0
  15. package/dist/agent/process-lifecycle.js +56 -0
  16. package/{templates/prompt.ts → dist/agent/prompt.js} +18 -47
  17. package/dist/agent/theme.js +37 -0
  18. package/dist/agent/types.js +1 -0
  19. package/dist/agents.js +30 -21
  20. package/dist/ai-providers.js +0 -13
  21. package/dist/config.js +1 -0
  22. package/dist/create/generate.js +11 -122
  23. package/dist/create/steps/DoneStep.js +1 -1
  24. package/dist/create/steps/NameStep.js +2 -1
  25. package/dist/index.js +34 -6
  26. package/dist/migrate-templates/MigrateApp.js +131 -0
  27. package/dist/migrate-templates/migrate.js +86 -0
  28. package/dist/start/AgentProcessManager.js +131 -0
  29. package/dist/start/Dashboard.js +88 -0
  30. package/dist/start/SelectAgentApp.js +58 -0
  31. package/dist/start/patch-headless.js +101 -0
  32. package/dist/start/patch-managed-mode.js +142 -0
  33. package/dist/start/start-command.js +22 -0
  34. package/package.json +6 -5
  35. package/templates/analysis.ts +0 -103
  36. package/templates/chat-prompt.ts +0 -94
  37. package/templates/components/AsciiTicker.tsx +0 -113
  38. package/templates/components/HoneycombBoot.tsx +0 -348
  39. package/templates/components/Spinner.tsx +0 -64
  40. package/templates/edit-section.ts +0 -64
  41. package/templates/fetch-rules.ts +0 -23
  42. package/templates/helpers.ts +0 -22
  43. package/templates/hive/agent.ts +0 -2
  44. package/templates/hive/config.ts +0 -96
  45. package/templates/hive/memory.ts +0 -1
  46. package/templates/hive/objects.ts +0 -26
  47. package/templates/hooks/useAgent.ts +0 -337
  48. package/templates/index.tsx +0 -257
  49. package/templates/process-lifecycle.ts +0 -66
  50. package/templates/theme.ts +0 -40
  51. package/templates/types.ts +0 -23
@@ -0,0 +1,37 @@
1
+ export const colors = {
2
+ honey: '#F5A623',
3
+ honeyDark: '#D4891A',
4
+ white: '#FFFFFF',
5
+ gray: '#888888',
6
+ grayDim: '#555555',
7
+ green: '#4CAF50',
8
+ red: '#F44336',
9
+ cyan: '#00BCD4',
10
+ };
11
+ export const symbols = {
12
+ hive: '\u2B21', // ⬡
13
+ diamond: '\u25C6', // ◆
14
+ diamondOpen: '\u25C7', // ◇
15
+ dot: '\u25CF', // ●
16
+ check: '\u2713', // ✓
17
+ cross: '\u2717', // ✗
18
+ arrow: '\u203A', // ›
19
+ circle: '\u25CB', // ○
20
+ };
21
+ export const border = {
22
+ horizontal: '\u2500', // ─
23
+ vertical: '\u2502', // │
24
+ topLeft: '\u250C', // ┌
25
+ topRight: '\u2510', // ┐
26
+ bottomLeft: '\u2514', // └
27
+ bottomRight: '\u2518', // ┘
28
+ teeLeft: '\u251C', // ├
29
+ teeRight: '\u2524', // ┤
30
+ };
31
+ export const animation = {
32
+ DATA_CHARS: '01\u25AA\u25AB\u2591\u2592',
33
+ HEX_CHARS: '\u2B21\u2B22',
34
+ TICK_MS: 120,
35
+ HEX_W: 8,
36
+ HEX_H: 4,
37
+ };
@@ -0,0 +1 @@
1
+ export {};
package/dist/agents.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ import { AI_PROVIDERS } from './ai-providers.js';
4
5
  export async function scanAgents() {
5
6
  const agentsDir = path.join(os.homedir(), '.hive', 'agents');
6
7
  const exists = await fs.pathExists(agentsDir);
@@ -18,29 +19,37 @@ export async function scanAgents() {
18
19
  if (!soulExists) {
19
20
  continue;
20
21
  }
21
- let provider = 'unknown';
22
- const envPath = path.join(agentsDir, entry.name, '.env');
23
- const envExists = await fs.pathExists(envPath);
24
- if (envExists) {
25
- const envContent = await fs.readFile(envPath, 'utf-8');
26
- if (envContent.includes('OPENAI_API_KEY')) {
27
- provider = 'OpenAI';
28
- }
29
- else if (envContent.includes('ANTHROPIC_API_KEY')) {
30
- provider = 'Anthropic';
31
- }
32
- else if (envContent.includes('GOOGLE_GENERATIVE_AI_API_KEY')) {
33
- provider = 'Google';
34
- }
35
- else if (envContent.includes('XAI_API_KEY')) {
36
- provider = 'xAI';
22
+ const agentDir = path.join(agentsDir, entry.name);
23
+ const provider = await detectProvider(agentDir);
24
+ const stat = await fs.stat(soulPath);
25
+ agents.push({ name: entry.name, dir: agentDir, provider, created: stat.birthtime });
26
+ }
27
+ return agents;
28
+ }
29
+ async function detectProvider(agentDir) {
30
+ // Try old-style detection: check package.json dependencies
31
+ const pkgPath = path.join(agentDir, 'package.json');
32
+ const pkgExists = await fs.pathExists(pkgPath);
33
+ if (pkgExists) {
34
+ const pkg = await fs.readJson(pkgPath);
35
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
36
+ for (const provider of AI_PROVIDERS) {
37
+ if (deps[provider.package]) {
38
+ return provider.label;
37
39
  }
38
- else if (envContent.includes('OPENROUTER_API_KEY')) {
39
- provider = 'OpenRouter';
40
+ }
41
+ }
42
+ // New-style detection: check .env for provider API keys
43
+ const envPath = path.join(agentDir, '.env');
44
+ const envExists = await fs.pathExists(envPath);
45
+ if (envExists) {
46
+ const envContent = await fs.readFile(envPath, 'utf-8');
47
+ for (const provider of AI_PROVIDERS) {
48
+ const pattern = new RegExp(`^${provider.envVar}=.+`, 'm');
49
+ if (pattern.test(envContent)) {
50
+ return provider.label;
40
51
  }
41
52
  }
42
- const stat = await fs.stat(soulPath);
43
- agents.push({ name: entry.name, provider, created: stat.birthtime });
44
53
  }
45
- return agents;
54
+ return 'unknown';
46
55
  }
@@ -47,16 +47,3 @@ export function getProvider(id) {
47
47
  }
48
48
  return provider;
49
49
  }
50
- /** Regex: default provider import line (and trailing newline). */
51
- const DEFAULT_IMPORT_LINE_REGEX = /import\s*\{\s*openai\s*\}\s*from\s*['"]@ai-sdk\/openai['"]\s*;\s*\n/;
52
- /** Regex: default model call in generateText() — global to catch all occurrences. */
53
- const DEFAULT_MODEL_CALL_REGEX = /openai\s*\(\s*['"]gpt-4o['"]\s*\)/g;
54
- /**
55
- * Replace the default (OpenAI) provider and model call in the example template with the chosen provider.
56
- */
57
- export function applyAdapterToChatTemplate(template, provider) {
58
- const importReplacement = provider.importLine + '\n';
59
- let out = template.replace(DEFAULT_IMPORT_LINE_REGEX, importReplacement);
60
- out = out.replaceAll(DEFAULT_MODEL_CALL_REGEX, provider.modelCall);
61
- return out;
62
- }
package/dist/config.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
+ export const HIVE_API_URL = 'https://api.zhive.ai';
4
5
  export function getHiveDir() {
5
6
  const hiveDir = path.join(os.homedir(), '.hive');
6
7
  return hiveDir;
@@ -1,12 +1,6 @@
1
1
  import fs from 'fs-extra';
2
2
  import path from 'path';
3
3
  import os from 'os';
4
- import { exec } from 'child_process';
5
- import { promisify } from 'util';
6
- import { fileURLToPath } from 'url';
7
- import { applyAdapterToChatTemplate } from '../ai-providers.js';
8
- const execAsync = promisify(exec);
9
- const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
4
  export async function scaffoldProject(projectName, provider, apiKey, soulContent, strategyContent, callbacks) {
11
5
  // Validate project name to prevent path traversal / command injection
12
6
  if (!/^[a-zA-Z0-9_-]+$/.test(projectName)) {
@@ -29,17 +23,7 @@ export async function scaffoldProject(projectName, provider, apiKey, soulContent
29
23
  // 1. Create directory
30
24
  callbacks.onStep('Creating project directory');
31
25
  await fs.ensureDir(projectDir);
32
- // 2. Initialize npm project
33
- callbacks.onStep('Initializing project');
34
- await execAsync('npm init -y', { cwd: projectDir });
35
- // 4. Install hive bootstrap
36
- callbacks.onStep('Installing Hive SDK');
37
- const templatesDir = path.resolve(__dirname, '../../templates');
38
- const hiveTemplatesDir = path.join(templatesDir, 'hive');
39
- const hiveDir = path.join(projectDir, 'hive');
40
- await fs.ensureDir(hiveDir);
41
- await fs.copy(hiveTemplatesDir, hiveDir);
42
- // 5. Write SOUL.md and STRATEGY.md
26
+ // 2. Write SOUL.md and STRATEGY.md
43
27
  callbacks.onStep('Writing personality files');
44
28
  await fs.writeFile(path.join(projectDir, 'SOUL.md'), soulContent, 'utf-8');
45
29
  await fs.writeFile(path.join(projectDir, 'STRATEGY.md'), strategyContent, 'utf-8');
@@ -58,116 +42,21 @@ export async function scaffoldProject(projectName, provider, apiKey, soulContent
58
42
  (none yet)
59
43
  `;
60
44
  await fs.writeFile(path.join(projectDir, 'MEMORY.md'), seedMemory, 'utf-8');
61
- // 6. Write .env
45
+ // 3. Write .env
62
46
  callbacks.onStep('Writing environment file');
63
- const envContent = `HIVE_API_URL="https://hive-backend.z3n.dev"
64
- ${provider.envVar}="${apiKey}"
47
+ const envContent = `${provider.envVar}="${apiKey}"
65
48
  `;
66
49
  await fs.writeFile(path.join(projectDir, '.env'), envContent, { encoding: 'utf-8', mode: 0o600 });
67
- // 7. Copy prompt.ts
68
- callbacks.onStep('Creating agent files');
69
- const promptPath = path.join(templatesDir, 'prompt.ts');
70
- const promptExists = await fs.pathExists(promptPath);
71
- if (promptExists) {
72
- const promptFileContent = await fs.readFile(promptPath, 'utf-8');
73
- await fs.writeFile(path.join(projectDir, 'prompt.ts'), promptFileContent, 'utf-8');
74
- }
75
- // 8. Copy and adapt index.tsx (Ink TUI agent — no model calls, just UI shell)
76
- const tuiTemplatePath = path.join(templatesDir, 'index.tsx');
77
- const tuiTemplate = await fs.readFile(tuiTemplatePath, 'utf-8');
78
- await fs.writeFile(path.join(projectDir, 'index.tsx'), tuiTemplate, 'utf-8');
79
- // Copy and adapt analysis.ts (contains model calls)
80
- const analysisTemplate = await fs.readFile(path.join(templatesDir, 'analysis.ts'), 'utf-8');
81
- const analysisContent = applyAdapterToChatTemplate(analysisTemplate, provider);
82
- await fs.writeFile(path.join(projectDir, 'analysis.ts'), analysisContent, 'utf-8');
83
- // Copy process-lifecycle.ts
84
- const lifecycleContent = await fs.readFile(path.join(templatesDir, 'process-lifecycle.ts'), 'utf-8');
85
- await fs.writeFile(path.join(projectDir, 'process-lifecycle.ts'), lifecycleContent, 'utf-8');
86
- // Copy hooks/ directory and adapt useAgent.ts (contains model calls)
87
- const hooksTemplatesDir = path.join(templatesDir, 'hooks');
88
- const hooksDir = path.join(projectDir, 'hooks');
89
- await fs.ensureDir(hooksDir);
90
- const useAgentTemplate = await fs.readFile(path.join(hooksTemplatesDir, 'useAgent.ts'), 'utf-8');
91
- const useAgentContent = applyAdapterToChatTemplate(useAgentTemplate, provider);
92
- await fs.writeFile(path.join(hooksDir, 'useAgent.ts'), useAgentContent, 'utf-8');
93
- // Copy theme.ts, types.ts, helpers.ts
94
- const themeContent = await fs.readFile(path.join(templatesDir, 'theme.ts'), 'utf-8');
95
- await fs.writeFile(path.join(projectDir, 'theme.ts'), themeContent, 'utf-8');
96
- const typesContent = await fs.readFile(path.join(templatesDir, 'types.ts'), 'utf-8');
97
- await fs.writeFile(path.join(projectDir, 'types.ts'), typesContent, 'utf-8');
98
- const helpersContent = await fs.readFile(path.join(templatesDir, 'helpers.ts'), 'utf-8');
99
- await fs.writeFile(path.join(projectDir, 'helpers.ts'), helpersContent, 'utf-8');
100
- // Copy components/ directory
101
- const componentsTemplatesDir = path.join(templatesDir, 'components');
102
- const componentsDir = path.join(projectDir, 'components');
103
- await fs.ensureDir(componentsDir);
104
- await fs.copy(componentsTemplatesDir, componentsDir);
105
- // Copy chat-prompt.ts
106
- const chatPromptPath = path.join(templatesDir, 'chat-prompt.ts');
107
- const chatPromptContent = await fs.readFile(chatPromptPath, 'utf-8');
108
- await fs.writeFile(path.join(projectDir, 'chat-prompt.ts'), chatPromptContent, 'utf-8');
109
- // Copy memory-prompt.ts
110
- const memoryPromptPath = path.join(templatesDir, 'memory-prompt.ts');
111
- const memoryPromptContent = await fs.readFile(memoryPromptPath, 'utf-8');
112
- await fs.writeFile(path.join(projectDir, 'memory-prompt.ts'), memoryPromptContent, 'utf-8');
113
- // Copy edit-section.ts
114
- const editSectionPath = path.join(templatesDir, 'edit-section.ts');
115
- const editSectionContent = await fs.readFile(editSectionPath, 'utf-8');
116
- await fs.writeFile(path.join(projectDir, 'edit-section.ts'), editSectionContent, 'utf-8');
117
- // Copy fetch-rules.ts
118
- const fetchRulesPath = path.join(templatesDir, 'fetch-rules.ts');
119
- const fetchRulesContent = await fs.readFile(fetchRulesPath, 'utf-8');
120
- await fs.writeFile(path.join(projectDir, 'fetch-rules.ts'), fetchRulesContent, 'utf-8');
121
- // 9. Configure package.json
50
+ // 4. Write minimal package.json — no deps needed, npx fetches @hive-org/agent@latest on every run
122
51
  callbacks.onStep('Configuring project');
123
- const packageJsonPath = path.join(projectDir, 'package.json');
124
- const packageJson = await fs.readJson(packageJsonPath);
125
- packageJson.type = 'module';
126
- packageJson.scripts = {
127
- ...packageJson.scripts,
128
- start: 'tsx index.tsx',
129
- };
130
- packageJson.dependencies = {
131
- ...packageJson.dependencies,
132
- '@hive-org/sdk': 'latest',
133
- dotenv: '^16.0.0',
134
- ai: '^6.0.71',
135
- chalk: '^5.4.1',
136
- zod: '^4.0.0',
137
- [provider.package]: '^3.0.0',
138
- ink: '^5.1.0',
139
- 'ink-text-input': '^6.0.0',
140
- react: '^18.3.1',
141
- };
142
- await fs.writeJson(packageJsonPath, packageJson, { spaces: 2 });
143
- // 10. Write tsconfig.json
144
- const tsConfig = {
145
- compilerOptions: {
146
- target: 'ES2020',
147
- module: 'ESNext',
148
- moduleResolution: 'bundler',
149
- jsx: 'react-jsx',
150
- outDir: './dist',
151
- rootDir: '.',
152
- strict: true,
153
- esModuleInterop: true,
154
- skipLibCheck: true,
155
- baseUrl: '.',
156
- paths: {
157
- '@/*': ['./*'],
158
- },
52
+ const packageJson = {
53
+ name: `hive-agent-${projectName}`,
54
+ private: true,
55
+ type: 'module',
56
+ scripts: {
57
+ start: 'npx @hive-org/cli@latest start',
159
58
  },
160
59
  };
161
- await fs.writeJson(path.join(projectDir, 'tsconfig.json'), tsConfig, { spaces: 2 });
162
- // 11. Install dependencies
163
- callbacks.onStep('Installing dependencies');
164
- const adapterInstall = `ai ${provider.package}`;
165
- try {
166
- await execAsync(`npm install --save-dev typescript @types/node @types/react tsx && npm install ${adapterInstall}`, { cwd: projectDir });
167
- }
168
- catch {
169
- callbacks.onError(`Failed to install dependencies. Run manually:\n cd ${normalizedProjectDir}\n npm install --save-dev typescript @types/node @types/react tsx && npm install ${adapterInstall}`);
170
- return;
171
- }
60
+ await fs.writeJson(path.join(projectDir, 'package.json'), packageJson, { spaces: 2 });
172
61
  callbacks.onDone(normalizedProjectDir);
173
62
  }
@@ -10,5 +10,5 @@ export function DoneStep({ projectDir }) {
10
10
  const termWidth = process.stdout.columns || 60;
11
11
  const boxWidth = Math.min(termWidth - 4, 60);
12
12
  const line = border.horizontal.repeat(boxWidth - 2);
13
- return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.topLeft, line, border.topRight] }) }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: border.vertical }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Agent created successfully!"] }), _jsx(Text, { children: ' '.repeat(Math.max(0, boxWidth - 32)) }), _jsx(Text, { color: colors.honey, children: border.vertical })] }), _jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.bottomLeft, line, border.bottomRight] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [_jsx(Text, { color: colors.white, bold: true, children: "Next steps:" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { color: colors.gray, children: [" 1. ", _jsxs(Text, { color: colors.white, children: ["cd ", projectDir] })] }), _jsxs(Text, { color: colors.gray, children: [" 2. ", _jsx(Text, { color: colors.white, children: "npm start" })] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.grayDim, children: " Edit SOUL.md and STRATEGY.md to fine-tune your agent." }) })] })] }));
13
+ return (_jsxs(Box, { flexDirection: "column", marginLeft: 2, children: [_jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.topLeft, line, border.topRight] }) }), _jsxs(Box, { children: [_jsx(Text, { color: colors.honey, children: border.vertical }), _jsx(Text, { children: " " }), _jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Agent created successfully!"] }), _jsx(Text, { children: ' '.repeat(Math.max(0, boxWidth - 32)) }), _jsx(Text, { color: colors.honey, children: border.vertical })] }), _jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [border.bottomLeft, line, border.bottomRight] }) }), _jsxs(Box, { flexDirection: "column", marginTop: 1, marginLeft: 1, children: [_jsx(Text, { color: colors.white, bold: true, children: "Next steps:" }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: _jsxs(Text, { color: colors.gray, children: [" 1. ", _jsx(Text, { color: colors.white, children: "npx @hive-org/cli@latest start" })] }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { color: colors.grayDim, children: " Edit SOUL.md and STRATEGY.md to fine-tune your agent." }) })] })] }));
14
14
  }
@@ -5,6 +5,7 @@ import axios from 'axios';
5
5
  import { TextPrompt } from '../../components/TextPrompt.js';
6
6
  import { Spinner } from '../../components/Spinner.js';
7
7
  import { colors, symbols } from '../../theme.js';
8
+ import { HIVE_API_URL } from '../../config.js';
8
9
  const ADJECTIVES = [
9
10
  'royal', 'golden', 'buzzy', 'honey', 'sweet', 'stung', 'waxed', 'bold',
10
11
  'swift', 'wild', 'keen', 'warm', 'hazy', 'calm', 'busy', 'amber',
@@ -38,7 +39,7 @@ export function NameStep({ onComplete }) {
38
39
  setChecking(true);
39
40
  setError('');
40
41
  try {
41
- const apiUrl = process.env['HIVE_API_URL'] ?? 'https://hive-backend.z3n.dev';
42
+ const apiUrl = HIVE_API_URL;
42
43
  const response = await axios.get(`${apiUrl}/agent/check-name`, {
43
44
  params: { name },
44
45
  timeout: 3000,
package/dist/index.js CHANGED
@@ -4,7 +4,8 @@ import { render } from 'ink';
4
4
  import React from 'react';
5
5
  import { CreateApp } from './create/CreateApp.js';
6
6
  import { ListApp } from './list/ListApp.js';
7
- import { StartApp } from './start/StartApp.js';
7
+ import { SelectAgentApp } from './start/SelectAgentApp.js';
8
+ import { startCommand } from './start/start-command.js';
8
9
  import { showWelcome } from './create/welcome.js';
9
10
  const require = createRequire(import.meta.url);
10
11
  const pkg = require('../package.json');
@@ -13,7 +14,9 @@ const HELP_TEXT = `@hive-org/cli v${pkg.version}
13
14
  Usage:
14
15
  npx @hive-org/cli@latest create [agent-name] Scaffold a new Hive agent
15
16
  npx @hive-org/cli@latest list List existing agents
16
- npx @hive-org/cli@latest start Start all agents
17
+ npx @hive-org/cli@latest start Select and start an agent
18
+ npx @hive-org/cli@latest start-all Start all agents
19
+ npx @hive-org/cli@latest migrate-templates Migrate old-style agents
17
20
  npx @hive-org/cli@latest --help Show this help message
18
21
  npx @hive-org/cli@latest --version Print version
19
22
 
@@ -21,7 +24,8 @@ Examples:
21
24
  npx @hive-org/cli@latest create alpha Creates ~/.hive/agents/alpha/
22
25
  npx @hive-org/cli@latest create Interactive setup
23
26
  npx @hive-org/cli@latest list Show all agents
24
- npx @hive-org/cli@latest start Launch all agents as child processes`;
27
+ npx @hive-org/cli@latest start Pick an agent and run it
28
+ npx @hive-org/cli@latest start-all Launch all agents as child processes`;
25
29
  const command = process.argv[2];
26
30
  const arg = process.argv[3];
27
31
  if (command === '--version' || command === '-v') {
@@ -36,9 +40,8 @@ if (command === 'list') {
36
40
  const { waitUntilExit } = render(React.createElement(ListApp));
37
41
  await waitUntilExit();
38
42
  }
39
- else if (command === 'start') {
40
- const { waitUntilExit } = render(React.createElement(StartApp));
41
- await waitUntilExit();
43
+ else if (command === 'start-all') {
44
+ await startCommand();
42
45
  }
43
46
  else if (command === 'create') {
44
47
  const projectName = arg;
@@ -46,6 +49,31 @@ else if (command === 'create') {
46
49
  const { waitUntilExit } = render(React.createElement(CreateApp, { initialName: projectName }));
47
50
  await waitUntilExit();
48
51
  }
52
+ else if (command === 'migrate-templates') {
53
+ await showWelcome();
54
+ const { MigrateApp } = await import('./migrate-templates/MigrateApp.js');
55
+ const { waitUntilExit } = render(React.createElement(MigrateApp));
56
+ await waitUntilExit();
57
+ }
58
+ else if (command === 'start') {
59
+ let selectedAgent = null;
60
+ const { waitUntilExit: waitForSelect } = render(React.createElement(SelectAgentApp, {
61
+ onSelect: (agent) => {
62
+ selectedAgent = agent;
63
+ },
64
+ }));
65
+ await waitForSelect();
66
+ if (selectedAgent) {
67
+ const picked = selectedAgent;
68
+ process.chdir(picked.dir);
69
+ await import('dotenv/config');
70
+ const { setupProcessLifecycle } = await import('./agent/process-lifecycle.js');
71
+ const { App } = await import('./agent/app.js');
72
+ setupProcessLifecycle();
73
+ const { waitUntilExit } = render(React.createElement(App));
74
+ await waitUntilExit();
75
+ }
76
+ }
49
77
  else {
50
78
  console.error(`Unknown command: ${command}\n`);
51
79
  console.log(HELP_TEXT);
@@ -0,0 +1,131 @@
1
+ import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect, useCallback } from 'react';
3
+ import { Box, Text, useInput, useApp } from 'ink';
4
+ import { scanAgents } from '../agents.js';
5
+ import { colors, symbols, styled, border } from '../theme.js';
6
+ import { isOldStyleAgent, migrateAgent } from './migrate.js';
7
+ export function MigrateApp() {
8
+ const { exit } = useApp();
9
+ const [phase, setPhase] = useState('scanning');
10
+ const [agents, setAgents] = useState([]);
11
+ const [cursor, setCursor] = useState(0);
12
+ const [results, setResults] = useState([]);
13
+ const [currentStep, setCurrentStep] = useState('');
14
+ const [currentAgent, setCurrentAgent] = useState('');
15
+ // ─── Scan phase ────────────────────────────────────
16
+ useEffect(() => {
17
+ const scan = async () => {
18
+ const discovered = await scanAgents();
19
+ if (discovered.length === 0) {
20
+ setPhase('done');
21
+ return;
22
+ }
23
+ const selectableAgents = discovered.map((info) => {
24
+ const oldStyle = isOldStyleAgent(info.dir);
25
+ return {
26
+ info,
27
+ selected: oldStyle,
28
+ isOldStyle: oldStyle,
29
+ };
30
+ });
31
+ const hasOldStyle = selectableAgents.some((a) => a.isOldStyle);
32
+ if (!hasOldStyle) {
33
+ setResults(selectableAgents.map((a) => ({
34
+ name: a.info.name,
35
+ success: true,
36
+ error: 'Already migrated',
37
+ })));
38
+ setPhase('done');
39
+ return;
40
+ }
41
+ setAgents(selectableAgents);
42
+ setPhase('selecting');
43
+ };
44
+ scan().catch((err) => {
45
+ const message = err instanceof Error ? err.message : String(err);
46
+ setResults([{ name: 'scan', success: false, error: message }]);
47
+ setPhase('done');
48
+ });
49
+ }, []);
50
+ // ─── Keyboard input (selecting phase) ──────────────
51
+ useInput((input, key) => {
52
+ if (phase !== 'selecting')
53
+ return;
54
+ const oldStyleAgents = agents.filter((a) => a.isOldStyle);
55
+ if (key.upArrow) {
56
+ setCursor((prev) => Math.max(0, prev - 1));
57
+ }
58
+ else if (key.downArrow) {
59
+ setCursor((prev) => Math.min(oldStyleAgents.length - 1, prev + 1));
60
+ }
61
+ else if (input === ' ') {
62
+ // Toggle selection
63
+ const targetName = oldStyleAgents[cursor]?.info.name;
64
+ if (targetName) {
65
+ setAgents((prev) => prev.map((a) => a.info.name === targetName ? { ...a, selected: !a.selected } : a));
66
+ }
67
+ }
68
+ else if (key.return) {
69
+ const selected = agents.filter((a) => a.selected && a.isOldStyle);
70
+ if (selected.length > 0) {
71
+ setPhase('migrating');
72
+ runMigrations(selected);
73
+ }
74
+ }
75
+ else if (input === 'q' || key.escape) {
76
+ exit();
77
+ }
78
+ }, { isActive: phase === 'selecting' });
79
+ // ─── Migrate phase ─────────────────────────────────
80
+ const runMigrations = useCallback(async (selected) => {
81
+ const migrateResults = [];
82
+ for (const agent of selected) {
83
+ setCurrentAgent(agent.info.name);
84
+ setCurrentStep('Starting migration');
85
+ const result = await migrateAgent(agent.info.dir, agent.info.name, (step) => {
86
+ setCurrentStep(step);
87
+ });
88
+ migrateResults.push(result);
89
+ setResults([...migrateResults]);
90
+ }
91
+ setCurrentAgent('');
92
+ setCurrentStep('');
93
+ setPhase('done');
94
+ }, []);
95
+ // ─── Done phase — exit after a short delay ─────────
96
+ useEffect(() => {
97
+ if (phase === 'done') {
98
+ const timer = setTimeout(() => {
99
+ exit();
100
+ }, 1500);
101
+ return () => {
102
+ clearTimeout(timer);
103
+ };
104
+ }
105
+ }, [phase, exit]);
106
+ // ─── Render ────────────────────────────────────────
107
+ const termWidth = process.stdout.columns || 60;
108
+ if (phase === 'scanning') {
109
+ return (_jsx(Box, { flexDirection: "column", paddingLeft: 1, children: _jsxs(Text, { color: colors.honey, children: [symbols.hive, " Scanning agents..."] }) }));
110
+ }
111
+ if (phase === 'selecting') {
112
+ const oldStyleAgents = agents.filter((a) => a.isOldStyle);
113
+ const newStyleAgents = agents.filter((a) => !a.isOldStyle);
114
+ return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migrate agents to @hive-org/cli"] }), _jsx(Text, { color: "gray", children: border.horizontal.repeat(termWidth - 4) }), _jsxs(Text, { color: "gray", children: ["Use ", styled.white('↑↓'), " to navigate, ", styled.white('space'), " to toggle, ", styled.white('enter'), " to confirm"] }), _jsx(Text, { children: " " }), oldStyleAgents.map((agent, i) => {
115
+ const isCursor = i === cursor;
116
+ const prefix = agent.selected ? symbols.check : symbols.diamondOpen;
117
+ const prefixColor = agent.selected ? colors.green : colors.gray;
118
+ const nameColor = isCursor ? 'white' : 'gray';
119
+ const cursorChar = isCursor ? symbols.arrow : ' ';
120
+ return (_jsxs(Box, { children: [_jsxs(Text, { color: colors.honey, children: [cursorChar, " "] }), _jsxs(Text, { color: prefixColor, children: [prefix, " "] }), _jsx(Text, { color: nameColor, bold: isCursor, children: agent.info.name }), _jsxs(Text, { color: "gray", children: [" (", agent.info.provider, ")"] })] }, agent.info.name));
121
+ }), newStyleAgents.length > 0 && (_jsxs(_Fragment, { children: [_jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: "Already migrated:" }), newStyleAgents.map((agent) => (_jsx(Box, { children: _jsxs(Text, { color: "gray", children: [' ', symbols.check, " ", agent.info.name] }) }, agent.info.name)))] })), _jsx(Text, { children: " " }), _jsx(Text, { color: "gray", children: styled.dim('q/esc to cancel') })] }));
122
+ }
123
+ if (phase === 'migrating') {
124
+ return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migrating agents..."] }), _jsx(Text, { color: "gray", children: border.horizontal.repeat(termWidth - 4) }), results.map((r) => (_jsx(Box, { children: _jsxs(Text, { color: r.success ? colors.green : colors.red, children: [r.success ? symbols.check : symbols.cross, " ", r.name] }) }, r.name))), currentAgent && (_jsx(Box, { children: _jsxs(Text, { color: colors.honey, children: [symbols.diamond, " ", currentAgent, ": ", currentStep] }) }))] }));
125
+ }
126
+ // phase === 'done'
127
+ const successCount = results.filter((r) => r.success && !r.error).length;
128
+ const alreadyNew = results.filter((r) => r.error === 'Already migrated').length;
129
+ const failCount = results.filter((r) => !r.success).length;
130
+ return (_jsxs(Box, { flexDirection: "column", paddingLeft: 1, children: [_jsxs(Text, { color: colors.honey, bold: true, children: [symbols.hive, " Migration complete"] }), _jsx(Text, { color: "gray", children: border.horizontal.repeat(termWidth - 4) }), results.map((r) => (_jsxs(Box, { children: [r.success && !r.error && (_jsxs(Text, { color: colors.green, children: [symbols.check, " ", r.name, " \u2014 migrated"] })), r.error === 'Already migrated' && (_jsxs(Text, { color: "gray", children: [symbols.check, " ", r.name, " \u2014 already migrated"] })), !r.success && r.error !== 'Already migrated' && (_jsxs(Text, { color: colors.red, children: [symbols.cross, " ", r.name, " \u2014 ", r.error] }))] }, r.name))), agents.length === 0 && results.length === 0 && (_jsx(Text, { color: "gray", children: "No agents found in ~/.hive/agents/" })), _jsx(Text, { children: " " }), successCount > 0 && (_jsxs(Text, { color: "gray", children: ["Agents now run via @hive-org/cli. ", styled.white('npx @hive-org/cli@latest start'), " always uses the latest version."] }))] }));
131
+ }
@@ -0,0 +1,86 @@
1
+ import fs from 'fs-extra';
2
+ import path from 'path';
3
+ /** Files scaffolded by the old `hive create` that are now in @hive-org/cli */
4
+ const OLD_FILES = [
5
+ 'index.tsx',
6
+ 'analysis.ts',
7
+ 'prompt.ts',
8
+ 'chat-prompt.ts',
9
+ 'memory-prompt.ts',
10
+ 'edit-section.ts',
11
+ 'fetch-rules.ts',
12
+ 'helpers.ts',
13
+ 'theme.ts',
14
+ 'types.ts',
15
+ 'process-lifecycle.ts',
16
+ 'tsconfig.json',
17
+ ];
18
+ /** Directories scaffolded by the old `hive create` */
19
+ const OLD_DIRS = ['hooks', 'components', 'hive'];
20
+ export function isOldStyleAgent(agentDir) {
21
+ const indexPath = path.join(agentDir, 'index.tsx');
22
+ return fs.pathExistsSync(indexPath);
23
+ }
24
+ export async function migrateAgent(agentDir, name, onStep) {
25
+ try {
26
+ // 1. Verify it's a valid agent
27
+ const soulPath = path.join(agentDir, 'SOUL.md');
28
+ const soulExists = await fs.pathExists(soulPath);
29
+ if (!soulExists) {
30
+ return { name, success: false, error: 'No SOUL.md found — not a valid agent' };
31
+ }
32
+ // 2. Delete old scaffolded files
33
+ onStep('Removing old runtime files');
34
+ for (const file of OLD_FILES) {
35
+ const filePath = path.join(agentDir, file);
36
+ const exists = await fs.pathExists(filePath);
37
+ if (exists) {
38
+ await fs.remove(filePath);
39
+ }
40
+ }
41
+ // 3. Delete old directories
42
+ onStep('Removing old directories');
43
+ for (const dir of OLD_DIRS) {
44
+ const dirPath = path.join(agentDir, dir);
45
+ const exists = await fs.pathExists(dirPath);
46
+ if (exists) {
47
+ await fs.remove(dirPath);
48
+ }
49
+ }
50
+ // 4. Rewrite package.json
51
+ onStep('Rewriting package.json');
52
+ const pkgPath = path.join(agentDir, 'package.json');
53
+ const pkgExists = await fs.pathExists(pkgPath);
54
+ let pkgName = `hive-agent-${name}`;
55
+ if (pkgExists) {
56
+ const oldPkg = await fs.readJson(pkgPath);
57
+ pkgName = oldPkg.name ?? pkgName;
58
+ }
59
+ const newPkg = {
60
+ name: pkgName,
61
+ private: true,
62
+ type: 'module',
63
+ scripts: {
64
+ start: 'npx @hive-org/cli@latest start',
65
+ },
66
+ };
67
+ await fs.writeJson(pkgPath, newPkg, { spaces: 2 });
68
+ // 5. Remove old node_modules and lock files (no longer needed)
69
+ onStep('Cleaning up old dependencies');
70
+ const nodeModulesPath = path.join(agentDir, 'node_modules');
71
+ const nodeModulesExists = await fs.pathExists(nodeModulesPath);
72
+ if (nodeModulesExists) {
73
+ await fs.remove(nodeModulesPath);
74
+ }
75
+ const lockPath = path.join(agentDir, 'package-lock.json');
76
+ const lockExists = await fs.pathExists(lockPath);
77
+ if (lockExists) {
78
+ await fs.remove(lockPath);
79
+ }
80
+ return { name, success: true };
81
+ }
82
+ catch (err) {
83
+ const message = err instanceof Error ? err.message : String(err);
84
+ return { name, success: false, error: message.slice(0, 200) };
85
+ }
86
+ }