@thantos66/claude-context-mcp 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.
Files changed (60) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +171 -0
  3. package/dist/constants.d.ts +33 -0
  4. package/dist/constants.js +59 -0
  5. package/dist/constants.js.map +1 -0
  6. package/dist/index.d.ts +2 -0
  7. package/dist/index.js +44 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/schemas/agent-frontmatter.d.ts +55 -0
  10. package/dist/schemas/agent-frontmatter.js +43 -0
  11. package/dist/schemas/agent-frontmatter.js.map +1 -0
  12. package/dist/schemas/mcp-config.d.ts +177 -0
  13. package/dist/schemas/mcp-config.js +38 -0
  14. package/dist/schemas/mcp-config.js.map +1 -0
  15. package/dist/schemas/settings.d.ts +70 -0
  16. package/dist/schemas/settings.js +28 -0
  17. package/dist/schemas/settings.js.map +1 -0
  18. package/dist/schemas/skill-frontmatter.d.ts +42 -0
  19. package/dist/schemas/skill-frontmatter.js +27 -0
  20. package/dist/schemas/skill-frontmatter.js.map +1 -0
  21. package/dist/services/agents.d.ts +56 -0
  22. package/dist/services/agents.js +155 -0
  23. package/dist/services/agents.js.map +1 -0
  24. package/dist/services/atomic-write.d.ts +10 -0
  25. package/dist/services/atomic-write.js +46 -0
  26. package/dist/services/atomic-write.js.map +1 -0
  27. package/dist/services/backup.d.ts +13 -0
  28. package/dist/services/backup.js +52 -0
  29. package/dist/services/backup.js.map +1 -0
  30. package/dist/services/mcp-config.d.ts +72 -0
  31. package/dist/services/mcp-config.js +163 -0
  32. package/dist/services/mcp-config.js.map +1 -0
  33. package/dist/services/paths.d.ts +42 -0
  34. package/dist/services/paths.js +91 -0
  35. package/dist/services/paths.js.map +1 -0
  36. package/dist/services/permissions.d.ts +38 -0
  37. package/dist/services/permissions.js +104 -0
  38. package/dist/services/permissions.js.map +1 -0
  39. package/dist/services/skills.d.ts +62 -0
  40. package/dist/services/skills.js +181 -0
  41. package/dist/services/skills.js.map +1 -0
  42. package/dist/tools/agents.d.ts +2 -0
  43. package/dist/tools/agents.js +134 -0
  44. package/dist/tools/agents.js.map +1 -0
  45. package/dist/tools/helpers.d.ts +25 -0
  46. package/dist/tools/helpers.js +36 -0
  47. package/dist/tools/helpers.js.map +1 -0
  48. package/dist/tools/mcp.d.ts +2 -0
  49. package/dist/tools/mcp.js +257 -0
  50. package/dist/tools/mcp.js.map +1 -0
  51. package/dist/tools/meta.d.ts +2 -0
  52. package/dist/tools/meta.js +79 -0
  53. package/dist/tools/meta.js.map +1 -0
  54. package/dist/tools/permissions.d.ts +2 -0
  55. package/dist/tools/permissions.js +144 -0
  56. package/dist/tools/permissions.js.map +1 -0
  57. package/dist/tools/skills.d.ts +2 -0
  58. package/dist/tools/skills.js +160 -0
  59. package/dist/tools/skills.js.map +1 -0
  60. package/package.json +55 -0
