@codemcp/ade-core 0.2.0 → 0.2.2
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/package.json +22 -14
- package/.prettierignore +0 -1
- package/.turbo/turbo-build.log +0 -4
- package/.turbo/turbo-format.log +0 -6
- package/.turbo/turbo-lint.log +0 -4
- package/.turbo/turbo-test.log +0 -21
- package/.turbo/turbo-typecheck.log +0 -4
- package/eslint.config.mjs +0 -40
- package/nodemon.json +0 -7
- package/src/catalog/catalog.spec.ts +0 -570
- package/src/catalog/facets/architecture.ts +0 -438
- package/src/catalog/facets/autonomy.ts +0 -106
- package/src/catalog/facets/backpressure.ts +0 -143
- package/src/catalog/facets/practices.ts +0 -173
- package/src/catalog/facets/process.ts +0 -50
- package/src/catalog/index.ts +0 -93
- package/src/config.spec.ts +0 -165
- package/src/config.ts +0 -39
- package/src/index.ts +0 -55
- package/src/registry.spec.ts +0 -145
- package/src/registry.ts +0 -70
- package/src/resolver.spec.ts +0 -626
- package/src/resolver.ts +0 -214
- package/src/types.ts +0 -179
- package/src/writers/git-hooks.ts +0 -9
- package/src/writers/instruction.spec.ts +0 -42
- package/src/writers/instruction.ts +0 -8
- package/src/writers/knowledge.spec.ts +0 -26
- package/src/writers/knowledge.ts +0 -15
- package/src/writers/permission-policy.ts +0 -8
- package/src/writers/setup-note.ts +0 -9
- package/src/writers/skills.spec.ts +0 -109
- package/src/writers/skills.ts +0 -9
- package/src/writers/workflows.spec.ts +0 -72
- package/src/writers/workflows.ts +0 -26
- package/tsconfig.build.json +0 -8
- package/tsconfig.json +0 -7
- package/tsconfig.tsbuildinfo +0 -1
- package/tsconfig.vitest.json +0 -7
- package/vitest.config.ts +0 -5
package/src/resolver.ts
DELETED
|
@@ -1,214 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
UserConfig,
|
|
3
|
-
Catalog,
|
|
4
|
-
WriterRegistry,
|
|
5
|
-
LogicalConfig,
|
|
6
|
-
McpServerEntry,
|
|
7
|
-
ResolutionContext,
|
|
8
|
-
DocsetDef,
|
|
9
|
-
Provision,
|
|
10
|
-
PermissionPolicy
|
|
11
|
-
} from "./types.js";
|
|
12
|
-
import { getFacet, getOption } from "./catalog/index.js";
|
|
13
|
-
import { getProvisionWriter } from "./registry.js";
|
|
14
|
-
import { permissionPolicyWriter } from "./writers/permission-policy.js";
|
|
15
|
-
|
|
16
|
-
export async function resolve(
|
|
17
|
-
userConfig: UserConfig,
|
|
18
|
-
catalog: Catalog,
|
|
19
|
-
registry: WriterRegistry
|
|
20
|
-
): Promise<LogicalConfig> {
|
|
21
|
-
const result: LogicalConfig = {
|
|
22
|
-
mcp_servers: [],
|
|
23
|
-
instructions: [],
|
|
24
|
-
cli_actions: [],
|
|
25
|
-
knowledge_sources: [],
|
|
26
|
-
skills: [],
|
|
27
|
-
git_hooks: [],
|
|
28
|
-
setup_notes: []
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
const context: ResolutionContext = { resolved: {} };
|
|
32
|
-
|
|
33
|
-
for (const [facetId, optionId] of Object.entries(userConfig.choices)) {
|
|
34
|
-
const facet = getFacet(catalog, facetId);
|
|
35
|
-
if (!facet) {
|
|
36
|
-
continue;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
const selectedIds = Array.isArray(optionId) ? optionId : [optionId];
|
|
40
|
-
|
|
41
|
-
for (const selectedId of selectedIds) {
|
|
42
|
-
const option = getOption(facet, selectedId);
|
|
43
|
-
if (!option) {
|
|
44
|
-
throw new Error(
|
|
45
|
-
`Unknown option "${selectedId}" for facet "${facetId}"`
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
context.resolved[facetId] = { optionId: selectedId, option };
|
|
50
|
-
|
|
51
|
-
for (const provision of option.recipe) {
|
|
52
|
-
const writer =
|
|
53
|
-
getProvisionWriter(registry, provision.writer) ??
|
|
54
|
-
getBuiltInProvisionWriter(provision);
|
|
55
|
-
if (!writer) {
|
|
56
|
-
continue;
|
|
57
|
-
}
|
|
58
|
-
const partial = await writer.write(provision.config, context);
|
|
59
|
-
mergeLogicalConfig(result, partial);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Collect docsets from all selected options, dedup by id, filter exclusions
|
|
65
|
-
const seenDocsets = new Map<string, DocsetDef>();
|
|
66
|
-
for (const [facetId, optionId] of Object.entries(userConfig.choices)) {
|
|
67
|
-
const facet = getFacet(catalog, facetId);
|
|
68
|
-
if (!facet) continue;
|
|
69
|
-
const selectedIds = Array.isArray(optionId) ? optionId : [optionId];
|
|
70
|
-
for (const selectedId of selectedIds) {
|
|
71
|
-
const option = getOption(facet, selectedId);
|
|
72
|
-
if (!option?.docsets) continue;
|
|
73
|
-
for (const docset of option.docsets) {
|
|
74
|
-
if (!seenDocsets.has(docset.id)) {
|
|
75
|
-
seenDocsets.set(docset.id, docset);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const excludedSet = new Set(userConfig.excluded_docsets ?? []);
|
|
82
|
-
for (const [id, docset] of seenDocsets) {
|
|
83
|
-
if (excludedSet.has(id)) continue;
|
|
84
|
-
result.knowledge_sources.push({
|
|
85
|
-
name: docset.id,
|
|
86
|
-
origin: docset.origin,
|
|
87
|
-
description: docset.description
|
|
88
|
-
});
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// Add knowledge-server MCP entry if any knowledge_sources were collected
|
|
92
|
-
if (result.knowledge_sources.length > 0) {
|
|
93
|
-
result.mcp_servers.push({
|
|
94
|
-
ref: "knowledge",
|
|
95
|
-
command: "npx",
|
|
96
|
-
args: ["-y", "@codemcp/knowledge-server"],
|
|
97
|
-
env: {}
|
|
98
|
-
});
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
// Add skills-server MCP entry if any skills were collected
|
|
102
|
-
if (result.skills.length > 0) {
|
|
103
|
-
result.mcp_servers.push({
|
|
104
|
-
ref: "agentskills",
|
|
105
|
-
command: "npx",
|
|
106
|
-
args: ["-y", "@codemcp/skills-server"],
|
|
107
|
-
env: {}
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Merge custom section
|
|
112
|
-
if (userConfig.custom) {
|
|
113
|
-
if (userConfig.custom.instructions) {
|
|
114
|
-
result.instructions.push(...userConfig.custom.instructions);
|
|
115
|
-
}
|
|
116
|
-
if (userConfig.custom.mcp_servers) {
|
|
117
|
-
result.mcp_servers.push(...userConfig.custom.mcp_servers);
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Dedup mcp_servers by ref (last wins)
|
|
122
|
-
const serversByRef = new Map<string, McpServerEntry>();
|
|
123
|
-
for (const server of result.mcp_servers) {
|
|
124
|
-
serversByRef.set(server.ref, server);
|
|
125
|
-
}
|
|
126
|
-
result.mcp_servers = Array.from(serversByRef.values());
|
|
127
|
-
|
|
128
|
-
return result;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
function getBuiltInProvisionWriter(provision: Provision) {
|
|
132
|
-
if (provision.writer === "permission-policy") {
|
|
133
|
-
return permissionPolicyWriter;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function mergeLogicalConfig(
|
|
140
|
-
result: LogicalConfig,
|
|
141
|
-
partial: Partial<LogicalConfig>
|
|
142
|
-
): void {
|
|
143
|
-
if (partial.mcp_servers) {
|
|
144
|
-
result.mcp_servers.push(...partial.mcp_servers);
|
|
145
|
-
}
|
|
146
|
-
if (partial.instructions) {
|
|
147
|
-
result.instructions.push(...partial.instructions);
|
|
148
|
-
}
|
|
149
|
-
if (partial.cli_actions) {
|
|
150
|
-
result.cli_actions.push(...partial.cli_actions);
|
|
151
|
-
}
|
|
152
|
-
if (partial.knowledge_sources) {
|
|
153
|
-
result.knowledge_sources.push(...partial.knowledge_sources);
|
|
154
|
-
}
|
|
155
|
-
if (partial.skills) {
|
|
156
|
-
result.skills.push(...partial.skills);
|
|
157
|
-
}
|
|
158
|
-
if (partial.git_hooks) {
|
|
159
|
-
result.git_hooks.push(...partial.git_hooks);
|
|
160
|
-
}
|
|
161
|
-
if (partial.setup_notes) {
|
|
162
|
-
result.setup_notes.push(...partial.setup_notes);
|
|
163
|
-
}
|
|
164
|
-
if (partial.permission_policy) {
|
|
165
|
-
result.permission_policy = mergePermissionPolicy(
|
|
166
|
-
result.permission_policy,
|
|
167
|
-
partial.permission_policy
|
|
168
|
-
);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
function mergePermissionPolicy(
|
|
173
|
-
existing: PermissionPolicy | undefined,
|
|
174
|
-
incoming: PermissionPolicy
|
|
175
|
-
): PermissionPolicy {
|
|
176
|
-
if (!existing) {
|
|
177
|
-
return incoming;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
return {
|
|
181
|
-
...existing,
|
|
182
|
-
...incoming,
|
|
183
|
-
capabilities: {
|
|
184
|
-
...existing.capabilities,
|
|
185
|
-
...incoming.capabilities
|
|
186
|
-
}
|
|
187
|
-
};
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Collect all unique docsets implied by the given choices.
|
|
192
|
-
* Used by the TUI to present docsets for confirmation before resolution.
|
|
193
|
-
*/
|
|
194
|
-
export function collectDocsets(
|
|
195
|
-
choices: Record<string, string | string[]>,
|
|
196
|
-
catalog: Catalog
|
|
197
|
-
): DocsetDef[] {
|
|
198
|
-
const seen = new Map<string, DocsetDef>();
|
|
199
|
-
for (const [facetId, optionId] of Object.entries(choices)) {
|
|
200
|
-
const facet = getFacet(catalog, facetId);
|
|
201
|
-
if (!facet) continue;
|
|
202
|
-
const selectedIds = Array.isArray(optionId) ? optionId : [optionId];
|
|
203
|
-
for (const selectedId of selectedIds) {
|
|
204
|
-
const option = getOption(facet, selectedId);
|
|
205
|
-
if (!option?.docsets) continue;
|
|
206
|
-
for (const docset of option.docsets) {
|
|
207
|
-
if (!seen.has(docset.id)) {
|
|
208
|
-
seen.set(docset.id, docset);
|
|
209
|
-
}
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
return Array.from(seen.values());
|
|
214
|
-
}
|
package/src/types.ts
DELETED
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
// --- Catalog types ---
|
|
2
|
-
|
|
3
|
-
export interface Catalog {
|
|
4
|
-
facets: Facet[];
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export interface Facet {
|
|
8
|
-
id: string;
|
|
9
|
-
label: string;
|
|
10
|
-
description: string;
|
|
11
|
-
required: boolean;
|
|
12
|
-
multiSelect?: boolean;
|
|
13
|
-
dependsOn?: string[];
|
|
14
|
-
options: Option[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface Option {
|
|
18
|
-
id: string;
|
|
19
|
-
label: string;
|
|
20
|
-
description: string;
|
|
21
|
-
recipe: Provision[];
|
|
22
|
-
docsets?: DocsetDef[];
|
|
23
|
-
available?: (deps: Record<string, Option | undefined>) => boolean;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface DocsetDef {
|
|
27
|
-
id: string;
|
|
28
|
-
label: string;
|
|
29
|
-
origin: string;
|
|
30
|
-
description: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface Provision {
|
|
34
|
-
writer: ProvisionWriter;
|
|
35
|
-
config: Record<string, unknown>;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export type ProvisionWriter =
|
|
39
|
-
| "workflows"
|
|
40
|
-
| "skills"
|
|
41
|
-
| "knowledge"
|
|
42
|
-
| "mcp-server"
|
|
43
|
-
| "instruction"
|
|
44
|
-
| "installable"
|
|
45
|
-
| "git-hooks"
|
|
46
|
-
| "setup-note"
|
|
47
|
-
| "permission-policy";
|
|
48
|
-
|
|
49
|
-
// --- LogicalConfig types ---
|
|
50
|
-
|
|
51
|
-
export interface InlineSkill {
|
|
52
|
-
name: string;
|
|
53
|
-
description: string;
|
|
54
|
-
body: string;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export interface ExternalSkill {
|
|
58
|
-
name: string;
|
|
59
|
-
source: string;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export type SkillDefinition = InlineSkill | ExternalSkill;
|
|
63
|
-
|
|
64
|
-
export interface GitHook {
|
|
65
|
-
phase: "pre-commit" | "pre-push";
|
|
66
|
-
script: string;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export type AutonomyProfile = "rigid" | "sensible-defaults" | "max-autonomy";
|
|
70
|
-
|
|
71
|
-
export type PermissionDecision = "ask" | "allow" | "deny";
|
|
72
|
-
|
|
73
|
-
export type AutonomyCapability =
|
|
74
|
-
| "read"
|
|
75
|
-
| "edit_write"
|
|
76
|
-
| "search_list"
|
|
77
|
-
| "bash_safe"
|
|
78
|
-
| "bash_unsafe"
|
|
79
|
-
| "web"
|
|
80
|
-
| "task_agent";
|
|
81
|
-
|
|
82
|
-
/**
|
|
83
|
-
* @deprecated Harness-specific tool-level rules are no longer produced by core.
|
|
84
|
-
* Kept temporarily as a compatibility type for downstream packages.
|
|
85
|
-
*/
|
|
86
|
-
export type PermissionRule =
|
|
87
|
-
| PermissionDecision
|
|
88
|
-
| Record<string, PermissionDecision>;
|
|
89
|
-
|
|
90
|
-
export interface PermissionPolicy extends Record<string, unknown> {
|
|
91
|
-
profile: AutonomyProfile;
|
|
92
|
-
capabilities: Record<AutonomyCapability, PermissionDecision>;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
export interface LogicalConfig extends Record<string, unknown> {
|
|
96
|
-
mcp_servers: McpServerEntry[];
|
|
97
|
-
instructions: string[];
|
|
98
|
-
cli_actions: CliAction[];
|
|
99
|
-
knowledge_sources: KnowledgeSource[];
|
|
100
|
-
skills: SkillDefinition[];
|
|
101
|
-
git_hooks: GitHook[];
|
|
102
|
-
setup_notes: string[];
|
|
103
|
-
permission_policy?: PermissionPolicy;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export interface McpServerEntry {
|
|
107
|
-
ref: string;
|
|
108
|
-
command: string;
|
|
109
|
-
args: string[];
|
|
110
|
-
env: Record<string, string>;
|
|
111
|
-
/**
|
|
112
|
-
* Tool names the agent is pre-approved to use from this server.
|
|
113
|
-
* Defaults to `["*"]` (all tools) when not specified.
|
|
114
|
-
*/
|
|
115
|
-
allowedTools?: string[];
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
export interface CliAction {
|
|
119
|
-
command: string;
|
|
120
|
-
args: string[];
|
|
121
|
-
phase: "setup" | "install";
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
export interface KnowledgeSource {
|
|
125
|
-
name: string;
|
|
126
|
-
origin: string;
|
|
127
|
-
description: string;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// --- Resolution context ---
|
|
131
|
-
|
|
132
|
-
export interface ResolutionContext {
|
|
133
|
-
resolved: Record<string, ResolvedFacet>;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
export interface ResolvedFacet {
|
|
137
|
-
optionId: string;
|
|
138
|
-
option: Option;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// --- Config file types ---
|
|
142
|
-
|
|
143
|
-
export interface UserConfig {
|
|
144
|
-
choices: Record<string, string | string[]>;
|
|
145
|
-
excluded_docsets?: string[];
|
|
146
|
-
harnesses?: string[];
|
|
147
|
-
custom?: {
|
|
148
|
-
mcp_servers?: McpServerEntry[];
|
|
149
|
-
instructions?: string[];
|
|
150
|
-
};
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
export interface LockFile {
|
|
154
|
-
version: 1;
|
|
155
|
-
generated_at: string;
|
|
156
|
-
choices: Record<string, string | string[]>;
|
|
157
|
-
harnesses?: string[];
|
|
158
|
-
logical_config: LogicalConfig;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
// --- Writer contracts (open, any package can implement) ---
|
|
162
|
-
|
|
163
|
-
export interface ProvisionWriterDef {
|
|
164
|
-
id: string;
|
|
165
|
-
write(
|
|
166
|
-
config: Record<string, unknown>,
|
|
167
|
-
context: ResolutionContext
|
|
168
|
-
): Promise<Partial<LogicalConfig>>;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export interface AgentWriterDef {
|
|
172
|
-
id: string;
|
|
173
|
-
install(config: LogicalConfig, projectRoot: string): Promise<void>;
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
export interface WriterRegistry {
|
|
177
|
-
provisions: Map<string, ProvisionWriterDef>;
|
|
178
|
-
agents: Map<string, AgentWriterDef>;
|
|
179
|
-
}
|
package/src/writers/git-hooks.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { instructionWriter } from "./instruction.js";
|
|
3
|
-
import type { ResolutionContext } from "../types.js";
|
|
4
|
-
|
|
5
|
-
describe("instructionWriter", () => {
|
|
6
|
-
const context: ResolutionContext = { resolved: {} };
|
|
7
|
-
|
|
8
|
-
it("has id 'instruction'", () => {
|
|
9
|
-
expect(instructionWriter.id).toBe("instruction");
|
|
10
|
-
});
|
|
11
|
-
|
|
12
|
-
it("returns the text wrapped in an instructions array", async () => {
|
|
13
|
-
const result = await instructionWriter.write(
|
|
14
|
-
{ text: "Always use strict mode" },
|
|
15
|
-
context
|
|
16
|
-
);
|
|
17
|
-
expect(result).toEqual({ instructions: ["Always use strict mode"] });
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
it("passes through the exact text without modification", async () => {
|
|
21
|
-
const verbatim = " leading spaces and trailing spaces ";
|
|
22
|
-
const result = await instructionWriter.write({ text: verbatim }, context);
|
|
23
|
-
expect(result).toEqual({ instructions: [verbatim] });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it("only returns instructions, not other LogicalConfig keys", async () => {
|
|
27
|
-
const result = await instructionWriter.write(
|
|
28
|
-
{ text: "some instruction" },
|
|
29
|
-
context
|
|
30
|
-
);
|
|
31
|
-
expect(Object.keys(result)).toEqual(["instructions"]);
|
|
32
|
-
expect(result).not.toHaveProperty("mcp_servers");
|
|
33
|
-
expect(result).not.toHaveProperty("cli_actions");
|
|
34
|
-
expect(result).not.toHaveProperty("knowledge_sources");
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
it("handles multi-line text correctly", async () => {
|
|
38
|
-
const multiLine = "Line one\nLine two\nLine three";
|
|
39
|
-
const result = await instructionWriter.write({ text: multiLine }, context);
|
|
40
|
-
expect(result).toEqual({ instructions: [multiLine] });
|
|
41
|
-
});
|
|
42
|
-
});
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { knowledgeWriter } from "./knowledge.js";
|
|
3
|
-
|
|
4
|
-
describe("knowledgeWriter", () => {
|
|
5
|
-
it("has id 'knowledge'", () => {
|
|
6
|
-
expect(knowledgeWriter.id).toBe("knowledge");
|
|
7
|
-
});
|
|
8
|
-
|
|
9
|
-
it("produces a knowledge_sources entry from config", async () => {
|
|
10
|
-
const result = await knowledgeWriter.write(
|
|
11
|
-
{
|
|
12
|
-
name: "react-docs",
|
|
13
|
-
origin: "https://github.com/facebook/react.git",
|
|
14
|
-
description: "Official React documentation"
|
|
15
|
-
},
|
|
16
|
-
{ resolved: {} }
|
|
17
|
-
);
|
|
18
|
-
|
|
19
|
-
expect(result.knowledge_sources).toHaveLength(1);
|
|
20
|
-
expect(result.knowledge_sources![0]).toEqual({
|
|
21
|
-
name: "react-docs",
|
|
22
|
-
origin: "https://github.com/facebook/react.git",
|
|
23
|
-
description: "Official React documentation"
|
|
24
|
-
});
|
|
25
|
-
});
|
|
26
|
-
});
|
package/src/writers/knowledge.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { ProvisionWriterDef } from "../types.js";
|
|
2
|
-
|
|
3
|
-
export const knowledgeWriter: ProvisionWriterDef = {
|
|
4
|
-
id: "knowledge",
|
|
5
|
-
async write(config) {
|
|
6
|
-
const { name, origin, description } = config as {
|
|
7
|
-
name: string;
|
|
8
|
-
origin: string;
|
|
9
|
-
description: string;
|
|
10
|
-
};
|
|
11
|
-
return {
|
|
12
|
-
knowledge_sources: [{ name, origin, description }]
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
};
|
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { skillsWriter } from "./skills.js";
|
|
3
|
-
|
|
4
|
-
describe("skillsWriter", () => {
|
|
5
|
-
const emptyContext = { resolved: {} };
|
|
6
|
-
|
|
7
|
-
it("returns skills from config", async () => {
|
|
8
|
-
const result = await skillsWriter.write(
|
|
9
|
-
{
|
|
10
|
-
skills: [
|
|
11
|
-
{
|
|
12
|
-
name: "my-skill",
|
|
13
|
-
description: "A test skill",
|
|
14
|
-
body: "Do the thing."
|
|
15
|
-
}
|
|
16
|
-
]
|
|
17
|
-
},
|
|
18
|
-
emptyContext
|
|
19
|
-
);
|
|
20
|
-
|
|
21
|
-
expect(result.skills).toHaveLength(1);
|
|
22
|
-
expect(result.skills![0]).toEqual({
|
|
23
|
-
name: "my-skill",
|
|
24
|
-
description: "A test skill",
|
|
25
|
-
body: "Do the thing."
|
|
26
|
-
});
|
|
27
|
-
});
|
|
28
|
-
|
|
29
|
-
it("returns multiple skills", async () => {
|
|
30
|
-
const result = await skillsWriter.write(
|
|
31
|
-
{
|
|
32
|
-
skills: [
|
|
33
|
-
{ name: "skill-a", description: "First", body: "Body A" },
|
|
34
|
-
{ name: "skill-b", description: "Second", body: "Body B" }
|
|
35
|
-
]
|
|
36
|
-
},
|
|
37
|
-
emptyContext
|
|
38
|
-
);
|
|
39
|
-
|
|
40
|
-
expect(result.skills).toHaveLength(2);
|
|
41
|
-
expect(result.skills!.map((s) => s.name)).toEqual(["skill-a", "skill-b"]);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
it("returns only the skills key", async () => {
|
|
45
|
-
const result = await skillsWriter.write(
|
|
46
|
-
{
|
|
47
|
-
skills: [{ name: "x", description: "desc", body: "body" }]
|
|
48
|
-
},
|
|
49
|
-
emptyContext
|
|
50
|
-
);
|
|
51
|
-
|
|
52
|
-
expect(Object.keys(result)).toEqual(["skills"]);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it("preserves multi-line body content", async () => {
|
|
56
|
-
const body =
|
|
57
|
-
"# Architecture\n\nUse layered architecture.\n\n## Rules\n- Rule 1\n- Rule 2";
|
|
58
|
-
const result = await skillsWriter.write(
|
|
59
|
-
{
|
|
60
|
-
skills: [{ name: "arch", description: "Architecture", body }]
|
|
61
|
-
},
|
|
62
|
-
emptyContext
|
|
63
|
-
);
|
|
64
|
-
|
|
65
|
-
expect(result.skills![0]).toMatchObject({ body });
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it("returns external skills with source reference", async () => {
|
|
69
|
-
const result = await skillsWriter.write(
|
|
70
|
-
{
|
|
71
|
-
skills: [
|
|
72
|
-
{
|
|
73
|
-
name: "playwright-cli",
|
|
74
|
-
source: "microsoft/playwright-cli/skills/playwright-cli"
|
|
75
|
-
}
|
|
76
|
-
]
|
|
77
|
-
},
|
|
78
|
-
emptyContext
|
|
79
|
-
);
|
|
80
|
-
|
|
81
|
-
expect(result.skills).toHaveLength(1);
|
|
82
|
-
expect(result.skills![0]).toEqual({
|
|
83
|
-
name: "playwright-cli",
|
|
84
|
-
source: "microsoft/playwright-cli/skills/playwright-cli"
|
|
85
|
-
});
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
it("handles mixed inline and external skills", async () => {
|
|
89
|
-
const result = await skillsWriter.write(
|
|
90
|
-
{
|
|
91
|
-
skills: [
|
|
92
|
-
{ name: "my-skill", description: "Inline", body: "Do stuff." },
|
|
93
|
-
{ name: "ext-skill", source: "org/repo/skills/ext" }
|
|
94
|
-
]
|
|
95
|
-
},
|
|
96
|
-
emptyContext
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
expect(result.skills).toHaveLength(2);
|
|
100
|
-
expect(result.skills![0]).toMatchObject({
|
|
101
|
-
name: "my-skill",
|
|
102
|
-
body: "Do stuff."
|
|
103
|
-
});
|
|
104
|
-
expect(result.skills![1]).toMatchObject({
|
|
105
|
-
name: "ext-skill",
|
|
106
|
-
source: "org/repo/skills/ext"
|
|
107
|
-
});
|
|
108
|
-
});
|
|
109
|
-
});
|
package/src/writers/skills.ts
DELETED