@simonfestl/husky-cli 1.25.1 → 1.25.3

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.
@@ -7,7 +7,10 @@ function toDate(value) {
7
7
  return value instanceof Date ? value : new Date(value);
8
8
  }
9
9
  function createBrain(agentId, agentType, options) {
10
- if (options?.useApi || (shouldUseApi() && options?.kb)) {
10
+ // Use API if:
11
+ // 1. Explicitly requested via --use-api
12
+ // 2. shouldUseApi() returns true (no Qdrant configured or session token available)
13
+ if (options?.useApi || shouldUseApi()) {
11
14
  return new ApiBrain({
12
15
  agentId,
13
16
  agentType: isValidAgentType(agentType) ? agentType : undefined,
@@ -698,112 +701,6 @@ brainCommand
698
701
  process.exit(1);
699
702
  }
700
703
  });
701
- // ============================================================================
702
- // Auto-Brain: Hook Integration Commands
703
- // ============================================================================
704
- brainCommand
705
- .command("auto-recall <prompt>")
706
- .description("Automatically search brain for relevant memories based on user prompt (for hook integration)")
707
- .option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
708
- .option("-l, --limit <num>", "Max results", "3")
709
- .option("-m, --min-score <score>", "Minimum similarity score (0-1)", "0.6")
710
- .option("--agent-type <type>", `Agent type for database selection (${AGENT_TYPES.join(", ")})`)
711
- .option("--format <format>", "Output format (hint, json, markdown)", "hint")
712
- .option("--quiet", "Suppress output if no results found")
713
- .action(async (prompt, options) => {
714
- try {
715
- // Skip very short prompts (likely not meaningful queries)
716
- if (prompt.length < 10) {
717
- if (!options.quiet) {
718
- console.log("");
719
- }
720
- return;
721
- }
722
- const brain = createBrain(options.agent, options.agentType);
723
- const results = await brain.recall(prompt, parseInt(options.limit, 10), parseFloat(options.minScore));
724
- if (results.length === 0) {
725
- if (!options.quiet) {
726
- console.log("");
727
- }
728
- return;
729
- }
730
- if (options.format === "json") {
731
- console.log(JSON.stringify({ success: true, results }));
732
- return;
733
- }
734
- if (options.format === "markdown") {
735
- console.log("\n## 🧠 Brain Recall - Relevante Erinnerungen\n");
736
- for (const r of results) {
737
- const tags = r.memory.tags.length > 0 ? ` (Tags: ${r.memory.tags.join(", ")})` : "";
738
- console.log(`- **[${(r.score * 100).toFixed(0)}%]** ${r.memory.content.slice(0, 150)}...${tags}`);
739
- }
740
- console.log("");
741
- return;
742
- }
743
- // Default: hint format (compact, for hooks)
744
- console.log("\n🧠 BRAIN RECALL - Relevante Erinnerungen:");
745
- console.log("─".repeat(50));
746
- for (const r of results) {
747
- const tags = r.memory.tags.length > 0 ? ` [${r.memory.tags.join(", ")}]` : "";
748
- console.log(` [${(r.score * 100).toFixed(0)}%] ${r.memory.content.slice(0, 120)}...${tags}`);
749
- }
750
- console.log("─".repeat(50));
751
- console.log("");
752
- }
753
- catch (error) {
754
- // Fail silently for hook integration - don't block agent workflow
755
- if (options.format === "json") {
756
- console.log(JSON.stringify({ success: false, error: error.message }));
757
- }
758
- }
759
- });
760
- brainCommand
761
- .command("auto-remember <content>")
762
- .description("Automatically store a learning/insight (for hook integration after task completion)")
763
- .option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
764
- .option("--agent-type <type>", `Agent type for database selection (${AGENT_TYPES.join(", ")})`)
765
- .option("--task-id <id>", "Associated task ID")
766
- .option("-t, --tags <tags>", "Comma-separated tags", "auto-learning")
767
- .option("--source <source>", "Source of learning (task, conversation, tool)", "task")
768
- .option("--json", "Output as JSON")
769
- .action(async (content, options) => {
770
- try {
771
- // Skip very short content
772
- if (content.length < 20) {
773
- if (options.json) {
774
- console.log(JSON.stringify({ success: false, error: "Content too short" }));
775
- }
776
- return;
777
- }
778
- const brain = createBrain(options.agent, options.agentType);
779
- const tags = options.tags.split(",").map((t) => t.trim());
780
- // Add source and task info to tags
781
- if (options.source && !tags.includes(options.source)) {
782
- tags.push(options.source);
783
- }
784
- if (options.taskId) {
785
- tags.push(`task:${options.taskId}`);
786
- }
787
- const id = await brain.remember(content, tags, {
788
- source: options.source,
789
- taskId: options.taskId,
790
- autoGenerated: true,
791
- timestamp: new Date().toISOString(),
792
- });
793
- if (options.json) {
794
- console.log(JSON.stringify({ success: true, id, tags }));
795
- }
796
- else {
797
- console.log(` āœ“ Auto-Remember: ${id.slice(0, 8)}... [${tags.join(", ")}]`);
798
- }
799
- }
800
- catch (error) {
801
- if (options.json) {
802
- console.log(JSON.stringify({ success: false, error: error.message }));
803
- }
804
- // Fail silently for hook integration
805
- }
806
- });
807
704
  brainCommand
808
705
  .command("kb-stats <kb>")
809
706
  .description("Show statistics for a knowledge base")
@@ -2,20 +2,45 @@ import { getConfig } from "../../commands/config.js";
2
2
  import { canAccessKnowledgeBase as checkKbAccess } from "../permissions-cache.js";
3
3
  async function apiRequest(path, options = {}) {
4
4
  const config = getConfig();
5
- if (!config.apiUrl || !config.apiKey) {
6
- throw new Error("API not configured. Run: husky config set api-url <url> && husky config set api-key <key>");
5
+ if (!config.apiUrl) {
6
+ throw new Error("API not configured. Run: husky config set api-url <url>");
7
+ }
8
+ // Prefer session token (JWT), fall back to API key for backwards compatibility
9
+ const headers = {
10
+ "Content-Type": "application/json",
11
+ };
12
+ if (config.sessionToken) {
13
+ // Check if session is expired
14
+ if (config.sessionExpiresAt) {
15
+ const expiresAt = new Date(config.sessionExpiresAt);
16
+ if (expiresAt < new Date()) {
17
+ throw new Error("Session expired. Run: husky auth login --agent <name>");
18
+ }
19
+ }
20
+ headers["Authorization"] = `Bearer ${config.sessionToken}`;
21
+ }
22
+ else if (config.apiKey) {
23
+ // Legacy fallback - will be rejected by new API but kept for error message
24
+ headers["x-api-key"] = config.apiKey;
25
+ }
26
+ else {
27
+ throw new Error("Not authenticated. Run: husky auth login --agent <name>");
7
28
  }
8
29
  const url = new URL(`/api/brain${path}`, config.apiUrl);
9
30
  const res = await fetch(url.toString(), {
10
31
  method: options.method || "POST",
11
- headers: {
12
- "x-api-key": config.apiKey,
13
- "Content-Type": "application/json",
14
- },
32
+ headers,
15
33
  body: options.body ? JSON.stringify(options.body) : undefined,
16
34
  });
17
35
  if (!res.ok) {
18
36
  const error = await res.json().catch(() => ({ error: res.statusText }));
37
+ if (res.status === 401) {
38
+ // Check if it's the new API rejecting API key auth
39
+ if (error.upgrade) {
40
+ throw new Error("Session required. Run: husky auth login --agent <name>");
41
+ }
42
+ throw new Error(error.message || "Authentication failed");
43
+ }
19
44
  if (res.status === 403) {
20
45
  throw new Error(`Access denied: ${error.error || 'Permission denied to this resource'}`);
21
46
  }
@@ -191,6 +216,13 @@ export class ApiBrain {
191
216
  }
192
217
  export function shouldUseApi() {
193
218
  const config = getConfig();
194
- return Boolean(config.apiUrl && config.apiKey);
219
+ // Use API if:
220
+ // 1. No Qdrant URL configured (can't use direct access)
221
+ // 2. Or session token is available (preferred auth method)
222
+ if (!config.qdrantUrl) {
223
+ return Boolean(config.apiUrl);
224
+ }
225
+ // If Qdrant is configured, still prefer API if session token exists
226
+ return Boolean(config.apiUrl && config.sessionToken);
195
227
  }
196
228
  export default ApiBrain;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simonfestl/husky-cli",
3
- "version": "1.25.1",
3
+ "version": "1.25.3",
4
4
  "description": "CLI for Huskyv0 Task Orchestration with Claude Agent SDK",
5
5
  "type": "module",
6
6
  "bin": {