@@ -0,0 +1,38 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Shape of one entry in `mcpServers`. Two forms:
4
+ * - stdio: `command` + optional `args`/`env`
5
+ * - http: `url` + optional `headers`
6
+ *
7
+ * We allow extra unknown fields to pass through (the spec is evolving),
8
+ * but we validate the known ones strictly.
9
+ */
10
+ export const StdioServerSchema = z
11
+ .object({
12
+ command: z.string().min(1),
13
+ args: z.array(z.string()).optional(),
14
+ env: z.record(z.string()).optional(),
15
+ cwd: z.string().optional(),
16
+ disabled: z.boolean().optional(),
17
+ })
18
+ .passthrough();
19
+ export const HttpServerSchema = z
20
+ .object({
21
+ url: z.string().url(),
22
+ headers: z.record(z.string()).optional(),
23
+ disabled: z.boolean().optional(),
24
+ })
25
+ .passthrough();
26
+ export const McpServerEntrySchema = z.union([StdioServerSchema, HttpServerSchema]);
27
+ /** The full top-level file shape — both claude_desktop_config.json and .mcp.json
28
+ * only use the `mcpServers` key; anything else is passed through untouched. */
29
+ export const McpConfigFileSchema = z
30
+ .object({
31
+ mcpServers: z.record(McpServerEntrySchema).optional(),
32
+ })
33
+ .passthrough();
34
+ /** Type guard: stdio vs http entry. */
35
+ export function isStdioServer(entry) {
36
+ return typeof entry.command === "string";
37
+ }
38
+ //# sourceMappingURL=mcp-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-config.js","sourceRoot":"","sources":["../../src/schemas/mcp-config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC;KAC/B,MAAM,CAAC;IACN,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1B,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACpC,GAAG,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACpC,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC1B,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC;KACD,WAAW,EAAE,CAAC;AAEjB,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC;KAC9B,MAAM,CAAC;IACN,GAAG,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE;IACrB,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACxC,QAAQ,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;CACjC,CAAC;KACD,WAAW,EAAE,CAAC;AAEjB,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,iBAAiB,EAAE,gBAAgB,CAAC,CAAC,CAAC;AAEnF;gFACgF;AAChF,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC;KACjC,MAAM,CAAC;IACN,UAAU,EAAE,CAAC,CAAC,MAAM,CAAC,oBAAoB,CAAC,CAAC,QAAQ,EAAE;CACtD,CAAC;KACD,WAAW,EAAE,CAAC;AAOjB,uCAAuC;AACvC,MAAM,UAAU,aAAa,CAAC,KAAqB;IACjD,OAAO,OAAQ,KAAqB,CAAC,OAAO,KAAK,QAAQ,CAAC;AAC5D,CAAC"}
@@ -0,0 +1,70 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Claude Code `settings.json` shape — we only validate the `permissions` key
4
+ * and `defaultMode`; everything else (hooks, model, env) passes through.
5
+ */
6
+ export declare const DefaultModeSchema: z.ZodEnum<["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"]>;
7
+ export declare const PermissionsBlockSchema: z.ZodObject<{
8
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
9
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
10
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
11
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
12
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
13
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
14
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
15
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
16
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
17
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
18
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
19
+ }, z.ZodTypeAny, "passthrough">>;
20
+ export declare const SettingsFileSchema: z.ZodObject<{
21
+ permissions: z.ZodOptional<z.ZodObject<{
22
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
23
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
24
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
25
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
26
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
27
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
28
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
29
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
30
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
31
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
32
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
33
+ }, z.ZodTypeAny, "passthrough">>>;
34
+ defaultMode: z.ZodOptional<z.ZodEnum<["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"]>>;
35
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
36
+ permissions: z.ZodOptional<z.ZodObject<{
37
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
38
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
39
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
40
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
41
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
42
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
43
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
44
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
45
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
46
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
47
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
48
+ }, z.ZodTypeAny, "passthrough">>>;
49
+ defaultMode: z.ZodOptional<z.ZodEnum<["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"]>>;
50
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
51
+ permissions: z.ZodOptional<z.ZodObject<{
52
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
53
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
54
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
55
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
56
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
57
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
58
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
59
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
60
+ allow: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
61
+ deny: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
62
+ ask: z.ZodOptional<z.ZodArray<z.ZodString, "many">>;
63
+ }, z.ZodTypeAny, "passthrough">>>;
64
+ defaultMode: z.ZodOptional<z.ZodEnum<["default", "acceptEdits", "plan", "auto", "dontAsk", "bypassPermissions"]>>;
65
+ }, z.ZodTypeAny, "passthrough">>;
66
+ export type DefaultMode = z.infer<typeof DefaultModeSchema>;
67
+ export type PermissionsBlock = z.infer<typeof PermissionsBlockSchema>;
68
+ export type SettingsFile = z.infer<typeof SettingsFileSchema>;
69
+ export declare const PermissionListName: z.ZodEnum<["allow", "deny", "ask"]>;
70
+ export type PermissionListName = z.infer<typeof PermissionListName>;
@@ -0,0 +1,28 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Claude Code `settings.json` shape — we only validate the `permissions` key
4
+ * and `defaultMode`; everything else (hooks, model, env) passes through.
5
+ */
6
+ export const DefaultModeSchema = z.enum([
7
+ "default",
8
+ "acceptEdits",
9
+ "plan",
10
+ "auto",
11
+ "dontAsk",
12
+ "bypassPermissions",
13
+ ]);
14
+ export const PermissionsBlockSchema = z
15
+ .object({
16
+ allow: z.array(z.string()).optional(),
17
+ deny: z.array(z.string()).optional(),
18
+ ask: z.array(z.string()).optional(),
19
+ })
20
+ .passthrough();
21
+ export const SettingsFileSchema = z
22
+ .object({
23
+ permissions: PermissionsBlockSchema.optional(),
24
+ defaultMode: DefaultModeSchema.optional(),
25
+ })
26
+ .passthrough();
27
+ export const PermissionListName = z.enum(["allow", "deny", "ask"]);
28
+ //# sourceMappingURL=settings.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"settings.js","sourceRoot":"","sources":["../../src/schemas/settings.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;GAGG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC;IACtC,SAAS;IACT,aAAa;IACb,MAAM;IACN,MAAM;IACN,SAAS;IACT,mBAAmB;CACpB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC;IACN,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACrC,IAAI,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;IACpC,GAAG,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,QAAQ,EAAE;CACpC,CAAC;KACD,WAAW,EAAE,CAAC;AAEjB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC;KAChC,MAAM,CAAC;IACN,WAAW,EAAE,sBAAsB,CAAC,QAAQ,EAAE;IAC9C,WAAW,EAAE,iBAAiB,CAAC,QAAQ,EAAE;CAC1C,CAAC;KACD,WAAW,EAAE,CAAC;AAMjB,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC"}
@@ -0,0 +1,42 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Claude Code skill (SKILL.md) frontmatter schema.
4
+ *
5
+ * Official fields per code.claude.com/docs/en/skills:
6
+ * name, description, allowed-tools, disable-model-invocation,
7
+ * user-invocable, model, effort, context, paths
8
+ */
9
+ export declare const SkillEffortSchema: z.ZodEnum<["low", "medium", "high", "max"]>;
10
+ export declare const SkillContextSchema: z.ZodEnum<["fork"]>;
11
+ export declare const SkillFrontmatterSchema: z.ZodObject<{
12
+ name: z.ZodString;
13
+ description: z.ZodString;
14
+ "allowed-tools": z.ZodOptional<z.ZodString>;
15
+ "disable-model-invocation": z.ZodOptional<z.ZodBoolean>;
16
+ "user-invocable": z.ZodOptional<z.ZodBoolean>;
17
+ model: z.ZodOptional<z.ZodString>;
18
+ effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "max"]>>;
19
+ context: z.ZodOptional<z.ZodEnum<["fork"]>>;
20
+ paths: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
21
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
22
+ name: z.ZodString;
23
+ description: z.ZodString;
24
+ "allowed-tools": z.ZodOptional<z.ZodString>;
25
+ "disable-model-invocation": z.ZodOptional<z.ZodBoolean>;
26
+ "user-invocable": z.ZodOptional<z.ZodBoolean>;
27
+ model: z.ZodOptional<z.ZodString>;
28
+ effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "max"]>>;
29
+ context: z.ZodOptional<z.ZodEnum<["fork"]>>;
30
+ paths: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
31
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
32
+ name: z.ZodString;
33
+ description: z.ZodString;
34
+ "allowed-tools": z.ZodOptional<z.ZodString>;
35
+ "disable-model-invocation": z.ZodOptional<z.ZodBoolean>;
36
+ "user-invocable": z.ZodOptional<z.ZodBoolean>;
37
+ model: z.ZodOptional<z.ZodString>;
38
+ effort: z.ZodOptional<z.ZodEnum<["low", "medium", "high", "max"]>>;
39
+ context: z.ZodOptional<z.ZodEnum<["fork"]>>;
40
+ paths: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodArray<z.ZodString, "many">]>>;
41
+ }, z.ZodTypeAny, "passthrough">>;
42
+ export type SkillFrontmatter = z.infer<typeof SkillFrontmatterSchema>;
@@ -0,0 +1,27 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Claude Code skill (SKILL.md) frontmatter schema.
4
+ *
5
+ * Official fields per code.claude.com/docs/en/skills:
6
+ * name, description, allowed-tools, disable-model-invocation,
7
+ * user-invocable, model, effort, context, paths
8
+ */
9
+ export const SkillEffortSchema = z.enum(["low", "medium", "high", "max"]);
10
+ export const SkillContextSchema = z.enum(["fork"]); // only documented value
11
+ export const SkillFrontmatterSchema = z
12
+ .object({
13
+ name: z
14
+ .string()
15
+ .regex(/^[a-z0-9][a-z0-9-]*$/, "skill name must be lowercase alphanumeric + hyphens")
16
+ .max(64),
17
+ description: z.string().min(1),
18
+ "allowed-tools": z.string().optional(),
19
+ "disable-model-invocation": z.boolean().optional(),
20
+ "user-invocable": z.boolean().optional(),
21
+ model: z.string().optional(),
22
+ effort: SkillEffortSchema.optional(),
23
+ context: SkillContextSchema.optional(),
24
+ paths: z.union([z.string(), z.array(z.string())]).optional(),
25
+ })
26
+ .passthrough();
27
+ //# sourceMappingURL=skill-frontmatter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-frontmatter.js","sourceRoot":"","sources":["../../src/schemas/skill-frontmatter.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;;;GAMG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAC,CAAC;AAC1E,MAAM,CAAC,MAAM,kBAAkB,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,wBAAwB;AAE5E,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC;KACpC,MAAM,CAAC;IACN,IAAI,EAAE,CAAC;SACJ,MAAM,EAAE;SACR,KAAK,CAAC,sBAAsB,EAAE,qDAAqD,CAAC;SACpF,GAAG,CAAC,EAAE,CAAC;IACV,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9B,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,0BAA0B,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IAClD,gBAAgB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,QAAQ,EAAE;IACxC,KAAK,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,MAAM,EAAE,iBAAiB,CAAC,QAAQ,EAAE;IACpC,OAAO,EAAE,kBAAkB,CAAC,QAAQ,EAAE;IACtC,KAAK,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;CAC7D,CAAC;KACD,WAAW,EAAE,CAAC"}
@@ -0,0 +1,56 @@
1
+ import { type AgentFrontmatter } from "../schemas/agent-frontmatter.js";
2
+ export interface AgentRecord {
3
+ scope: "user" | "project";
4
+ name: string;
5
+ path: string;
6
+ frontmatter: AgentFrontmatter;
7
+ body: string;
8
+ }
9
+ export interface AgentWriteResult {
10
+ path: string;
11
+ backup: string | null;
12
+ dryRun: boolean;
13
+ preview?: string;
14
+ }
15
+ /** List all agent markdown files in a scope. */
16
+ export declare function listAgents(scope: "user" | "project", projectPath?: string): Promise<AgentRecord[]>;
17
+ /** Read one agent file (by agent name, not file name). */
18
+ export declare function getAgent(params: {
19
+ scope: "user" | "project";
20
+ name: string;
21
+ projectPath?: string;
22
+ }): Promise<AgentRecord | null>;
23
+ /** Serialize an agent record back to the on-disk SKILL.md format. */
24
+ export declare function serializeAgent(frontmatter: AgentFrontmatter, body: string): string;
25
+ /** Create a new agent file. Fails if it already exists (unless overwrite). */
26
+ export declare function createAgent(params: {
27
+ scope: "user" | "project";
28
+ name: string;
29
+ description: string;
30
+ model?: string;
31
+ tools?: string | string[];
32
+ color?: string;
33
+ body?: string;
34
+ projectPath?: string;
35
+ overwrite?: boolean;
36
+ dryRun?: boolean;
37
+ }): Promise<AgentWriteResult>;
38
+ /** Patch frontmatter fields and/or replace the body of an existing agent. */
39
+ export declare function updateAgent(params: {
40
+ scope: "user" | "project";
41
+ name: string;
42
+ description?: string;
43
+ model?: string;
44
+ tools?: string | string[];
45
+ color?: string;
46
+ body?: string;
47
+ projectPath?: string;
48
+ dryRun?: boolean;
49
+ }): Promise<AgentWriteResult>;
50
+ /** Delete an agent file. */
51
+ export declare function deleteAgent(params: {
52
+ scope: "user" | "project";
53
+ name: string;
54
+ projectPath?: string;
55
+ dryRun?: boolean;
56
+ }): Promise<AgentWriteResult>;
@@ -0,0 +1,155 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import matter from "gray-matter";
4
+ import { AgentFrontmatterSchema, normalizeTools, } from "../schemas/agent-frontmatter.js";
5
+ import { atomicWriteFile } from "./atomic-write.js";
6
+ import { backupFile } from "./backup.js";
7
+ import { agentsDirFor } from "./paths.js";
8
+ /** List all agent markdown files in a scope. */
9
+ export async function listAgents(scope, projectPath) {
10
+ const dir = agentsDirFor(scope, projectPath);
11
+ let names;
12
+ try {
13
+ names = await fs.readdir(dir);
14
+ }
15
+ catch (err) {
16
+ if (err.code === "ENOENT")
17
+ return [];
18
+ throw err;
19
+ }
20
+ const results = [];
21
+ for (const name of names) {
22
+ if (!name.endsWith(".md"))
23
+ continue;
24
+ const filePath = path.join(dir, name);
25
+ try {
26
+ const rec = await readAgentFile(scope, filePath);
27
+ results.push(rec);
28
+ }
29
+ catch {
30
+ // Skip malformed agent files rather than failing the whole list.
31
+ continue;
32
+ }
33
+ }
34
+ return results;
35
+ }
36
+ /** Read one agent file (by agent name, not file name). */
37
+ export async function getAgent(params) {
38
+ const dir = agentsDirFor(params.scope, params.projectPath);
39
+ const filePath = path.join(dir, `${params.name}.md`);
40
+ try {
41
+ return await readAgentFile(params.scope, filePath);
42
+ }
43
+ catch (err) {
44
+ if (err.code === "ENOENT")
45
+ return null;
46
+ throw err;
47
+ }
48
+ }
49
+ async function readAgentFile(scope, filePath) {
50
+ const raw = await fs.readFile(filePath, "utf8");
51
+ const parsed = matter(raw);
52
+ const frontmatter = AgentFrontmatterSchema.parse(parsed.data);
53
+ return {
54
+ scope,
55
+ name: frontmatter.name,
56
+ path: filePath,
57
+ frontmatter,
58
+ body: parsed.content.trimStart(),
59
+ };
60
+ }
61
+ /** Serialize an agent record back to the on-disk SKILL.md format. */
62
+ export function serializeAgent(frontmatter, body) {
63
+ // gray-matter writes the fields in insertion order, so construct deliberately.
64
+ const ordered = {
65
+ name: frontmatter.name,
66
+ description: frontmatter.description,
67
+ };
68
+ if (frontmatter.model !== undefined)
69
+ ordered.model = frontmatter.model;
70
+ if (frontmatter.tools !== undefined)
71
+ ordered.tools = frontmatter.tools;
72
+ if (frontmatter.color !== undefined)
73
+ ordered.color = frontmatter.color;
74
+ for (const [k, v] of Object.entries(frontmatter)) {
75
+ if (v === undefined)
76
+ continue;
77
+ if (!(k in ordered))
78
+ ordered[k] = v;
79
+ }
80
+ const bodyText = body.endsWith("\n") ? body : body + "\n";
81
+ return matter.stringify(bodyText, ordered);
82
+ }
83
+ /** Create a new agent file. Fails if it already exists (unless overwrite). */
84
+ export async function createAgent(params) {
85
+ const frontmatter = AgentFrontmatterSchema.parse({
86
+ name: params.name,
87
+ description: params.description,
88
+ model: params.model,
89
+ tools: normalizeTools(params.tools),
90
+ color: params.color,
91
+ });
92
+ const body = params.body ?? "";
93
+ const dir = agentsDirFor(params.scope, params.projectPath);
94
+ const filePath = path.join(dir, `${frontmatter.name}.md`);
95
+ if (!params.overwrite) {
96
+ try {
97
+ await fs.access(filePath);
98
+ throw new Error(`Agent "${frontmatter.name}" already exists at ${filePath}. Pass overwrite: true to replace.`);
99
+ }
100
+ catch (err) {
101
+ if (err.code !== "ENOENT")
102
+ throw err;
103
+ }
104
+ }
105
+ const contents = serializeAgent(frontmatter, body);
106
+ if (params.dryRun) {
107
+ return { path: filePath, backup: null, dryRun: true, preview: contents };
108
+ }
109
+ await fs.mkdir(dir, { recursive: true });
110
+ const backup = await backupFile(filePath);
111
+ await atomicWriteFile(filePath, contents);
112
+ return { path: filePath, backup, dryRun: false };
113
+ }
114
+ /** Patch frontmatter fields and/or replace the body of an existing agent. */
115
+ export async function updateAgent(params) {
116
+ const current = await getAgent({
117
+ scope: params.scope,
118
+ name: params.name,
119
+ projectPath: params.projectPath,
120
+ });
121
+ if (!current)
122
+ throw new Error(`Agent "${params.name}" not found in scope ${params.scope}.`);
123
+ const merged = AgentFrontmatterSchema.parse({
124
+ ...current.frontmatter,
125
+ ...(params.description !== undefined && { description: params.description }),
126
+ ...(params.model !== undefined && { model: params.model }),
127
+ ...(params.tools !== undefined && { tools: normalizeTools(params.tools) }),
128
+ ...(params.color !== undefined && { color: params.color }),
129
+ });
130
+ const body = params.body ?? current.body;
131
+ const contents = serializeAgent(merged, body);
132
+ if (params.dryRun) {
133
+ return { path: current.path, backup: null, dryRun: true, preview: contents };
134
+ }
135
+ const backup = await backupFile(current.path);
136
+ await atomicWriteFile(current.path, contents);
137
+ return { path: current.path, backup, dryRun: false };
138
+ }
139
+ /** Delete an agent file. */
140
+ export async function deleteAgent(params) {
141
+ const current = await getAgent({
142
+ scope: params.scope,
143
+ name: params.name,
144
+ projectPath: params.projectPath,
145
+ });
146
+ if (!current)
147
+ throw new Error(`Agent "${params.name}" not found in scope ${params.scope}.`);
148
+ if (params.dryRun) {
149
+ return { path: current.path, backup: null, dryRun: true };
150
+ }
151
+ const backup = await backupFile(current.path);
152
+ await fs.unlink(current.path);
153
+ return { path: current.path, backup, dryRun: false };
154
+ }
155
+ //# sourceMappingURL=agents.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/services/agents.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Atomically write `contents` to `target`:
3
+ * 1. ensure parent dir exists
4
+ * 2. write to a sibling temp file
5
+ * 3. fsync + rename over target (atomic on POSIX)
6
+ * If any step fails, the temp file is cleaned up and the target is untouched.
7
+ */
8
+ export declare function atomicWriteFile(target: string, contents: string): Promise<void>;
9
+ /** Serialize JSON with 2-space indent and a trailing newline. */
10
+ export declare function stringifyJson(value: unknown): string;
@@ -0,0 +1,46 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import crypto from "node:crypto";
4
+ /**
5
+ * Atomically write `contents` to `target`:
6
+ * 1. ensure parent dir exists
7
+ * 2. write to a sibling temp file
8
+ * 3. fsync + rename over target (atomic on POSIX)
9
+ * If any step fails, the temp file is cleaned up and the target is untouched.
10
+ */
11
+ export async function atomicWriteFile(target, contents) {
12
+ const dir = path.dirname(target);
13
+ await fs.mkdir(dir, { recursive: true });
14
+ const tmp = path.join(dir, `.${path.basename(target)}.${crypto.randomBytes(6).toString("hex")}.tmp`);
15
+ let handle = null;
16
+ try {
17
+ handle = await fs.open(tmp, "w", 0o600);
18
+ await handle.writeFile(contents, "utf8");
19
+ await handle.sync();
20
+ await handle.close();
21
+ handle = null;
22
+ await fs.rename(tmp, target);
23
+ }
24
+ catch (err) {
25
+ if (handle) {
26
+ try {
27
+ await handle.close();
28
+ }
29
+ catch {
30
+ /* swallow */
31
+ }
32
+ }
33
+ try {
34
+ await fs.unlink(tmp);
35
+ }
36
+ catch {
37
+ /* swallow */
38
+ }
39
+ throw err;
40
+ }
41
+ }
42
+ /** Serialize JSON with 2-space indent and a trailing newline. */
43
+ export function stringifyJson(value) {
44
+ return JSON.stringify(value, null, 2) + "\n";
45
+ }
46
+ //# sourceMappingURL=atomic-write.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"atomic-write.js","sourceRoot":"","sources":["../../src/services/atomic-write.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,MAAM,MAAM,aAAa,CAAC;AAEjC;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,MAAc,EACd,QAAgB;IAEhB,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;IACjC,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CACnB,GAAG,EACH,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,MAAM,CACzE,CAAC;IACF,IAAI,MAAM,GAAyB,IAAI,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,GAAG,MAAM,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;QACrB,MAAM,GAAG,IAAI,CAAC;QACd,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC/B,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,MAAM,EAAE,CAAC;YACX,IAAI,CAAC;gBACH,MAAM,MAAM,CAAC,KAAK,EAAE,CAAC;YACvB,CAAC;YAAC,MAAM,CAAC;gBACP,aAAa;YACf,CAAC;QACH,CAAC;QACD,IAAI,CAAC;YACH,MAAM,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QACvB,CAAC;QAAC,MAAM,CAAC;YACP,aAAa;QACf,CAAC;QACD,MAAM,GAAG,CAAC;IACZ,CAAC;AACH,CAAC;AAED,iEAAiE;AACjE,MAAM,UAAU,aAAa,CAAC,KAAc;IAC1C,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC;AAC/C,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Snapshot a file to ~/.claude/backups/<basename>.<iso-timestamp>.bak
3
+ * before any destructive/mutating write.
4
+ *
5
+ * No-ops silently if the source doesn't exist yet (first-time write).
6
+ * Returns the absolute path of the backup file, or `null` if skipped.
7
+ */
8
+ export declare function backupFile(source: string): Promise<string | null>;
9
+ /**
10
+ * Snapshot a *directory* recursively (for skill folders). Preserves relative
11
+ * layout under ~/.claude/backups/<basename>.<iso-timestamp>.bak.d/.
12
+ */
13
+ export declare function backupDir(source: string): Promise<string | null>;
@@ -0,0 +1,52 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { backupsDir } from "../constants.js";
4
+ /**
5
+ * Snapshot a file to ~/.claude/backups/<basename>.<iso-timestamp>.bak
6
+ * before any destructive/mutating write.
7
+ *
8
+ * No-ops silently if the source doesn't exist yet (first-time write).
9
+ * Returns the absolute path of the backup file, or `null` if skipped.
10
+ */
11
+ export async function backupFile(source) {
12
+ let data;
13
+ try {
14
+ data = await fs.readFile(source);
15
+ }
16
+ catch (err) {
17
+ if (isEnoent(err))
18
+ return null;
19
+ throw err;
20
+ }
21
+ const dir = backupsDir();
22
+ await fs.mkdir(dir, { recursive: true });
23
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
24
+ const dest = path.join(dir, `${path.basename(source)}.${stamp}.bak`);
25
+ await fs.writeFile(dest, data, { mode: 0o600 });
26
+ return dest;
27
+ }
28
+ /**
29
+ * Snapshot a *directory* recursively (for skill folders). Preserves relative
30
+ * layout under ~/.claude/backups/<basename>.<iso-timestamp>.bak.d/.
31
+ */
32
+ export async function backupDir(source) {
33
+ try {
34
+ await fs.access(source);
35
+ }
36
+ catch (err) {
37
+ if (isEnoent(err))
38
+ return null;
39
+ throw err;
40
+ }
41
+ const stamp = new Date().toISOString().replace(/[:.]/g, "-");
42
+ const dest = path.join(backupsDir(), `${path.basename(source)}.${stamp}.bak.d`);
43
+ await fs.cp(source, dest, { recursive: true });
44
+ return dest;
45
+ }
46
+ function isEnoent(err) {
47
+ return (typeof err === "object" &&
48
+ err !== null &&
49
+ "code" in err &&
50
+ err.code === "ENOENT");
51
+ }
52
+ //# sourceMappingURL=backup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"backup.js","sourceRoot":"","sources":["../../src/services/backup.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAE7C;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc;IAC7C,IAAI,IAAY,CAAC;IACjB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;IACnC,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,GAAG,GAAG,UAAU,EAAE,CAAC;IACzB,MAAM,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC;IACrE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IAChD,OAAO,IAAI,CAAC;AACd,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc;IAC5C,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;IAC1B,CAAC;IAAC,OAAO,GAAY,EAAE,CAAC;QACtB,IAAI,QAAQ,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QAC/B,MAAM,GAAG,CAAC;IACZ,CAAC;IACD,MAAM,KAAK,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC;IAC7D,MAAM,IAAI,GAAG,IAAI,CAAC,IAAI,CACpB,UAAU,EAAE,EACZ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,CAC1C,CAAC;IACF,MAAM,EAAE,CAAC,EAAE,CAAC,MAAM,EAAE,IAAI,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/C,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,GAAY;IAC5B,OAAO,CACL,OAAO,GAAG,KAAK,QAAQ;QACvB,GAAG,KAAK,IAAI;QACZ,MAAM,IAAI,GAAG;QACZ,GAAyB,CAAC,IAAI,KAAK,QAAQ,CAC7C,CAAC;AACJ,CAAC"}
@@ -0,0 +1,72 @@
1
+ import { type McpConfigFile, type McpServerEntry } from "../schemas/mcp-config.js";
2
+ import type { Scope } from "../constants.js";
3
+ export interface WriteResult {
4
+ path: string;
5
+ backup: string | null;
6
+ dryRun: boolean;
7
+ diff?: string;
8
+ }
9
+ /** Load an MCP config file, returning `{ mcpServers: {} }` if absent. */
10
+ export declare function loadMcpConfig(path: string): Promise<McpConfigFile>;
11
+ /**
12
+ * Write an MCP config file: backup → atomic write.
13
+ * When `dryRun: true`, returns a diff and leaves disk untouched.
14
+ */
15
+ export declare function saveMcpConfig(path: string, next: McpConfigFile, { dryRun }?: {
16
+ dryRun?: boolean;
17
+ }): Promise<WriteResult>;
18
+ export interface ListEntry {
19
+ scope: Scope;
20
+ name: string;
21
+ path: string;
22
+ entry: McpServerEntry;
23
+ }
24
+ /** List servers across one scope. Redacts secrets by default. */
25
+ export declare function listServers(scope: Scope, opts?: {
26
+ projectPath?: string;
27
+ revealSecrets?: boolean;
28
+ }): Promise<ListEntry[]>;
29
+ /** Add or replace a server entry in a scope. */
30
+ export declare function addServer(params: {
31
+ scope: Scope;
32
+ name: string;
33
+ entry: McpServerEntry;
34
+ projectPath?: string;
35
+ dryRun?: boolean;
36
+ overwrite?: boolean;
37
+ }): Promise<WriteResult>;
38
+ /** Patch individual fields of an existing server entry. */
39
+ export declare function updateServer(params: {
40
+ scope: Scope;
41
+ name: string;
42
+ patch: Partial<McpServerEntry>;
43
+ projectPath?: string;
44
+ dryRun?: boolean;
45
+ }): Promise<WriteResult>;
46
+ /** Remove a server by name. */
47
+ export declare function removeServer(params: {
48
+ scope: Scope;
49
+ name: string;
50
+ projectPath?: string;
51
+ dryRun?: boolean;
52
+ }): Promise<WriteResult>;
53
+ /** Toggle the `disabled` flag on an existing server entry. */
54
+ export declare function setServerDisabled(params: {
55
+ scope: Scope;
56
+ name: string;
57
+ disabled: boolean;
58
+ projectPath?: string;
59
+ dryRun?: boolean;
60
+ }): Promise<WriteResult>;
61
+ /** Get a single server entry. */
62
+ export declare function getServer(params: {
63
+ scope: Scope;
64
+ name: string;
65
+ projectPath?: string;
66
+ revealSecrets?: boolean;
67
+ }): Promise<ListEntry | null>;
68
+ /**
69
+ * Minimal unified diff for dry-run output. Not a full diff engine — just
70
+ * enough to read at a glance in tool responses.
71
+ */
72
+ export declare function makeUnifiedDiff(a: string, b: string, label: string): string;