@codemcp/ade-core 0.1.1 → 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.
Files changed (40) hide show
  1. package/package.json +22 -14
  2. package/.prettierignore +0 -1
  3. package/.turbo/turbo-build.log +0 -4
  4. package/.turbo/turbo-format.log +0 -6
  5. package/.turbo/turbo-lint.log +0 -4
  6. package/.turbo/turbo-test.log +0 -21
  7. package/.turbo/turbo-typecheck.log +0 -4
  8. package/eslint.config.mjs +0 -40
  9. package/nodemon.json +0 -7
  10. package/src/catalog/catalog.spec.ts +0 -570
  11. package/src/catalog/facets/architecture.ts +0 -438
  12. package/src/catalog/facets/autonomy.ts +0 -106
  13. package/src/catalog/facets/backpressure.ts +0 -143
  14. package/src/catalog/facets/practices.ts +0 -173
  15. package/src/catalog/facets/process.ts +0 -50
  16. package/src/catalog/index.ts +0 -93
  17. package/src/config.spec.ts +0 -165
  18. package/src/config.ts +0 -39
  19. package/src/index.ts +0 -55
  20. package/src/registry.spec.ts +0 -145
  21. package/src/registry.ts +0 -70
  22. package/src/resolver.spec.ts +0 -626
  23. package/src/resolver.ts +0 -214
  24. package/src/types.ts +0 -179
  25. package/src/writers/git-hooks.ts +0 -9
  26. package/src/writers/instruction.spec.ts +0 -42
  27. package/src/writers/instruction.ts +0 -8
  28. package/src/writers/knowledge.spec.ts +0 -26
  29. package/src/writers/knowledge.ts +0 -15
  30. package/src/writers/permission-policy.ts +0 -8
  31. package/src/writers/setup-note.ts +0 -9
  32. package/src/writers/skills.spec.ts +0 -109
  33. package/src/writers/skills.ts +0 -9
  34. package/src/writers/workflows.spec.ts +0 -72
  35. package/src/writers/workflows.ts +0 -26
  36. package/tsconfig.build.json +0 -8
  37. package/tsconfig.json +0 -7
  38. package/tsconfig.tsbuildinfo +0 -1
  39. package/tsconfig.vitest.json +0 -7
  40. 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
- }
@@ -1,9 +0,0 @@
1
- import type { ProvisionWriterDef, GitHook } from "../types.js";
2
-
3
- export const gitHooksWriter: ProvisionWriterDef = {
4
- id: "git-hooks",
5
- async write(config) {
6
- const { hooks } = config as { hooks: GitHook[] };
7
- return { git_hooks: hooks };
8
- }
9
- };
@@ -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,8 +0,0 @@
1
- import type { ProvisionWriterDef } from "../types.js";
2
-
3
- export const instructionWriter: ProvisionWriterDef = {
4
- id: "instruction",
5
- async write(config) {
6
- return { instructions: [(config as { text: string }).text] };
7
- }
8
- };
@@ -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
- });
@@ -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,8 +0,0 @@
1
- import type { PermissionPolicy, ProvisionWriterDef } from "../types.js";
2
-
3
- export const permissionPolicyWriter: ProvisionWriterDef = {
4
- id: "permission-policy",
5
- async write(config) {
6
- return { permission_policy: config as PermissionPolicy };
7
- }
8
- };
@@ -1,9 +0,0 @@
1
- import type { ProvisionWriterDef } from "../types.js";
2
-
3
- export const setupNoteWriter: ProvisionWriterDef = {
4
- id: "setup-note",
5
- async write(config) {
6
- const { text } = config as { text: string };
7
- return { setup_notes: [text] };
8
- }
9
- };
@@ -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
- });
@@ -1,9 +0,0 @@
1
- import type { ProvisionWriterDef, SkillDefinition } from "../types.js";
2
-
3
- export const skillsWriter: ProvisionWriterDef = {
4
- id: "skills",
5
- async write(config) {
6
- const { skills } = config as { skills: SkillDefinition[] };
7
- return { skills };
8
- }
9
- };