@simonfestl/husky-cli 0.9.6 → 1.0.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.
- package/README.md +19 -13
- package/dist/commands/chat.d.ts +2 -0
- package/dist/commands/chat.js +162 -0
- package/dist/commands/completion.js +0 -9
- package/dist/commands/interactive/tasks.js +93 -9
- package/dist/commands/interactive.js +0 -5
- package/dist/commands/llm-context.d.ts +0 -4
- package/dist/commands/llm-context.js +9 -15
- package/dist/commands/task.js +101 -15
- package/dist/index.js +7 -5
- package/dist/lib/project-resolver.d.ts +26 -0
- package/dist/lib/project-resolver.js +111 -0
- package/package.json +1 -1
- package/dist/commands/interactive/jules-sessions.d.ts +0 -1
- package/dist/commands/interactive/jules-sessions.js +0 -460
- package/dist/commands/jules.d.ts +0 -2
- package/dist/commands/jules.js +0 -593
- package/dist/commands/services.d.ts +0 -2
- package/dist/commands/services.js +0 -381
package/README.md
CHANGED
|
@@ -111,19 +111,6 @@ husky vm update <session-id> --status approved
|
|
|
111
111
|
husky vm delete <session-id>
|
|
112
112
|
```
|
|
113
113
|
|
|
114
|
-
### Jules Session Management
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
husky jules list
|
|
118
|
-
husky jules create --task-id <id> --prompt "..."
|
|
119
|
-
husky jules get <session-id>
|
|
120
|
-
husky jules message <session-id> --content "..."
|
|
121
|
-
husky jules approve <session-id>
|
|
122
|
-
husky jules activities <session-id>
|
|
123
|
-
husky jules update <session-id> --status completed
|
|
124
|
-
husky jules delete <session-id>
|
|
125
|
-
```
|
|
126
|
-
|
|
127
114
|
### Business Strategy
|
|
128
115
|
|
|
129
116
|
```bash
|
|
@@ -283,6 +270,25 @@ husky --version
|
|
|
283
270
|
|
|
284
271
|
## Changelog
|
|
285
272
|
|
|
273
|
+
### v1.0.0 (2026-01-08) - Supervisor Architecture
|
|
274
|
+
|
|
275
|
+
**BREAKING CHANGES:**
|
|
276
|
+
- Removed: `husky jules` commands (replaced by supervisor architecture)
|
|
277
|
+
- Removed: `husky services` commands (deprecated)
|
|
278
|
+
- Removed: VM Pool management (now automated by supervisor)
|
|
279
|
+
- Deprecated: `husky agent` commands (use `husky task` instead)
|
|
280
|
+
|
|
281
|
+
**New Features:**
|
|
282
|
+
- Added: `husky chat` command for supervisor communication
|
|
283
|
+
- Added: Project resolver to prevent orphaned tasks
|
|
284
|
+
- Added: Implementation plans via `husky task plan`
|
|
285
|
+
- Added: QA review pipeline support
|
|
286
|
+
|
|
287
|
+
**Improvements:**
|
|
288
|
+
- Version now read dynamically from package.json
|
|
289
|
+
- Simplified task workflow
|
|
290
|
+
- Scope-based API key system support
|
|
291
|
+
|
|
286
292
|
### v0.5.0 (2026-01-06)
|
|
287
293
|
- Full Dashboard feature parity (69 new commands)
|
|
288
294
|
- Added: project, workflow, idea, department, vm, jules, process, strategy, settings, vm-config commands
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { getConfig } from "./config.js";
|
|
3
|
+
export const chatCommand = new Command("chat")
|
|
4
|
+
.description("Communicate with the dashboard chat");
|
|
5
|
+
chatCommand
|
|
6
|
+
.command("pending")
|
|
7
|
+
.description("Get pending messages from user")
|
|
8
|
+
.option("--json", "Output as JSON")
|
|
9
|
+
.action(async (options) => {
|
|
10
|
+
const config = getConfig();
|
|
11
|
+
if (!config.apiUrl) {
|
|
12
|
+
console.error("Error: API URL not configured.");
|
|
13
|
+
process.exit(1);
|
|
14
|
+
}
|
|
15
|
+
try {
|
|
16
|
+
const res = await fetch(`${config.apiUrl}/api/chat/pending`, {
|
|
17
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
18
|
+
});
|
|
19
|
+
if (!res.ok) {
|
|
20
|
+
throw new Error(`API error: ${res.status}`);
|
|
21
|
+
}
|
|
22
|
+
const data = await res.json();
|
|
23
|
+
const messages = data.messages || [];
|
|
24
|
+
if (options.json) {
|
|
25
|
+
console.log(JSON.stringify(messages, null, 2));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (messages.length === 0) {
|
|
29
|
+
console.log("No pending messages.");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
console.log("\n Pending Messages");
|
|
33
|
+
console.log(" " + "─".repeat(60));
|
|
34
|
+
for (const msg of messages) {
|
|
35
|
+
const time = new Date(msg.createdAt).toLocaleTimeString();
|
|
36
|
+
console.log(` [${time}] ${msg.content.slice(0, 60)}${msg.content.length > 60 ? "..." : ""}`);
|
|
37
|
+
if (msg.taskId) {
|
|
38
|
+
console.log(` Task: ${msg.taskId}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
console.log("");
|
|
42
|
+
}
|
|
43
|
+
catch (error) {
|
|
44
|
+
console.error("Error fetching messages:", error);
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
chatCommand
|
|
49
|
+
.command("list")
|
|
50
|
+
.description("List recent chat messages")
|
|
51
|
+
.option("--limit <n>", "Number of messages", "20")
|
|
52
|
+
.option("--json", "Output as JSON")
|
|
53
|
+
.action(async (options) => {
|
|
54
|
+
const config = getConfig();
|
|
55
|
+
if (!config.apiUrl) {
|
|
56
|
+
console.error("Error: API URL not configured.");
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
try {
|
|
60
|
+
const res = await fetch(`${config.apiUrl}/api/chat?limit=${options.limit}`, {
|
|
61
|
+
headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
|
|
62
|
+
});
|
|
63
|
+
if (!res.ok) {
|
|
64
|
+
throw new Error(`API error: ${res.status}`);
|
|
65
|
+
}
|
|
66
|
+
const data = await res.json();
|
|
67
|
+
const messages = data.messages || [];
|
|
68
|
+
if (options.json) {
|
|
69
|
+
console.log(JSON.stringify(messages, null, 2));
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (messages.length === 0) {
|
|
73
|
+
console.log("No messages.");
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.log("\n Chat History");
|
|
77
|
+
console.log(" " + "─".repeat(60));
|
|
78
|
+
for (const msg of messages) {
|
|
79
|
+
const time = new Date(msg.createdAt).toLocaleTimeString();
|
|
80
|
+
const role = msg.role === "user" ? "USER" : msg.role === "supervisor" ? "SUPV" : "SYS";
|
|
81
|
+
const icon = msg.role === "user" ? "👤" : msg.role === "supervisor" ? "🤖" : "⚙️";
|
|
82
|
+
console.log(` ${icon} [${time}] ${role}: ${msg.content.slice(0, 50)}${msg.content.length > 50 ? "..." : ""}`);
|
|
83
|
+
}
|
|
84
|
+
console.log("");
|
|
85
|
+
}
|
|
86
|
+
catch (error) {
|
|
87
|
+
console.error("Error fetching messages:", error);
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
chatCommand
|
|
92
|
+
.command("send <message>")
|
|
93
|
+
.description("Send a message as supervisor")
|
|
94
|
+
.option("--task-id <id>", "Link to a specific task")
|
|
95
|
+
.action(async (message, options) => {
|
|
96
|
+
const config = getConfig();
|
|
97
|
+
if (!config.apiUrl) {
|
|
98
|
+
console.error("Error: API URL not configured.");
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
const res = await fetch(`${config.apiUrl}/api/chat/supervisor`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
107
|
+
},
|
|
108
|
+
body: JSON.stringify({
|
|
109
|
+
content: message,
|
|
110
|
+
taskId: options.taskId,
|
|
111
|
+
}),
|
|
112
|
+
});
|
|
113
|
+
if (!res.ok) {
|
|
114
|
+
throw new Error(`API error: ${res.status}`);
|
|
115
|
+
}
|
|
116
|
+
console.log("Message sent.");
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
console.error("Error sending message:", error);
|
|
120
|
+
process.exit(1);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
chatCommand
|
|
124
|
+
.command("reply <messageId> <response>")
|
|
125
|
+
.description("Reply to a specific user message")
|
|
126
|
+
.option("--task-id <id>", "Link to a specific task")
|
|
127
|
+
.action(async (messageId, response, options) => {
|
|
128
|
+
const config = getConfig();
|
|
129
|
+
if (!config.apiUrl) {
|
|
130
|
+
console.error("Error: API URL not configured.");
|
|
131
|
+
process.exit(1);
|
|
132
|
+
}
|
|
133
|
+
try {
|
|
134
|
+
const res = await fetch(`${config.apiUrl}/api/chat/${messageId}/reply`, {
|
|
135
|
+
method: "POST",
|
|
136
|
+
headers: {
|
|
137
|
+
"Content-Type": "application/json",
|
|
138
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
139
|
+
},
|
|
140
|
+
body: JSON.stringify({
|
|
141
|
+
content: response,
|
|
142
|
+
taskId: options.taskId,
|
|
143
|
+
}),
|
|
144
|
+
});
|
|
145
|
+
if (!res.ok) {
|
|
146
|
+
throw new Error(`API error: ${res.status}`);
|
|
147
|
+
}
|
|
148
|
+
await fetch(`${config.apiUrl}/api/chat`, {
|
|
149
|
+
method: "PATCH",
|
|
150
|
+
headers: {
|
|
151
|
+
"Content-Type": "application/json",
|
|
152
|
+
...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
|
|
153
|
+
},
|
|
154
|
+
body: JSON.stringify({ messageIds: [messageId] }),
|
|
155
|
+
});
|
|
156
|
+
console.log("Reply sent and message marked as read.");
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
console.error("Error replying:", error);
|
|
160
|
+
process.exit(1);
|
|
161
|
+
}
|
|
162
|
+
});
|
|
@@ -20,7 +20,6 @@ const COMMANDS = {
|
|
|
20
20
|
idea: ["list", "create", "get", "update", "delete", "convert"],
|
|
21
21
|
department: ["list", "create", "get", "update", "delete"],
|
|
22
22
|
vm: ["list", "create", "get", "update", "delete", "start", "stop", "logs", "approve", "reject"],
|
|
23
|
-
jules: ["list", "create", "get", "update", "delete", "message", "approve", "activities", "sources"],
|
|
24
23
|
process: ["list", "create", "get", "update", "delete"],
|
|
25
24
|
strategy: [
|
|
26
25
|
"show", "set-vision", "set-mission", "add-value", "update-value", "delete-value",
|
|
@@ -195,7 +194,6 @@ _husky() {
|
|
|
195
194
|
'idea:Manage ideas'
|
|
196
195
|
'department:Manage departments'
|
|
197
196
|
'vm:Manage VM sessions'
|
|
198
|
-
'jules:Manage Jules AI coding sessions'
|
|
199
197
|
'process:Manage processes'
|
|
200
198
|
'strategy:Manage business strategy'
|
|
201
199
|
'settings:Manage application settings'
|
|
@@ -286,7 +284,6 @@ function getCommandDescription(cmd) {
|
|
|
286
284
|
idea: "Manage ideas",
|
|
287
285
|
department: "Manage departments",
|
|
288
286
|
vm: "Manage VM sessions",
|
|
289
|
-
jules: "Manage Jules AI coding sessions",
|
|
290
287
|
process: "Manage processes",
|
|
291
288
|
strategy: "Manage business strategy",
|
|
292
289
|
settings: "Manage application settings",
|
|
@@ -356,12 +353,6 @@ function getSubcommandDescription(cmd, sub) {
|
|
|
356
353
|
approve: "Approve VM session plan",
|
|
357
354
|
reject: "Reject VM session plan",
|
|
358
355
|
},
|
|
359
|
-
jules: {
|
|
360
|
-
message: "Send a message to a Jules session",
|
|
361
|
-
approve: "Approve a Jules session plan",
|
|
362
|
-
activities: "Get session activities",
|
|
363
|
-
sources: "List available Jules sources",
|
|
364
|
-
},
|
|
365
356
|
strategy: {
|
|
366
357
|
"set-vision": "Set/update the company vision",
|
|
367
358
|
"set-mission": "Set/update the company mission",
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { select, input, confirm } from "@inquirer/prompts";
|
|
2
2
|
import { ensureConfig, pressEnterToContinue, truncate } from "./utils.js";
|
|
3
|
+
import { resolveProject } from "../../lib/project-resolver.js";
|
|
3
4
|
export async function tasksMenu() {
|
|
4
5
|
const config = ensureConfig();
|
|
5
6
|
const menuItems = [
|
|
@@ -157,7 +158,6 @@ async function createTask(config) {
|
|
|
157
158
|
],
|
|
158
159
|
default: "medium",
|
|
159
160
|
});
|
|
160
|
-
// Optional: Link to project
|
|
161
161
|
const projects = await fetchProjects(config);
|
|
162
162
|
let projectId;
|
|
163
163
|
if (projects.length > 0) {
|
|
@@ -166,8 +166,49 @@ async function createTask(config) {
|
|
|
166
166
|
default: false,
|
|
167
167
|
});
|
|
168
168
|
if (linkToProject) {
|
|
169
|
-
const
|
|
170
|
-
|
|
169
|
+
const selectionMethod = await select({
|
|
170
|
+
message: "How to select project?",
|
|
171
|
+
choices: [
|
|
172
|
+
{ name: "Choose from list", value: "list" },
|
|
173
|
+
{ name: "Type project name", value: "type" },
|
|
174
|
+
],
|
|
175
|
+
});
|
|
176
|
+
if (selectionMethod === "list") {
|
|
177
|
+
const projectChoices = projects.map((p) => ({ name: p.name, value: p.id }));
|
|
178
|
+
projectId = await select({ message: "Select project:", choices: projectChoices });
|
|
179
|
+
}
|
|
180
|
+
else {
|
|
181
|
+
const projectInput = await input({
|
|
182
|
+
message: "Project name or ID:",
|
|
183
|
+
validate: (value) => (value.length > 0 ? true : "Project name required"),
|
|
184
|
+
});
|
|
185
|
+
const resolved = await resolveProject(projectInput, config);
|
|
186
|
+
if (!resolved) {
|
|
187
|
+
console.log(`\n ❌ Project "${projectInput}" not found.`);
|
|
188
|
+
console.log(" Available projects:");
|
|
189
|
+
for (const p of projects) {
|
|
190
|
+
console.log(` - ${p.name}`);
|
|
191
|
+
}
|
|
192
|
+
console.log("");
|
|
193
|
+
await pressEnterToContinue();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
if (resolved.resolvedBy === "fuzzy-match") {
|
|
197
|
+
const confirmFuzzy = await confirm({
|
|
198
|
+
message: `Did you mean "${resolved.projectName}"? (${Math.round(resolved.confidence * 100)}% match)`,
|
|
199
|
+
default: true,
|
|
200
|
+
});
|
|
201
|
+
if (!confirmFuzzy) {
|
|
202
|
+
console.log("\n Cancelled.\n");
|
|
203
|
+
await pressEnterToContinue();
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (resolved.resolvedBy !== "exact-id") {
|
|
208
|
+
console.log(` ℹ️ Resolved to: ${resolved.projectName}`);
|
|
209
|
+
}
|
|
210
|
+
projectId = resolved.projectId;
|
|
211
|
+
}
|
|
171
212
|
}
|
|
172
213
|
}
|
|
173
214
|
const res = await fetch(`${config.apiUrl}/api/tasks`, {
|
|
@@ -254,16 +295,59 @@ async function updateTask(config) {
|
|
|
254
295
|
});
|
|
255
296
|
break;
|
|
256
297
|
case "project":
|
|
257
|
-
const
|
|
258
|
-
if (
|
|
298
|
+
const projectsForUpdate = await fetchProjects(config);
|
|
299
|
+
if (projectsForUpdate.length === 0) {
|
|
259
300
|
console.log("\n No projects available.\n");
|
|
260
301
|
await pressEnterToContinue();
|
|
261
302
|
return;
|
|
262
303
|
}
|
|
263
|
-
const
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
304
|
+
const projectSelectionMethod = await select({
|
|
305
|
+
message: "How to select project?",
|
|
306
|
+
choices: [
|
|
307
|
+
{ name: "Choose from list", value: "list" },
|
|
308
|
+
{ name: "Type project name", value: "type" },
|
|
309
|
+
{ name: "Remove project link", value: "remove" },
|
|
310
|
+
],
|
|
311
|
+
});
|
|
312
|
+
if (projectSelectionMethod === "remove") {
|
|
313
|
+
updateData.projectId = "";
|
|
314
|
+
}
|
|
315
|
+
else if (projectSelectionMethod === "list") {
|
|
316
|
+
const projectChoicesForUpdate = projectsForUpdate.map((p) => ({ name: p.name, value: p.id }));
|
|
317
|
+
updateData.projectId = await select({ message: "Select project:", choices: projectChoicesForUpdate });
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
const projectInputForUpdate = await input({
|
|
321
|
+
message: "Project name or ID:",
|
|
322
|
+
validate: (value) => (value.length > 0 ? true : "Project name required"),
|
|
323
|
+
});
|
|
324
|
+
const resolvedForUpdate = await resolveProject(projectInputForUpdate, config);
|
|
325
|
+
if (!resolvedForUpdate) {
|
|
326
|
+
console.log(`\n ❌ Project "${projectInputForUpdate}" not found.`);
|
|
327
|
+
console.log(" Available projects:");
|
|
328
|
+
for (const p of projectsForUpdate) {
|
|
329
|
+
console.log(` - ${p.name}`);
|
|
330
|
+
}
|
|
331
|
+
console.log("");
|
|
332
|
+
await pressEnterToContinue();
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (resolvedForUpdate.resolvedBy === "fuzzy-match") {
|
|
336
|
+
const confirmFuzzyUpdate = await confirm({
|
|
337
|
+
message: `Did you mean "${resolvedForUpdate.projectName}"? (${Math.round(resolvedForUpdate.confidence * 100)}% match)`,
|
|
338
|
+
default: true,
|
|
339
|
+
});
|
|
340
|
+
if (!confirmFuzzyUpdate) {
|
|
341
|
+
console.log("\n Cancelled.\n");
|
|
342
|
+
await pressEnterToContinue();
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (resolvedForUpdate.resolvedBy !== "exact-id") {
|
|
347
|
+
console.log(` ℹ️ Resolved to: ${resolvedForUpdate.projectName}`);
|
|
348
|
+
}
|
|
349
|
+
updateData.projectId = resolvedForUpdate.projectId;
|
|
350
|
+
}
|
|
267
351
|
break;
|
|
268
352
|
case "title":
|
|
269
353
|
updateData.title = await input({
|
|
@@ -8,7 +8,6 @@ import { departmentsMenu } from "./interactive/departments.js";
|
|
|
8
8
|
import { processesMenu } from "./interactive/processes.js";
|
|
9
9
|
import { workflowsMenu } from "./interactive/workflows.js";
|
|
10
10
|
import { vmSessionsMenu } from "./interactive/vm-sessions.js";
|
|
11
|
-
import { julesSessionsMenu } from "./interactive/jules-sessions.js";
|
|
12
11
|
import { roadmapsMenu } from "./interactive/roadmaps.js";
|
|
13
12
|
import { strategyMenu } from "./interactive/strategy.js";
|
|
14
13
|
import { changelogMenu } from "./interactive/changelog.js";
|
|
@@ -34,7 +33,6 @@ export async function runInteractiveMode() {
|
|
|
34
33
|
{ name: "Processes", value: "processes", description: "Manage processes" },
|
|
35
34
|
{ name: "---", value: "separator2", description: "" },
|
|
36
35
|
{ name: "VM Sessions", value: "vm", description: "Manage VM sessions" },
|
|
37
|
-
{ name: "Jules Sessions", value: "jules", description: "Manage Jules AI sessions" },
|
|
38
36
|
{ name: "Worktrees", value: "worktrees", description: "Manage Git worktrees for agent isolation" },
|
|
39
37
|
{ name: "---", value: "separator3", description: "" },
|
|
40
38
|
{ name: "Business Operations", value: "business", description: "Billbee, Zendesk, SeaTable, Qdrant" },
|
|
@@ -75,9 +73,6 @@ export async function runInteractiveMode() {
|
|
|
75
73
|
case "vm":
|
|
76
74
|
await vmSessionsMenu();
|
|
77
75
|
break;
|
|
78
|
-
case "jules":
|
|
79
|
-
await julesSessionsMenu();
|
|
80
|
-
break;
|
|
81
76
|
case "worktrees":
|
|
82
77
|
await worktreesMenu();
|
|
83
78
|
break;
|
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
const VERSION = "0.9.6";
|
|
1
|
+
import { createRequire } from "module";
|
|
2
|
+
const require = createRequire(import.meta.url);
|
|
3
|
+
const packageJson = require("../../package.json");
|
|
6
4
|
export function generateLLMContext() {
|
|
7
|
-
return `# Husky CLI Reference (v${
|
|
5
|
+
return `# Husky CLI Reference (v${packageJson.version})
|
|
8
6
|
|
|
9
7
|
> [!CAUTION]
|
|
10
8
|
> ## MANDATORY: You MUST Use Husky CLI
|
|
@@ -54,11 +52,15 @@ husky config test
|
|
|
54
52
|
husky task list [--status <status>] # List tasks
|
|
55
53
|
husky task get <id> # Get task details
|
|
56
54
|
husky task create # Create new task
|
|
57
|
-
husky task start <id> # Start
|
|
55
|
+
husky task start <id> # Start task (auto-creates worktree!)
|
|
56
|
+
husky task start <id> --no-worktree # Start without worktree
|
|
58
57
|
husky task done <id> [--pr <url>] # Mark task as done
|
|
59
58
|
husky task update <id> # Update task fields
|
|
60
59
|
husky task assign <id> <assignee> # Assign task
|
|
61
60
|
husky task log <id> # View task activity log
|
|
61
|
+
husky task message <id> "msg" # Post status message (positional)
|
|
62
|
+
husky task message -m "msg" --id <id> # Post status message (flags)
|
|
63
|
+
husky task message -m "msg" # Uses HUSKY_TASK_ID env var
|
|
62
64
|
\`\`\`
|
|
63
65
|
|
|
64
66
|
### Projects
|
|
@@ -142,14 +144,6 @@ husky vm ssh <id> # SSH into VM
|
|
|
142
144
|
husky vm delete <id> # Delete VM
|
|
143
145
|
\`\`\`
|
|
144
146
|
|
|
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
147
|
### Workers (Multi-Agent Coordination)
|
|
154
148
|
\`\`\`bash
|
|
155
149
|
husky worker whoami # Show current worker identity
|