@simonfestl/husky-cli 1.3.1 → 1.4.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/dist/commands/biz/tickets.js +31 -1
- package/dist/commands/brain.d.ts +3 -0
- package/dist/commands/brain.js +168 -0
- package/dist/index.js +2 -0
- package/dist/lib/biz/agent-brain.d.ts +32 -0
- package/dist/lib/biz/agent-brain.js +132 -0
- package/dist/lib/biz/embeddings.js +1 -1
- package/package.json +7 -6
|
@@ -126,6 +126,8 @@ ticketsCommand
|
|
|
126
126
|
.description("Update ticket properties")
|
|
127
127
|
.option("--status <status>", "New status (open, pending, solved)")
|
|
128
128
|
.option("--priority <priority>", "New priority (low, normal, high, urgent)")
|
|
129
|
+
.option("-f, --field <field...>", "Set custom field (format: field_id=value or name=value)")
|
|
130
|
+
.option("--json", "Output as JSON")
|
|
129
131
|
.action(async (id, options) => {
|
|
130
132
|
try {
|
|
131
133
|
const client = ZendeskClient.fromConfig();
|
|
@@ -134,13 +136,41 @@ ticketsCommand
|
|
|
134
136
|
updates.status = options.status;
|
|
135
137
|
if (options.priority)
|
|
136
138
|
updates.priority = options.priority;
|
|
139
|
+
if (options.field && options.field.length > 0) {
|
|
140
|
+
updates.custom_fields = options.field.map((f) => {
|
|
141
|
+
const [key, ...valueParts] = f.split("=");
|
|
142
|
+
const value = valueParts.join("=");
|
|
143
|
+
const fieldId = parseInt(key, 10);
|
|
144
|
+
if (isNaN(fieldId)) {
|
|
145
|
+
throw new Error(`Invalid field ID: ${key}. Use numeric field ID (e.g., 28080124674706=value)`);
|
|
146
|
+
}
|
|
147
|
+
if (value === "true")
|
|
148
|
+
return { id: fieldId, value: true };
|
|
149
|
+
if (value === "false")
|
|
150
|
+
return { id: fieldId, value: false };
|
|
151
|
+
if (value === "null" || value === "")
|
|
152
|
+
return { id: fieldId, value: null };
|
|
153
|
+
const numValue = parseInt(value, 10);
|
|
154
|
+
if (!isNaN(numValue) && String(numValue) === value) {
|
|
155
|
+
return { id: fieldId, value: numValue };
|
|
156
|
+
}
|
|
157
|
+
return { id: fieldId, value };
|
|
158
|
+
});
|
|
159
|
+
}
|
|
137
160
|
if (Object.keys(updates).length === 0) {
|
|
138
|
-
console.error("Error: Provide --status or --
|
|
161
|
+
console.error("Error: Provide --status, --priority, or --field");
|
|
139
162
|
process.exit(1);
|
|
140
163
|
}
|
|
141
164
|
const ticket = await client.updateTicket(parseInt(id, 10), updates);
|
|
165
|
+
if (options.json) {
|
|
166
|
+
console.log(JSON.stringify(ticket, null, 2));
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
142
169
|
console.log(`✓ Updated ticket #${ticket.id}`);
|
|
143
170
|
console.log(` Status: ${ticket.status}, Priority: ${ticket.priority || "normal"}`);
|
|
171
|
+
if (updates.custom_fields) {
|
|
172
|
+
console.log(` Custom fields updated: ${updates.custom_fields.length}`);
|
|
173
|
+
}
|
|
144
174
|
}
|
|
145
175
|
catch (error) {
|
|
146
176
|
console.error("Error:", error.message);
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import { AgentBrain } from "../lib/biz/agent-brain.js";
|
|
3
|
+
const DEFAULT_AGENT = process.env.HUSKY_AGENT_ID || 'default';
|
|
4
|
+
export const brainCommand = new Command("brain")
|
|
5
|
+
.description("Agent memory and knowledge management");
|
|
6
|
+
brainCommand
|
|
7
|
+
.command("remember <content>")
|
|
8
|
+
.description("Store a memory")
|
|
9
|
+
.option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
|
|
10
|
+
.option("-t, --tags <tags>", "Comma-separated tags")
|
|
11
|
+
.option("--json", "Output as JSON")
|
|
12
|
+
.action(async (content, options) => {
|
|
13
|
+
try {
|
|
14
|
+
const brain = new AgentBrain(options.agent);
|
|
15
|
+
const tags = options.tags ? options.tags.split(",").map((t) => t.trim()) : [];
|
|
16
|
+
console.log(` Storing memory for agent: ${options.agent}...`);
|
|
17
|
+
const id = await brain.remember(content, tags);
|
|
18
|
+
if (options.json) {
|
|
19
|
+
console.log(JSON.stringify({ success: true, id, agent: options.agent }));
|
|
20
|
+
}
|
|
21
|
+
else {
|
|
22
|
+
console.log(` ✓ Memory stored: ${id}`);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
console.error("Error:", error.message);
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
});
|
|
30
|
+
brainCommand
|
|
31
|
+
.command("recall <query>")
|
|
32
|
+
.description("Search memories semantically")
|
|
33
|
+
.option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
|
|
34
|
+
.option("-l, --limit <num>", "Max results", "5")
|
|
35
|
+
.option("-m, --min-score <score>", "Minimum similarity score (0-1)", "0.5")
|
|
36
|
+
.option("--json", "Output as JSON")
|
|
37
|
+
.action(async (query, options) => {
|
|
38
|
+
try {
|
|
39
|
+
const brain = new AgentBrain(options.agent);
|
|
40
|
+
console.log(` Searching memories for: "${query}"...`);
|
|
41
|
+
const results = await brain.recall(query, parseInt(options.limit, 10), parseFloat(options.minScore));
|
|
42
|
+
if (options.json) {
|
|
43
|
+
console.log(JSON.stringify({ success: true, query, results }));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
console.log(`\n 🧠 Memories for "${query}" (${results.length} found)\n`);
|
|
47
|
+
if (results.length === 0) {
|
|
48
|
+
console.log(" No relevant memories found.");
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
for (const r of results) {
|
|
52
|
+
const tags = r.memory.tags.length > 0 ? ` [${r.memory.tags.join(", ")}]` : "";
|
|
53
|
+
console.log(` [${(r.score * 100).toFixed(1)}%] ${r.memory.content.slice(0, 80)}${tags}`);
|
|
54
|
+
}
|
|
55
|
+
console.log("");
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
console.error("Error:", error.message);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
brainCommand
|
|
63
|
+
.command("list")
|
|
64
|
+
.description("List recent memories")
|
|
65
|
+
.option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
|
|
66
|
+
.option("-l, --limit <num>", "Max results", "20")
|
|
67
|
+
.option("--json", "Output as JSON")
|
|
68
|
+
.action(async (options) => {
|
|
69
|
+
try {
|
|
70
|
+
const brain = new AgentBrain(options.agent);
|
|
71
|
+
const memories = await brain.listMemories(parseInt(options.limit, 10));
|
|
72
|
+
if (options.json) {
|
|
73
|
+
console.log(JSON.stringify({ success: true, memories }));
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
console.log(`\n 🧠 Memories for agent: ${options.agent} (${memories.length})\n`);
|
|
77
|
+
if (memories.length === 0) {
|
|
78
|
+
console.log(" No memories stored yet.");
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
for (const m of memories) {
|
|
82
|
+
const date = m.createdAt.toLocaleDateString("de-DE");
|
|
83
|
+
const tags = m.tags.length > 0 ? ` [${m.tags.join(", ")}]` : "";
|
|
84
|
+
console.log(` ${date} │ ${m.content.slice(0, 60)}...${tags}`);
|
|
85
|
+
}
|
|
86
|
+
console.log("");
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
console.error("Error:", error.message);
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
brainCommand
|
|
94
|
+
.command("forget <id>")
|
|
95
|
+
.description("Delete a memory")
|
|
96
|
+
.option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
|
|
97
|
+
.action(async (memoryId, options) => {
|
|
98
|
+
try {
|
|
99
|
+
const brain = new AgentBrain(options.agent);
|
|
100
|
+
await brain.forget(memoryId);
|
|
101
|
+
console.log(` ✓ Memory deleted: ${memoryId}`);
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
console.error("Error:", error.message);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
brainCommand
|
|
109
|
+
.command("stats")
|
|
110
|
+
.description("Show memory statistics")
|
|
111
|
+
.option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
|
|
112
|
+
.option("--json", "Output as JSON")
|
|
113
|
+
.action(async (options) => {
|
|
114
|
+
try {
|
|
115
|
+
const brain = new AgentBrain(options.agent);
|
|
116
|
+
const stats = await brain.stats();
|
|
117
|
+
if (options.json) {
|
|
118
|
+
console.log(JSON.stringify({ success: true, agent: options.agent, ...stats }));
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
console.log(`\n 🧠 Brain Stats for: ${options.agent}`);
|
|
122
|
+
console.log(` ────────────────────────────────`);
|
|
123
|
+
console.log(` Total memories: ${stats.count}`);
|
|
124
|
+
if (Object.keys(stats.tags).length > 0) {
|
|
125
|
+
console.log(`\n Tags:`);
|
|
126
|
+
const sortedTags = Object.entries(stats.tags).sort((a, b) => b[1] - a[1]);
|
|
127
|
+
for (const [tag, count] of sortedTags.slice(0, 10)) {
|
|
128
|
+
console.log(` ${tag}: ${count}`);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
console.log("");
|
|
132
|
+
}
|
|
133
|
+
catch (error) {
|
|
134
|
+
console.error("Error:", error.message);
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
brainCommand
|
|
139
|
+
.command("tags <tags>")
|
|
140
|
+
.description("Find memories by tags")
|
|
141
|
+
.option("-a, --agent <id>", "Agent ID", DEFAULT_AGENT)
|
|
142
|
+
.option("-l, --limit <num>", "Max results", "10")
|
|
143
|
+
.option("--json", "Output as JSON")
|
|
144
|
+
.action(async (tags, options) => {
|
|
145
|
+
try {
|
|
146
|
+
const brain = new AgentBrain(options.agent);
|
|
147
|
+
const tagList = tags.split(",").map((t) => t.trim());
|
|
148
|
+
const memories = await brain.recallByTags(tagList, parseInt(options.limit, 10));
|
|
149
|
+
if (options.json) {
|
|
150
|
+
console.log(JSON.stringify({ success: true, tags: tagList, memories }));
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
console.log(`\n 🏷️ Memories with tags: ${tagList.join(", ")} (${memories.length})\n`);
|
|
154
|
+
if (memories.length === 0) {
|
|
155
|
+
console.log(" No memories found with these tags.");
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
for (const m of memories) {
|
|
159
|
+
console.log(` ${m.content.slice(0, 70)}...`);
|
|
160
|
+
}
|
|
161
|
+
console.log("");
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
console.error("Error:", error.message);
|
|
165
|
+
process.exit(1);
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
export default brainCommand;
|
package/dist/index.js
CHANGED
|
@@ -27,6 +27,7 @@ import { serviceAccountCommand } from "./commands/service-account.js";
|
|
|
27
27
|
import { chatCommand } from "./commands/chat.js";
|
|
28
28
|
import { previewCommand } from "./commands/preview.js";
|
|
29
29
|
import { initCommand } from "./commands/init.js";
|
|
30
|
+
import { brainCommand } from "./commands/brain.js";
|
|
30
31
|
// Read version from package.json
|
|
31
32
|
const require = createRequire(import.meta.url);
|
|
32
33
|
const packageJson = require("../package.json");
|
|
@@ -61,6 +62,7 @@ program.addCommand(previewCommand);
|
|
|
61
62
|
program.addCommand(llmCommand);
|
|
62
63
|
program.addCommand(initCommand);
|
|
63
64
|
program.addCommand(agentMsgCommand);
|
|
65
|
+
program.addCommand(brainCommand);
|
|
64
66
|
// Handle --llm flag specially
|
|
65
67
|
if (process.argv.includes("--llm")) {
|
|
66
68
|
printLLMContext();
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
export interface Memory {
|
|
2
|
+
id: string;
|
|
3
|
+
agent: string;
|
|
4
|
+
content: string;
|
|
5
|
+
tags: string[];
|
|
6
|
+
embedding?: number[];
|
|
7
|
+
createdAt: Date;
|
|
8
|
+
updatedAt: Date;
|
|
9
|
+
metadata?: Record<string, unknown>;
|
|
10
|
+
}
|
|
11
|
+
export interface RecallResult {
|
|
12
|
+
memory: Memory;
|
|
13
|
+
score: number;
|
|
14
|
+
}
|
|
15
|
+
export declare class AgentBrain {
|
|
16
|
+
private db;
|
|
17
|
+
private embeddings;
|
|
18
|
+
private agentId;
|
|
19
|
+
private collectionPath;
|
|
20
|
+
constructor(agentId: string, projectId?: string);
|
|
21
|
+
remember(content: string, tags?: string[], metadata?: Record<string, unknown>): Promise<string>;
|
|
22
|
+
recall(query: string, limit?: number, minScore?: number): Promise<RecallResult[]>;
|
|
23
|
+
recallByTags(tags: string[], limit?: number): Promise<Memory[]>;
|
|
24
|
+
forget(memoryId: string): Promise<void>;
|
|
25
|
+
listMemories(limit?: number): Promise<Memory[]>;
|
|
26
|
+
stats(): Promise<{
|
|
27
|
+
count: number;
|
|
28
|
+
tags: Record<string, number>;
|
|
29
|
+
}>;
|
|
30
|
+
private cosineSimilarity;
|
|
31
|
+
}
|
|
32
|
+
export default AgentBrain;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { initializeApp, getApps } from 'firebase-admin/app';
|
|
2
|
+
import { getFirestore, Timestamp } from 'firebase-admin/firestore';
|
|
3
|
+
import { EmbeddingService } from './embeddings.js';
|
|
4
|
+
import { getConfig } from '../../commands/config.js';
|
|
5
|
+
const BRAIN_COLLECTION = 'agent_brains';
|
|
6
|
+
export class AgentBrain {
|
|
7
|
+
db;
|
|
8
|
+
embeddings;
|
|
9
|
+
agentId;
|
|
10
|
+
collectionPath;
|
|
11
|
+
constructor(agentId, projectId) {
|
|
12
|
+
this.agentId = agentId;
|
|
13
|
+
const config = getConfig();
|
|
14
|
+
const gcpProject = projectId || config.gcpProjectId || process.env.GOOGLE_CLOUD_PROJECT || 'tigerv0';
|
|
15
|
+
if (getApps().length === 0) {
|
|
16
|
+
initializeApp({ projectId: gcpProject });
|
|
17
|
+
}
|
|
18
|
+
this.db = getFirestore();
|
|
19
|
+
this.embeddings = new EmbeddingService({
|
|
20
|
+
projectId: gcpProject,
|
|
21
|
+
location: config.gcpLocation || 'europe-west1'
|
|
22
|
+
});
|
|
23
|
+
this.collectionPath = `${BRAIN_COLLECTION}/${agentId}/memories`;
|
|
24
|
+
}
|
|
25
|
+
async remember(content, tags = [], metadata) {
|
|
26
|
+
const embedding = await this.embeddings.embed(content);
|
|
27
|
+
const memory = {
|
|
28
|
+
agent: this.agentId,
|
|
29
|
+
content,
|
|
30
|
+
tags,
|
|
31
|
+
embedding,
|
|
32
|
+
metadata: metadata || {},
|
|
33
|
+
createdAt: Timestamp.now(),
|
|
34
|
+
updatedAt: Timestamp.now(),
|
|
35
|
+
};
|
|
36
|
+
const ref = await this.db.collection(this.collectionPath).add(memory);
|
|
37
|
+
return ref.id;
|
|
38
|
+
}
|
|
39
|
+
async recall(query, limit = 5, minScore = 0.5) {
|
|
40
|
+
const queryEmbedding = await this.embeddings.embed(query);
|
|
41
|
+
const snapshot = await this.db.collection(this.collectionPath).get();
|
|
42
|
+
const results = [];
|
|
43
|
+
for (const doc of snapshot.docs) {
|
|
44
|
+
const data = doc.data();
|
|
45
|
+
if (!data.embedding)
|
|
46
|
+
continue;
|
|
47
|
+
const score = this.cosineSimilarity(queryEmbedding, data.embedding);
|
|
48
|
+
if (score >= minScore) {
|
|
49
|
+
results.push({
|
|
50
|
+
memory: {
|
|
51
|
+
id: doc.id,
|
|
52
|
+
agent: data.agent,
|
|
53
|
+
content: data.content,
|
|
54
|
+
tags: data.tags || [],
|
|
55
|
+
createdAt: data.createdAt?.toDate() || new Date(),
|
|
56
|
+
updatedAt: data.updatedAt?.toDate() || new Date(),
|
|
57
|
+
metadata: data.metadata,
|
|
58
|
+
},
|
|
59
|
+
score,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return results
|
|
64
|
+
.sort((a, b) => b.score - a.score)
|
|
65
|
+
.slice(0, limit);
|
|
66
|
+
}
|
|
67
|
+
async recallByTags(tags, limit = 10) {
|
|
68
|
+
const snapshot = await this.db
|
|
69
|
+
.collection(this.collectionPath)
|
|
70
|
+
.where('tags', 'array-contains-any', tags)
|
|
71
|
+
.orderBy('createdAt', 'desc')
|
|
72
|
+
.limit(limit)
|
|
73
|
+
.get();
|
|
74
|
+
return snapshot.docs.map(doc => ({
|
|
75
|
+
id: doc.id,
|
|
76
|
+
agent: doc.data().agent,
|
|
77
|
+
content: doc.data().content,
|
|
78
|
+
tags: doc.data().tags || [],
|
|
79
|
+
createdAt: doc.data().createdAt?.toDate() || new Date(),
|
|
80
|
+
updatedAt: doc.data().updatedAt?.toDate() || new Date(),
|
|
81
|
+
metadata: doc.data().metadata,
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
async forget(memoryId) {
|
|
85
|
+
await this.db.collection(this.collectionPath).doc(memoryId).delete();
|
|
86
|
+
}
|
|
87
|
+
async listMemories(limit = 20) {
|
|
88
|
+
const snapshot = await this.db
|
|
89
|
+
.collection(this.collectionPath)
|
|
90
|
+
.orderBy('createdAt', 'desc')
|
|
91
|
+
.limit(limit)
|
|
92
|
+
.get();
|
|
93
|
+
return snapshot.docs.map(doc => ({
|
|
94
|
+
id: doc.id,
|
|
95
|
+
agent: doc.data().agent,
|
|
96
|
+
content: doc.data().content,
|
|
97
|
+
tags: doc.data().tags || [],
|
|
98
|
+
createdAt: doc.data().createdAt?.toDate() || new Date(),
|
|
99
|
+
updatedAt: doc.data().updatedAt?.toDate() || new Date(),
|
|
100
|
+
metadata: doc.data().metadata,
|
|
101
|
+
}));
|
|
102
|
+
}
|
|
103
|
+
async stats() {
|
|
104
|
+
const snapshot = await this.db.collection(this.collectionPath).get();
|
|
105
|
+
const tagCounts = {};
|
|
106
|
+
for (const doc of snapshot.docs) {
|
|
107
|
+
const tags = doc.data().tags || [];
|
|
108
|
+
for (const tag of tags) {
|
|
109
|
+
tagCounts[tag] = (tagCounts[tag] || 0) + 1;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
count: snapshot.size,
|
|
114
|
+
tags: tagCounts,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
cosineSimilarity(a, b) {
|
|
118
|
+
if (a.length !== b.length)
|
|
119
|
+
return 0;
|
|
120
|
+
let dotProduct = 0;
|
|
121
|
+
let normA = 0;
|
|
122
|
+
let normB = 0;
|
|
123
|
+
for (let i = 0; i < a.length; i++) {
|
|
124
|
+
dotProduct += a[i] * b[i];
|
|
125
|
+
normA += a[i] * a[i];
|
|
126
|
+
normB += b[i] * b[i];
|
|
127
|
+
}
|
|
128
|
+
const magnitude = Math.sqrt(normA) * Math.sqrt(normB);
|
|
129
|
+
return magnitude === 0 ? 0 : dotProduct / magnitude;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
export default AgentBrain;
|
|
@@ -27,7 +27,7 @@ export class EmbeddingService {
|
|
|
27
27
|
static fromConfig() {
|
|
28
28
|
const config = getConfig();
|
|
29
29
|
const embeddingConfig = {
|
|
30
|
-
projectId: config.gcpProjectId || process.env.GOOGLE_PROJECT_ID || process.env.GCP_PROJECT_ID || '',
|
|
30
|
+
projectId: config.gcpProjectId || process.env.GOOGLE_CLOUD_PROJECT || process.env.GOOGLE_PROJECT_ID || process.env.GCP_PROJECT_ID || '',
|
|
31
31
|
location: config.gcpLocation || process.env.GOOGLE_LOCATION || 'europe-west1',
|
|
32
32
|
model: process.env.EMBEDDING_MODEL || EMBEDDING_MODELS.TEXT_EMBEDDING_004,
|
|
33
33
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simonfestl/husky-cli",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.1",
|
|
4
4
|
"description": "CLI for Huskyv0 Task Orchestration with Claude Agent SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -21,15 +21,16 @@
|
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@anthropic-ai/claude-code": "^1.0.0",
|
|
23
23
|
"@inquirer/prompts": "^8.1.0",
|
|
24
|
-
"commander": "^12.1.0"
|
|
24
|
+
"commander": "^12.1.0",
|
|
25
|
+
"firebase-admin": "^13.6.0"
|
|
25
26
|
},
|
|
26
27
|
"devDependencies": {
|
|
27
28
|
"@types/node": "^22",
|
|
28
|
-
"typescript": "^5",
|
|
29
|
-
"vitest": "^2.1.8",
|
|
30
29
|
"@vitest/coverage-v8": "^2.1.8",
|
|
30
|
+
"memfs": "^4.14.0",
|
|
31
31
|
"msw": "^2.6.8",
|
|
32
|
-
"
|
|
32
|
+
"typescript": "^5",
|
|
33
|
+
"vitest": "^2.1.8"
|
|
33
34
|
},
|
|
34
35
|
"files": [
|
|
35
36
|
"dist"
|
|
@@ -43,4 +44,4 @@
|
|
|
43
44
|
"bugs": {
|
|
44
45
|
"url": "https://github.com/simon-sfxecom/huskyv0/issues"
|
|
45
46
|
}
|
|
46
|
-
}
|
|
47
|
+
}
|