@simonfestl/husky-cli 0.9.3 ā 0.9.6
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.d.ts +2 -0
- package/dist/commands/llm-context.js +105 -3
- 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/vm.js +230 -6
- package/dist/commands/worktree.js +481 -1
- package/dist/index.js +7 -2
- 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
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* LLM Context Generator
|
|
3
3
|
* Outputs markdown reference for LLM agents
|
|
4
4
|
*/
|
|
5
|
-
const VERSION = "0.9.
|
|
5
|
+
const VERSION = "0.9.6";
|
|
6
6
|
export function generateLLMContext() {
|
|
7
7
|
return `# Husky CLI Reference (v${VERSION})
|
|
8
8
|
|
|
@@ -57,6 +57,8 @@ husky task create # Create new task
|
|
|
57
57
|
husky task start <id> # Start working on task
|
|
58
58
|
husky task done <id> [--pr <url>] # Mark task as done
|
|
59
59
|
husky task update <id> # Update task fields
|
|
60
|
+
husky task assign <id> <assignee> # Assign task
|
|
61
|
+
husky task log <id> # View task activity log
|
|
60
62
|
\`\`\`
|
|
61
63
|
|
|
62
64
|
### Projects
|
|
@@ -64,13 +66,106 @@ husky task update <id> # Update task fields
|
|
|
64
66
|
husky project list # List projects
|
|
65
67
|
husky project get <id> # Get project details
|
|
66
68
|
husky project create # Create new project
|
|
69
|
+
husky project tasks <id> # List project tasks
|
|
67
70
|
\`\`\`
|
|
68
71
|
|
|
69
|
-
###
|
|
72
|
+
### Roadmaps
|
|
73
|
+
\`\`\`bash
|
|
74
|
+
husky roadmap list # List roadmaps
|
|
75
|
+
husky roadmap get <id> # Get roadmap details
|
|
76
|
+
husky roadmap create # Create new roadmap
|
|
77
|
+
husky roadmap milestones <id> # List milestones
|
|
78
|
+
husky roadmap add-milestone <id> # Add milestone to roadmap
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
### Ideas
|
|
82
|
+
\`\`\`bash
|
|
83
|
+
husky idea list # List ideas
|
|
84
|
+
husky idea get <id> # Get idea details
|
|
85
|
+
husky idea create # Create new idea
|
|
86
|
+
husky idea vote <id> # Vote on idea
|
|
87
|
+
\`\`\`
|
|
88
|
+
|
|
89
|
+
### Workflows
|
|
90
|
+
\`\`\`bash
|
|
91
|
+
husky workflow list # List workflows
|
|
92
|
+
husky workflow get <id> # Get workflow details
|
|
93
|
+
husky workflow create # Create new workflow
|
|
94
|
+
husky workflow run <id> # Execute workflow
|
|
95
|
+
\`\`\`
|
|
96
|
+
|
|
97
|
+
### Departments
|
|
98
|
+
\`\`\`bash
|
|
99
|
+
husky department list # List departments
|
|
100
|
+
husky department get <id> # Get department details
|
|
101
|
+
husky department members <id> # List department members
|
|
102
|
+
\`\`\`
|
|
103
|
+
|
|
104
|
+
### Processes
|
|
105
|
+
\`\`\`bash
|
|
106
|
+
husky process list # List processes
|
|
107
|
+
husky process get <id> # Get process details
|
|
108
|
+
husky process run <id> # Run process
|
|
109
|
+
\`\`\`
|
|
110
|
+
|
|
111
|
+
### Strategy
|
|
112
|
+
\`\`\`bash
|
|
113
|
+
husky strategy list # List strategy items
|
|
114
|
+
husky strategy get <id> # Get strategy details
|
|
115
|
+
husky strategy update <id> # Update strategy
|
|
116
|
+
\`\`\`
|
|
117
|
+
|
|
118
|
+
### Changelog
|
|
119
|
+
\`\`\`bash
|
|
120
|
+
husky changelog generate # Generate changelog from commits
|
|
121
|
+
husky changelog list # List changelogs
|
|
122
|
+
\`\`\`
|
|
123
|
+
|
|
124
|
+
### Worktrees (Git Isolation)
|
|
70
125
|
\`\`\`bash
|
|
71
126
|
husky worktree list # List worktrees
|
|
72
127
|
husky worktree create <name> # Create isolated worktree
|
|
73
|
-
husky worktree
|
|
128
|
+
husky worktree create <name> --task-id <id> # Create and register with task
|
|
129
|
+
husky worktree merge <name> # Merge back to base branch
|
|
130
|
+
husky worktree push <name> # Push branch to remote
|
|
131
|
+
husky worktree pr <name> -t "Title" # Create pull request
|
|
132
|
+
husky worktree remove <name> # Remove worktree
|
|
133
|
+
husky worktree sync-stats <name> --task-id <id> # Sync stats to dashboard
|
|
134
|
+
\`\`\`
|
|
135
|
+
|
|
136
|
+
### VM Sessions (Cloud Agents)
|
|
137
|
+
\`\`\`bash
|
|
138
|
+
husky vm list # List VM sessions
|
|
139
|
+
husky vm create # Create new VM
|
|
140
|
+
husky vm status <id> # Get VM status
|
|
141
|
+
husky vm ssh <id> # SSH into VM
|
|
142
|
+
husky vm delete <id> # Delete VM
|
|
143
|
+
\`\`\`
|
|
144
|
+
|
|
145
|
+
### Jules Sessions (AI Agent)
|
|
146
|
+
\`\`\`bash
|
|
147
|
+
husky jules list # List Jules sessions
|
|
148
|
+
husky jules create # Create new session
|
|
149
|
+
husky jules status <id> # Get session status
|
|
150
|
+
husky jules logs <id> # View session logs
|
|
151
|
+
\`\`\`
|
|
152
|
+
|
|
153
|
+
### Workers (Multi-Agent Coordination)
|
|
154
|
+
\`\`\`bash
|
|
155
|
+
husky worker whoami # Show current worker identity
|
|
156
|
+
husky worker register # Register worker with API
|
|
157
|
+
husky worker list # List all workers
|
|
158
|
+
husky worker sessions # List active sessions
|
|
159
|
+
husky worker activity # Who is working on what
|
|
160
|
+
\`\`\`
|
|
161
|
+
|
|
162
|
+
### Utility Commands
|
|
163
|
+
\`\`\`bash
|
|
164
|
+
husky explain <command> # Explain CLI commands
|
|
165
|
+
husky settings list # List dashboard settings
|
|
166
|
+
husky settings set <key> <value> # Update setting
|
|
167
|
+
husky completion bash|zsh|fish # Generate shell completion
|
|
168
|
+
husky llm # Output LLM reference docs
|
|
74
169
|
\`\`\`
|
|
75
170
|
|
|
76
171
|
---
|
|
@@ -157,3 +252,10 @@ husky biz qdrant search <coll> "<q>" # Semantic search
|
|
|
157
252
|
export function printLLMContext() {
|
|
158
253
|
console.log(generateLLMContext());
|
|
159
254
|
}
|
|
255
|
+
// Command export for subcommand registration
|
|
256
|
+
import { Command } from "commander";
|
|
257
|
+
export const llmCommand = new Command("llm")
|
|
258
|
+
.description("Output LLM reference documentation (markdown)")
|
|
259
|
+
.action(() => {
|
|
260
|
+
printLLMContext();
|
|
261
|
+
});
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
const API_URL = process.env.HUSKY_API_URL || "https://huskyv0-dashboard-474966775596.europe-west1.run.app";
|
|
3
|
+
const API_KEY = process.env.HUSKY_API_KEY || "";
|
|
4
|
+
async function fetchApi(path, options = {}) {
|
|
5
|
+
const response = await fetch(`${API_URL}${path}`, {
|
|
6
|
+
...options,
|
|
7
|
+
headers: {
|
|
8
|
+
"Content-Type": "application/json",
|
|
9
|
+
"x-api-key": API_KEY,
|
|
10
|
+
...options.headers,
|
|
11
|
+
},
|
|
12
|
+
});
|
|
13
|
+
if (!response.ok) {
|
|
14
|
+
const error = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
15
|
+
throw new Error(error.error || `HTTP ${response.status}`);
|
|
16
|
+
}
|
|
17
|
+
return response.json();
|
|
18
|
+
}
|
|
19
|
+
function formatPersona(personaType) {
|
|
20
|
+
const icons = {
|
|
21
|
+
"support-agent": "š§",
|
|
22
|
+
"research-agent": "š¬",
|
|
23
|
+
"accounting-agent": "š",
|
|
24
|
+
"marketing-agent": "š¢",
|
|
25
|
+
"developer-agent": "š»",
|
|
26
|
+
"devops-agent": "š§",
|
|
27
|
+
};
|
|
28
|
+
return `${icons[personaType] || "š¤"} ${personaType}`;
|
|
29
|
+
}
|
|
30
|
+
function formatPermissionLevel(level) {
|
|
31
|
+
const colors = {
|
|
32
|
+
"read-only": "\x1b[90m", // gray
|
|
33
|
+
"developer": "\x1b[34m", // blue
|
|
34
|
+
"admin": "\x1b[31m", // red
|
|
35
|
+
};
|
|
36
|
+
const reset = "\x1b[0m";
|
|
37
|
+
return `${colors[level] || ""}${level}${reset}`;
|
|
38
|
+
}
|
|
39
|
+
export const serviceAccountCommand = new Command("sa")
|
|
40
|
+
.description("Manage Agent Service Accounts")
|
|
41
|
+
.addHelpText("after", `
|
|
42
|
+
Examples:
|
|
43
|
+
husky sa list List all service account bindings
|
|
44
|
+
husky sa get developer-agent Show binding for developer persona
|
|
45
|
+
husky sa sync Sync bindings from GCP config
|
|
46
|
+
husky sa verify Verify current VM's service account
|
|
47
|
+
`);
|
|
48
|
+
// List all bindings
|
|
49
|
+
serviceAccountCommand
|
|
50
|
+
.command("list")
|
|
51
|
+
.description("List all service account bindings")
|
|
52
|
+
.option("--json", "Output as JSON")
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
try {
|
|
55
|
+
const data = await fetchApi("/api/service-account-bindings");
|
|
56
|
+
const bindings = (data.bindings || []);
|
|
57
|
+
if (options.json) {
|
|
58
|
+
console.log(JSON.stringify(bindings, null, 2));
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
if (bindings.length === 0) {
|
|
62
|
+
console.log("No service account bindings found.");
|
|
63
|
+
console.log("Run 'husky sa sync' to create bindings from GCP config.");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
console.log("\nš Service Account Bindings\n");
|
|
67
|
+
console.log("ā".repeat(80));
|
|
68
|
+
for (const binding of bindings) {
|
|
69
|
+
console.log(`${formatPersona(binding.personaType)}`);
|
|
70
|
+
console.log(` Email: ${binding.serviceAccountEmail}`);
|
|
71
|
+
console.log(` Permission: ${formatPermissionLevel(binding.permissionLevel)}`);
|
|
72
|
+
console.log(` Roles: ${binding.iamRoles.join(", ") || "none"}`);
|
|
73
|
+
if (binding.lastSyncedAt) {
|
|
74
|
+
console.log(` Last Synced: ${new Date(binding.lastSyncedAt).toLocaleString()}`);
|
|
75
|
+
}
|
|
76
|
+
console.log("ā".repeat(80));
|
|
77
|
+
}
|
|
78
|
+
console.log(`\nTotal: ${bindings.length} bindings`);
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
// Get specific binding
|
|
86
|
+
serviceAccountCommand
|
|
87
|
+
.command("get <persona>")
|
|
88
|
+
.description("Get service account binding for a persona")
|
|
89
|
+
.option("--json", "Output as JSON")
|
|
90
|
+
.action(async (persona, options) => {
|
|
91
|
+
try {
|
|
92
|
+
// First list all and filter by persona
|
|
93
|
+
const data = await fetchApi("/api/service-account-bindings");
|
|
94
|
+
const bindings = (data.bindings || []);
|
|
95
|
+
const binding = bindings.find((b) => b.personaType === persona);
|
|
96
|
+
if (!binding) {
|
|
97
|
+
console.log(`No binding found for persona: ${persona}`);
|
|
98
|
+
console.log("\nAvailable personas:");
|
|
99
|
+
bindings.forEach((b) => console.log(` - ${b.personaType}`));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (options.json) {
|
|
103
|
+
console.log(JSON.stringify(binding, null, 2));
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
console.log(`\n${formatPersona(binding.personaType)}\n`);
|
|
107
|
+
console.log(`Email: ${binding.serviceAccountEmail}`);
|
|
108
|
+
console.log(`Permission: ${formatPermissionLevel(binding.permissionLevel)}`);
|
|
109
|
+
console.log(`Project: ${binding.projectId}`);
|
|
110
|
+
console.log(`Roles:`);
|
|
111
|
+
binding.iamRoles.forEach((role) => console.log(` - ${role}`));
|
|
112
|
+
if (binding.lastSyncedAt) {
|
|
113
|
+
console.log(`Last Synced: ${new Date(binding.lastSyncedAt).toLocaleString()}`);
|
|
114
|
+
}
|
|
115
|
+
console.log(`Created: ${new Date(binding.createdAt).toLocaleString()}`);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
119
|
+
process.exit(1);
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
// Sync from GCP
|
|
123
|
+
serviceAccountCommand
|
|
124
|
+
.command("sync")
|
|
125
|
+
.description("Sync service account bindings from GCP config")
|
|
126
|
+
.action(async () => {
|
|
127
|
+
try {
|
|
128
|
+
console.log("š Syncing service account bindings...\n");
|
|
129
|
+
const data = await fetchApi("/api/service-account-bindings/sync", {
|
|
130
|
+
method: "POST",
|
|
131
|
+
});
|
|
132
|
+
console.log(`ā
Synced ${data.synced} bindings\n`);
|
|
133
|
+
for (const result of (data.results || [])) {
|
|
134
|
+
const icon = result.status === "synced" ? "ā" : "ā";
|
|
135
|
+
console.log(` ${icon} ${result.personaType}: ${result.email || result.status}`);
|
|
136
|
+
}
|
|
137
|
+
console.log("\nRun 'husky sa list' to see all bindings.");
|
|
138
|
+
}
|
|
139
|
+
catch (error) {
|
|
140
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
});
|
|
144
|
+
// Verify current VM's SA (for use on VMs)
|
|
145
|
+
serviceAccountCommand
|
|
146
|
+
.command("verify")
|
|
147
|
+
.description("Verify current VM's service account (run on VM)")
|
|
148
|
+
.action(async () => {
|
|
149
|
+
try {
|
|
150
|
+
// Check if running on GCP by querying metadata service
|
|
151
|
+
const metadataUrl = "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/default/email";
|
|
152
|
+
console.log("š Checking GCP metadata service...\n");
|
|
153
|
+
const response = await fetch(metadataUrl, {
|
|
154
|
+
headers: { "Metadata-Flavor": "Google" },
|
|
155
|
+
}).catch(() => null);
|
|
156
|
+
if (!response || !response.ok) {
|
|
157
|
+
console.log("ā Not running on a GCP VM (metadata service unavailable)");
|
|
158
|
+
console.log("\nThis command is designed to run on GCP Compute Engine VMs.");
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
const saEmail = await response.text();
|
|
162
|
+
console.log(`ā
Running as service account: ${saEmail}`);
|
|
163
|
+
// Try to match to a persona
|
|
164
|
+
const personaMatch = saEmail.match(/husky-(\w+)@/);
|
|
165
|
+
if (personaMatch) {
|
|
166
|
+
const persona = personaMatch[1] + "-agent";
|
|
167
|
+
console.log(` Persona: ${formatPersona(persona)}`);
|
|
168
|
+
}
|
|
169
|
+
// Report to Husky if we have a task ID
|
|
170
|
+
const taskId = process.env.HUSKY_TASK_ID;
|
|
171
|
+
if (taskId) {
|
|
172
|
+
console.log(`\nš¤ Reporting to Husky (task: ${taskId})...`);
|
|
173
|
+
// The VM startup script handles this, but we can also report here
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
console.error("Error:", error instanceof Error ? error.message : error);
|
|
178
|
+
process.exit(1);
|
|
179
|
+
}
|
|
180
|
+
});
|