subconscious-cli 0.3.0 → 0.3.1
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/README.md +65 -93
- package/bin/agents.js +119 -87
- package/bin/auth.js +25 -7
- package/bin/cli.js +135 -85
- package/bin/profiles.js +129 -199
- package/bin/registry.generated.json +40 -12
- package/bin/runbook/README.md +12 -15
- package/bin/runbook/claude-code/install.sh +18 -44
- package/bin/runbook/claude-code/run.sh +21 -21
- package/bin/runbook/codex/hook.sh +1 -1
- package/bin/runbook/codex/hooks-lib.sh +139 -0
- package/bin/runbook/codex/install.sh +21 -463
- package/bin/runbook/codex/run.sh +7 -23
- package/bin/runbook/copilot/install.sh +9 -8
- package/bin/runbook/cursor/install.sh +36 -11
- package/bin/runbook/opencode/install.sh +46 -46
- package/bin/runbook/opencode/run.sh +4 -5
- package/bin/runbook/pi/install.sh +37 -41
- package/bin/runbook/pi/run.sh +3 -3
- package/package.json +2 -2
package/bin/profiles.js
CHANGED
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Named .env profiles stored independently of the installed package.
|
|
3
3
|
*
|
|
4
4
|
* Profiles use a small .env format so users can inspect or edit them directly:
|
|
5
5
|
* ~/.subconscious/profiles/default.env
|
|
6
6
|
* ~/.subconscious/profiles/<name>.env
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { spawn, execFileSync } from 'node:child_process';
|
|
9
10
|
import fs from 'node:fs/promises';
|
|
10
11
|
import os from 'node:os';
|
|
11
12
|
import path from 'node:path';
|
|
12
13
|
import { readFileSync } from 'node:fs';
|
|
13
|
-
import readline from 'node:readline';
|
|
14
|
-
import { Writable } from 'node:stream';
|
|
15
14
|
import { c } from './colors.js';
|
|
16
15
|
|
|
17
16
|
const registry = JSON.parse(
|
|
@@ -299,7 +298,7 @@ export function resolvedProfileValues(profile) {
|
|
|
299
298
|
function profileTemplate() {
|
|
300
299
|
const modelComments = SUPPORTED_MODELS.map((model) => `# ${model}`).join('\n');
|
|
301
300
|
return `# Subconscious coding-agent profile
|
|
302
|
-
# Generated by subc.
|
|
301
|
+
# Generated by subc. Extra KEY=value lines are passed through to launches.
|
|
303
302
|
|
|
304
303
|
GATEWAY_URL={GATEWAY_URL}
|
|
305
304
|
API_KEY={API_KEY}
|
|
@@ -308,7 +307,6 @@ ${modelComments}
|
|
|
308
307
|
MODEL={MODEL}
|
|
309
308
|
|
|
310
309
|
# Optional per-agent keys. Leave blank to use API_KEY above.
|
|
311
|
-
CLAUDE_GATEWAY_URL={CLAUDE_GATEWAY_URL}
|
|
312
310
|
CLAUDE_CODE_API_KEY={CLAUDE_CODE_API_KEY}
|
|
313
311
|
CODEX_API_KEY={CODEX_API_KEY}
|
|
314
312
|
OPENCODE_API_KEY={OPENCODE_API_KEY}
|
|
@@ -317,6 +315,8 @@ COPILOT_API_KEY={COPILOT_API_KEY}
|
|
|
317
315
|
PI_API_KEY={PI_API_KEY}
|
|
318
316
|
|
|
319
317
|
# Claude Code
|
|
318
|
+
# Leave CLAUDE_GATEWAY_URL blank to use GATEWAY_URL above.
|
|
319
|
+
CLAUDE_GATEWAY_URL={CLAUDE_GATEWAY_URL}
|
|
320
320
|
CLAUDE_CODE_AUTO_COMPACT_WINDOW={CLAUDE_CODE_AUTO_COMPACT_WINDOW}
|
|
321
321
|
CLAUDE_CODE_MAX_CONTEXT_TOKENS={CLAUDE_CODE_MAX_CONTEXT_TOKENS}
|
|
322
322
|
MAX_CONCURRENT_SUBAGENTS={MAX_CONCURRENT_SUBAGENTS}
|
|
@@ -341,6 +341,8 @@ PI_MAX_TOKENS={PI_MAX_TOKENS}
|
|
|
341
341
|
COPILOT_MAX_INPUT_TOKENS={COPILOT_MAX_INPUT_TOKENS}
|
|
342
342
|
COPILOT_MAX_OUTPUT_TOKENS={COPILOT_MAX_OUTPUT_TOKENS}
|
|
343
343
|
VSCODE_APP={VSCODE_APP}
|
|
344
|
+
|
|
345
|
+
# Extra KEY=value lines are passed through to launches.
|
|
344
346
|
`;
|
|
345
347
|
}
|
|
346
348
|
|
|
@@ -481,11 +483,31 @@ export async function clearProfileApiKey(name = DEFAULT_PROFILE) {
|
|
|
481
483
|
return true;
|
|
482
484
|
}
|
|
483
485
|
|
|
486
|
+
function isSecretKey(key) {
|
|
487
|
+
const upper = key.toUpperCase();
|
|
488
|
+
return (
|
|
489
|
+
upper.endsWith('API_KEY') ||
|
|
490
|
+
upper.includes('TOKEN') ||
|
|
491
|
+
upper.includes('SECRET') ||
|
|
492
|
+
upper.includes('PASSWORD') ||
|
|
493
|
+
upper.includes('AUTH')
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
|
|
484
497
|
function redact(key, value) {
|
|
485
|
-
if (!key
|
|
498
|
+
if (!isSecretKey(key) || !value) return value;
|
|
486
499
|
return value.length <= 8 ? '********' : `${value.slice(0, 4)}…${value.slice(-4)}`;
|
|
487
500
|
}
|
|
488
501
|
|
|
502
|
+
function redactEnvLine(line) {
|
|
503
|
+
const match = line.match(/^(\s*(?:export\s+)?)([A-Za-z_][A-Za-z0-9_]*)=(.*)$/);
|
|
504
|
+
if (!match) return line;
|
|
505
|
+
const [, prefix, key, raw] = match;
|
|
506
|
+
const value = decodeValue(raw);
|
|
507
|
+
if (!isSecretKey(key) || !value) return line;
|
|
508
|
+
return `${prefix}${key}=${redact(key, value)}`;
|
|
509
|
+
}
|
|
510
|
+
|
|
489
511
|
export async function listProfiles() {
|
|
490
512
|
try {
|
|
491
513
|
const entries = await fs.readdir(PROFILES_DIR, { withFileTypes: true });
|
|
@@ -499,62 +521,24 @@ export async function listProfiles() {
|
|
|
499
521
|
}
|
|
500
522
|
}
|
|
501
523
|
|
|
502
|
-
function printProfile(profile) {
|
|
524
|
+
async function printProfile(profile) {
|
|
503
525
|
console.log(`\n ${c.bold}Profile: ${profile.name}${c.reset}`);
|
|
504
|
-
console.log(` ${c.dim}
|
|
526
|
+
console.log(` ${c.dim}${profile.path}${c.reset}\n`);
|
|
505
527
|
if (!profile.exists) {
|
|
506
528
|
console.log(` ${c.yellow}Not configured.${c.reset}\n`);
|
|
507
529
|
return;
|
|
508
530
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
531
|
+
const text = await fs.readFile(profile.path, 'utf-8');
|
|
532
|
+
console.log(
|
|
533
|
+
text
|
|
534
|
+
.replace(/\n$/, '')
|
|
535
|
+
.split('\n')
|
|
536
|
+
.map(redactEnvLine)
|
|
537
|
+
.join('\n'),
|
|
538
|
+
);
|
|
512
539
|
console.log();
|
|
513
540
|
}
|
|
514
541
|
|
|
515
|
-
function createPrompter() {
|
|
516
|
-
let muted = false;
|
|
517
|
-
let rejectPending = null;
|
|
518
|
-
const output = new Writable({
|
|
519
|
-
write(chunk, _encoding, callback) {
|
|
520
|
-
if (!muted) process.stdout.write(chunk);
|
|
521
|
-
callback();
|
|
522
|
-
},
|
|
523
|
-
});
|
|
524
|
-
const rl = readline.createInterface({ input: process.stdin, output, terminal: true });
|
|
525
|
-
|
|
526
|
-
rl.on('SIGINT', () => {
|
|
527
|
-
const error = new Error('Interactive configuration cancelled.');
|
|
528
|
-
error.code = 'SUBC_CANCELLED';
|
|
529
|
-
rejectPending?.(error);
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
return {
|
|
533
|
-
question(prompt, options = {}) {
|
|
534
|
-
return new Promise((resolve, reject) => {
|
|
535
|
-
rejectPending = reject;
|
|
536
|
-
if (options.secret) {
|
|
537
|
-
process.stdout.write(prompt);
|
|
538
|
-
muted = true;
|
|
539
|
-
}
|
|
540
|
-
rl.question(options.secret ? '' : prompt, (answer) => {
|
|
541
|
-
if (options.secret) {
|
|
542
|
-
muted = false;
|
|
543
|
-
process.stdout.write('\n');
|
|
544
|
-
}
|
|
545
|
-
rejectPending = null;
|
|
546
|
-
resolve(answer);
|
|
547
|
-
});
|
|
548
|
-
});
|
|
549
|
-
},
|
|
550
|
-
close() {
|
|
551
|
-
muted = false;
|
|
552
|
-
rejectPending = null;
|
|
553
|
-
rl.close();
|
|
554
|
-
},
|
|
555
|
-
};
|
|
556
|
-
}
|
|
557
|
-
|
|
558
542
|
export function validateSettingValue(setting, value) {
|
|
559
543
|
if (!value) {
|
|
560
544
|
if (setting.required) return `${setting.label} cannot be blank`;
|
|
@@ -587,146 +571,103 @@ export function validateSettingValue(setting, value) {
|
|
|
587
571
|
return null;
|
|
588
572
|
}
|
|
589
573
|
|
|
590
|
-
|
|
591
|
-
console.log(`\n ${c.bold}${setting.label}${c.reset} ${c.dim}(${setting.key})${c.reset}`);
|
|
592
|
-
console.log(` ${c.dim}${setting.description}${c.reset}`);
|
|
574
|
+
const ALLOWED_EDITORS = new Set(['vim', 'nano']);
|
|
593
575
|
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
while (true) {
|
|
601
|
-
const answer = (await prompter.question(' Select a number (Enter keeps current): ')).trim();
|
|
602
|
-
if (!answer) return current;
|
|
603
|
-
const index = Number(answer) - 1;
|
|
604
|
-
if (Number.isInteger(index) && setting.choices[index] !== undefined) {
|
|
605
|
-
return setting.choices[index];
|
|
606
|
-
}
|
|
607
|
-
if (setting.choices.includes(answer)) return answer;
|
|
608
|
-
console.log(` ${c.yellow}Choose one of the listed values.${c.reset}`);
|
|
609
|
-
}
|
|
576
|
+
function commandExists(bin) {
|
|
577
|
+
try {
|
|
578
|
+
execFileSync('which', [bin], { stdio: 'ignore' });
|
|
579
|
+
return true;
|
|
580
|
+
} catch {
|
|
581
|
+
return false;
|
|
610
582
|
}
|
|
583
|
+
}
|
|
611
584
|
|
|
612
|
-
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
{ secret: setting.type === 'secret' },
|
|
619
|
-
)
|
|
620
|
-
).trim();
|
|
621
|
-
if (!answer) return current;
|
|
622
|
-
const value = answer === '-' && !setting.required ? '' : answer;
|
|
623
|
-
const error = validateSettingValue(setting, value);
|
|
624
|
-
if (!error) return value;
|
|
625
|
-
console.log(` ${c.yellow}${error}.${c.reset}`);
|
|
626
|
-
}
|
|
585
|
+
function parseEditorCommand(editor) {
|
|
586
|
+
const parts = String(editor || '')
|
|
587
|
+
.split(/\s+/)
|
|
588
|
+
.filter(Boolean);
|
|
589
|
+
if (!parts.length) throw new Error('Editor is empty');
|
|
590
|
+
return { cmd: parts[0], args: parts.slice(1) };
|
|
627
591
|
}
|
|
628
592
|
|
|
629
|
-
function
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
}
|
|
634
|
-
return [
|
|
593
|
+
function resolveEditor(requested) {
|
|
594
|
+
if (requested) return { cmd: requested, args: [] };
|
|
595
|
+
const fromEnv = process.env.VISUAL?.trim() || process.env.EDITOR?.trim();
|
|
596
|
+
if (fromEnv) return parseEditorCommand(fromEnv);
|
|
597
|
+
if (commandExists('vim')) return { cmd: 'vim', args: [] };
|
|
598
|
+
if (commandExists('nano')) return { cmd: 'nano', args: [] };
|
|
599
|
+
throw new Error('No editor found. Install vim or nano, or set $VISUAL / $EDITOR.');
|
|
635
600
|
}
|
|
636
601
|
|
|
637
|
-
async function
|
|
602
|
+
async function editProfile(profileName, requestedEditor) {
|
|
638
603
|
if (!process.stdin.isTTY || !process.stdout.isTTY) {
|
|
639
|
-
|
|
604
|
+
console.error('Editing a profile requires a terminal.');
|
|
605
|
+
console.error(profilePath(profileName));
|
|
606
|
+
process.exitCode = 1;
|
|
607
|
+
return;
|
|
640
608
|
}
|
|
641
609
|
|
|
642
|
-
const
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
const updates = {};
|
|
656
|
-
|
|
657
|
-
while (true) {
|
|
658
|
-
console.log(`\n ${c.bold}Profile: ${targetName}${c.reset}`);
|
|
659
|
-
PROFILE_SETTING_GROUPS.forEach((group, index) => {
|
|
660
|
-
console.log(` ${index + 1}. ${group.label}`);
|
|
661
|
-
});
|
|
662
|
-
const allIndex = PROFILE_SETTING_GROUPS.length + 1;
|
|
663
|
-
const saveIndex = allIndex + 1;
|
|
664
|
-
console.log(` ${allIndex}. All settings`);
|
|
665
|
-
console.log(` ${saveIndex}. Save and exit`);
|
|
666
|
-
console.log(' 0. Cancel');
|
|
667
|
-
if (Object.keys(updates).length) {
|
|
668
|
-
console.log(` ${c.dim}${Object.keys(updates).length} pending change(s)${c.reset}`);
|
|
669
|
-
}
|
|
610
|
+
const profile = await ensureProfile(profileName);
|
|
611
|
+
const { cmd, args } = resolveEditor(requestedEditor);
|
|
612
|
+
await new Promise((resolve, reject) => {
|
|
613
|
+
const child = spawn(cmd, [...args, profile.path], { stdio: 'inherit' });
|
|
614
|
+
child.on('error', (error) => {
|
|
615
|
+
reject(new Error(`Failed to launch editor '${cmd}': ${error.message}`));
|
|
616
|
+
});
|
|
617
|
+
child.on('close', (code) => {
|
|
618
|
+
if (code && code !== 0) process.exitCode = code;
|
|
619
|
+
resolve();
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
}
|
|
670
623
|
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
const saved = Object.keys(updates).length
|
|
682
|
-
? await updateProfile(targetName, updates)
|
|
683
|
-
: await ensureProfile(targetName);
|
|
684
|
-
console.log(`\n ${c.green}${c.bold}✓ Saved profile '${targetName}'.${c.reset}`);
|
|
685
|
-
console.log(` ${c.dim}${saved.path}${c.reset}\n`);
|
|
686
|
-
return;
|
|
687
|
-
}
|
|
624
|
+
export function printConfigHelp() {
|
|
625
|
+
console.log(`
|
|
626
|
+
Usage:
|
|
627
|
+
subc config
|
|
628
|
+
subc config help
|
|
629
|
+
subc -p NAME config
|
|
630
|
+
subc config edit [vim|nano]
|
|
631
|
+
subc -p NAME config edit [vim|nano]
|
|
632
|
+
subc config [show|path|list|delete]
|
|
633
|
+
[--gateway-url URL] [--api-key KEY] [--model MODEL]
|
|
688
634
|
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
selectedSettings = group.settings;
|
|
699
|
-
}
|
|
635
|
+
subc config List every profile and its file path
|
|
636
|
+
subc -p NAME config Print that profile's path and env file
|
|
637
|
+
subc config edit Open the selected profile in $VISUAL, $EDITOR, vim, or nano
|
|
638
|
+
subc config edit vim Open the selected profile in vim
|
|
639
|
+
subc config edit nano Open the selected profile in nano
|
|
640
|
+
subc config path Print the selected profile path
|
|
641
|
+
subc -p NAME config delete Delete a non-default profile
|
|
642
|
+
`);
|
|
643
|
+
}
|
|
700
644
|
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
}
|
|
711
|
-
} catch (error) {
|
|
712
|
-
if (error.code === 'SUBC_CANCELLED') {
|
|
713
|
-
console.log(`\n ${c.dim}No settings were written.${c.reset}\n`);
|
|
714
|
-
return;
|
|
715
|
-
}
|
|
716
|
-
throw error;
|
|
717
|
-
} finally {
|
|
718
|
-
prompter.close();
|
|
645
|
+
async function printProfileList(activeName) {
|
|
646
|
+
const profiles = await listProfiles();
|
|
647
|
+
if (!profiles.length) {
|
|
648
|
+
console.log(`\n ${c.dim}No profiles yet. Run subc login.${c.reset}\n`);
|
|
649
|
+
return;
|
|
650
|
+
}
|
|
651
|
+
console.log(`\n ${c.bold}Profiles${c.reset}`);
|
|
652
|
+
for (const name of profiles) {
|
|
653
|
+
const active = name === activeName ? ` ${c.green}(active)${c.reset}` : '';
|
|
654
|
+
console.log(` ${c.cyan}${name}${c.reset}${active} ${c.dim}${profilePath(name)}${c.reset}`);
|
|
719
655
|
}
|
|
656
|
+
console.log();
|
|
720
657
|
}
|
|
721
658
|
|
|
722
|
-
export async function configCommand(argv, profileName = DEFAULT_PROFILE) {
|
|
723
|
-
let action = '
|
|
659
|
+
export async function configCommand(argv, profileName = DEFAULT_PROFILE, options = {}) {
|
|
660
|
+
let action = 'default';
|
|
661
|
+
let editor;
|
|
724
662
|
const updates = {};
|
|
725
663
|
|
|
726
664
|
for (let i = 0; i < argv.length; i++) {
|
|
727
665
|
const arg = argv[i];
|
|
728
|
-
if (
|
|
729
|
-
|
|
666
|
+
if (arg === 'help' || arg === '-h' || arg === '--help') {
|
|
667
|
+
printConfigHelp();
|
|
668
|
+
return;
|
|
669
|
+
} else if (['show', 'path', 'list', 'delete', 'edit', 'interactive'].includes(arg)) {
|
|
670
|
+
action = arg === 'interactive' ? 'edit' : arg;
|
|
730
671
|
} else if (arg === '--gateway-url' || arg === '--api-key' || arg === '--model') {
|
|
731
672
|
const value = argv[++i];
|
|
732
673
|
if (!value) throw new Error(`${arg} requires a value`);
|
|
@@ -736,37 +677,26 @@ export async function configCommand(argv, profileName = DEFAULT_PROFILE) {
|
|
|
736
677
|
'--model': 'MODEL',
|
|
737
678
|
}[arg];
|
|
738
679
|
updates[key] = value;
|
|
739
|
-
} else if (
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
subc --profile NAME config [...]
|
|
745
|
-
|
|
746
|
-
Interactive wizard:
|
|
747
|
-
subc settings
|
|
748
|
-
subc --profile NAME config interactive
|
|
749
|
-
`);
|
|
750
|
-
return;
|
|
680
|
+
} else if (action === 'edit' && ALLOWED_EDITORS.has(arg)) {
|
|
681
|
+
if (editor) throw new Error('Specify only one editor (vim or nano)');
|
|
682
|
+
editor = arg;
|
|
683
|
+
} else if (action === 'edit') {
|
|
684
|
+
throw new Error(`Unknown editor '${arg}' (use vim or nano)`);
|
|
751
685
|
} else {
|
|
752
686
|
throw new Error(`Unknown config argument: ${arg}`);
|
|
753
687
|
}
|
|
754
688
|
}
|
|
755
689
|
|
|
756
|
-
if (action === '
|
|
757
|
-
|
|
690
|
+
if (action === 'edit') {
|
|
691
|
+
if (Object.keys(updates).length) {
|
|
692
|
+
throw new Error('config edit cannot be combined with --gateway-url, --api-key, or --model');
|
|
693
|
+
}
|
|
694
|
+
await editProfile(profileName, editor);
|
|
758
695
|
return;
|
|
759
696
|
}
|
|
760
697
|
|
|
761
|
-
if (action === 'list') {
|
|
762
|
-
|
|
763
|
-
if (!profiles.length) {
|
|
764
|
-
console.log(`\n ${c.dim}No profiles yet. Run subc login.${c.reset}\n`);
|
|
765
|
-
return;
|
|
766
|
-
}
|
|
767
|
-
console.log();
|
|
768
|
-
for (const name of profiles) console.log(` ${name} ${profilePath(name)}`);
|
|
769
|
-
console.log();
|
|
698
|
+
if (action === 'list' || (action === 'default' && !options.profileExplicit && !Object.keys(updates).length)) {
|
|
699
|
+
await printProfileList(profileName);
|
|
770
700
|
return;
|
|
771
701
|
}
|
|
772
702
|
|
|
@@ -792,11 +722,11 @@ Interactive wizard:
|
|
|
792
722
|
if (Object.keys(updates).length) {
|
|
793
723
|
const profile = await updateProfile(profileName, updates);
|
|
794
724
|
console.log(`Updated profile '${profileName}'.`);
|
|
795
|
-
printProfile(profile);
|
|
725
|
+
await printProfile(profile);
|
|
796
726
|
return;
|
|
797
727
|
}
|
|
798
728
|
|
|
799
|
-
printProfile(await loadProfile(profileName));
|
|
729
|
+
await printProfile(await loadProfile(profileName));
|
|
800
730
|
}
|
|
801
731
|
|
|
802
732
|
export function modelsCommand() {
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"claudecode"
|
|
24
24
|
],
|
|
25
25
|
"displayName": "Claude Code + Subconscious",
|
|
26
|
-
"description": "Run Claude Code on your hosted Subconscious model
|
|
26
|
+
"description": "Run Claude Code on your hosted Subconscious model with gateway env, context limits, and telemetry.",
|
|
27
27
|
"framework": "Claude Code",
|
|
28
28
|
"language": "shell",
|
|
29
29
|
"category": "coding-agent",
|
|
@@ -44,7 +44,11 @@
|
|
|
44
44
|
"runbook": {
|
|
45
45
|
"mode": "launch",
|
|
46
46
|
"script": "claude-code/run.sh",
|
|
47
|
-
"setupScript": "claude-code/install.sh"
|
|
47
|
+
"setupScript": "claude-code/install.sh",
|
|
48
|
+
"setupActions": [
|
|
49
|
+
"status",
|
|
50
|
+
"uninstall"
|
|
51
|
+
]
|
|
48
52
|
},
|
|
49
53
|
"launch": "claude",
|
|
50
54
|
"env": {
|
|
@@ -83,7 +87,7 @@
|
|
|
83
87
|
"name": "Codex CLI",
|
|
84
88
|
"aliases": [],
|
|
85
89
|
"displayName": "Codex + Subconscious",
|
|
86
|
-
"description": "Run Codex on your hosted Subconscious model
|
|
90
|
+
"description": "Run Codex on your hosted Subconscious model with a temporary provider catalog and compaction hooks.",
|
|
87
91
|
"framework": "Codex",
|
|
88
92
|
"language": "shell",
|
|
89
93
|
"category": "coding-agent",
|
|
@@ -103,7 +107,12 @@
|
|
|
103
107
|
"runbook": {
|
|
104
108
|
"mode": "launch",
|
|
105
109
|
"script": "codex/run.sh",
|
|
106
|
-
"setupScript": "codex/install.sh"
|
|
110
|
+
"setupScript": "codex/install.sh",
|
|
111
|
+
"setupActions": [
|
|
112
|
+
"install",
|
|
113
|
+
"status",
|
|
114
|
+
"uninstall"
|
|
115
|
+
]
|
|
107
116
|
},
|
|
108
117
|
"launch": "codex -c model={model} -c model_provider=subconscious -c web_search=disabled -c model_providers.subconscious.name=Subconscious -c model_providers.subconscious.base_url={baseUrlV1} -c model_providers.subconscious.wire_api=responses -c model_providers.subconscious.env_key=SUBCONSCIOUS_API_KEY -c model_providers.subconscious.stream_idle_timeout_ms=300000",
|
|
109
118
|
"env": {
|
|
@@ -120,7 +129,7 @@
|
|
|
120
129
|
"open-code"
|
|
121
130
|
],
|
|
122
131
|
"displayName": "OpenCode + Subconscious",
|
|
123
|
-
"description": "Run OpenCode on your hosted Subconscious model
|
|
132
|
+
"description": "Run OpenCode on your hosted Subconscious model with the Subconscious provider and context settings.",
|
|
124
133
|
"framework": "OpenCode",
|
|
125
134
|
"language": "shell",
|
|
126
135
|
"category": "coding-agent",
|
|
@@ -140,7 +149,11 @@
|
|
|
140
149
|
"runbook": {
|
|
141
150
|
"mode": "launch",
|
|
142
151
|
"script": "opencode/run.sh",
|
|
143
|
-
"setupScript": "opencode/install.sh"
|
|
152
|
+
"setupScript": "opencode/install.sh",
|
|
153
|
+
"setupActions": [
|
|
154
|
+
"status",
|
|
155
|
+
"uninstall"
|
|
156
|
+
]
|
|
144
157
|
},
|
|
145
158
|
"launch": "opencode",
|
|
146
159
|
"configEnv": "OPENCODE_CONFIG_CONTENT",
|
|
@@ -199,7 +212,7 @@
|
|
|
199
212
|
"command": "cursor",
|
|
200
213
|
"name": "Cursor",
|
|
201
214
|
"aliases": [],
|
|
202
|
-
"description": "Install
|
|
215
|
+
"description": "Install Cursor conversation and compaction hooks, then finish model setup in Cursor Settings.",
|
|
203
216
|
"framework": "Cursor",
|
|
204
217
|
"language": "shell",
|
|
205
218
|
"category": "coding-agent",
|
|
@@ -213,7 +226,12 @@
|
|
|
213
226
|
"runbook": {
|
|
214
227
|
"mode": "setup",
|
|
215
228
|
"script": "cursor/install.sh",
|
|
216
|
-
"setupScript": "cursor/install.sh"
|
|
229
|
+
"setupScript": "cursor/install.sh",
|
|
230
|
+
"setupActions": [
|
|
231
|
+
"install",
|
|
232
|
+
"status",
|
|
233
|
+
"uninstall"
|
|
234
|
+
]
|
|
217
235
|
}
|
|
218
236
|
},
|
|
219
237
|
{
|
|
@@ -224,7 +242,7 @@
|
|
|
224
242
|
"vscode",
|
|
225
243
|
"vs-code"
|
|
226
244
|
],
|
|
227
|
-
"description": "Install the
|
|
245
|
+
"description": "Install the VS Code custom endpoint and Copilot conversation/compaction hooks.",
|
|
228
246
|
"framework": "GitHub Copilot",
|
|
229
247
|
"language": "shell",
|
|
230
248
|
"category": "coding-agent",
|
|
@@ -238,7 +256,12 @@
|
|
|
238
256
|
"runbook": {
|
|
239
257
|
"mode": "setup",
|
|
240
258
|
"script": "copilot/install.sh",
|
|
241
|
-
"setupScript": "copilot/install.sh"
|
|
259
|
+
"setupScript": "copilot/install.sh",
|
|
260
|
+
"setupActions": [
|
|
261
|
+
"install",
|
|
262
|
+
"status",
|
|
263
|
+
"uninstall"
|
|
264
|
+
]
|
|
242
265
|
}
|
|
243
266
|
},
|
|
244
267
|
{
|
|
@@ -246,7 +269,7 @@
|
|
|
246
269
|
"command": "pi",
|
|
247
270
|
"name": "Pi",
|
|
248
271
|
"aliases": [],
|
|
249
|
-
"description": "Launch Pi with the Subconscious provider and active profile model configured by subc
|
|
272
|
+
"description": "Launch Pi with the Subconscious provider and active profile model configured by subc pi install.",
|
|
250
273
|
"framework": "Pi",
|
|
251
274
|
"language": "shell",
|
|
252
275
|
"category": "coding-agent",
|
|
@@ -261,7 +284,12 @@
|
|
|
261
284
|
"runbook": {
|
|
262
285
|
"mode": "launch",
|
|
263
286
|
"script": "pi/run.sh",
|
|
264
|
-
"setupScript": "pi/install.sh"
|
|
287
|
+
"setupScript": "pi/install.sh",
|
|
288
|
+
"setupActions": [
|
|
289
|
+
"install",
|
|
290
|
+
"status",
|
|
291
|
+
"uninstall"
|
|
292
|
+
]
|
|
265
293
|
}
|
|
266
294
|
}
|
|
267
295
|
]
|
package/bin/runbook/README.md
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Coding-agent integrations
|
|
2
2
|
|
|
3
|
-
These
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
`
|
|
9
|
-
also disabled by default to honor the gateway's 128-tool request limit.
|
|
3
|
+
These scripts are part of `subc`. They launch or surgically install Claude Code,
|
|
4
|
+
Codex, OpenCode, Cursor, Copilot, and Pi against the Subconscious gateway.
|
|
5
|
+
|
|
6
|
+
`subc` reads `~/.subconscious/profiles/<name>.env` and injects those values into
|
|
7
|
+
the scripts, so users do not need a sibling `.env` file. When a script is run
|
|
8
|
+
directly, `SUBC_ENV_FILE` (or `cli/bin/runbook/.env`) can supply the same keys.
|
|
10
9
|
|
|
11
10
|
Included integrations:
|
|
12
11
|
|
|
13
12
|
- `claude-code/install.sh` and `run.sh`
|
|
14
|
-
- `codex/install.sh`, `run.sh`, `hook.sh`, and `hooks.
|
|
13
|
+
- `codex/install.sh`, `run.sh`, `hook.sh`, `hooks.json`, and `hooks-lib.sh`
|
|
15
14
|
- `opencode/install.sh`, `run.sh`, and `subconscious-compaction.ts`
|
|
16
15
|
- `cursor/install.sh`, `hook.sh`, and `hooks.json`
|
|
17
16
|
- `copilot/install.sh`, `hook.sh`, and `hooks.json`
|
|
18
17
|
- `pi/install.sh`, `run.sh`, and `subconscious-compaction.ts`
|
|
19
18
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
into these scripts, so users do not need to create a runbook checkout or source
|
|
25
|
-
an env file manually.
|
|
19
|
+
Each CLI-enabled entry in `agents/registry.json` points here with a `runbook`
|
|
20
|
+
block. Persistent writes are merge/unmerge only: they never replace a user's
|
|
21
|
+
`config.toml`, `opencode.json`, `models.json`, `hooks.json`, or VS Code
|
|
22
|
+
provider list.
|