@simonfestl/husky-cli 1.7.0 ā 1.9.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 +4 -0
- package/dist/commands/biz/tickets.js +33 -3
- package/dist/commands/brain.js +279 -8
- package/dist/commands/chat.js +108 -0
- package/dist/commands/config.d.ts +4 -0
- package/dist/commands/config.js +23 -13
- package/dist/commands/e2e.js +107 -0
- package/dist/commands/image.d.ts +2 -0
- package/dist/commands/image.js +141 -0
- package/dist/commands/llm-context.js +69 -2
- package/dist/commands/task.js +100 -19
- package/dist/commands/youtube.d.ts +2 -0
- package/dist/commands/youtube.js +178 -0
- package/dist/index.js +4 -0
- package/dist/lib/agent-identity.d.ts +25 -0
- package/dist/lib/agent-identity.js +73 -0
- package/dist/lib/biz/agent-brain.d.ts +63 -1
- package/dist/lib/biz/agent-brain.js +316 -4
- package/dist/lib/biz/learning-capture.d.ts +42 -0
- package/dist/lib/biz/learning-capture.js +107 -0
- package/dist/lib/biz/pii-filter.d.ts +34 -0
- package/dist/lib/biz/pii-filter.js +125 -0
- package/dist/lib/biz/qdrant.d.ts +2 -1
- package/dist/lib/biz/qdrant.js +17 -6
- package/dist/lib/biz/sop-generator.d.ts +39 -0
- package/dist/lib/biz/sop-generator.js +131 -0
- package/dist/lib/error-hints.d.ts +69 -0
- package/dist/lib/error-hints.js +164 -0
- package/dist/lib/permissions.js +3 -0
- package/package.json +7 -2
package/dist/lib/biz/qdrant.js
CHANGED
|
@@ -15,21 +15,23 @@ export class QdrantClient {
|
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
17
|
* Create client from Husky config
|
|
18
|
-
* Priority: PROD_* env vars >
|
|
18
|
+
* Priority: HUSKY_QDRANT_URL (VM standard) > PROD_* env vars > config > localhost
|
|
19
19
|
*/
|
|
20
20
|
static fromConfig() {
|
|
21
21
|
const config = getConfig();
|
|
22
22
|
const env = process.env.HUSKY_ENV || 'PROD';
|
|
23
23
|
const qdrantConfig = {
|
|
24
|
-
|
|
24
|
+
// Priority: HUSKY_QDRANT_URL (VM standard) > PROD_QDRANT_URL > config > localhost
|
|
25
|
+
url: process.env.HUSKY_QDRANT_URL || process.env[`${env}_QDRANT_URL`] || process.env.QDRANT_URL || config.qdrantUrl || 'http://localhost:6333',
|
|
26
|
+
// API key optional - internal Qdrant VM is VPC secured
|
|
25
27
|
apiKey: process.env[`${env}_QDRANT_API_KEY`] || process.env.QDRANT_API_KEY || config.qdrantApiKey,
|
|
26
28
|
};
|
|
27
29
|
if (!qdrantConfig.url || qdrantConfig.url === 'http://localhost:6333') {
|
|
28
|
-
if (!process.env.QDRANT_URL && !process.env[`${env}_QDRANT_URL`]) {
|
|
30
|
+
if (!process.env.QDRANT_URL && !process.env[`${env}_QDRANT_URL`] && !process.env.HUSKY_QDRANT_URL) {
|
|
29
31
|
throw new Error('Missing Qdrant URL. Configure with:\n' +
|
|
30
|
-
' husky config set qdrant-url
|
|
31
|
-
'
|
|
32
|
-
'
|
|
32
|
+
' husky config set qdrant-url http://10.132.0.46:6333\n' +
|
|
33
|
+
'Or set env var: HUSKY_QDRANT_URL\n\n' +
|
|
34
|
+
'Note: Internal Qdrant VM - no API key needed (VPC secured)');
|
|
33
35
|
}
|
|
34
36
|
}
|
|
35
37
|
return new QdrantClient(qdrantConfig);
|
|
@@ -153,6 +155,15 @@ export class QdrantClient {
|
|
|
153
155
|
body: JSON.stringify({ points: ids }),
|
|
154
156
|
});
|
|
155
157
|
}
|
|
158
|
+
async setPayload(collectionName, pointId, payload) {
|
|
159
|
+
await this.request(`/collections/${collectionName}/points/payload?wait=true`, {
|
|
160
|
+
method: 'POST',
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
points: [pointId],
|
|
163
|
+
payload,
|
|
164
|
+
}),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
156
167
|
async count(collectionName) {
|
|
157
168
|
const info = await this.getCollection(collectionName);
|
|
158
169
|
return info.pointsCount;
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SOP (Standard Operating Procedure) Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates SOPs from agent learnings using LLM.
|
|
5
|
+
* Leverages PII-free embeddings for safe processing.
|
|
6
|
+
*/
|
|
7
|
+
import { AgentType } from './agent-brain.js';
|
|
8
|
+
export interface SOPGenerationOptions {
|
|
9
|
+
topic: string;
|
|
10
|
+
agentType?: AgentType;
|
|
11
|
+
minMemories?: number;
|
|
12
|
+
format?: 'markdown' | 'json';
|
|
13
|
+
}
|
|
14
|
+
export interface SOPSection {
|
|
15
|
+
title: string;
|
|
16
|
+
content: string;
|
|
17
|
+
examples?: string[];
|
|
18
|
+
}
|
|
19
|
+
export interface SOP {
|
|
20
|
+
title: string;
|
|
21
|
+
topic: string;
|
|
22
|
+
sections: SOPSection[];
|
|
23
|
+
sourceCount: number;
|
|
24
|
+
generatedAt: string;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Generate SOP from learnings
|
|
28
|
+
*
|
|
29
|
+
* TODO: In future, use Vertex AI/Gemini to:
|
|
30
|
+
* 1. Recall relevant memories by topic
|
|
31
|
+
* 2. Group and cluster similar learnings
|
|
32
|
+
* 3. Extract patterns and best practices
|
|
33
|
+
* 4. Generate structured SOP document
|
|
34
|
+
*/
|
|
35
|
+
export declare function generateSOP(agentId: string, options: SOPGenerationOptions): Promise<SOP>;
|
|
36
|
+
/**
|
|
37
|
+
* Format SOP as Markdown
|
|
38
|
+
*/
|
|
39
|
+
export declare function formatSOPAsMarkdown(sop: SOP): string;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SOP (Standard Operating Procedure) Generator
|
|
3
|
+
*
|
|
4
|
+
* Generates SOPs from agent learnings using LLM.
|
|
5
|
+
* Leverages PII-free embeddings for safe processing.
|
|
6
|
+
*/
|
|
7
|
+
import { AgentBrain } from './agent-brain.js';
|
|
8
|
+
/**
|
|
9
|
+
* Generate SOP from learnings
|
|
10
|
+
*
|
|
11
|
+
* TODO: In future, use Vertex AI/Gemini to:
|
|
12
|
+
* 1. Recall relevant memories by topic
|
|
13
|
+
* 2. Group and cluster similar learnings
|
|
14
|
+
* 3. Extract patterns and best practices
|
|
15
|
+
* 4. Generate structured SOP document
|
|
16
|
+
*/
|
|
17
|
+
export async function generateSOP(agentId, options) {
|
|
18
|
+
const { topic, agentType, minMemories = 5, format = 'markdown' } = options;
|
|
19
|
+
// Initialize brain
|
|
20
|
+
const brain = new AgentBrain({ agentId, agentType });
|
|
21
|
+
// Recall memories related to topic
|
|
22
|
+
const results = await brain.recall(topic, 50, 0.3);
|
|
23
|
+
if (results.length < minMemories) {
|
|
24
|
+
throw new Error(`Not enough learnings found for topic "${topic}" (found ${results.length}, need ${minMemories})`);
|
|
25
|
+
}
|
|
26
|
+
// Group memories by tags
|
|
27
|
+
const grouped = groupMemoriesByTags(results.map(r => r.memory));
|
|
28
|
+
// Generate SOP sections (basic version - TODO: use LLM for better structure)
|
|
29
|
+
const sections = [];
|
|
30
|
+
for (const [tag, memories] of Object.entries(grouped)) {
|
|
31
|
+
sections.push({
|
|
32
|
+
title: formatTagAsTitle(tag),
|
|
33
|
+
content: summarizeMemories(memories),
|
|
34
|
+
examples: memories.slice(0, 3).map(m => m.content),
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
return {
|
|
38
|
+
title: `SOP: ${topic}`,
|
|
39
|
+
topic,
|
|
40
|
+
sections,
|
|
41
|
+
sourceCount: results.length,
|
|
42
|
+
generatedAt: new Date().toISOString(),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Group memories by tags
|
|
47
|
+
*/
|
|
48
|
+
function groupMemoriesByTags(memories) {
|
|
49
|
+
const groups = {};
|
|
50
|
+
for (const memory of memories) {
|
|
51
|
+
for (const tag of memory.tags) {
|
|
52
|
+
// Skip task/project specific tags
|
|
53
|
+
if (tag.startsWith('task:') || tag.startsWith('project:')) {
|
|
54
|
+
continue;
|
|
55
|
+
}
|
|
56
|
+
if (!groups[tag]) {
|
|
57
|
+
groups[tag] = [];
|
|
58
|
+
}
|
|
59
|
+
groups[tag].push(memory);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
// Sort by number of memories
|
|
63
|
+
return Object.fromEntries(Object.entries(groups).sort((a, b) => b[1].length - a[1].length));
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Format tag as section title
|
|
67
|
+
*/
|
|
68
|
+
function formatTagAsTitle(tag) {
|
|
69
|
+
// Remove prefixes
|
|
70
|
+
const cleaned = tag.replace(/^(type|source|category):/, '');
|
|
71
|
+
// Capitalize
|
|
72
|
+
return cleaned
|
|
73
|
+
.split(/[-_]/)
|
|
74
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
75
|
+
.join(' ');
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Summarize memories into a paragraph
|
|
79
|
+
*/
|
|
80
|
+
function summarizeMemories(memories) {
|
|
81
|
+
// Basic summarization (TODO: use LLM for better summary)
|
|
82
|
+
if (memories.length === 1) {
|
|
83
|
+
return memories[0].content;
|
|
84
|
+
}
|
|
85
|
+
const commonThemes = extractCommonThemes(memories);
|
|
86
|
+
return `Based on ${memories.length} learnings: ${commonThemes}`;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Extract common themes from memories
|
|
90
|
+
*/
|
|
91
|
+
function extractCommonThemes(memories) {
|
|
92
|
+
// Simple word frequency analysis (TODO: use LLM for semantic analysis)
|
|
93
|
+
const words = {};
|
|
94
|
+
for (const memory of memories) {
|
|
95
|
+
const tokens = memory.content
|
|
96
|
+
.toLowerCase()
|
|
97
|
+
.split(/\W+/)
|
|
98
|
+
.filter(w => w.length > 4); // Only words > 4 chars
|
|
99
|
+
for (const word of tokens) {
|
|
100
|
+
words[word] = (words[word] || 0) + 1;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
// Get top 5 words
|
|
104
|
+
const topWords = Object.entries(words)
|
|
105
|
+
.sort((a, b) => b[1] - a[1])
|
|
106
|
+
.slice(0, 5)
|
|
107
|
+
.map(([word]) => word);
|
|
108
|
+
return `Common themes include: ${topWords.join(', ')}`;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Format SOP as Markdown
|
|
112
|
+
*/
|
|
113
|
+
export function formatSOPAsMarkdown(sop) {
|
|
114
|
+
let md = `# ${sop.title}\n\n`;
|
|
115
|
+
md += `**Topic:** ${sop.topic} \n`;
|
|
116
|
+
md += `**Generated:** ${new Date(sop.generatedAt).toLocaleString()} \n`;
|
|
117
|
+
md += `**Sources:** ${sop.sourceCount} learnings \n\n`;
|
|
118
|
+
md += `---\n\n`;
|
|
119
|
+
for (const section of sop.sections) {
|
|
120
|
+
md += `## ${section.title}\n\n`;
|
|
121
|
+
md += `${section.content}\n\n`;
|
|
122
|
+
if (section.examples && section.examples.length > 0) {
|
|
123
|
+
md += `### Examples\n\n`;
|
|
124
|
+
for (const example of section.examples) {
|
|
125
|
+
md += `- ${example}\n`;
|
|
126
|
+
}
|
|
127
|
+
md += `\n`;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
return md;
|
|
131
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Hints System
|
|
3
|
+
*
|
|
4
|
+
* Provides helpful hints and references to `husky explain` when users encounter errors.
|
|
5
|
+
* This makes the CLI more user-friendly by guiding users to relevant documentation.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Map of error categories to their corresponding `husky explain` topics
|
|
9
|
+
*/
|
|
10
|
+
export declare enum ExplainTopic {
|
|
11
|
+
TASK = "task",
|
|
12
|
+
CONFIG = "config",
|
|
13
|
+
ROADMAP = "roadmap",
|
|
14
|
+
CHANGELOG = "changelog",
|
|
15
|
+
AGENT = "agent"
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Print an error message with an automatic hint based on content
|
|
19
|
+
*/
|
|
20
|
+
export declare function errorWithAutoHint(message: string, exitCode?: number): never;
|
|
21
|
+
/**
|
|
22
|
+
* Print an error message with a specific hint topic
|
|
23
|
+
*/
|
|
24
|
+
export declare function errorWithHint(message: string, topic: ExplainTopic, customHint?: string, exitCode?: number): never;
|
|
25
|
+
/**
|
|
26
|
+
* Print an error message with no hint (for errors where no help is available)
|
|
27
|
+
*/
|
|
28
|
+
export declare function errorWithoutHint(message: string, exitCode?: number): never;
|
|
29
|
+
/**
|
|
30
|
+
* Predefined error helpers for common scenarios
|
|
31
|
+
*/
|
|
32
|
+
export declare const ErrorHelpers: {
|
|
33
|
+
/**
|
|
34
|
+
* Error: Task ID not provided
|
|
35
|
+
*/
|
|
36
|
+
missingTaskId: () => never;
|
|
37
|
+
/**
|
|
38
|
+
* Error: API URL not configured
|
|
39
|
+
*/
|
|
40
|
+
missingApiUrl: () => never;
|
|
41
|
+
/**
|
|
42
|
+
* Error: API key not configured
|
|
43
|
+
*/
|
|
44
|
+
missingApiKey: () => never;
|
|
45
|
+
/**
|
|
46
|
+
* Error: Both API URL and key not configured
|
|
47
|
+
*/
|
|
48
|
+
missingConfig: () => never;
|
|
49
|
+
/**
|
|
50
|
+
* Error: Permission denied
|
|
51
|
+
*/
|
|
52
|
+
permissionDenied: (operation: string) => never;
|
|
53
|
+
/**
|
|
54
|
+
* Error: Invalid task status
|
|
55
|
+
*/
|
|
56
|
+
invalidTaskStatus: (status: string) => never;
|
|
57
|
+
/**
|
|
58
|
+
* Error: Task operation failed
|
|
59
|
+
*/
|
|
60
|
+
taskOperationFailed: (operation: string, reason: string) => never;
|
|
61
|
+
/**
|
|
62
|
+
* Error: API request failed
|
|
63
|
+
*/
|
|
64
|
+
apiRequestFailed: (status: number, message: string) => never;
|
|
65
|
+
};
|
|
66
|
+
/**
|
|
67
|
+
* Format a warning message with a hint (non-fatal)
|
|
68
|
+
*/
|
|
69
|
+
export declare function warningWithHint(message: string, topic: ExplainTopic, customHint?: string): void;
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error Hints System
|
|
3
|
+
*
|
|
4
|
+
* Provides helpful hints and references to `husky explain` when users encounter errors.
|
|
5
|
+
* This makes the CLI more user-friendly by guiding users to relevant documentation.
|
|
6
|
+
*/
|
|
7
|
+
/**
|
|
8
|
+
* Map of error categories to their corresponding `husky explain` topics
|
|
9
|
+
*/
|
|
10
|
+
export var ExplainTopic;
|
|
11
|
+
(function (ExplainTopic) {
|
|
12
|
+
ExplainTopic["TASK"] = "task";
|
|
13
|
+
ExplainTopic["CONFIG"] = "config";
|
|
14
|
+
ExplainTopic["ROADMAP"] = "roadmap";
|
|
15
|
+
ExplainTopic["CHANGELOG"] = "changelog";
|
|
16
|
+
ExplainTopic["AGENT"] = "agent";
|
|
17
|
+
})(ExplainTopic || (ExplainTopic = {}));
|
|
18
|
+
const ERROR_PATTERNS = [
|
|
19
|
+
{
|
|
20
|
+
keywords: ["task id", "HUSKY_TASK_ID", "task status", "task complete", "task start"],
|
|
21
|
+
topic: ExplainTopic.TASK,
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
keywords: ["api url", "api key", "not configured", "config set", "authentication"],
|
|
25
|
+
topic: ExplainTopic.CONFIG,
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
keywords: ["roadmap", "phase", "feature"],
|
|
29
|
+
topic: ExplainTopic.ROADMAP,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
keywords: ["changelog", "version", "commits"],
|
|
33
|
+
topic: ExplainTopic.CHANGELOG,
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
keywords: ["workflow", "agent", "session"],
|
|
37
|
+
topic: ExplainTopic.AGENT,
|
|
38
|
+
},
|
|
39
|
+
];
|
|
40
|
+
/**
|
|
41
|
+
* Detect which explain topic is most relevant based on error message
|
|
42
|
+
*/
|
|
43
|
+
function detectExplainTopic(message) {
|
|
44
|
+
const lowerMessage = message.toLowerCase();
|
|
45
|
+
for (const pattern of ERROR_PATTERNS) {
|
|
46
|
+
if (pattern.keywords.some(keyword => lowerMessage.includes(keyword))) {
|
|
47
|
+
return pattern.topic;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Format a hint message for a specific explain topic
|
|
54
|
+
*/
|
|
55
|
+
function formatHint(topic, customHint) {
|
|
56
|
+
if (customHint) {
|
|
57
|
+
return `\nš” Hint: ${customHint}\n Run: husky explain ${topic}`;
|
|
58
|
+
}
|
|
59
|
+
const hints = {
|
|
60
|
+
[ExplainTopic.TASK]: "For task workflow help",
|
|
61
|
+
[ExplainTopic.CONFIG]: "For configuration help",
|
|
62
|
+
[ExplainTopic.ROADMAP]: "For roadmap commands help",
|
|
63
|
+
[ExplainTopic.CHANGELOG]: "For changelog commands help",
|
|
64
|
+
[ExplainTopic.AGENT]: "For agent workflow examples",
|
|
65
|
+
};
|
|
66
|
+
return `\nš” ${hints[topic]}: husky explain ${topic}`;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Print an error message with an automatic hint based on content
|
|
70
|
+
*/
|
|
71
|
+
export function errorWithAutoHint(message, exitCode = 1) {
|
|
72
|
+
console.error(`Error: ${message}`);
|
|
73
|
+
const topic = detectExplainTopic(message);
|
|
74
|
+
if (topic) {
|
|
75
|
+
console.error(formatHint(topic));
|
|
76
|
+
}
|
|
77
|
+
process.exit(exitCode);
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Print an error message with a specific hint topic
|
|
81
|
+
*/
|
|
82
|
+
export function errorWithHint(message, topic, customHint, exitCode = 1) {
|
|
83
|
+
console.error(`Error: ${message}`);
|
|
84
|
+
console.error(formatHint(topic, customHint));
|
|
85
|
+
process.exit(exitCode);
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Print an error message with no hint (for errors where no help is available)
|
|
89
|
+
*/
|
|
90
|
+
export function errorWithoutHint(message, exitCode = 1) {
|
|
91
|
+
console.error(`Error: ${message}`);
|
|
92
|
+
process.exit(exitCode);
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Predefined error helpers for common scenarios
|
|
96
|
+
*/
|
|
97
|
+
export const ErrorHelpers = {
|
|
98
|
+
/**
|
|
99
|
+
* Error: Task ID not provided
|
|
100
|
+
*/
|
|
101
|
+
missingTaskId: () => {
|
|
102
|
+
errorWithHint("Task ID required. Use --id or set HUSKY_TASK_ID environment variable.", ExplainTopic.TASK, "Learn about task ID usage and environment variables");
|
|
103
|
+
},
|
|
104
|
+
/**
|
|
105
|
+
* Error: API URL not configured
|
|
106
|
+
*/
|
|
107
|
+
missingApiUrl: () => {
|
|
108
|
+
errorWithHint("API URL not configured. Run: husky config set api-url <url>", ExplainTopic.CONFIG, "Learn how to configure the CLI");
|
|
109
|
+
},
|
|
110
|
+
/**
|
|
111
|
+
* Error: API key not configured
|
|
112
|
+
*/
|
|
113
|
+
missingApiKey: () => {
|
|
114
|
+
errorWithHint("API key not configured. Run: husky config set api-key <key>", ExplainTopic.CONFIG, "Learn how to configure authentication");
|
|
115
|
+
},
|
|
116
|
+
/**
|
|
117
|
+
* Error: Both API URL and key not configured
|
|
118
|
+
*/
|
|
119
|
+
missingConfig: () => {
|
|
120
|
+
errorWithHint("API URL and key required. Run: husky config test", ExplainTopic.CONFIG, "Learn about CLI configuration");
|
|
121
|
+
},
|
|
122
|
+
/**
|
|
123
|
+
* Error: Permission denied
|
|
124
|
+
*/
|
|
125
|
+
permissionDenied: (operation) => {
|
|
126
|
+
errorWithAutoHint(`Permission denied: ${operation}`);
|
|
127
|
+
},
|
|
128
|
+
/**
|
|
129
|
+
* Error: Invalid task status
|
|
130
|
+
*/
|
|
131
|
+
invalidTaskStatus: (status) => {
|
|
132
|
+
errorWithHint(`Invalid status: ${status}. Valid statuses: backlog, in_progress, review, done`, ExplainTopic.TASK, "See all available task statuses");
|
|
133
|
+
},
|
|
134
|
+
/**
|
|
135
|
+
* Error: Task operation failed
|
|
136
|
+
*/
|
|
137
|
+
taskOperationFailed: (operation, reason) => {
|
|
138
|
+
errorWithHint(`Failed to ${operation}: ${reason}`, ExplainTopic.TASK, "Learn about task management");
|
|
139
|
+
},
|
|
140
|
+
/**
|
|
141
|
+
* Error: API request failed
|
|
142
|
+
*/
|
|
143
|
+
apiRequestFailed: (status, message) => {
|
|
144
|
+
if (status === 401) {
|
|
145
|
+
errorWithHint("Authentication failed. Check your API key.", ExplainTopic.CONFIG, "Learn how to configure authentication");
|
|
146
|
+
}
|
|
147
|
+
else if (status === 403) {
|
|
148
|
+
errorWithAutoHint(`Permission denied: ${message}`);
|
|
149
|
+
}
|
|
150
|
+
else if (status === 404) {
|
|
151
|
+
errorWithoutHint(`Resource not found: ${message}`);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
errorWithAutoHint(`API error (${status}): ${message}`);
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Format a warning message with a hint (non-fatal)
|
|
160
|
+
*/
|
|
161
|
+
export function warningWithHint(message, topic, customHint) {
|
|
162
|
+
console.warn(`ā Warning: ${message}`);
|
|
163
|
+
console.warn(formatHint(topic, customHint).replace("š”", "ā¹ļø"));
|
|
164
|
+
}
|
package/dist/lib/permissions.js
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
* Permissions are fetched from the API and cached locally.
|
|
6
6
|
*/
|
|
7
7
|
import { getConfig, hasPermission, getRole, fetchAndCacheRole, clearRoleCache } from "../commands/config.js";
|
|
8
|
+
import { ExplainTopic } from "./error-hints.js";
|
|
8
9
|
/**
|
|
9
10
|
* Check if current user has a specific permission.
|
|
10
11
|
* Uses cached permissions from config.
|
|
@@ -27,6 +28,7 @@ export function requirePermission(permission) {
|
|
|
27
28
|
else {
|
|
28
29
|
console.error("Run 'husky config test' to refresh your role and permissions.");
|
|
29
30
|
}
|
|
31
|
+
console.error(`\nš” For configuration help: husky explain ${ExplainTopic.CONFIG}`);
|
|
30
32
|
process.exit(1);
|
|
31
33
|
}
|
|
32
34
|
}
|
|
@@ -43,6 +45,7 @@ export function requireAnyPermission(permissions) {
|
|
|
43
45
|
if (config.role) {
|
|
44
46
|
console.error(`Your role (${config.role}) does not have these permissions.`);
|
|
45
47
|
}
|
|
48
|
+
console.error(`\nš” For configuration help: husky explain ${ExplainTopic.CONFIG}`);
|
|
46
49
|
process.exit(1);
|
|
47
50
|
}
|
|
48
51
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonfestl/husky-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "CLI for Huskyv0 Task Orchestration with Claude Agent SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -20,9 +20,14 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@anthropic-ai/claude-code": "^1.0.0",
|
|
23
|
+
"@google-cloud/vertexai": "^1.10.0",
|
|
24
|
+
"@google/generative-ai": "^0.24.1",
|
|
23
25
|
"@inquirer/prompts": "^8.1.0",
|
|
24
26
|
"commander": "^12.1.0",
|
|
25
|
-
"firebase-admin": "^13.6.0"
|
|
27
|
+
"firebase-admin": "^13.6.0",
|
|
28
|
+
"sharp": "^0.34.5",
|
|
29
|
+
"youtube-transcript": "^1.2.1",
|
|
30
|
+
"zod": "^4.3.5"
|
|
26
31
|
},
|
|
27
32
|
"devDependencies": {
|
|
28
33
|
"@types/node": "^22",
|