@simonfestl/husky-cli 0.3.0 → 0.5.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.
@@ -0,0 +1,240 @@
1
+ import { Command } from "commander";
2
+ import { getConfig } from "./config.js";
3
+ export const departmentCommand = new Command("department")
4
+ .description("Manage departments");
5
+ // Helper: Ensure API is configured
6
+ function ensureConfig() {
7
+ const config = getConfig();
8
+ if (!config.apiUrl) {
9
+ console.error("Error: API URL not configured. Run: husky config set api-url <url>");
10
+ process.exit(1);
11
+ }
12
+ return config;
13
+ }
14
+ // husky department list
15
+ departmentCommand
16
+ .command("list")
17
+ .description("List all departments")
18
+ .option("--json", "Output as JSON")
19
+ .action(async (options) => {
20
+ const config = ensureConfig();
21
+ try {
22
+ const res = await fetch(`${config.apiUrl}/api/departments`, {
23
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
24
+ });
25
+ if (!res.ok) {
26
+ throw new Error(`API error: ${res.status}`);
27
+ }
28
+ const departments = await res.json();
29
+ if (options.json) {
30
+ console.log(JSON.stringify(departments, null, 2));
31
+ }
32
+ else {
33
+ printDepartments(departments);
34
+ }
35
+ }
36
+ catch (error) {
37
+ console.error("Error fetching departments:", error);
38
+ process.exit(1);
39
+ }
40
+ });
41
+ // husky department create <name>
42
+ departmentCommand
43
+ .command("create <name>")
44
+ .description("Create a new department")
45
+ .option("-d, --description <description>", "Department description")
46
+ .option("--json", "Output as JSON")
47
+ .action(async (name, options) => {
48
+ const config = ensureConfig();
49
+ try {
50
+ const res = await fetch(`${config.apiUrl}/api/departments`, {
51
+ method: "POST",
52
+ headers: {
53
+ "Content-Type": "application/json",
54
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
55
+ },
56
+ body: JSON.stringify({
57
+ name,
58
+ description: options.description,
59
+ }),
60
+ });
61
+ if (!res.ok) {
62
+ throw new Error(`API error: ${res.status}`);
63
+ }
64
+ const department = await res.json();
65
+ if (options.json) {
66
+ console.log(JSON.stringify(department, null, 2));
67
+ }
68
+ else {
69
+ console.log(`✓ Created department: ${department.name}`);
70
+ console.log(` ID: ${department.id}`);
71
+ }
72
+ }
73
+ catch (error) {
74
+ console.error("Error creating department:", error);
75
+ process.exit(1);
76
+ }
77
+ });
78
+ // husky department get <id>
79
+ departmentCommand
80
+ .command("get <id>")
81
+ .description("Get department details")
82
+ .option("--json", "Output as JSON")
83
+ .action(async (id, options) => {
84
+ const config = ensureConfig();
85
+ try {
86
+ const res = await fetch(`${config.apiUrl}/api/departments/${id}`, {
87
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
88
+ });
89
+ if (!res.ok) {
90
+ if (res.status === 404) {
91
+ console.error(`Error: Department ${id} not found`);
92
+ }
93
+ else {
94
+ console.error(`Error: API returned ${res.status}`);
95
+ }
96
+ process.exit(1);
97
+ }
98
+ const department = await res.json();
99
+ if (options.json) {
100
+ console.log(JSON.stringify(department, null, 2));
101
+ }
102
+ else {
103
+ printDepartmentDetail(department);
104
+ }
105
+ }
106
+ catch (error) {
107
+ console.error("Error fetching department:", error);
108
+ process.exit(1);
109
+ }
110
+ });
111
+ // husky department update <id>
112
+ departmentCommand
113
+ .command("update <id>")
114
+ .description("Update a department")
115
+ .option("-n, --name <name>", "New name")
116
+ .option("-d, --description <description>", "New description")
117
+ .option("--json", "Output as JSON")
118
+ .action(async (id, options) => {
119
+ const config = ensureConfig();
120
+ // Build update payload
121
+ const updateData = {};
122
+ if (options.name) {
123
+ updateData.name = options.name;
124
+ }
125
+ if (options.description) {
126
+ updateData.description = options.description;
127
+ }
128
+ if (Object.keys(updateData).length === 0) {
129
+ console.error("Error: No update options provided. Use -n or -d");
130
+ process.exit(1);
131
+ }
132
+ try {
133
+ const res = await fetch(`${config.apiUrl}/api/departments/${id}`, {
134
+ method: "PATCH",
135
+ headers: {
136
+ "Content-Type": "application/json",
137
+ ...(config.apiKey ? { "x-api-key": config.apiKey } : {}),
138
+ },
139
+ body: JSON.stringify(updateData),
140
+ });
141
+ if (!res.ok) {
142
+ if (res.status === 404) {
143
+ console.error(`Error: Department ${id} not found`);
144
+ }
145
+ else {
146
+ const errorBody = await res.json().catch(() => ({}));
147
+ console.error(`Error: API returned ${res.status}`, errorBody.error || "");
148
+ }
149
+ process.exit(1);
150
+ }
151
+ const department = await res.json();
152
+ if (options.json) {
153
+ console.log(JSON.stringify(department, null, 2));
154
+ }
155
+ else {
156
+ console.log(`✓ Department updated successfully`);
157
+ console.log(` Name: ${department.name}`);
158
+ if (department.description) {
159
+ console.log(` Description: ${department.description}`);
160
+ }
161
+ }
162
+ }
163
+ catch (error) {
164
+ console.error("Error updating department:", error);
165
+ process.exit(1);
166
+ }
167
+ });
168
+ // husky department delete <id>
169
+ departmentCommand
170
+ .command("delete <id>")
171
+ .description("Delete a department")
172
+ .option("--force", "Skip confirmation")
173
+ .option("--json", "Output as JSON")
174
+ .action(async (id, options) => {
175
+ const config = ensureConfig();
176
+ if (!options.force) {
177
+ console.log("Warning: This will permanently delete the department.");
178
+ console.log("Use --force to confirm deletion.");
179
+ process.exit(1);
180
+ }
181
+ try {
182
+ const res = await fetch(`${config.apiUrl}/api/departments/${id}`, {
183
+ method: "DELETE",
184
+ headers: config.apiKey ? { "x-api-key": config.apiKey } : {},
185
+ });
186
+ if (!res.ok) {
187
+ if (res.status === 404) {
188
+ console.error(`Error: Department ${id} not found`);
189
+ }
190
+ else {
191
+ console.error(`Error: API returned ${res.status}`);
192
+ }
193
+ process.exit(1);
194
+ }
195
+ if (options.json) {
196
+ console.log(JSON.stringify({ deleted: true, id }, null, 2));
197
+ }
198
+ else {
199
+ console.log(`✓ Department deleted`);
200
+ }
201
+ }
202
+ catch (error) {
203
+ console.error("Error deleting department:", error);
204
+ process.exit(1);
205
+ }
206
+ });
207
+ function printDepartments(departments) {
208
+ if (departments.length === 0) {
209
+ console.log("\n No departments found.");
210
+ console.log(" Create one with: husky department create <name>\n");
211
+ return;
212
+ }
213
+ console.log("\n DEPARTMENTS");
214
+ console.log(" " + "─".repeat(70));
215
+ console.log(` ${"ID".padEnd(24)} ${"NAME".padEnd(25)} DESCRIPTION`);
216
+ console.log(" " + "─".repeat(70));
217
+ for (const dept of departments) {
218
+ const truncatedName = dept.name.length > 23 ? dept.name.substring(0, 20) + "..." : dept.name;
219
+ const description = dept.description
220
+ ? (dept.description.length > 30 ? dept.description.substring(0, 27) + "..." : dept.description)
221
+ : "-";
222
+ console.log(` ${dept.id.padEnd(24)} ${truncatedName.padEnd(25)} ${description}`);
223
+ }
224
+ console.log(" " + "─".repeat(70));
225
+ console.log(`\n Total: ${departments.length} departments`);
226
+ console.log("");
227
+ }
228
+ function printDepartmentDetail(department) {
229
+ console.log(`\n Department: ${department.name}`);
230
+ console.log(" " + "─".repeat(50));
231
+ console.log(` ID: ${department.id}`);
232
+ console.log(` Name: ${department.name}`);
233
+ if (department.description) {
234
+ console.log(` Description:`);
235
+ console.log(` ${department.description}`);
236
+ }
237
+ console.log(` Created: ${new Date(department.createdAt).toLocaleString()}`);
238
+ console.log(` Updated: ${new Date(department.updatedAt).toLocaleString()}`);
239
+ console.log("");
240
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const explainCommand: Command;
@@ -0,0 +1,411 @@
1
+ import { Command } from "commander";
2
+ export const explainCommand = new Command("explain")
3
+ .description("Explain CLI commands for AI agents")
4
+ .argument("[command]", "Command to explain (task, roadmap, changelog, config, agent)")
5
+ .action((command) => {
6
+ if (!command) {
7
+ printOverview();
8
+ }
9
+ else {
10
+ switch (command.toLowerCase()) {
11
+ case "task":
12
+ printTaskHelp();
13
+ break;
14
+ case "roadmap":
15
+ printRoadmapHelp();
16
+ break;
17
+ case "changelog":
18
+ printChangelogHelp();
19
+ break;
20
+ case "config":
21
+ printConfigHelp();
22
+ break;
23
+ case "agent":
24
+ printAgentWorkflowHelp();
25
+ break;
26
+ default:
27
+ console.log(`Unknown command: ${command}`);
28
+ console.log("Available: task, roadmap, changelog, config, agent");
29
+ process.exit(1);
30
+ }
31
+ }
32
+ });
33
+ function printOverview() {
34
+ console.log(`
35
+ HUSKY CLI - AI Task Orchestration
36
+
37
+ Available Commands:
38
+ task Task management (list, create, start, complete, qa)
39
+ roadmap Roadmap & feature planning
40
+ changelog Generate changelogs from commits
41
+ config Configure API URL and key
42
+
43
+ Quick Start:
44
+ 1. Configure: husky config set api-url <dashboard-url>
45
+ 2. Set key: husky config set api-key <key>
46
+ 3. Get task: husky task get --id <id>
47
+ 4. Work: husky task status "Working on..."
48
+ 5. Complete: husky task complete --pr <url>
49
+
50
+ For detailed help on a command: husky explain <command>
51
+ For agent workflow guidance: husky explain agent
52
+
53
+ Environment Variables:
54
+ HUSKY_TASK_ID Default task ID for commands (avoids --id flag)
55
+ `);
56
+ }
57
+ function printTaskHelp() {
58
+ console.log(`
59
+ HUSKY TASK COMMANDS
60
+
61
+ Task ID can be passed via --id flag or HUSKY_TASK_ID environment variable.
62
+
63
+ LIST TASKS
64
+ husky task list
65
+ husky task list --status backlog
66
+ husky task list --status in_progress
67
+
68
+ Options:
69
+ -s, --status <status> Filter by status (backlog, in_progress, review, done)
70
+
71
+ GET TASK DETAILS
72
+ husky task get --id <id>
73
+ husky task get --id <id> --json
74
+
75
+ Options:
76
+ --id <id> Task ID (or set HUSKY_TASK_ID)
77
+ --json Output as JSON (useful for parsing)
78
+
79
+ CREATE TASK
80
+ husky task create "Task title"
81
+ husky task create "Task title" -d "Description" --project proj123
82
+
83
+ Options:
84
+ -d, --description <desc> Task description
85
+ --project <projectId> Project ID
86
+ --path <path> Path in project
87
+ -p, --priority <priority> Priority: low, medium, high (default: medium)
88
+
89
+ START TASK
90
+ husky task start <id>
91
+
92
+ Marks task as in_progress and assigns to agent.
93
+
94
+ REPORT STATUS
95
+ husky task status "Working on feature X"
96
+ husky task status "Fixing tests" --id <id>
97
+
98
+ Options:
99
+ --id <id> Task ID (or set HUSKY_TASK_ID)
100
+
101
+ Use this to report progress during task execution.
102
+
103
+ SUBMIT PLAN
104
+ husky task plan --summary "Implementation plan"
105
+ husky task plan --file plan.md
106
+ husky task plan --steps "step1,step2,step3"
107
+ echo "Plan content" | husky task plan --stdin
108
+
109
+ Options:
110
+ --id <id> Task ID (or set HUSKY_TASK_ID)
111
+ --summary <text> Plan summary
112
+ --file <path> Read plan from file
113
+ --stdin Read plan from stdin
114
+ --steps <steps> Comma-separated steps
115
+
116
+ WAIT FOR APPROVAL
117
+ husky task wait-approval
118
+ husky task wait-approval --timeout 3600
119
+
120
+ Options:
121
+ --id <id> Task ID (or set HUSKY_TASK_ID)
122
+ --timeout <seconds> Timeout in seconds (default: 1800)
123
+
124
+ Exit codes:
125
+ 0 = approved
126
+ 1 = rejected
127
+ 2 = timeout
128
+
129
+ COMPLETE TASK
130
+ husky task complete --output "Task completed successfully"
131
+ husky task complete --pr https://github.com/org/repo/pull/123
132
+ husky task complete --error "Failed due to X"
133
+
134
+ Options:
135
+ --id <id> Task ID (or set HUSKY_TASK_ID)
136
+ --output <text> Completion summary
137
+ --pr <url> Pull request URL
138
+ --error <text> Error message (marks task as failed)
139
+
140
+ DONE (Simple completion)
141
+ husky task done <id>
142
+ husky task done <id> --pr <url>
143
+
144
+ Options:
145
+ --pr <url> Link to PR
146
+
147
+ QA VALIDATION COMMANDS
148
+
149
+ husky task qa-start [--max-iterations <n>] [--no-auto-fix]
150
+ Start QA validation for a task.
151
+
152
+ husky task qa-status [--json]
153
+ Get current QA validation status.
154
+
155
+ husky task qa-approve [--notes <text>]
156
+ Manually approve QA.
157
+
158
+ husky task qa-reject [--notes <text>]
159
+ Manually reject QA.
160
+
161
+ husky task qa-iteration --iteration <n> --status <status> [--issues <json>]
162
+ Add a QA iteration result (for automated agents).
163
+ Status: approved, rejected, error
164
+
165
+ husky task qa-escalate
166
+ Escalate QA to human review.
167
+
168
+ MERGE CONFLICT RESOLUTION
169
+ husky task merge-conflict --file <path> --ours <content> --theirs <content>
170
+ husky task merge-conflict --file <path> --ours-file ours.txt --theirs-file theirs.txt
171
+
172
+ Options:
173
+ --file <path> Path to conflicted file (required)
174
+ --ours <content> Content from current branch
175
+ --theirs <content> Content from incoming branch
176
+ --base <content> Content from common ancestor (3-way merge)
177
+ --context <text> Additional context about the merge
178
+ --json Output as JSON
179
+ --ours-file <path> Read ours content from file
180
+ --theirs-file <path> Read theirs content from file
181
+ --base-file <path> Read base content from file
182
+ `);
183
+ }
184
+ function printRoadmapHelp() {
185
+ console.log(`
186
+ HUSKY ROADMAP COMMANDS
187
+
188
+ LIST ROADMAPS
189
+ husky roadmap list
190
+ husky roadmap list --type global
191
+ husky roadmap list --project proj123 --json
192
+
193
+ Options:
194
+ --type <type> Filter by type (global, project)
195
+ --project <id> Filter by project ID
196
+ --json Output as JSON
197
+
198
+ GET ROADMAP
199
+ husky roadmap get <id>
200
+ husky roadmap get <id> --json
201
+
202
+ Options:
203
+ --json Output as JSON
204
+
205
+ CREATE ROADMAP
206
+ husky roadmap create "Roadmap name"
207
+ husky roadmap create "Project roadmap" --type project --project proj123
208
+
209
+ Options:
210
+ --type <type> Roadmap type: global, project (default: global)
211
+ --project <projectId> Project ID (required for project type)
212
+ --vision <vision> Product vision
213
+ --audience <audience> Primary target audience
214
+
215
+ ADD PHASE
216
+ husky roadmap add-phase <roadmapId> "Phase name"
217
+ husky roadmap add-phase rm123 "MVP" --description "Minimum viable product"
218
+
219
+ Options:
220
+ --description <desc> Phase description
221
+
222
+ ADD FEATURE
223
+ husky roadmap add-feature <roadmapId> <phaseId> "Feature title"
224
+ husky roadmap add-feature rm123 ph456 "User auth" --priority must --complexity high
225
+
226
+ Options:
227
+ --description <desc> Feature description
228
+ --priority <priority> Priority: must, should, could, wont (default: should)
229
+ --complexity <complexity> Complexity: low, medium, high (default: medium)
230
+ --impact <impact> Impact: low, medium, high (default: medium)
231
+
232
+ GENERATE WITH AI
233
+ husky roadmap generate <roadmapId>
234
+ husky roadmap generate rm123 --context "E-commerce platform"
235
+
236
+ Options:
237
+ --context <context> Additional context for AI generation
238
+ --project <projectId> Project ID for context
239
+
240
+ DELETE ROADMAP
241
+ husky roadmap delete <id> --force
242
+
243
+ Options:
244
+ --force Skip confirmation (required)
245
+ `);
246
+ }
247
+ function printChangelogHelp() {
248
+ console.log(`
249
+ HUSKY CHANGELOG COMMANDS
250
+
251
+ GENERATE FROM GIT COMMITS
252
+ husky changelog generate --version 1.0.0 --project proj123
253
+ husky changelog generate --since v0.9.0 --until HEAD --version 1.0.0 --project proj123
254
+ husky changelog generate --version 1.0.0 --project proj123 --dry-run
255
+
256
+ Options:
257
+ --since <ref> Git ref to start from (tag, commit, branch)
258
+ --until <ref> Git ref to end at (default: HEAD)
259
+ -v, --version <ver> Version for changelog (required)
260
+ -p, --project <id> Project ID (required)
261
+ --dry-run Preview commits without generating
262
+
263
+ LIST CHANGELOGS
264
+ husky changelog list
265
+ husky changelog list --project proj123 --json
266
+
267
+ Options:
268
+ -p, --project <id> Filter by project ID
269
+ --json Output as JSON
270
+
271
+ SHOW CHANGELOG
272
+ husky changelog show <id>
273
+ husky changelog show <id> --json
274
+ husky changelog show <id> --markdown
275
+
276
+ Options:
277
+ --json Output as JSON
278
+ --markdown Output as Markdown
279
+
280
+ PUBLISH CHANGELOG
281
+ husky changelog publish <id>
282
+
283
+ Changes status from draft to published.
284
+
285
+ DELETE CHANGELOG
286
+ husky changelog delete <id> --yes
287
+
288
+ Options:
289
+ -y, --yes Skip confirmation (required)
290
+ `);
291
+ }
292
+ function printConfigHelp() {
293
+ console.log(`
294
+ HUSKY CONFIG COMMANDS
295
+
296
+ SET CONFIGURATION
297
+ husky config set api-url https://dashboard.example.com
298
+ husky config set api-key your-api-key
299
+
300
+ Available keys:
301
+ api-url Dashboard API URL (required for all commands)
302
+ api-key API key for authentication
303
+
304
+ GET CONFIGURATION
305
+ husky config get api-url
306
+ husky config get api-key
307
+
308
+ LIST ALL CONFIGURATION
309
+ husky config list
310
+
311
+ Shows all configured values (api-key is masked).
312
+
313
+ Configuration is stored in: ~/.husky/config.json
314
+ `);
315
+ }
316
+ function printAgentWorkflowHelp() {
317
+ console.log(`
318
+ HUSKY AGENT WORKFLOW
319
+
320
+ This guide explains how an AI agent should use the Husky CLI to work on tasks.
321
+
322
+ TYPICAL WORKFLOW
323
+
324
+ 1. SETUP (once per session)
325
+ export HUSKY_TASK_ID="<task-id>"
326
+
327
+ 2. GET TASK DETAILS
328
+ husky task get --json
329
+
330
+ 3. REPORT PROGRESS (during work)
331
+ husky task status "Analyzing codebase..."
332
+ husky task status "Implementing feature X..."
333
+ husky task status "Running tests..."
334
+
335
+ 4. SUBMIT PLAN (for complex tasks)
336
+ husky task plan --summary "Will implement X by doing Y"
337
+ husky task wait-approval
338
+
339
+ 5. COMPLETE TASK
340
+ husky task complete --output "Implemented feature X" --pr https://github.com/...
341
+
342
+ Or if failed:
343
+ husky task complete --error "Could not complete due to X"
344
+
345
+ EXAMPLE: FULL TASK EXECUTION
346
+
347
+ # Setup
348
+ export HUSKY_TASK_ID="abc123"
349
+
350
+ # Check task details
351
+ husky task get
352
+
353
+ # Start working
354
+ husky task status "Starting task..."
355
+
356
+ # Do the work (code changes, etc.)
357
+ husky task status "Implementing changes..."
358
+
359
+ # If plan approval needed
360
+ husky task plan --summary "Will add new endpoint and update tests"
361
+ husky task wait-approval
362
+
363
+ # Continue work after approval
364
+ husky task status "Running tests..."
365
+
366
+ # Complete
367
+ husky task complete --output "Added /api/users endpoint" --pr "https://..."
368
+
369
+ QA WORKFLOW
370
+
371
+ After implementation, run QA validation:
372
+
373
+ husky task qa-start
374
+ husky task qa-status
375
+
376
+ # If issues found, fix and report
377
+ husky task qa-iteration --iteration 1 --status rejected --issues '[{"type":"major","title":"Test failure"}]'
378
+
379
+ # After fixes
380
+ husky task qa-iteration --iteration 2 --status approved
381
+
382
+ # If stuck, escalate
383
+ husky task qa-escalate
384
+
385
+ MERGE CONFLICT RESOLUTION
386
+
387
+ When encountering git merge conflicts:
388
+
389
+ husky task merge-conflict \\
390
+ --file src/app.ts \\
391
+ --ours-file .git/MERGE_HEAD/app.ts \\
392
+ --theirs-file .git/HEAD/app.ts \\
393
+ --context "Merging feature branch"
394
+
395
+ BEST PRACTICES
396
+
397
+ 1. Always set HUSKY_TASK_ID at the start
398
+ 2. Report status frequently (every significant step)
399
+ 3. Use --json flag when parsing output programmatically
400
+ 4. Submit plans for tasks that modify multiple files
401
+ 5. Include PR URL when completing tasks
402
+ 6. Use qa-escalate if stuck after max iterations
403
+
404
+ EXIT CODES
405
+
406
+ Most commands use standard exit codes:
407
+ 0 = Success
408
+ 1 = Error/Failure
409
+ 2 = Timeout (for wait-approval)
410
+ `);
411
+ }
@@ -0,0 +1,2 @@
1
+ import { Command } from "commander";
2
+ export declare const ideaCommand: Command;