@lakitu/sdk 0.1.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 +166 -0
- package/convex/_generated/api.d.ts +45 -0
- package/convex/_generated/api.js +23 -0
- package/convex/_generated/dataModel.d.ts +58 -0
- package/convex/_generated/server.d.ts +143 -0
- package/convex/_generated/server.js +93 -0
- package/convex/cloud/CLAUDE.md +238 -0
- package/convex/cloud/_generated/api.ts +84 -0
- package/convex/cloud/_generated/component.ts +861 -0
- package/convex/cloud/_generated/dataModel.ts +60 -0
- package/convex/cloud/_generated/server.ts +156 -0
- package/convex/cloud/convex.config.ts +16 -0
- package/convex/cloud/index.ts +29 -0
- package/convex/cloud/intentSchema/generate.ts +447 -0
- package/convex/cloud/intentSchema/index.ts +16 -0
- package/convex/cloud/intentSchema/types.ts +418 -0
- package/convex/cloud/ksaPolicy.ts +554 -0
- package/convex/cloud/mail.ts +92 -0
- package/convex/cloud/schema.ts +322 -0
- package/convex/cloud/utils/kanbanContext.ts +229 -0
- package/convex/cloud/workflows/agentBoard.ts +451 -0
- package/convex/cloud/workflows/agentPrompt.ts +272 -0
- package/convex/cloud/workflows/agentThread.ts +374 -0
- package/convex/cloud/workflows/compileSandbox.ts +146 -0
- package/convex/cloud/workflows/crudBoard.ts +217 -0
- package/convex/cloud/workflows/crudKSAs.ts +262 -0
- package/convex/cloud/workflows/crudLorobeads.ts +371 -0
- package/convex/cloud/workflows/crudSkills.ts +205 -0
- package/convex/cloud/workflows/crudThreads.ts +708 -0
- package/convex/cloud/workflows/lifecycleSandbox.ts +1396 -0
- package/convex/cloud/workflows/sandboxConvex.ts +1046 -0
- package/convex/sandbox/README.md +90 -0
- package/convex/sandbox/_generated/api.d.ts +2934 -0
- package/convex/sandbox/_generated/api.js +23 -0
- package/convex/sandbox/_generated/dataModel.d.ts +60 -0
- package/convex/sandbox/_generated/server.d.ts +143 -0
- package/convex/sandbox/_generated/server.js +93 -0
- package/convex/sandbox/actions/bash.ts +130 -0
- package/convex/sandbox/actions/browser.ts +282 -0
- package/convex/sandbox/actions/file.ts +336 -0
- package/convex/sandbox/actions/lsp.ts +325 -0
- package/convex/sandbox/actions/pdf.ts +119 -0
- package/convex/sandbox/agent/codeExecLoop.ts +535 -0
- package/convex/sandbox/agent/decisions.ts +284 -0
- package/convex/sandbox/agent/index.ts +515 -0
- package/convex/sandbox/agent/subagents.ts +651 -0
- package/convex/sandbox/brandResearch/index.ts +417 -0
- package/convex/sandbox/context/index.ts +7 -0
- package/convex/sandbox/context/session.ts +402 -0
- package/convex/sandbox/convex.config.ts +17 -0
- package/convex/sandbox/index.ts +51 -0
- package/convex/sandbox/nodeActions/codeExec.ts +130 -0
- package/convex/sandbox/planning/beads.ts +187 -0
- package/convex/sandbox/planning/index.ts +8 -0
- package/convex/sandbox/planning/sync.ts +194 -0
- package/convex/sandbox/prompts/codeExec.ts +852 -0
- package/convex/sandbox/prompts/modes.ts +231 -0
- package/convex/sandbox/prompts/system.ts +142 -0
- package/convex/sandbox/schema.ts +510 -0
- package/convex/sandbox/state/artifacts.ts +99 -0
- package/convex/sandbox/state/checkpoints.ts +341 -0
- package/convex/sandbox/state/files.ts +383 -0
- package/convex/sandbox/state/index.ts +10 -0
- package/convex/sandbox/state/verification.actions.ts +268 -0
- package/convex/sandbox/state/verification.ts +101 -0
- package/convex/sandbox/tsconfig.json +25 -0
- package/convex/sandbox/utils/codeExecHelpers.ts +52 -0
- package/dist/cli/commands/build.d.ts +19 -0
- package/dist/cli/commands/build.d.ts.map +1 -0
- package/dist/cli/commands/build.js +223 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.d.ts.map +1 -0
- package/dist/cli/commands/init.js +148 -0
- package/dist/cli/commands/publish.d.ts +12 -0
- package/dist/cli/commands/publish.d.ts.map +1 -0
- package/dist/cli/commands/publish.js +33 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +40 -0
- package/dist/sdk/builders.d.ts +104 -0
- package/dist/sdk/builders.d.ts.map +1 -0
- package/dist/sdk/builders.js +214 -0
- package/dist/sdk/index.d.ts +29 -0
- package/dist/sdk/index.d.ts.map +1 -0
- package/dist/sdk/index.js +38 -0
- package/dist/sdk/types.d.ts +107 -0
- package/dist/sdk/types.d.ts.map +1 -0
- package/dist/sdk/types.js +6 -0
- package/ksa/README.md +263 -0
- package/ksa/_generated/REFERENCE.md +2954 -0
- package/ksa/_generated/registry.ts +257 -0
- package/ksa/_shared/configReader.ts +302 -0
- package/ksa/_shared/configSchemas.ts +649 -0
- package/ksa/_shared/gateway.ts +175 -0
- package/ksa/_shared/ksaBehaviors.ts +411 -0
- package/ksa/_shared/ksaProxy.ts +248 -0
- package/ksa/_shared/localDb.ts +302 -0
- package/ksa/index.ts +134 -0
- package/package.json +93 -0
- package/runtime/browser/agent-browser.ts +330 -0
- package/runtime/entrypoint.ts +194 -0
- package/runtime/lsp/manager.ts +366 -0
- package/runtime/pdf/pdf-generator.ts +50 -0
- package/runtime/pdf/renderer.ts +357 -0
- package/runtime/pdf/schema.ts +97 -0
- package/runtime/services/file-watcher.ts +191 -0
- package/template/build.ts +307 -0
- package/template/e2b/Dockerfile +69 -0
- package/template/e2b/e2b.toml +13 -0
- package/template/e2b/prebuild.sh +68 -0
- package/template/e2b/start.sh +14 -0
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sandbox Compiler
|
|
3
|
+
*
|
|
4
|
+
* Manages compilation manifest for sandbox definitions.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { v } from "convex/values";
|
|
8
|
+
import { query, internalMutation } from "../_generated/server";
|
|
9
|
+
|
|
10
|
+
// ============================================
|
|
11
|
+
// Manifest Management
|
|
12
|
+
// ============================================
|
|
13
|
+
|
|
14
|
+
/** Save compilation manifest */
|
|
15
|
+
export const saveManifest = internalMutation({
|
|
16
|
+
args: {
|
|
17
|
+
version: v.string(),
|
|
18
|
+
manifest: v.array(v.object({
|
|
19
|
+
type: v.string(),
|
|
20
|
+
name: v.string(),
|
|
21
|
+
path: v.string(),
|
|
22
|
+
})),
|
|
23
|
+
},
|
|
24
|
+
handler: async (ctx, args) => {
|
|
25
|
+
// Clear old entries for this version
|
|
26
|
+
const existing = await ctx.db
|
|
27
|
+
.query("compiledSandbox")
|
|
28
|
+
.withIndex("by_version", (q) => q.eq("version", args.version))
|
|
29
|
+
.collect();
|
|
30
|
+
|
|
31
|
+
for (const entry of existing) {
|
|
32
|
+
await ctx.db.delete(entry._id);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Insert new entries
|
|
36
|
+
for (const item of args.manifest) {
|
|
37
|
+
await ctx.db.insert("compiledSandbox", {
|
|
38
|
+
version: args.version,
|
|
39
|
+
type: item.type as "tool" | "skill" | "agent" | "service",
|
|
40
|
+
name: item.name,
|
|
41
|
+
r2Key: item.path,
|
|
42
|
+
contentHash: "",
|
|
43
|
+
createdAt: Date.now(),
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return { saved: args.manifest.length };
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
/** Get the latest compiled manifest */
|
|
52
|
+
export const getManifest = query({
|
|
53
|
+
args: {
|
|
54
|
+
version: v.optional(v.string()),
|
|
55
|
+
},
|
|
56
|
+
handler: async (ctx, args) => {
|
|
57
|
+
if (args.version) {
|
|
58
|
+
return await ctx.db
|
|
59
|
+
.query("compiledSandbox")
|
|
60
|
+
.withIndex("by_version", (q) => q.eq("version", args.version!))
|
|
61
|
+
.collect();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Get latest version
|
|
65
|
+
const latest = await ctx.db
|
|
66
|
+
.query("compiledSandbox")
|
|
67
|
+
.order("desc")
|
|
68
|
+
.first();
|
|
69
|
+
|
|
70
|
+
if (!latest) return [];
|
|
71
|
+
|
|
72
|
+
return await ctx.db
|
|
73
|
+
.query("compiledSandbox")
|
|
74
|
+
.withIndex("by_version", (q) => q.eq("version", latest.version))
|
|
75
|
+
.collect();
|
|
76
|
+
},
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
/** Get manifest version info */
|
|
80
|
+
export const getLatestVersion = query({
|
|
81
|
+
args: {},
|
|
82
|
+
handler: async (ctx) => {
|
|
83
|
+
const latest = await ctx.db
|
|
84
|
+
.query("compiledSandbox")
|
|
85
|
+
.order("desc")
|
|
86
|
+
.first();
|
|
87
|
+
|
|
88
|
+
return latest?.version ?? null;
|
|
89
|
+
},
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// ============================================
|
|
93
|
+
// Custom Tools Queries
|
|
94
|
+
// ============================================
|
|
95
|
+
|
|
96
|
+
/** List custom tools (for compilation) */
|
|
97
|
+
export const listCustomTools = query({
|
|
98
|
+
args: {},
|
|
99
|
+
handler: async (ctx) => {
|
|
100
|
+
return await ctx.db
|
|
101
|
+
.query("customTools")
|
|
102
|
+
.filter((q) => q.eq(q.field("enabled"), true))
|
|
103
|
+
.collect();
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
/** Get tool implementation for a custom tool */
|
|
108
|
+
export const getToolImplementation = query({
|
|
109
|
+
args: { toolId: v.string() },
|
|
110
|
+
handler: async (ctx, args) => {
|
|
111
|
+
const tool = await ctx.db
|
|
112
|
+
.query("customTools")
|
|
113
|
+
.withIndex("by_toolId", (q) => q.eq("toolId", args.toolId))
|
|
114
|
+
.first();
|
|
115
|
+
|
|
116
|
+
return tool?.implementation || null;
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// ============================================
|
|
121
|
+
// Built-in Metadata Queries (deprecated - metadata now in Lakitu)
|
|
122
|
+
// ============================================
|
|
123
|
+
|
|
124
|
+
/** Get all built-in tool metadata */
|
|
125
|
+
export const getBuiltInTools = query({
|
|
126
|
+
args: {},
|
|
127
|
+
handler: async () => [],
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
/** Get all built-in skill metadata */
|
|
131
|
+
export const getBuiltInSkills = query({
|
|
132
|
+
args: {},
|
|
133
|
+
handler: async () => [],
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
/** Get all built-in deliverable metadata */
|
|
137
|
+
export const getBuiltInDeliverables = query({
|
|
138
|
+
args: {},
|
|
139
|
+
handler: async () => [],
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
/** Get all agent definitions */
|
|
143
|
+
export const getAgents = query({
|
|
144
|
+
args: {},
|
|
145
|
+
handler: async () => [],
|
|
146
|
+
});
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Board Planning Workflow - AI-assisted board creation
|
|
3
|
+
*
|
|
4
|
+
* Spawns an agent with board-manager skill to generate a plan,
|
|
5
|
+
* then executes the plan on user approval.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { v } from "convex/values";
|
|
9
|
+
import { action, mutation, query, internalAction } from "../_generated/server";
|
|
10
|
+
import { api, internal } from "../_generated/api";
|
|
11
|
+
|
|
12
|
+
// ============================================
|
|
13
|
+
// Schema
|
|
14
|
+
// ============================================
|
|
15
|
+
|
|
16
|
+
const boardPlanSchema = v.object({
|
|
17
|
+
title: v.string(),
|
|
18
|
+
description: v.string(),
|
|
19
|
+
stages: v.array(v.object({
|
|
20
|
+
name: v.string(),
|
|
21
|
+
type: v.union(v.literal("agent"), v.literal("human")),
|
|
22
|
+
description: v.string(),
|
|
23
|
+
skillId: v.optional(v.string()),
|
|
24
|
+
order: v.number(),
|
|
25
|
+
})),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
// ============================================
|
|
29
|
+
// Generate Plan - Spawns agent to create plan
|
|
30
|
+
// ============================================
|
|
31
|
+
|
|
32
|
+
export const generatePlan = action({
|
|
33
|
+
args: {
|
|
34
|
+
workspaceId: v.string(),
|
|
35
|
+
userPrompt: v.string(),
|
|
36
|
+
useOpenCode: v.optional(v.boolean()), // Feature flag for new OpenCode sandbox
|
|
37
|
+
},
|
|
38
|
+
handler: async (ctx, args) => {
|
|
39
|
+
// Use OpenCode by default for board planning (more reliable)
|
|
40
|
+
const useOpenCode = args.useOpenCode !== false;
|
|
41
|
+
|
|
42
|
+
// Build prompt - OpenCode uses built-in file editing
|
|
43
|
+
const prompt = useOpenCode
|
|
44
|
+
? `Create a workflow board for: ${args.userPrompt}
|
|
45
|
+
|
|
46
|
+
Write a JSON file to /home/user/workspace/board_plan.json with this structure:
|
|
47
|
+
{
|
|
48
|
+
"title": "Board name",
|
|
49
|
+
"description": "What this workflow accomplishes",
|
|
50
|
+
"stages": [
|
|
51
|
+
{"name": "Stage Name", "type": "agent" or "human", "description": "What happens", "order": 0}
|
|
52
|
+
]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
Rules:
|
|
56
|
+
- Create 3-6 stages
|
|
57
|
+
- First stage: usually "human" (user provides input)
|
|
58
|
+
- Middle stages: "agent" (AI automated tasks)
|
|
59
|
+
- Last stage: "human" (review) or "agent" (deliver result)`
|
|
60
|
+
: args.userPrompt;
|
|
61
|
+
|
|
62
|
+
const systemPrompt = useOpenCode ? undefined : `You are CodeMode. Create workflow boards.
|
|
63
|
+
|
|
64
|
+
## Rules
|
|
65
|
+
- Create 3-6 stages
|
|
66
|
+
- First stage: usually "human" (user input)
|
|
67
|
+
- Middle: "agent" stages with relevant skills
|
|
68
|
+
- Last: "human" (review) or "agent" (deliver)
|
|
69
|
+
|
|
70
|
+
Output a JSON plan with title, description, and stages array.`;
|
|
71
|
+
|
|
72
|
+
try {
|
|
73
|
+
// Start Lakitu sandbox session
|
|
74
|
+
const result = await ctx.runAction(api.workflows.sandboxConvex.startSession, {
|
|
75
|
+
projectId: args.workspaceId,
|
|
76
|
+
prompt,
|
|
77
|
+
config: {
|
|
78
|
+
model: "anthropic/claude-3.5-haiku",
|
|
79
|
+
purpose: "board-planning",
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
if (!result.success) {
|
|
84
|
+
return { success: false, error: result.error || "Failed to start session" };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const sessionId = result.sessionId;
|
|
88
|
+
|
|
89
|
+
// Wait for completion (poll session status)
|
|
90
|
+
let session: any = null;
|
|
91
|
+
for (let i = 0; i < 120; i++) { // 2 min max for planning
|
|
92
|
+
await new Promise(r => setTimeout(r, 1000));
|
|
93
|
+
session = await ctx.runQuery(api.workflows.sandboxConvex.getSession, { sessionId });
|
|
94
|
+
|
|
95
|
+
if (session?.status === "completed" || session?.status === "failed") {
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if (!session || session.status !== "completed") {
|
|
101
|
+
return { success: false, sessionId, error: "Timeout or session failed" };
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const output = session.output as any;
|
|
105
|
+
|
|
106
|
+
// Try to parse plan from response
|
|
107
|
+
const response = output?.response || "";
|
|
108
|
+
if (response) {
|
|
109
|
+
const jsonMatch = response.match(/\{[\s\S]*"title"[\s\S]*"stages"[\s\S]*\}/);
|
|
110
|
+
if (jsonMatch) {
|
|
111
|
+
try {
|
|
112
|
+
const plan = JSON.parse(jsonMatch[0]);
|
|
113
|
+
if (plan.title && plan.stages) {
|
|
114
|
+
return { success: true, sessionId, plan };
|
|
115
|
+
}
|
|
116
|
+
} catch {}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Try to find plan in tool call results
|
|
121
|
+
const toolCalls = output?.toolCalls || [];
|
|
122
|
+
for (const tc of toolCalls) {
|
|
123
|
+
if (tc.result && typeof tc.result === "string") {
|
|
124
|
+
const jsonMatch = tc.result.match(/\{[\s\S]*"title"[\s\S]*"stages"[\s\S]*\}/);
|
|
125
|
+
if (jsonMatch) {
|
|
126
|
+
try {
|
|
127
|
+
const plan = JSON.parse(jsonMatch[0]);
|
|
128
|
+
if (plan.title && plan.stages) {
|
|
129
|
+
return { success: true, sessionId, plan };
|
|
130
|
+
}
|
|
131
|
+
} catch {}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return {
|
|
137
|
+
success: false,
|
|
138
|
+
sessionId,
|
|
139
|
+
error: "Agent did not generate a structured plan. Please try again.",
|
|
140
|
+
debug: { response: response?.substring(0, 500), toolCalls: toolCalls?.length },
|
|
141
|
+
};
|
|
142
|
+
} catch (error: any) {
|
|
143
|
+
console.error("[generatePlan] Error:", error);
|
|
144
|
+
return {
|
|
145
|
+
success: false,
|
|
146
|
+
error: error.message,
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
},
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ============================================
|
|
153
|
+
// Execute Plan - Creates board from approved plan
|
|
154
|
+
// ============================================
|
|
155
|
+
|
|
156
|
+
export const executePlan = action({
|
|
157
|
+
args: {
|
|
158
|
+
workspaceId: v.string(),
|
|
159
|
+
plan: boardPlanSchema,
|
|
160
|
+
},
|
|
161
|
+
handler: async (ctx, args) => {
|
|
162
|
+
const { workspaceId, plan } = args;
|
|
163
|
+
|
|
164
|
+
try {
|
|
165
|
+
// Create board
|
|
166
|
+
const boardId = await ctx.runMutation(api.features.kanban.boards.create, {
|
|
167
|
+
workspaceId,
|
|
168
|
+
name: plan.title,
|
|
169
|
+
description: plan.description,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// Create tasks for each stage
|
|
173
|
+
const taskIds: string[] = [];
|
|
174
|
+
for (const stage of plan.stages) {
|
|
175
|
+
const taskId = await ctx.runMutation(api.features.kanban.boards.addTask, {
|
|
176
|
+
boardId,
|
|
177
|
+
name: stage.name,
|
|
178
|
+
description: stage.description,
|
|
179
|
+
stageType: stage.type,
|
|
180
|
+
skillId: stage.skillId,
|
|
181
|
+
order: stage.order,
|
|
182
|
+
});
|
|
183
|
+
taskIds.push(taskId);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
success: true,
|
|
188
|
+
boardId,
|
|
189
|
+
taskIds,
|
|
190
|
+
};
|
|
191
|
+
} catch (error: any) {
|
|
192
|
+
return {
|
|
193
|
+
success: false,
|
|
194
|
+
error: error.message,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
// ============================================
|
|
201
|
+
// Stream-based generation (for real-time UI)
|
|
202
|
+
// ============================================
|
|
203
|
+
|
|
204
|
+
export const streamGeneratePlan = internalAction({
|
|
205
|
+
args: {
|
|
206
|
+
workspaceId: v.string(),
|
|
207
|
+
userPrompt: v.string(),
|
|
208
|
+
planSessionId: v.string(), // For streaming updates
|
|
209
|
+
},
|
|
210
|
+
handler: async (ctx, args) => {
|
|
211
|
+
// This allows streaming updates to the UI via the session
|
|
212
|
+
return ctx.runAction(internal.workflows.create.board.generatePlan, {
|
|
213
|
+
workspaceId: args.workspaceId,
|
|
214
|
+
userPrompt: args.userPrompt,
|
|
215
|
+
});
|
|
216
|
+
},
|
|
217
|
+
});
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* KSA CRUD Operations
|
|
3
|
+
*
|
|
4
|
+
* Provides queries for the KSA registry to the frontend.
|
|
5
|
+
* KSAs are defined in ksaPolicy.ts - this just exposes them.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { query } from "../_generated/server";
|
|
9
|
+
import { v } from "convex/values";
|
|
10
|
+
import {
|
|
11
|
+
KSA_REGISTRY,
|
|
12
|
+
CONFIG_SCHEMAS,
|
|
13
|
+
CONFIG_DEFAULTS,
|
|
14
|
+
CORE_KSAS,
|
|
15
|
+
CATEGORY_LABELS,
|
|
16
|
+
CATEGORY_DESCRIPTIONS,
|
|
17
|
+
GROUP_LABELS,
|
|
18
|
+
GROUP_DESCRIPTIONS,
|
|
19
|
+
GROUP_ICONS,
|
|
20
|
+
GROUP_ORDER,
|
|
21
|
+
getKSA,
|
|
22
|
+
getKSAsByCategory,
|
|
23
|
+
getKSAsByGroup,
|
|
24
|
+
getSkillsByGroup,
|
|
25
|
+
getKSAsByNames,
|
|
26
|
+
getDefaultKSAs,
|
|
27
|
+
getConfigSchema,
|
|
28
|
+
getConfigDefaults,
|
|
29
|
+
mergeWithDefaults,
|
|
30
|
+
searchKSAs,
|
|
31
|
+
type KSAInfo,
|
|
32
|
+
type KSACategory,
|
|
33
|
+
type KSAGroup,
|
|
34
|
+
type ConfigField,
|
|
35
|
+
} from "../ksaPolicy";
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* List all available KSAs.
|
|
39
|
+
*/
|
|
40
|
+
export const list = query({
|
|
41
|
+
args: {
|
|
42
|
+
category: v.optional(v.union(v.literal("core"), v.literal("skills"), v.literal("deliverables"))),
|
|
43
|
+
},
|
|
44
|
+
handler: async (_ctx, args): Promise<KSAInfo[]> => {
|
|
45
|
+
if (args.category) {
|
|
46
|
+
return getKSAsByCategory(args.category);
|
|
47
|
+
}
|
|
48
|
+
return KSA_REGISTRY;
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Get a single KSA by name.
|
|
54
|
+
*/
|
|
55
|
+
export const get = query({
|
|
56
|
+
args: { name: v.string() },
|
|
57
|
+
handler: async (_ctx, args): Promise<KSAInfo | null> => {
|
|
58
|
+
return getKSA(args.name) || null;
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Get multiple KSAs by names.
|
|
64
|
+
*/
|
|
65
|
+
export const getByNames = query({
|
|
66
|
+
args: { names: v.array(v.string()) },
|
|
67
|
+
handler: async (_ctx, args): Promise<KSAInfo[]> => {
|
|
68
|
+
return getKSAsByNames(args.names);
|
|
69
|
+
},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Get KSAs grouped by category.
|
|
74
|
+
*/
|
|
75
|
+
export const listGrouped = query({
|
|
76
|
+
args: {},
|
|
77
|
+
handler: async (): Promise<{
|
|
78
|
+
core: KSAInfo[];
|
|
79
|
+
skills: KSAInfo[];
|
|
80
|
+
deliverables: KSAInfo[];
|
|
81
|
+
}> => {
|
|
82
|
+
return {
|
|
83
|
+
core: getKSAsByCategory("core"),
|
|
84
|
+
skills: getKSAsByCategory("skills"),
|
|
85
|
+
deliverables: getKSAsByCategory("deliverables"),
|
|
86
|
+
};
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Get category metadata.
|
|
92
|
+
*/
|
|
93
|
+
export const getCategories = query({
|
|
94
|
+
args: {},
|
|
95
|
+
handler: async (): Promise<
|
|
96
|
+
Array<{
|
|
97
|
+
id: KSACategory;
|
|
98
|
+
label: string;
|
|
99
|
+
description: string;
|
|
100
|
+
ksaCount: number;
|
|
101
|
+
}>
|
|
102
|
+
> => {
|
|
103
|
+
const categories: KSACategory[] = ["core", "skills", "deliverables"];
|
|
104
|
+
return categories.map((id) => ({
|
|
105
|
+
id,
|
|
106
|
+
label: CATEGORY_LABELS[id],
|
|
107
|
+
description: CATEGORY_DESCRIPTIONS[id],
|
|
108
|
+
ksaCount: getKSAsByCategory(id).length,
|
|
109
|
+
}));
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Get the names of core KSAs (always available).
|
|
115
|
+
*/
|
|
116
|
+
export const getCoreKSANames = query({
|
|
117
|
+
args: {},
|
|
118
|
+
handler: async (): Promise<string[]> => {
|
|
119
|
+
return CORE_KSAS;
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get a default set of KSAs for a given purpose.
|
|
125
|
+
*/
|
|
126
|
+
export const getDefaultKSASet = query({
|
|
127
|
+
args: {
|
|
128
|
+
purpose: v.union(
|
|
129
|
+
v.literal("research"),
|
|
130
|
+
v.literal("content"),
|
|
131
|
+
v.literal("automation"),
|
|
132
|
+
v.literal("minimal"),
|
|
133
|
+
v.literal("all")
|
|
134
|
+
),
|
|
135
|
+
},
|
|
136
|
+
handler: async (_ctx, args): Promise<string[]> => {
|
|
137
|
+
return getDefaultKSAs(args.purpose);
|
|
138
|
+
},
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
// ============================================================================
|
|
142
|
+
// Config Schema Queries
|
|
143
|
+
// ============================================================================
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get config schema for a KSA.
|
|
147
|
+
*/
|
|
148
|
+
export const getKSAConfigSchema = query({
|
|
149
|
+
args: { name: v.string() },
|
|
150
|
+
handler: async (_ctx, args): Promise<Record<string, ConfigField> | null> => {
|
|
151
|
+
return getConfigSchema(args.name) || null;
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Get default config for a KSA.
|
|
157
|
+
*/
|
|
158
|
+
export const getKSADefaults = query({
|
|
159
|
+
args: { name: v.string() },
|
|
160
|
+
handler: async (_ctx, args): Promise<Record<string, unknown> | null> => {
|
|
161
|
+
return getConfigDefaults(args.name) || null;
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Get all config schemas.
|
|
167
|
+
*/
|
|
168
|
+
export const getAllConfigSchemas = query({
|
|
169
|
+
args: {},
|
|
170
|
+
handler: async (): Promise<Record<string, Record<string, ConfigField>>> => {
|
|
171
|
+
return CONFIG_SCHEMAS;
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Merge user config with defaults for a KSA.
|
|
177
|
+
*/
|
|
178
|
+
export const getMergedConfig = query({
|
|
179
|
+
args: {
|
|
180
|
+
name: v.string(),
|
|
181
|
+
userConfig: v.any(),
|
|
182
|
+
},
|
|
183
|
+
handler: async (_ctx, args): Promise<Record<string, unknown>> => {
|
|
184
|
+
return mergeWithDefaults(args.name, args.userConfig || {});
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// ============================================================================
|
|
189
|
+
// Search
|
|
190
|
+
// ============================================================================
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Search KSAs by keyword.
|
|
194
|
+
*/
|
|
195
|
+
export const search = query({
|
|
196
|
+
args: { keyword: v.string() },
|
|
197
|
+
handler: async (_ctx, args): Promise<KSAInfo[]> => {
|
|
198
|
+
return searchKSAs(args.keyword);
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Get KSAs grouped for the library panel.
|
|
204
|
+
*/
|
|
205
|
+
export const listForLibrary = query({
|
|
206
|
+
args: {},
|
|
207
|
+
handler: async (): Promise<{
|
|
208
|
+
byCategory: {
|
|
209
|
+
core: KSAInfo[];
|
|
210
|
+
skills: KSAInfo[];
|
|
211
|
+
deliverables: KSAInfo[];
|
|
212
|
+
};
|
|
213
|
+
skillsByGroup: Record<KSAGroup, KSAInfo[]>;
|
|
214
|
+
groupOrder: KSAGroup[];
|
|
215
|
+
}> => {
|
|
216
|
+
return {
|
|
217
|
+
byCategory: {
|
|
218
|
+
core: getKSAsByCategory("core"),
|
|
219
|
+
skills: getKSAsByCategory("skills"),
|
|
220
|
+
deliverables: getKSAsByCategory("deliverables"),
|
|
221
|
+
},
|
|
222
|
+
skillsByGroup: getSkillsByGroup(),
|
|
223
|
+
groupOrder: GROUP_ORDER,
|
|
224
|
+
};
|
|
225
|
+
},
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get group metadata for skills subcategories.
|
|
230
|
+
*/
|
|
231
|
+
export const getGroups = query({
|
|
232
|
+
args: {},
|
|
233
|
+
handler: async (): Promise<
|
|
234
|
+
Array<{
|
|
235
|
+
id: KSAGroup;
|
|
236
|
+
label: string;
|
|
237
|
+
description: string;
|
|
238
|
+
icon: string;
|
|
239
|
+
ksaCount: number;
|
|
240
|
+
}>
|
|
241
|
+
> => {
|
|
242
|
+
return GROUP_ORDER.map((id) => ({
|
|
243
|
+
id,
|
|
244
|
+
label: GROUP_LABELS[id],
|
|
245
|
+
description: GROUP_DESCRIPTIONS[id],
|
|
246
|
+
icon: GROUP_ICONS[id],
|
|
247
|
+
ksaCount: getKSAsByGroup(id).length,
|
|
248
|
+
}));
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Get KSAs by group.
|
|
254
|
+
*/
|
|
255
|
+
export const listByGroup = query({
|
|
256
|
+
args: {
|
|
257
|
+
group: v.literal("research"),
|
|
258
|
+
},
|
|
259
|
+
handler: async (_ctx, args): Promise<KSAInfo[]> => {
|
|
260
|
+
return getKSAsByGroup(args.group);
|
|
261
|
+
},
|
|
262
|
+
});
|