@simonfestl/husky-cli 0.9.5 → 0.9.7
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/llm-context.js +12 -4
- package/dist/commands/service-account.d.ts +2 -0
- package/dist/commands/service-account.js +180 -0
- package/dist/commands/services.d.ts +2 -0
- package/dist/commands/services.js +381 -0
- package/dist/commands/task.js +35 -4
- package/dist/commands/vm.js +230 -6
- package/dist/commands/worktree.js +481 -1
- package/dist/index.js +5 -1
- package/dist/lib/agent-templates.d.ts +20 -0
- package/dist/lib/agent-templates.js +142 -0
- package/dist/lib/worktree.d.ts +26 -0
- package/dist/lib/worktree.js +127 -0
- package/package.json +1 -1
package/dist/commands/vm.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { getConfig } from "./config.js";
|
|
3
3
|
import * as readline from "readline";
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
import * as fs from "fs";
|
|
5
|
+
import * as path from "path";
|
|
6
|
+
import { DEFAULT_AGENT_CONFIGS, generateStartupScript, listDefaultAgentTypes, getDefaultAgentConfig, } from "../lib/agent-templates.js";
|
|
7
|
+
export const vmCommand = new Command("vm").description("Manage VM sessions");
|
|
6
8
|
// Helper: Ensure API is configured
|
|
7
9
|
function ensureConfig() {
|
|
8
10
|
const config = getConfig();
|
|
@@ -12,6 +14,50 @@ function ensureConfig() {
|
|
|
12
14
|
}
|
|
13
15
|
return config;
|
|
14
16
|
}
|
|
17
|
+
async function fetchAgentTypes() {
|
|
18
|
+
const config = getConfig();
|
|
19
|
+
if (!config.apiUrl)
|
|
20
|
+
return null;
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(`${config.apiUrl}/api/agent-types`, {
|
|
23
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
24
|
+
});
|
|
25
|
+
if (!res.ok)
|
|
26
|
+
return null;
|
|
27
|
+
return await res.json();
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
async function fetchAgentTypeBySlug(slug) {
|
|
34
|
+
const config = getConfig();
|
|
35
|
+
if (!config.apiUrl)
|
|
36
|
+
return null;
|
|
37
|
+
try {
|
|
38
|
+
const res = await fetch(`${config.apiUrl}/api/agent-types?slug=${slug}`, {
|
|
39
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
40
|
+
});
|
|
41
|
+
if (!res.ok)
|
|
42
|
+
return null;
|
|
43
|
+
return await res.json();
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async function getAgentTypeConfig(slug) {
|
|
50
|
+
const apiType = await fetchAgentTypeBySlug(slug);
|
|
51
|
+
if (apiType) {
|
|
52
|
+
return { config: apiType.agentConfig, name: apiType.name };
|
|
53
|
+
}
|
|
54
|
+
const defaultConfig = getDefaultAgentConfig(slug);
|
|
55
|
+
if (defaultConfig) {
|
|
56
|
+
const name = slug.charAt(0).toUpperCase() + slug.slice(1) + " Agent";
|
|
57
|
+
return { config: defaultConfig, name };
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
15
61
|
// Helper: Prompt for confirmation
|
|
16
62
|
async function confirm(message) {
|
|
17
63
|
const rl = readline.createInterface({
|
|
@@ -71,19 +117,40 @@ vmCommand
|
|
|
71
117
|
.command("create <name>")
|
|
72
118
|
.description("Create a new VM session")
|
|
73
119
|
.option("-p, --prompt <prompt>", "Initial prompt for the agent")
|
|
74
|
-
.option("--agent <agent>", "Agent type (claude-code, gemini-cli, aider, custom)", "
|
|
120
|
+
.option("--agent <agent>", "Agent type (claude-code, gemini-cli, aider, custom)", "gemini-cli")
|
|
121
|
+
.option("-t, --type <type>", "Business agent type (support, accounting, marketing, research)")
|
|
75
122
|
.option("--config <configId>", "VM config to use")
|
|
76
123
|
.option("--project <projectId>", "Link to project")
|
|
77
124
|
.option("--task <taskId>", "Link to task")
|
|
78
125
|
.option("--repo <repoUrl>", "Git repository URL")
|
|
79
126
|
.option("--branch <branch>", "Git branch to use")
|
|
80
127
|
.option("--machine-type <machineType>", "GCP machine type", "e2-medium")
|
|
81
|
-
.option("--zone <zone>", "GCP zone", "
|
|
128
|
+
.option("--zone <zone>", "GCP zone", "europe-west1-b")
|
|
82
129
|
.option("--json", "Output as JSON")
|
|
83
130
|
.action(async (name, options) => {
|
|
84
131
|
const config = ensureConfig();
|
|
132
|
+
const validBusinessTypes = [
|
|
133
|
+
"support",
|
|
134
|
+
"accounting",
|
|
135
|
+
"marketing",
|
|
136
|
+
"research",
|
|
137
|
+
];
|
|
138
|
+
if (options.type && !validBusinessTypes.includes(options.type)) {
|
|
139
|
+
console.error(`Error: Invalid business agent type '${options.type}'`);
|
|
140
|
+
console.error(`Valid types: ${validBusinessTypes.join(", ")}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
if (options.type && !options.prompt) {
|
|
144
|
+
const defaultPrompts = {
|
|
145
|
+
support: "Du bist ein Support Agent. Starte mit /shift-start um die Schicht zu beginnen.",
|
|
146
|
+
accounting: "Du bist ein Accounting Agent. Pruefe /inbox fuer neue Belege.",
|
|
147
|
+
marketing: "Du bist ein Marketing Agent. Pruefe /campaigns fuer aktuelle Kampagnen.",
|
|
148
|
+
research: "Du bist ein Research Agent. Pruefe /youtube fuer neue Videos zu analysieren.",
|
|
149
|
+
};
|
|
150
|
+
options.prompt = defaultPrompts[options.type] || "Agent bereit.";
|
|
151
|
+
}
|
|
85
152
|
if (!options.prompt) {
|
|
86
|
-
console.error("Error: --prompt is required");
|
|
153
|
+
console.error("Error: --prompt is required (or use --type for default prompt)");
|
|
87
154
|
process.exit(1);
|
|
88
155
|
}
|
|
89
156
|
try {
|
|
@@ -97,6 +164,7 @@ vmCommand
|
|
|
97
164
|
name,
|
|
98
165
|
prompt: options.prompt,
|
|
99
166
|
agentType: options.agent,
|
|
167
|
+
businessAgentType: options.type,
|
|
100
168
|
taskId: options.task,
|
|
101
169
|
workflowId: options.project,
|
|
102
170
|
repoUrl: options.repo,
|
|
@@ -118,9 +186,16 @@ vmCommand
|
|
|
118
186
|
console.log(`Created VM session: ${session.name}`);
|
|
119
187
|
console.log(` ID: ${session.id}`);
|
|
120
188
|
console.log(` Agent: ${session.agentType}`);
|
|
189
|
+
if (session.businessAgentType) {
|
|
190
|
+
console.log(` Type: ${session.businessAgentType}`);
|
|
191
|
+
}
|
|
121
192
|
console.log(` Status: ${formatStatus(session.vmStatus)}`);
|
|
122
193
|
console.log(` VM Name: ${session.vmName}`);
|
|
194
|
+
console.log(` Zone: ${options.zone}`);
|
|
123
195
|
console.log(`\nTo start the VM, run: husky vm start ${session.id}`);
|
|
196
|
+
if (session.businessAgentType) {
|
|
197
|
+
console.log(`\nAfter VM is ready, the ${session.businessAgentType} agent will be auto-configured.`);
|
|
198
|
+
}
|
|
124
199
|
}
|
|
125
200
|
}
|
|
126
201
|
catch (error) {
|
|
@@ -523,6 +598,150 @@ vmCommand
|
|
|
523
598
|
process.exit(1);
|
|
524
599
|
}
|
|
525
600
|
});
|
|
601
|
+
vmCommand
|
|
602
|
+
.command("types")
|
|
603
|
+
.description("List available business agent types")
|
|
604
|
+
.option("--json", "Output as JSON")
|
|
605
|
+
.action(async (options) => {
|
|
606
|
+
const apiTypes = await fetchAgentTypes();
|
|
607
|
+
if (apiTypes && apiTypes.length > 0) {
|
|
608
|
+
if (options.json) {
|
|
609
|
+
console.log(JSON.stringify(apiTypes, null, 2));
|
|
610
|
+
return;
|
|
611
|
+
}
|
|
612
|
+
console.log("\n Available Agent Types (from API)");
|
|
613
|
+
console.log(" " + "-".repeat(70));
|
|
614
|
+
for (const type of apiTypes) {
|
|
615
|
+
console.log(`\n ${type.slug.toUpperCase()} - ${type.name}`);
|
|
616
|
+
console.log(` ${type.description}`);
|
|
617
|
+
console.log(` Directories: ${type.agentConfig.directories.slice(0, 3).join(", ")}...`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
else {
|
|
621
|
+
const types = listDefaultAgentTypes();
|
|
622
|
+
if (options.json) {
|
|
623
|
+
console.log(JSON.stringify(DEFAULT_AGENT_CONFIGS, null, 2));
|
|
624
|
+
return;
|
|
625
|
+
}
|
|
626
|
+
console.log("\n Available Agent Types (defaults)");
|
|
627
|
+
console.log(" " + "-".repeat(70));
|
|
628
|
+
for (const type of types) {
|
|
629
|
+
const config = DEFAULT_AGENT_CONFIGS[type];
|
|
630
|
+
console.log(`\n ${type.toUpperCase()}`);
|
|
631
|
+
console.log(` Default prompt: ${config.defaultPrompt}`);
|
|
632
|
+
console.log(` Directories: ${config.directories.slice(0, 3).join(", ")}...`);
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
console.log("\n Usage:");
|
|
636
|
+
console.log(" husky vm create my-agent --type support");
|
|
637
|
+
console.log(" husky vm init --type accounting\n");
|
|
638
|
+
});
|
|
639
|
+
vmCommand
|
|
640
|
+
.command("init")
|
|
641
|
+
.description("Initialize agent workspace on current machine (run this ON the VM)")
|
|
642
|
+
.requiredOption("-t, --type <type>", "Business agent type slug (e.g., support, accounting, marketing, research)")
|
|
643
|
+
.option("--workspace <path>", "Workspace directory", process.env.HOME + "/workspace")
|
|
644
|
+
.option("--json", "Output as JSON")
|
|
645
|
+
.action(async (options) => {
|
|
646
|
+
const agentTypeSlug = options.type;
|
|
647
|
+
const typeData = await getAgentTypeConfig(agentTypeSlug);
|
|
648
|
+
if (!typeData) {
|
|
649
|
+
const validTypes = listDefaultAgentTypes();
|
|
650
|
+
console.error(`Error: Unknown agent type '${agentTypeSlug}'`);
|
|
651
|
+
console.error(`Default types: ${validTypes.join(", ")}`);
|
|
652
|
+
console.error(`Or configure API to fetch custom types.`);
|
|
653
|
+
process.exit(1);
|
|
654
|
+
}
|
|
655
|
+
const { config, name: agentName } = typeData;
|
|
656
|
+
const workspace = options.workspace;
|
|
657
|
+
console.log(`\n Initializing ${agentName}...`);
|
|
658
|
+
console.log(` Workspace: ${workspace}\n`);
|
|
659
|
+
try {
|
|
660
|
+
if (!fs.existsSync(workspace)) {
|
|
661
|
+
fs.mkdirSync(workspace, { recursive: true });
|
|
662
|
+
}
|
|
663
|
+
for (const dir of config.directories) {
|
|
664
|
+
const fullPath = path.join(workspace, dir);
|
|
665
|
+
if (!fs.existsSync(fullPath)) {
|
|
666
|
+
fs.mkdirSync(fullPath, { recursive: true });
|
|
667
|
+
console.log(` Created: ${dir}`);
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
const geminiDir = path.join(workspace, ".gemini", "commands");
|
|
671
|
+
if (!fs.existsSync(geminiDir)) {
|
|
672
|
+
fs.mkdirSync(geminiDir, { recursive: true });
|
|
673
|
+
}
|
|
674
|
+
const scriptsDir = path.join(workspace, "scripts");
|
|
675
|
+
if (!fs.existsSync(scriptsDir)) {
|
|
676
|
+
fs.mkdirSync(scriptsDir, { recursive: true });
|
|
677
|
+
}
|
|
678
|
+
const geminiMd = `# ${agentName} VM - Project Rules
|
|
679
|
+
|
|
680
|
+
## Rolle
|
|
681
|
+
Du bist ein autonomer ${agentName}.
|
|
682
|
+
|
|
683
|
+
## Tech Stack
|
|
684
|
+
- Runtime: Google Cloud VM
|
|
685
|
+
- AI Model: Google Vertex AI (europe-west1) - DSGVO-konform
|
|
686
|
+
- CLI: Husky CLI fuer alle Business-Operationen
|
|
687
|
+
|
|
688
|
+
## Workspace
|
|
689
|
+
${config.directories.map((d) => `- ${d}/`).join("\n")}
|
|
690
|
+
|
|
691
|
+
## Status Updates
|
|
692
|
+
\`\`\`bash
|
|
693
|
+
husky task message <task-id> -m "<status>"
|
|
694
|
+
\`\`\`
|
|
695
|
+
`;
|
|
696
|
+
fs.writeFileSync(path.join(workspace, "GEMINI.md"), geminiMd);
|
|
697
|
+
console.log(` Created: GEMINI.md`);
|
|
698
|
+
if (options.json) {
|
|
699
|
+
console.log(JSON.stringify({ success: true, workspace, type: agentTypeSlug, name: agentName }, null, 2));
|
|
700
|
+
}
|
|
701
|
+
else {
|
|
702
|
+
console.log(`\n ${agentName} initialized!`);
|
|
703
|
+
console.log(`\n Next steps:`);
|
|
704
|
+
console.log(` cd ${workspace}`);
|
|
705
|
+
console.log(` gemini`);
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
catch (error) {
|
|
709
|
+
console.error("Error initializing agent:", error);
|
|
710
|
+
process.exit(1);
|
|
711
|
+
}
|
|
712
|
+
});
|
|
713
|
+
vmCommand
|
|
714
|
+
.command("startup-script")
|
|
715
|
+
.description("Generate VM startup script for agent type")
|
|
716
|
+
.requiredOption("-t, --type <type>", "Business agent type slug")
|
|
717
|
+
.option("--husky-url <url>", "Husky API URL")
|
|
718
|
+
.option("--husky-key <key>", "Husky API Key")
|
|
719
|
+
.option("--project <project>", "GCP Project ID")
|
|
720
|
+
.action(async (options) => {
|
|
721
|
+
const apiType = await fetchAgentTypeBySlug(options.type);
|
|
722
|
+
if (apiType) {
|
|
723
|
+
const script = generateStartupScript({
|
|
724
|
+
id: apiType.id,
|
|
725
|
+
departmentId: apiType.departmentId || "",
|
|
726
|
+
name: apiType.name,
|
|
727
|
+
slug: apiType.slug,
|
|
728
|
+
description: apiType.description,
|
|
729
|
+
agentConfig: apiType.agentConfig,
|
|
730
|
+
createdAt: apiType.createdAt,
|
|
731
|
+
updatedAt: apiType.updatedAt,
|
|
732
|
+
}, options.huskyUrl, options.huskyKey, options.project);
|
|
733
|
+
console.log(script);
|
|
734
|
+
return;
|
|
735
|
+
}
|
|
736
|
+
const validTypes = listDefaultAgentTypes();
|
|
737
|
+
if (!validTypes.includes(options.type)) {
|
|
738
|
+
console.error(`Error: Unknown agent type '${options.type}'`);
|
|
739
|
+
console.error(`Default types: ${validTypes.join(", ")}`);
|
|
740
|
+
process.exit(1);
|
|
741
|
+
}
|
|
742
|
+
const script = generateStartupScript(options.type, options.huskyUrl, options.huskyKey, options.project);
|
|
743
|
+
console.log(script);
|
|
744
|
+
});
|
|
526
745
|
// Print helpers
|
|
527
746
|
function printVMSessions(sessions, stats) {
|
|
528
747
|
if (sessions.length === 0) {
|
|
@@ -538,7 +757,9 @@ function printVMSessions(sessions, stats) {
|
|
|
538
757
|
console.log(` ${"ID".padEnd(24)} ${"NAME".padEnd(20)} ${"STATUS".padEnd(16)} ${"AGENT".padEnd(14)} CREATED`);
|
|
539
758
|
console.log(" " + "-".repeat(90));
|
|
540
759
|
for (const session of sessions) {
|
|
541
|
-
const truncatedName = session.name.length > 18
|
|
760
|
+
const truncatedName = session.name.length > 18
|
|
761
|
+
? session.name.substring(0, 15) + "..."
|
|
762
|
+
: session.name;
|
|
542
763
|
const status = formatStatus(session.vmStatus).padEnd(16);
|
|
543
764
|
const createdAt = new Date(session.createdAt).toLocaleDateString();
|
|
544
765
|
console.log(` ${session.id.padEnd(24)} ${truncatedName.padEnd(20)} ${status} ${session.agentType.padEnd(14)} ${createdAt}`);
|
|
@@ -551,6 +772,9 @@ function printVMSessionDetail(session) {
|
|
|
551
772
|
console.log(` ID: ${session.id}`);
|
|
552
773
|
console.log(` Status: ${formatStatus(session.vmStatus)}`);
|
|
553
774
|
console.log(` Agent: ${session.agentType}`);
|
|
775
|
+
if (session.businessAgentType) {
|
|
776
|
+
console.log(` Type: ${session.businessAgentType}`);
|
|
777
|
+
}
|
|
554
778
|
console.log(` VM Name: ${session.vmName}`);
|
|
555
779
|
console.log(` Zone: ${session.vmZone}`);
|
|
556
780
|
console.log(` Machine Type: ${session.machineType}`);
|