@link-assistant/hive-mind 1.59.6 → 1.60.0

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.
@@ -3,6 +3,7 @@
3
3
 
4
4
  import { exec } from 'child_process';
5
5
  import { promisify } from 'util';
6
+ import { parseCliArgumentsWithLino } from './cli-arguments.lib.mjs';
6
7
 
7
8
  const execAsync = promisify(exec);
8
9
 
@@ -10,6 +11,72 @@ const execAsync = promisify(exec);
10
11
  // This ensures consistent URL validation across all commands (hive, solve, start-screen)
11
12
  const { parseGitHubUrl } = await import('./github.lib.mjs');
12
13
 
14
+ const START_SCREEN_USAGE = ['Usage: start-screen [--auto-terminate] <solve|hive> <github-url> [additional-args...]', '', 'Options:', ' --auto-terminate Session terminates after command completes (old behavior)', ' By default, session stays alive for review and reattachment', '', 'Examples:', ' start-screen solve https://github.com/user/repo/issues/123 --dry-run', ' start-screen --auto-terminate solve https://github.com/user/repo/issues/456', ' start-screen hive https://github.com/user/repo --flag value'];
15
+
16
+ const printUsage = (log = console.error) => {
17
+ for (const line of START_SCREEN_USAGE) {
18
+ log(line);
19
+ }
20
+ };
21
+
22
+ const createStartScreenYargsConfig = yargsInstance =>
23
+ yargsInstance
24
+ .usage(START_SCREEN_USAGE[0])
25
+ .command('$0 <command> <github-url> [additional-args..]', 'Launch solve or hive in a GNU screen session', yargs =>
26
+ yargs
27
+ .positional('command', {
28
+ type: 'string',
29
+ description: 'Command to run',
30
+ })
31
+ .positional('github-url', {
32
+ type: 'string',
33
+ description: 'GitHub repository or issue URL',
34
+ })
35
+ .positional('additional-args', {
36
+ array: true,
37
+ type: 'string',
38
+ description: 'Arguments to pass through to the command',
39
+ })
40
+ )
41
+ .option('auto-terminate', {
42
+ type: 'boolean',
43
+ description: 'Session terminates after command completes',
44
+ default: false,
45
+ })
46
+ .option('help', {
47
+ type: 'boolean',
48
+ description: 'Show help',
49
+ alias: 'h',
50
+ default: false,
51
+ })
52
+ .parserConfiguration({
53
+ 'boolean-negation': true,
54
+ 'unknown-options-as-args': true,
55
+ })
56
+ .help(false)
57
+ .version(false)
58
+ .strict(false);
59
+
60
+ const parseStartScreenArgs = args => {
61
+ const help = args.includes('--help') || args.includes('-h');
62
+ const parsed = parseCliArgumentsWithLino({
63
+ argv: args.filter(arg => arg !== '--help' && arg !== '-h'),
64
+ commandName: 'start-screen',
65
+ createYargsConfig: createStartScreenYargsConfig,
66
+ positionalAliases: ['command', 'github-url', 'additional-args'],
67
+ lenv: { enabled: false },
68
+ getenv: { enabled: false },
69
+ });
70
+ const additionalArgs = parsed.additionalArgs || parsed['additional-args'] || [];
71
+ return {
72
+ autoTerminate: parsed.autoTerminate === true || parsed['auto-terminate'] === true,
73
+ help,
74
+ command: parsed.command,
75
+ githubUrl: parsed.githubUrl || parsed['github-url'],
76
+ commandArgs: Array.isArray(additionalArgs) ? additionalArgs : [additionalArgs],
77
+ };
78
+ };
79
+
13
80
  /**
14
81
  * Generate a screen session name based on the command and GitHub URL
15
82
  * @param {string} command - Either 'solve' or 'hive'
@@ -221,23 +288,18 @@ async function createOrEnterScreen(sessionName, command, args, autoTerminate = f
221
288
  async function main() {
222
289
  const args = process.argv.slice(2);
223
290
 
291
+ if (args.includes('--help') || args.includes('-h')) {
292
+ printUsage(console.log);
293
+ process.exit(0);
294
+ }
295
+
224
296
  if (args.length < 2) {
225
- console.error('Usage: start-screen [--auto-terminate] <solve|hive> <github-url> [additional-args...]');
226
- console.error('');
227
- console.error('Options:');
228
- console.error(' --auto-terminate Session terminates after command completes (old behavior)');
229
- console.error(' By default, session stays alive for review and reattachment');
230
- console.error('');
231
- console.error('Examples:');
232
- console.error(' start-screen solve https://github.com/user/repo/issues/123 --dry-run');
233
- console.error(' start-screen --auto-terminate solve https://github.com/user/repo/issues/456');
234
- console.error(' start-screen hive https://github.com/user/repo --flag value');
297
+ printUsage(console.error);
235
298
  process.exit(1);
236
299
  }
237
300
 
238
301
  // Check for --auto-terminate flag at the beginning
239
302
  // Also validate that first arg is not an unrecognized option with em-dash or other invalid dash
240
- let autoTerminate = false;
241
303
  let argsOffset = 0;
242
304
 
243
305
  // Check for various dash characters in first argument (em-dash \u2014, en-dash \u2013, etc.)
@@ -249,7 +311,6 @@ async function main() {
249
311
  }
250
312
 
251
313
  if (args[0] === '--auto-terminate') {
252
- autoTerminate = true;
253
314
  argsOffset = 1;
254
315
 
255
316
  if (args.length < 3) {
@@ -277,9 +338,7 @@ async function main() {
277
338
  }
278
339
  }
279
340
 
280
- const command = args[argsOffset];
281
- const githubUrl = args[argsOffset + 1];
282
- const commandArgs = args.slice(argsOffset + 2);
341
+ const { autoTerminate, command, githubUrl, commandArgs } = parseStartScreenArgs(args);
283
342
 
284
343
  // Validate command
285
344
  if (command !== 'solve' && command !== 'hive') {
@@ -0,0 +1,61 @@
1
+ import path from 'path';
2
+ import { constants as fsConstants, promises as fs } from 'fs';
3
+ import { createRequire } from 'module';
4
+
5
+ const require = createRequire(import.meta.url);
6
+
7
+ function startAgentExecutableName() {
8
+ return process.platform === 'win32' ? 'start-agent.cmd' : 'start-agent';
9
+ }
10
+
11
+ async function canExecute(filePath) {
12
+ try {
13
+ await fs.access(filePath, fsConstants.X_OK);
14
+ return true;
15
+ } catch {
16
+ return false;
17
+ }
18
+ }
19
+
20
+ export function getBundledStartAgentCandidate() {
21
+ try {
22
+ const entryPath = require.resolve('agent-commander');
23
+ return path.join(path.dirname(path.dirname(entryPath)), 'bin', 'start-agent.mjs');
24
+ } catch {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ export async function resolveStartAgentCommand(options = {}) {
30
+ const { cwd = process.cwd(), runCommand } = options;
31
+ const candidates = [getBundledStartAgentCandidate(), path.join(cwd, 'node_modules', '.bin', startAgentExecutableName())].filter(Boolean);
32
+
33
+ for (const candidate of candidates) {
34
+ if (await canExecute(candidate)) return candidate;
35
+ }
36
+
37
+ if (!runCommand) return null;
38
+
39
+ const lookupCommand = process.platform === 'win32' ? 'where' : 'which';
40
+ const result = await runCommand(lookupCommand, ['start-agent']);
41
+ if (result.code !== 0) return null;
42
+
43
+ return (
44
+ (result.stdout || '')
45
+ .split(/\r?\n/)
46
+ .map(line => line.trim())
47
+ .find(Boolean) || null
48
+ );
49
+ }
50
+
51
+ export function buildStartAgentArgs(options) {
52
+ const { tool, workingDirectory, prompt, systemPrompt, model, isolation, screenName, verbose } = options;
53
+ const args = ['--tool', tool, '--working-directory', workingDirectory, '--prompt', prompt, '--system-prompt', systemPrompt, '--model', model, '--isolation', isolation, '--read-only'];
54
+
55
+ if (isolation === 'screen' && screenName) {
56
+ args.push('--screen-name', screenName);
57
+ }
58
+ if (verbose) args.push('--verbose');
59
+
60
+ return args;
61
+ }
@@ -0,0 +1,122 @@
1
+ import { buildModelOptionDescription, defaultModels } from './models/index.mjs';
2
+ import { parseCliArgumentsWithLino } from './cli-arguments.lib.mjs';
3
+
4
+ export const TASK_TOOL_CHOICES = ['claude', 'codex', 'opencode', 'agent'];
5
+
6
+ export function getDefaultTaskModel(tool) {
7
+ return defaultModels[tool] || defaultModels.claude;
8
+ }
9
+
10
+ export const createYargsConfig = yargsInstance =>
11
+ yargsInstance
12
+ .usage('Usage: task.mjs <task-description> [options]')
13
+ .command('$0 <task-input>', 'Clarify, decompose, or split a task', yargs => {
14
+ yargs.positional('task-input', {
15
+ type: 'string',
16
+ description: 'GitHub issue URL for --split, or a task description for clarify/decompose mode',
17
+ });
18
+ })
19
+ .option('clarify', {
20
+ type: 'boolean',
21
+ description: 'Enable clarification mode',
22
+ default: true,
23
+ })
24
+ .option('decompose', {
25
+ type: 'boolean',
26
+ description: 'Enable decomposition mode',
27
+ default: true,
28
+ })
29
+ .option('only-clarify', {
30
+ type: 'boolean',
31
+ description: 'Only run clarification mode',
32
+ default: false,
33
+ })
34
+ .option('only-decompose', {
35
+ type: 'boolean',
36
+ description: 'Only run decomposition mode',
37
+ default: false,
38
+ })
39
+ .option('split', {
40
+ type: 'boolean',
41
+ description: 'Split a GitHub issue into smaller GitHub issues',
42
+ default: false,
43
+ })
44
+ .option('split-count', {
45
+ type: 'number',
46
+ description: 'Number of issues to split into',
47
+ default: 2,
48
+ })
49
+ .option('tool', {
50
+ type: 'string',
51
+ description: 'AI tool to use through agent-commander read-only mode',
52
+ choices: TASK_TOOL_CHOICES,
53
+ default: 'claude',
54
+ })
55
+ .option('model', {
56
+ type: 'string',
57
+ description: buildModelOptionDescription(),
58
+ alias: 'm',
59
+ })
60
+ .option('isolation', {
61
+ type: 'string',
62
+ description: 'agent-commander isolation mode',
63
+ choices: ['screen', 'none', 'docker'],
64
+ default: 'screen',
65
+ })
66
+ .option('screen-name', {
67
+ type: 'string',
68
+ description: 'Screen session name when --isolation screen is used',
69
+ })
70
+ .option('dry-run', {
71
+ type: 'boolean',
72
+ description: 'Print planned split issues without creating or linking GitHub issues',
73
+ default: false,
74
+ })
75
+ .option('verbose', {
76
+ type: 'boolean',
77
+ description: 'Enable verbose logging',
78
+ alias: 'v',
79
+ default: false,
80
+ })
81
+ .option('output-format', {
82
+ type: 'string',
83
+ description: 'Output format',
84
+ alias: 'o',
85
+ choices: ['text', 'json'],
86
+ default: 'text',
87
+ })
88
+ .check(argv => {
89
+ if (!argv['task-input'] && !argv._[0]) {
90
+ throw new Error('Please provide a GitHub issue URL or task description');
91
+ }
92
+ if (argv['only-clarify'] && argv['only-decompose']) {
93
+ throw new Error('Cannot use both --only-clarify and --only-decompose at the same time');
94
+ }
95
+ if (argv.split && (argv['only-clarify'] || argv['only-decompose'])) {
96
+ throw new Error('Cannot use --split with --only-clarify or --only-decompose');
97
+ }
98
+ if (argv.split && argv['split-count'] < 2) {
99
+ throw new Error('--split-count must be at least 2');
100
+ }
101
+ if (argv['only-clarify']) argv.decompose = false;
102
+ if (argv['only-decompose']) argv.clarify = false;
103
+ if (argv.split) {
104
+ argv.clarify = false;
105
+ argv.decompose = false;
106
+ }
107
+ return true;
108
+ })
109
+ .parserConfiguration({
110
+ 'boolean-negation': true,
111
+ })
112
+ .strict()
113
+ .help('h')
114
+ .alias('h', 'help');
115
+
116
+ export const parseTaskArguments = (argv = process.argv) =>
117
+ parseCliArgumentsWithLino({
118
+ argv,
119
+ commandName: 'task',
120
+ createYargsConfig,
121
+ positionalAliases: ['task-input'],
122
+ });