@ambitresearch/genie 1.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -0
- package/dist/auth/bearer.d.ts +49 -0
- package/dist/auth/bearer.d.ts.map +1 -0
- package/dist/auth/bearer.js +159 -0
- package/dist/auth/bearer.js.map +1 -0
- package/dist/auth/oauth/authorize.d.ts +19 -0
- package/dist/auth/oauth/authorize.d.ts.map +1 -0
- package/dist/auth/oauth/authorize.js +111 -0
- package/dist/auth/oauth/authorize.js.map +1 -0
- package/dist/auth/oauth/config.d.ts +10 -0
- package/dist/auth/oauth/config.d.ts.map +1 -0
- package/dist/auth/oauth/config.js +16 -0
- package/dist/auth/oauth/config.js.map +1 -0
- package/dist/auth/oauth/dcr.d.ts +15 -0
- package/dist/auth/oauth/dcr.d.ts.map +1 -0
- package/dist/auth/oauth/dcr.js +38 -0
- package/dist/auth/oauth/dcr.js.map +1 -0
- package/dist/auth/oauth/jwt.d.ts +13 -0
- package/dist/auth/oauth/jwt.d.ts.map +1 -0
- package/dist/auth/oauth/jwt.js +46 -0
- package/dist/auth/oauth/jwt.js.map +1 -0
- package/dist/auth/oauth/metadata.d.ts +9 -0
- package/dist/auth/oauth/metadata.d.ts.map +1 -0
- package/dist/auth/oauth/metadata.js +19 -0
- package/dist/auth/oauth/metadata.js.map +1 -0
- package/dist/auth/oauth/router.d.ts +32 -0
- package/dist/auth/oauth/router.d.ts.map +1 -0
- package/dist/auth/oauth/router.js +159 -0
- package/dist/auth/oauth/router.js.map +1 -0
- package/dist/auth/oauth/store.d.ts +55 -0
- package/dist/auth/oauth/store.d.ts.map +1 -0
- package/dist/auth/oauth/store.js +63 -0
- package/dist/auth/oauth/store.js.map +1 -0
- package/dist/auth/oauth/token.d.ts +21 -0
- package/dist/auth/oauth/token.d.ts.map +1 -0
- package/dist/auth/oauth/token.js +77 -0
- package/dist/auth/oauth/token.js.map +1 -0
- package/dist/auth/oidc/group-policy.d.ts +43 -0
- package/dist/auth/oidc/group-policy.d.ts.map +1 -0
- package/dist/auth/oidc/group-policy.js +54 -0
- package/dist/auth/oidc/group-policy.js.map +1 -0
- package/dist/auth/oidc/verifier.d.ts +42 -0
- package/dist/auth/oidc/verifier.d.ts.map +1 -0
- package/dist/auth/oidc/verifier.js +145 -0
- package/dist/auth/oidc/verifier.js.map +1 -0
- package/dist/auth/token-cli.d.ts +9 -0
- package/dist/auth/token-cli.d.ts.map +1 -0
- package/dist/auth/token-cli.js +98 -0
- package/dist/auth/token-cli.js.map +1 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +149 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/redact.d.ts +20 -0
- package/dist/config/redact.d.ts.map +1 -0
- package/dist/config/redact.js +77 -0
- package/dist/config/redact.js.map +1 -0
- package/dist/config/secrets.d.ts +100 -0
- package/dist/config/secrets.d.ts.map +1 -0
- package/dist/config/secrets.js +130 -0
- package/dist/config/secrets.js.map +1 -0
- package/dist/framework/html.d.ts +103 -0
- package/dist/framework/html.d.ts.map +1 -0
- package/dist/framework/html.js +271 -0
- package/dist/framework/html.js.map +1 -0
- package/dist/framework/interface.d.ts +130 -0
- package/dist/framework/interface.d.ts.map +1 -0
- package/dist/framework/interface.js +112 -0
- package/dist/framework/interface.js.map +1 -0
- package/dist/framework/react.d.ts +133 -0
- package/dist/framework/react.d.ts.map +1 -0
- package/dist/framework/react.js +243 -0
- package/dist/framework/react.js.map +1 -0
- package/dist/framework/vue.d.ts +99 -0
- package/dist/framework/vue.d.ts.map +1 -0
- package/dist/framework/vue.js +375 -0
- package/dist/framework/vue.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/client.d.ts +81 -0
- package/dist/llm/client.d.ts.map +1 -0
- package/dist/llm/client.js +141 -0
- package/dist/llm/client.js.map +1 -0
- package/dist/llm/component-response.d.ts +111 -0
- package/dist/llm/component-response.d.ts.map +1 -0
- package/dist/llm/component-response.js +167 -0
- package/dist/llm/component-response.js.map +1 -0
- package/dist/llm/generated-files.d.ts +10 -0
- package/dist/llm/generated-files.d.ts.map +1 -0
- package/dist/llm/generated-files.js +26 -0
- package/dist/llm/generated-files.js.map +1 -0
- package/dist/llm/prompts/CHANGELOG.md +70 -0
- package/dist/llm/prompts/generate-component.system.md +99 -0
- package/dist/llm/prompts/refine-component.system.md +112 -0
- package/dist/llm/prompts.d.ts +66 -0
- package/dist/llm/prompts.d.ts.map +1 -0
- package/dist/llm/prompts.js +78 -0
- package/dist/llm/prompts.js.map +1 -0
- package/dist/llm/retry.d.ts +183 -0
- package/dist/llm/retry.d.ts.map +1 -0
- package/dist/llm/retry.js +475 -0
- package/dist/llm/retry.js.map +1 -0
- package/dist/llm/schema.d.ts +129 -0
- package/dist/llm/schema.d.ts.map +1 -0
- package/dist/llm/schema.js +147 -0
- package/dist/llm/schema.js.map +1 -0
- package/dist/llm/validate.d.ts +107 -0
- package/dist/llm/validate.d.ts.map +1 -0
- package/dist/llm/validate.js +174 -0
- package/dist/llm/validate.js.map +1 -0
- package/dist/manifest/compiler.d.ts +112 -0
- package/dist/manifest/compiler.d.ts.map +1 -0
- package/dist/manifest/compiler.js +306 -0
- package/dist/manifest/compiler.js.map +1 -0
- package/dist/manifest/ensure.d.ts +46 -0
- package/dist/manifest/ensure.d.ts.map +1 -0
- package/dist/manifest/ensure.js +49 -0
- package/dist/manifest/ensure.js.map +1 -0
- package/dist/manifest/index.d.ts +13 -0
- package/dist/manifest/index.d.ts.map +1 -0
- package/dist/manifest/index.js +13 -0
- package/dist/manifest/index.js.map +1 -0
- package/dist/middleware/plan-guard.d.ts +167 -0
- package/dist/middleware/plan-guard.d.ts.map +1 -0
- package/dist/middleware/plan-guard.js +190 -0
- package/dist/middleware/plan-guard.js.map +1 -0
- package/dist/plans/index.d.ts +71 -0
- package/dist/plans/index.d.ts.map +1 -0
- package/dist/plans/index.js +252 -0
- package/dist/plans/index.js.map +1 -0
- package/dist/schemas/component.schema.json +114 -0
- package/dist/server.d.ts +96 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +258 -0
- package/dist/server.js.map +1 -0
- package/dist/store/empty-manifest.d.ts +58 -0
- package/dist/store/empty-manifest.d.ts.map +1 -0
- package/dist/store/empty-manifest.js +59 -0
- package/dist/store/empty-manifest.js.map +1 -0
- package/dist/store/git-host.d.ts +120 -0
- package/dist/store/git-host.d.ts.map +1 -0
- package/dist/store/git-host.js +796 -0
- package/dist/store/git-host.js.map +1 -0
- package/dist/store/index.d.ts +5 -0
- package/dist/store/index.d.ts.map +1 -0
- package/dist/store/index.js +4 -0
- package/dist/store/index.js.map +1 -0
- package/dist/store/interface.d.ts +322 -0
- package/dist/store/interface.d.ts.map +1 -0
- package/dist/store/interface.js +100 -0
- package/dist/store/interface.js.map +1 -0
- package/dist/store/kit-files.d.ts +88 -0
- package/dist/store/kit-files.d.ts.map +1 -0
- package/dist/store/kit-files.js +199 -0
- package/dist/store/kit-files.js.map +1 -0
- package/dist/store/local.d.ts +70 -0
- package/dist/store/local.d.ts.map +1 -0
- package/dist/store/local.js +651 -0
- package/dist/store/local.js.map +1 -0
- package/dist/store/manifest.d.ts +44 -0
- package/dist/store/manifest.d.ts.map +1 -0
- package/dist/store/manifest.js +116 -0
- package/dist/store/manifest.js.map +1 -0
- package/dist/store/viewer-assets.d.ts +20 -0
- package/dist/store/viewer-assets.d.ts.map +1 -0
- package/dist/store/viewer-assets.js +118 -0
- package/dist/store/viewer-assets.js.map +1 -0
- package/dist/sync/anchor.d.ts +125 -0
- package/dist/sync/anchor.d.ts.map +1 -0
- package/dist/sync/anchor.js +210 -0
- package/dist/sync/anchor.js.map +1 -0
- package/dist/sync/index.d.ts +15 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +15 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/orchestrator.d.ts +160 -0
- package/dist/sync/orchestrator.d.ts.map +1 -0
- package/dist/sync/orchestrator.js +288 -0
- package/dist/sync/orchestrator.js.map +1 -0
- package/dist/tools/bind_kit.d.ts +29 -0
- package/dist/tools/bind_kit.d.ts.map +1 -0
- package/dist/tools/bind_kit.js +70 -0
- package/dist/tools/bind_kit.js.map +1 -0
- package/dist/tools/conjure.d.ts +141 -0
- package/dist/tools/conjure.d.ts.map +1 -0
- package/dist/tools/conjure.js +655 -0
- package/dist/tools/conjure.js.map +1 -0
- package/dist/tools/conjure_screen.d.ts +174 -0
- package/dist/tools/conjure_screen.d.ts.map +1 -0
- package/dist/tools/conjure_screen.js +458 -0
- package/dist/tools/conjure_screen.js.map +1 -0
- package/dist/tools/create_kit.d.ts +32 -0
- package/dist/tools/create_kit.d.ts.map +1 -0
- package/dist/tools/create_kit.js +131 -0
- package/dist/tools/create_kit.js.map +1 -0
- package/dist/tools/create_project.d.ts +267 -0
- package/dist/tools/create_project.d.ts.map +1 -0
- package/dist/tools/create_project.js +546 -0
- package/dist/tools/create_project.js.map +1 -0
- package/dist/tools/delete_files.d.ts +93 -0
- package/dist/tools/delete_files.d.ts.map +1 -0
- package/dist/tools/delete_files.js +276 -0
- package/dist/tools/delete_files.js.map +1 -0
- package/dist/tools/delete_project.d.ts +43 -0
- package/dist/tools/delete_project.d.ts.map +1 -0
- package/dist/tools/delete_project.js +118 -0
- package/dist/tools/delete_project.js.map +1 -0
- package/dist/tools/get_kit.d.ts +28 -0
- package/dist/tools/get_kit.d.ts.map +1 -0
- package/dist/tools/get_kit.js +95 -0
- package/dist/tools/get_kit.js.map +1 -0
- package/dist/tools/get_project.d.ts +37 -0
- package/dist/tools/get_project.d.ts.map +1 -0
- package/dist/tools/get_project.js +71 -0
- package/dist/tools/get_project.js.map +1 -0
- package/dist/tools/list_components.d.ts +42 -0
- package/dist/tools/list_components.d.ts.map +1 -0
- package/dist/tools/list_components.js +125 -0
- package/dist/tools/list_components.js.map +1 -0
- package/dist/tools/list_files.d.ts +32 -0
- package/dist/tools/list_files.d.ts.map +1 -0
- package/dist/tools/list_files.js +109 -0
- package/dist/tools/list_files.js.map +1 -0
- package/dist/tools/list_kits.d.ts +14 -0
- package/dist/tools/list_kits.d.ts.map +1 -0
- package/dist/tools/list_kits.js +41 -0
- package/dist/tools/list_kits.js.map +1 -0
- package/dist/tools/list_projects.d.ts +23 -0
- package/dist/tools/list_projects.d.ts.map +1 -0
- package/dist/tools/list_projects.js +66 -0
- package/dist/tools/list_projects.js.map +1 -0
- package/dist/tools/plan.d.ts +16 -0
- package/dist/tools/plan.d.ts.map +1 -0
- package/dist/tools/plan.js +146 -0
- package/dist/tools/plan.js.map +1 -0
- package/dist/tools/preview.d.ts +289 -0
- package/dist/tools/preview.d.ts.map +1 -0
- package/dist/tools/preview.js +672 -0
- package/dist/tools/preview.js.map +1 -0
- package/dist/tools/read_file.d.ts +40 -0
- package/dist/tools/read_file.d.ts.map +1 -0
- package/dist/tools/read_file.js +114 -0
- package/dist/tools/read_file.js.map +1 -0
- package/dist/tools/refine.d.ts +220 -0
- package/dist/tools/refine.d.ts.map +1 -0
- package/dist/tools/refine.js +501 -0
- package/dist/tools/refine.js.map +1 -0
- package/dist/tools/validate.d.ts +74 -0
- package/dist/tools/validate.d.ts.map +1 -0
- package/dist/tools/validate.js +224 -0
- package/dist/tools/validate.js.map +1 -0
- package/dist/tools/write_files.d.ts +158 -0
- package/dist/tools/write_files.d.ts.map +1 -0
- package/dist/tools/write_files.js +541 -0
- package/dist/tools/write_files.js.map +1 -0
- package/dist/transport.d.ts +77 -0
- package/dist/transport.d.ts.map +1 -0
- package/dist/transport.js +448 -0
- package/dist/transport.js.map +1 -0
- package/dist/ui/card-asset-broker.d.ts +42 -0
- package/dist/ui/card-asset-broker.d.ts.map +1 -0
- package/dist/ui/card-asset-broker.js +479 -0
- package/dist/ui/card-asset-broker.js.map +1 -0
- package/dist/ui/grid-resource.d.ts +252 -0
- package/dist/ui/grid-resource.d.ts.map +1 -0
- package/dist/ui/grid-resource.js +680 -0
- package/dist/ui/grid-resource.js.map +1 -0
- package/dist/ui/viewer-static/index.html +95 -0
- package/dist/ui/viewer-static/viewer.css +451 -0
- package/dist/ui/viewer-static/viewer.js +1405 -0
- package/dist/validate/full-scan.d.ts +87 -0
- package/dist/validate/full-scan.d.ts.map +1 -0
- package/dist/validate/full-scan.js +299 -0
- package/dist/validate/full-scan.js.map +1 -0
- package/dist/validate/index.d.ts +15 -0
- package/dist/validate/index.d.ts.map +1 -0
- package/dist/validate/index.js +18 -0
- package/dist/validate/index.js.map +1 -0
- package/dist/validate/marker.d.ts +89 -0
- package/dist/validate/marker.d.ts.map +1 -0
- package/dist/validate/marker.js +87 -0
- package/dist/validate/marker.js.map +1 -0
- package/dist/validate/phash.d.ts +135 -0
- package/dist/validate/phash.d.ts.map +1 -0
- package/dist/validate/phash.js +307 -0
- package/dist/validate/phash.js.map +1 -0
- package/dist/validate/render.d.ts +38 -0
- package/dist/validate/render.d.ts.map +1 -0
- package/dist/validate/render.js +155 -0
- package/dist/validate/render.js.map +1 -0
- package/dist/watch/index.d.ts +12 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +12 -0
- package/dist/watch/index.js.map +1 -0
- package/dist/watch/watcher.d.ts +65 -0
- package/dist/watch/watcher.d.ts.map +1 -0
- package/dist/watch/watcher.js +238 -0
- package/dist/watch/watcher.js.map +1 -0
- package/package.json +70 -0
|
@@ -0,0 +1,546 @@
|
|
|
1
|
+
import { cp, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { join, relative } from "node:path";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { getKit, ProjectNotFoundError, WrongProjectTypeError } from "./get_kit.js";
|
|
6
|
+
export const CREATE_PROJECT_TOOL_NAME = "mcp__genie__create_project";
|
|
7
|
+
export const PROJECT_ID_PATTERN = /^[a-z0-9-]{3,64}$/;
|
|
8
|
+
const projectKindSchema = z.enum(["workspace", "blueprint"]);
|
|
9
|
+
/** Shared shape for a single kit binding entry (see `KitBinding`). Exported so
|
|
10
|
+
* `bind_kit`'s output schema can't drift from what `create_project`/`get_project`
|
|
11
|
+
* already declare for the same field (mirrors the `projectSummaryShape` pattern
|
|
12
|
+
* just below). */
|
|
13
|
+
export const kitBindingShape = {
|
|
14
|
+
kitId: z.string().min(1),
|
|
15
|
+
default: z.boolean().optional(),
|
|
16
|
+
};
|
|
17
|
+
const kitBindingSchema = z.object(kitBindingShape).strict();
|
|
18
|
+
const createProjectArgsSchema = z
|
|
19
|
+
.object({
|
|
20
|
+
name: z.string().min(1).max(128),
|
|
21
|
+
kind: projectKindSchema,
|
|
22
|
+
fromBlueprintId: z.string().regex(PROJECT_ID_PATTERN).optional(),
|
|
23
|
+
kitBindings: z.array(kitBindingSchema).max(32).optional(),
|
|
24
|
+
})
|
|
25
|
+
.strict();
|
|
26
|
+
/** A recorded screen artifact within a project (written by `conjure_screen`, M1-21). */
|
|
27
|
+
const projectScreenSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
id: z.string(),
|
|
30
|
+
path: z.string(),
|
|
31
|
+
title: z.string(),
|
|
32
|
+
updatedAt: z.string(),
|
|
33
|
+
})
|
|
34
|
+
.strict();
|
|
35
|
+
const projectManifestSchema = z
|
|
36
|
+
.object({
|
|
37
|
+
id: z.string().regex(PROJECT_ID_PATTERN),
|
|
38
|
+
name: z.string(),
|
|
39
|
+
kind: projectKindSchema,
|
|
40
|
+
defaultKitId: z.string().optional(),
|
|
41
|
+
kitBindings: z.array(kitBindingSchema),
|
|
42
|
+
createdAt: z.string(),
|
|
43
|
+
updatedAt: z.string(),
|
|
44
|
+
sourceBlueprintId: z.string().optional(),
|
|
45
|
+
// Optional + absent from every manifest `create_project` writes today: no M1 tool
|
|
46
|
+
// records screens yet (that lands with `conjure_screen`, M1-21). Optional keeps
|
|
47
|
+
// existing on-disk manifests parsing unchanged; `get_project` defaults this to `[]`.
|
|
48
|
+
screens: z.array(projectScreenSchema).optional(),
|
|
49
|
+
})
|
|
50
|
+
.strict();
|
|
51
|
+
/**
|
|
52
|
+
* Shared output shape for a `ProjectSummary`, consumed by both `list_projects` and
|
|
53
|
+
* `get_project` so the two tools' schemas can't drift apart (Implementation Notes:
|
|
54
|
+
* "Share schema helpers with list_projects").
|
|
55
|
+
*/
|
|
56
|
+
export const projectSummaryShape = {
|
|
57
|
+
id: z.string().regex(PROJECT_ID_PATTERN),
|
|
58
|
+
name: z.string(),
|
|
59
|
+
kind: projectKindSchema,
|
|
60
|
+
defaultKitId: z.string().optional(),
|
|
61
|
+
kitBindings: z.array(kitBindingSchema),
|
|
62
|
+
updatedAt: z.string(),
|
|
63
|
+
canEdit: z.boolean(),
|
|
64
|
+
};
|
|
65
|
+
/** Shared output shape for a single recorded screen entry (see `ProjectDetail.screens`). */
|
|
66
|
+
export const projectScreenShape = {
|
|
67
|
+
id: z.string(),
|
|
68
|
+
path: z.string(),
|
|
69
|
+
title: z.string(),
|
|
70
|
+
updatedAt: z.string(),
|
|
71
|
+
};
|
|
72
|
+
export class ProjectStoreError extends Error {
|
|
73
|
+
code;
|
|
74
|
+
suggestedSlug;
|
|
75
|
+
projectId;
|
|
76
|
+
kitId;
|
|
77
|
+
constructor(code, message, options = {}) {
|
|
78
|
+
super(message);
|
|
79
|
+
this.name = "ProjectStoreError";
|
|
80
|
+
this.code = code;
|
|
81
|
+
this.suggestedSlug = options.suggestedSlug;
|
|
82
|
+
this.projectId = options.projectId;
|
|
83
|
+
this.kitId = options.kitId;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
export class ProjectStore {
|
|
87
|
+
root;
|
|
88
|
+
kitStore;
|
|
89
|
+
/**
|
|
90
|
+
* `kitStore` is optional so every existing single-arg `new ProjectStore(root)`
|
|
91
|
+
* call site (this file's + sibling tools' tests, `server.ts` prior to M1-20)
|
|
92
|
+
* keeps compiling unchanged. It is required in practice only by `bindKit`,
|
|
93
|
+
* which asserts its presence at call time — `server.ts` always supplies the
|
|
94
|
+
* real `LocalFsKitStore` it already constructs for the kit verbs.
|
|
95
|
+
*/
|
|
96
|
+
constructor(root, kitStore) {
|
|
97
|
+
this.root = root;
|
|
98
|
+
this.kitStore = kitStore;
|
|
99
|
+
}
|
|
100
|
+
async createProject(args) {
|
|
101
|
+
const parsed = createProjectArgsSchema.parse(args);
|
|
102
|
+
if (parsed.fromBlueprintId && parsed.kind !== "workspace") {
|
|
103
|
+
throw new ProjectStoreError("ERR_BLUEPRINT_NOT_FOUND", "Blueprint instantiation is only supported for workspace projects.");
|
|
104
|
+
}
|
|
105
|
+
await mkdir(this.root, { recursive: true });
|
|
106
|
+
const projectId = slugify(parsed.name);
|
|
107
|
+
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
108
|
+
throw new ProjectStoreError("ERR_INVALID_PROJECT_NAME", "Project name must produce a 3-64 character slug containing only lowercase letters, numbers, and hyphens.");
|
|
109
|
+
}
|
|
110
|
+
const existing = await this.findExistingProject(parsed.name, projectId);
|
|
111
|
+
if (existing) {
|
|
112
|
+
throw new ProjectStoreError("ERR_PROJECT_EXISTS", `Project "${parsed.name}" already exists.`, {
|
|
113
|
+
suggestedSlug: await this.suggestSlug(projectId),
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
const sourceBlueprint = parsed.fromBlueprintId
|
|
117
|
+
? await this.readBlueprint(parsed.fromBlueprintId)
|
|
118
|
+
: undefined;
|
|
119
|
+
const targetRoot = this.projectRoot(projectId);
|
|
120
|
+
if (sourceBlueprint) {
|
|
121
|
+
await cp(this.projectRoot(sourceBlueprint.id), targetRoot, {
|
|
122
|
+
recursive: true,
|
|
123
|
+
errorOnExist: true,
|
|
124
|
+
force: false,
|
|
125
|
+
filter: (source) => relative(this.projectRoot(sourceBlueprint.id), source).replaceAll("\\", "/") !==
|
|
126
|
+
".genie/project.json",
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
await mkdir(targetRoot, { recursive: false });
|
|
131
|
+
}
|
|
132
|
+
const now = new Date().toISOString();
|
|
133
|
+
const kitBindings = parsed.kitBindings ?? sourceBlueprint?.kitBindings ?? [];
|
|
134
|
+
const manifestDefaultKitId = defaultKitId(kitBindings);
|
|
135
|
+
const manifest = {
|
|
136
|
+
id: projectId,
|
|
137
|
+
name: parsed.name,
|
|
138
|
+
kind: parsed.kind,
|
|
139
|
+
...(manifestDefaultKitId ? { defaultKitId: manifestDefaultKitId } : {}),
|
|
140
|
+
kitBindings,
|
|
141
|
+
createdAt: now,
|
|
142
|
+
updatedAt: now,
|
|
143
|
+
...(sourceBlueprint ? { sourceBlueprintId: sourceBlueprint.id } : {}),
|
|
144
|
+
};
|
|
145
|
+
await this.writeManifest(projectId, manifest);
|
|
146
|
+
return { projectId };
|
|
147
|
+
}
|
|
148
|
+
async listProjects() {
|
|
149
|
+
await mkdir(this.root, { recursive: true });
|
|
150
|
+
const entries = await readdir(this.root, { withFileTypes: true });
|
|
151
|
+
const projects = [];
|
|
152
|
+
for (const entry of entries) {
|
|
153
|
+
if (!entry.isDirectory())
|
|
154
|
+
continue;
|
|
155
|
+
if (!PROJECT_ID_PATTERN.test(entry.name))
|
|
156
|
+
continue;
|
|
157
|
+
const manifest = await this.readListManifest(entry.name);
|
|
158
|
+
if (!manifest)
|
|
159
|
+
continue;
|
|
160
|
+
projects.push(projectSummary(manifest));
|
|
161
|
+
}
|
|
162
|
+
return projects.sort(compareProjectSummaries);
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Get full detail for a single project (workspace or blueprint — same shape,
|
|
166
|
+
* no special-case tool family; AC4). Throws `ProjectStoreError("ERR_PROJECT_NOT_FOUND")`
|
|
167
|
+
* with the id echoed (AC5) for a missing, unreadable, or malformed manifest — the same
|
|
168
|
+
* leniency `readListManifest` already applies when enumerating projects.
|
|
169
|
+
*
|
|
170
|
+
* Validates `projectId` against `PROJECT_ID_PATTERN` before any filesystem access —
|
|
171
|
+
* matching `createProject`/`listProjects`, which only ever touch paths built from
|
|
172
|
+
* slug-shaped ids. `get_project`'s MCP tool layer already rejects non-slug ids via
|
|
173
|
+
* its own regex-typed input schema, but `ProjectStore` is a public export and
|
|
174
|
+
* `getProject` a public method: a direct/programmatic caller (bypassing the MCP
|
|
175
|
+
* schema) could otherwise pass something like `"../secret"` and probe paths outside
|
|
176
|
+
* the projects root. Rejecting here, before `readListManifest`/`join()`, closes that
|
|
177
|
+
* gap without changing the external contract (still `ERR_PROJECT_NOT_FOUND`).
|
|
178
|
+
*/
|
|
179
|
+
async getProject(projectId) {
|
|
180
|
+
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
181
|
+
throw new ProjectStoreError("ERR_PROJECT_NOT_FOUND", `Project "${projectId}" was not found.`, { projectId });
|
|
182
|
+
}
|
|
183
|
+
const manifest = await this.readListManifest(projectId);
|
|
184
|
+
if (!manifest) {
|
|
185
|
+
throw new ProjectStoreError("ERR_PROJECT_NOT_FOUND", `Project "${projectId}" was not found.`, { projectId });
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
...projectSummary(manifest),
|
|
189
|
+
canEdit: !existsSync(join(this.projectRoot(projectId), ".genie", ".readonly")),
|
|
190
|
+
screens: manifest.screens ?? [],
|
|
191
|
+
...(manifest.sourceBlueprintId ? { sourceBlueprintId: manifest.sourceBlueprintId } : {}),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Delete a project directory (M1-14a-1 / DRO-531 — the `delete_project`
|
|
196
|
+
* store-injection re-plumb). Owns the *persistence policy* the
|
|
197
|
+
* `delete_project` tool previously reached into the filesystem for directly,
|
|
198
|
+
* so the verb now routes through the injected `ProjectStore` (LocalFs by
|
|
199
|
+
* default, a git-host backend when one exists) instead of a raw `projectsRoot`
|
|
200
|
+
* path + `node:fs`.
|
|
201
|
+
*
|
|
202
|
+
* Semantics (preserved verbatim from the pre-seam tool):
|
|
203
|
+
* - Missing project → NOT an error. Returns `{ existed: false }` so the tool
|
|
204
|
+
* can report the idempotent "already deleted" warning. A caller re-running
|
|
205
|
+
* a delete after a crash must not see a hard failure.
|
|
206
|
+
* - Read-only project (`.genie/.readonly` marker present) → throws
|
|
207
|
+
* `ProjectStoreError("ERR_PROJECT_READONLY")`; nothing is removed. Same
|
|
208
|
+
* guard `bindKit`/`recordScreen` apply on the write path.
|
|
209
|
+
* - Otherwise removes the project tree recursively and returns
|
|
210
|
+
* `{ existed: true }`. Deleting a blueprint does NOT touch workspaces
|
|
211
|
+
* instantiated from it — those were copied at create time and live in
|
|
212
|
+
* their own directories.
|
|
213
|
+
*
|
|
214
|
+
* `projectId` is regex-checked before any filesystem access (same
|
|
215
|
+
* defense-in-depth as `getProject`/`bindKit`/`recordScreen`: `ProjectStore` is
|
|
216
|
+
* a public export and this a public method, so a direct caller bypassing the
|
|
217
|
+
* tool's Zod schema could otherwise pass `"../secret"` into `projectRoot()`).
|
|
218
|
+
* A malformed id resolves nothing to delete → `{ existed: false }`, never an
|
|
219
|
+
* `rm` outside the projects root.
|
|
220
|
+
*/
|
|
221
|
+
async deleteProject(projectId) {
|
|
222
|
+
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
223
|
+
// Defense in depth: a non-slug id never names a real project directory.
|
|
224
|
+
// Treat as "nothing here" rather than resolving a traversal path — the
|
|
225
|
+
// tool's Zod schema is the primary gate and already rejects these with
|
|
226
|
+
// ERR_INVALID_PROJECT_ID before the store is reached.
|
|
227
|
+
return { existed: false };
|
|
228
|
+
}
|
|
229
|
+
const root = this.projectRoot(projectId);
|
|
230
|
+
if (!existsSync(root)) {
|
|
231
|
+
return { existed: false };
|
|
232
|
+
}
|
|
233
|
+
if (existsSync(join(root, ".genie", ".readonly"))) {
|
|
234
|
+
throw new ProjectStoreError("ERR_PROJECT_READONLY", `Project "${projectId}" is read-only and cannot be deleted.`, { projectId });
|
|
235
|
+
}
|
|
236
|
+
await rm(root, { recursive: true, force: true });
|
|
237
|
+
return { existed: true };
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Bind a kit to a project (M1-20). Writes the binding into
|
|
241
|
+
* `.genie/project.json` and returns the updated summary.
|
|
242
|
+
*
|
|
243
|
+
* - AC3: a new `kitId` is appended to `kitBindings`.
|
|
244
|
+
* - AC4: `default: true` sets `defaultKitId` and clears `default` from every
|
|
245
|
+
* other binding, so exactly one binding is ever the default. `default: false`
|
|
246
|
+
* clears default status from just this binding (if it held it). Omitting
|
|
247
|
+
* `default` entirely preserves whatever this binding's current default
|
|
248
|
+
* status already is (or `false` for a brand-new binding) — this is what
|
|
249
|
+
* keeps a same-kit re-bind idempotent (AC8) instead of silently demoting an
|
|
250
|
+
* existing default on every unrelated re-bind call.
|
|
251
|
+
* - AC5/AC6: validates the project and kit both exist first — no partial
|
|
252
|
+
* writes on a bad reference. `projectId` is also regex-checked up front
|
|
253
|
+
* (same defense-in-depth as `getProject`, above) before any filesystem
|
|
254
|
+
* access.
|
|
255
|
+
* - AC7: no blueprint special-case; `createProject` already copies
|
|
256
|
+
* `kitBindings` into a workspace instantiated `fromBlueprintId`, so a
|
|
257
|
+
* binding recorded here on a blueprint carries forward automatically.
|
|
258
|
+
* - AC8: re-binding the same `kitId` updates that one entry in place rather
|
|
259
|
+
* than appending a duplicate.
|
|
260
|
+
*/
|
|
261
|
+
async bindKit(args) {
|
|
262
|
+
const { projectId, kitId, default: isDefault } = args;
|
|
263
|
+
// Same defense-in-depth as `getProject` (above): `ProjectStore` is a public
|
|
264
|
+
// export and `bindKit` a public method, so a direct/programmatic caller
|
|
265
|
+
// bypassing the MCP schema's regex-typed input could otherwise pass
|
|
266
|
+
// something like `"../secret"` and probe paths outside the projects root
|
|
267
|
+
// via `projectRoot()`/`join()` below. Reject before any filesystem access,
|
|
268
|
+
// same external contract (`ERR_PROJECT_NOT_FOUND`).
|
|
269
|
+
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
270
|
+
throw new ProjectStoreError("ERR_PROJECT_NOT_FOUND", `Project "${projectId}" was not found.`, { projectId });
|
|
271
|
+
}
|
|
272
|
+
const manifest = await this.readListManifest(projectId);
|
|
273
|
+
if (!manifest) {
|
|
274
|
+
throw new ProjectStoreError("ERR_PROJECT_NOT_FOUND", `Project "${projectId}" was not found.`, { projectId });
|
|
275
|
+
}
|
|
276
|
+
if (existsSync(join(this.projectRoot(projectId), ".genie", ".readonly"))) {
|
|
277
|
+
throw new ProjectStoreError("ERR_PROJECT_READONLY", `Project "${projectId}" is read-only and cannot be modified.`, { projectId });
|
|
278
|
+
}
|
|
279
|
+
await this.assertKitExists(kitId);
|
|
280
|
+
const nextKitBindings = upsertKitBinding(manifest.kitBindings, kitId, isDefault);
|
|
281
|
+
// Recompute the top-level `defaultKitId` from the new bindings rather than
|
|
282
|
+
// carrying over `manifest.defaultKitId` — same pattern `createProject` uses
|
|
283
|
+
// (`defaultKitId(kitBindings)`). Without this, an old default surviving in
|
|
284
|
+
// the spread below would shadow the newly-bound default in `projectSummary`,
|
|
285
|
+
// since that helper prefers a truthy stored `defaultKitId` over recomputing.
|
|
286
|
+
const nextDefaultKitId = defaultKitId(nextKitBindings);
|
|
287
|
+
const updated = {
|
|
288
|
+
...manifest,
|
|
289
|
+
kitBindings: nextKitBindings,
|
|
290
|
+
...(nextDefaultKitId ? { defaultKitId: nextDefaultKitId } : { defaultKitId: undefined }),
|
|
291
|
+
updatedAt: new Date().toISOString(),
|
|
292
|
+
};
|
|
293
|
+
await this.writeManifest(projectId, updated);
|
|
294
|
+
return projectSummary(updated);
|
|
295
|
+
}
|
|
296
|
+
/**
|
|
297
|
+
* Record a generated screen artifact reference into `.genie/project.json`
|
|
298
|
+
* (M1-21, `conjure_screen` → AC7). Appends to `manifest.screens` (the array
|
|
299
|
+
* `get_project` surfaces and defaults to `[]`) and bumps `updatedAt`, then
|
|
300
|
+
* returns the stored entry with its server-stamped `updatedAt`.
|
|
301
|
+
*
|
|
302
|
+
* Mirrors `bindKit`'s write-path guards exactly:
|
|
303
|
+
* - `projectId` is regex-checked before any filesystem access (same
|
|
304
|
+
* defense-in-depth as `getProject`/`bindKit`: `ProjectStore` is a public
|
|
305
|
+
* export and this a public method, so a direct caller bypassing the MCP
|
|
306
|
+
* schema could otherwise pass `"../secret"` into `projectRoot()`/`join()`).
|
|
307
|
+
* - a missing/malformed manifest → `ERR_PROJECT_NOT_FOUND` (id echoed).
|
|
308
|
+
* - a read-only project → `ERR_PROJECT_READONLY`; no partial write.
|
|
309
|
+
*
|
|
310
|
+
* The store persists only the *reference* (id/path/title/updatedAt); the
|
|
311
|
+
* artifact bytes are returned to the caller as an artifact set (Issue
|
|
312
|
+
* "Keep output as an artifact set"), never inlined into the manifest.
|
|
313
|
+
*/
|
|
314
|
+
async recordScreen(input) {
|
|
315
|
+
const { projectId, screen } = input;
|
|
316
|
+
if (!PROJECT_ID_PATTERN.test(projectId)) {
|
|
317
|
+
throw new ProjectStoreError("ERR_PROJECT_NOT_FOUND", `Project "${projectId}" was not found.`, { projectId });
|
|
318
|
+
}
|
|
319
|
+
const manifest = await this.readListManifest(projectId);
|
|
320
|
+
if (!manifest) {
|
|
321
|
+
throw new ProjectStoreError("ERR_PROJECT_NOT_FOUND", `Project "${projectId}" was not found.`, { projectId });
|
|
322
|
+
}
|
|
323
|
+
if (existsSync(join(this.projectRoot(projectId), ".genie", ".readonly"))) {
|
|
324
|
+
throw new ProjectStoreError("ERR_PROJECT_READONLY", `Project "${projectId}" is read-only and cannot be modified.`, { projectId });
|
|
325
|
+
}
|
|
326
|
+
const now = new Date().toISOString();
|
|
327
|
+
const recorded = {
|
|
328
|
+
id: screen.id,
|
|
329
|
+
path: screen.path,
|
|
330
|
+
title: screen.title,
|
|
331
|
+
updatedAt: now,
|
|
332
|
+
};
|
|
333
|
+
const updated = {
|
|
334
|
+
...manifest,
|
|
335
|
+
screens: [...(manifest.screens ?? []), recorded],
|
|
336
|
+
updatedAt: now,
|
|
337
|
+
};
|
|
338
|
+
await this.writeManifest(projectId, updated);
|
|
339
|
+
return recorded;
|
|
340
|
+
}
|
|
341
|
+
/**
|
|
342
|
+
* Verify `kitId` resolves to a writable `GENIE_KIT`, reusing `get_kit`'s own
|
|
343
|
+
* validation (Implementation Notes: "Reuse get_kit validation before writing
|
|
344
|
+
* the project manifest") rather than re-deriving kit-lookup semantics here.
|
|
345
|
+
* Maps every rejection shape `getKit` can throw — a malformed id (Zod), a
|
|
346
|
+
* missing kit (`ProjectNotFoundError`), or a non-kit project
|
|
347
|
+
* (`WrongProjectTypeError`) — onto the single `ERR_KIT_NOT_FOUND` code
|
|
348
|
+
* `bind_kit`'s own AC6 specifies.
|
|
349
|
+
*/
|
|
350
|
+
async assertKitExists(kitId) {
|
|
351
|
+
if (!this.kitStore) {
|
|
352
|
+
// Every production wiring path (server.ts) supplies a KitStore alongside
|
|
353
|
+
// the ProjectStore. This is only reachable if a caller constructs
|
|
354
|
+
// `new ProjectStore(root)` (no second arg) and then calls `bindKit`
|
|
355
|
+
// directly — fail loudly rather than silently skipping kit validation.
|
|
356
|
+
throw new Error("ProjectStore.bindKit requires a KitStore: construct with `new ProjectStore(root, kitStore)`.");
|
|
357
|
+
}
|
|
358
|
+
try {
|
|
359
|
+
await getKit(this.kitStore, { kitId });
|
|
360
|
+
}
|
|
361
|
+
catch (error) {
|
|
362
|
+
if (error instanceof ProjectNotFoundError ||
|
|
363
|
+
error instanceof WrongProjectTypeError ||
|
|
364
|
+
error instanceof z.ZodError) {
|
|
365
|
+
throw new ProjectStoreError("ERR_KIT_NOT_FOUND", `Kit "${kitId}" was not found.`, {
|
|
366
|
+
kitId,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
throw error;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
projectRoot(projectId) {
|
|
373
|
+
return join(this.root, projectId);
|
|
374
|
+
}
|
|
375
|
+
manifestPath(projectId) {
|
|
376
|
+
return join(this.projectRoot(projectId), ".genie", "project.json");
|
|
377
|
+
}
|
|
378
|
+
async writeManifest(projectId, manifest) {
|
|
379
|
+
await mkdir(join(this.projectRoot(projectId), ".genie"), { recursive: true });
|
|
380
|
+
await writeFile(this.manifestPath(projectId), `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
|
|
381
|
+
}
|
|
382
|
+
async readManifest(projectId) {
|
|
383
|
+
const path = this.manifestPath(projectId);
|
|
384
|
+
if (!existsSync(path))
|
|
385
|
+
return undefined;
|
|
386
|
+
const raw = await readFile(path, "utf8");
|
|
387
|
+
const manifest = projectManifestSchema.parse(JSON.parse(raw));
|
|
388
|
+
if (manifest.id !== projectId) {
|
|
389
|
+
throw new ProjectStoreError("ERR_BLUEPRINT_NOT_FOUND", `Project manifest id "${manifest.id}" does not match directory "${projectId}".`);
|
|
390
|
+
}
|
|
391
|
+
return manifest;
|
|
392
|
+
}
|
|
393
|
+
async readBlueprint(projectId) {
|
|
394
|
+
const manifest = await this.readManifest(projectId);
|
|
395
|
+
if (!manifest || manifest.kind !== "blueprint") {
|
|
396
|
+
throw new ProjectStoreError("ERR_BLUEPRINT_NOT_FOUND", `Blueprint "${projectId}" was not found.`);
|
|
397
|
+
}
|
|
398
|
+
return manifest;
|
|
399
|
+
}
|
|
400
|
+
async readListManifest(projectId) {
|
|
401
|
+
try {
|
|
402
|
+
return await this.readManifest(projectId);
|
|
403
|
+
}
|
|
404
|
+
catch (error) {
|
|
405
|
+
if (error instanceof SyntaxError ||
|
|
406
|
+
error instanceof z.ZodError ||
|
|
407
|
+
error instanceof ProjectStoreError) {
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
throw error;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
async findExistingProject(name, projectId) {
|
|
414
|
+
const entries = await readdir(this.root, { withFileTypes: true });
|
|
415
|
+
for (const entry of entries) {
|
|
416
|
+
if (!entry.isDirectory())
|
|
417
|
+
continue;
|
|
418
|
+
if (!PROJECT_ID_PATTERN.test(entry.name))
|
|
419
|
+
continue;
|
|
420
|
+
if (entry.name === projectId)
|
|
421
|
+
return (await this.readManifest(entry.name)) ?? undefined;
|
|
422
|
+
const manifest = await this.readManifest(entry.name);
|
|
423
|
+
if (manifest?.name.toLocaleLowerCase() === name.toLocaleLowerCase())
|
|
424
|
+
return manifest;
|
|
425
|
+
}
|
|
426
|
+
return undefined;
|
|
427
|
+
}
|
|
428
|
+
async suggestSlug(baseSlug) {
|
|
429
|
+
for (let suffix = 2; suffix < 1000; suffix += 1) {
|
|
430
|
+
const candidate = `${baseSlug}-${suffix}`;
|
|
431
|
+
if (!existsSync(this.projectRoot(candidate)))
|
|
432
|
+
return candidate;
|
|
433
|
+
}
|
|
434
|
+
return `${baseSlug}-${Date.now()}`;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
export const CREATE_PROJECT_DESCRIPTION = "Create a new blank workspace, create a reusable blueprint project, or instantiate a new " +
|
|
438
|
+
"workspace from a blueprint. Use this only when starting a new project; for an existing " +
|
|
439
|
+
"project call list_projects first and pass the selected project's id as projectId to " +
|
|
440
|
+
"bind_kit or conjure_screen.";
|
|
441
|
+
export function registerCreateProjectTool(server, store) {
|
|
442
|
+
server.registerTool(CREATE_PROJECT_TOOL_NAME, {
|
|
443
|
+
title: "Create project",
|
|
444
|
+
description: CREATE_PROJECT_DESCRIPTION,
|
|
445
|
+
inputSchema: {
|
|
446
|
+
name: z.string().min(1).max(128),
|
|
447
|
+
kind: projectKindSchema,
|
|
448
|
+
fromBlueprintId: z.string().regex(PROJECT_ID_PATTERN).optional(),
|
|
449
|
+
kitBindings: z.array(kitBindingSchema).max(32).optional(),
|
|
450
|
+
},
|
|
451
|
+
outputSchema: {
|
|
452
|
+
projectId: z.string(),
|
|
453
|
+
},
|
|
454
|
+
}, async (args) => {
|
|
455
|
+
try {
|
|
456
|
+
const result = await store.createProject(args);
|
|
457
|
+
return {
|
|
458
|
+
content: [{ type: "text", text: JSON.stringify(result) }],
|
|
459
|
+
structuredContent: result,
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
if (error instanceof ProjectStoreError) {
|
|
464
|
+
return {
|
|
465
|
+
isError: true,
|
|
466
|
+
content: [
|
|
467
|
+
{
|
|
468
|
+
type: "text",
|
|
469
|
+
text: JSON.stringify({
|
|
470
|
+
code: error.code,
|
|
471
|
+
message: error.message,
|
|
472
|
+
...(error.suggestedSlug ? { suggestedSlug: error.suggestedSlug } : {}),
|
|
473
|
+
}),
|
|
474
|
+
},
|
|
475
|
+
],
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
throw error;
|
|
479
|
+
}
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
function slugify(name) {
|
|
483
|
+
return name
|
|
484
|
+
.trim()
|
|
485
|
+
.toLocaleLowerCase()
|
|
486
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
487
|
+
.replace(/^-+|-+$/g, "")
|
|
488
|
+
.slice(0, 64)
|
|
489
|
+
.replace(/-+$/g, "");
|
|
490
|
+
}
|
|
491
|
+
function projectSummary(manifest) {
|
|
492
|
+
const defaultId = manifest.defaultKitId ?? defaultKitId(manifest.kitBindings);
|
|
493
|
+
return {
|
|
494
|
+
id: manifest.id,
|
|
495
|
+
name: manifest.name,
|
|
496
|
+
kind: manifest.kind,
|
|
497
|
+
...(defaultId ? { defaultKitId: defaultId } : {}),
|
|
498
|
+
kitBindings: manifest.kitBindings,
|
|
499
|
+
updatedAt: manifest.updatedAt,
|
|
500
|
+
canEdit: true,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
function defaultKitId(kitBindings) {
|
|
504
|
+
return kitBindings.find((binding) => binding.default)?.kitId;
|
|
505
|
+
}
|
|
506
|
+
/**
|
|
507
|
+
* Insert-or-update a single kit binding by `kitId` (AC8 — re-binding the same
|
|
508
|
+
* kit updates in place rather than appending a duplicate).
|
|
509
|
+
*
|
|
510
|
+
* `isDefault` semantics (AC4):
|
|
511
|
+
* - `true` → this binding becomes the default; `default` is cleared (set to
|
|
512
|
+
* `undefined`, not `false` — keeps existing manifests' shape, since a
|
|
513
|
+
* fresh binding never had the field at all) on every other binding.
|
|
514
|
+
* - `false` → this binding is explicitly marked not-default; every other
|
|
515
|
+
* binding's default status is left untouched.
|
|
516
|
+
* - `undefined` (omitted) → this binding's default status is left exactly as
|
|
517
|
+
* it already was (`false`/absent for a brand-new binding) — this is what
|
|
518
|
+
* makes an unrelated re-bind call idempotent instead of demoting an
|
|
519
|
+
* existing default as a side effect.
|
|
520
|
+
*/
|
|
521
|
+
function upsertKitBinding(bindings, kitId, isDefault) {
|
|
522
|
+
const existing = bindings.find((binding) => binding.kitId === kitId);
|
|
523
|
+
const nextDefault = isDefault ?? existing?.default ?? false;
|
|
524
|
+
const updatedBinding = { kitId, ...(nextDefault ? { default: true } : {}) };
|
|
525
|
+
// Clear default from every *other* binding when this one becomes the new
|
|
526
|
+
// default (AC4: "clears default status from any previous binding"); leave
|
|
527
|
+
// every other binding untouched otherwise. `KitBinding` only ever has
|
|
528
|
+
// `{ kitId, default? }` (kitBindingShape), so rebuilding with just `kitId`
|
|
529
|
+
// is exactly "clear default", not a lossy copy.
|
|
530
|
+
const clearOthersDefault = (binding) => nextDefault && binding.kitId !== kitId ? { kitId: binding.kitId } : binding;
|
|
531
|
+
if (!existing) {
|
|
532
|
+
return [...bindings.map(clearOthersDefault), updatedBinding];
|
|
533
|
+
}
|
|
534
|
+
// Update in place (AC8) — preserves existing binding order instead of
|
|
535
|
+
// moving the re-bound kit to the end of the array.
|
|
536
|
+
return bindings.map((binding) => binding.kitId === kitId ? updatedBinding : clearOthersDefault(binding));
|
|
537
|
+
}
|
|
538
|
+
function compareProjectSummaries(a, b) {
|
|
539
|
+
return compareText(a.kind, b.kind) || compareText(a.name, b.name) || compareText(a.id, b.id);
|
|
540
|
+
}
|
|
541
|
+
function compareText(a, b) {
|
|
542
|
+
const aNorm = a.normalize("NFC").toLowerCase();
|
|
543
|
+
const bNorm = b.normalize("NFC").toLowerCase();
|
|
544
|
+
return aNorm < bNorm ? -1 : aNorm > bNorm ? 1 : a < b ? -1 : a > b ? 1 : 0;
|
|
545
|
+
}
|
|
546
|
+
//# sourceMappingURL=create_project.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"create_project.js","sourceRoot":"","sources":["../../src/tools/create_project.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MCP tool: delete_files (M1-09; re-plumbed onto KitStore in M1-14a-1a / DRO-540).
|
|
3
|
+
*
|
|
4
|
+
* The plan-gated companion to `write_files`. Every requested path must be
|
|
5
|
+
* covered by a glob in the plan's `deletes` list (locked at `plan` time) —
|
|
6
|
+
* otherwise the whole call is rejected with `PathOutsidePlanError` and nothing
|
|
7
|
+
* is touched. A path that is authorized but no longer exists is NOT an error:
|
|
8
|
+
* it is recorded in `notFoundPaths` and the call still succeeds. This is the
|
|
9
|
+
* "known-good failure to silently retry past" from the research report — e.g.
|
|
10
|
+
* deleting a floor-card component's `_preview/*.html` that was never generated
|
|
11
|
+
* remotely.
|
|
12
|
+
*
|
|
13
|
+
* Plan-gating stays here (planId → `deletes` globs is a genie concept, not a
|
|
14
|
+
* store concept). The physical removal is delegated to the injected
|
|
15
|
+
* `KitStore.deleteFile`, so the SAME verb deletes from a `LocalFsKitStore` (fs
|
|
16
|
+
* unlink) or a `GitHostKitStore` (contents-API DELETE on the default branch)
|
|
17
|
+
* with identical gating and result shaping.
|
|
18
|
+
*
|
|
19
|
+
* Input: { planId: string, paths: string[] }
|
|
20
|
+
* Output: { deletedPaths: string[], notFoundPaths: string[] }
|
|
21
|
+
*
|
|
22
|
+
* Ordering: paths are deleted longest-first so a file is always removed before
|
|
23
|
+
* any (shorter) prefix that names its containing directory — avoiding a
|
|
24
|
+
* spurious ENOTEMPTY. (Recursive directory delete is out of scope: paths must
|
|
25
|
+
* name files. A directory target surfaces as a hard `DeleteFailed` error.)
|
|
26
|
+
*/
|
|
27
|
+
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
import type { KitStore } from "../store/interface.js";
|
|
30
|
+
export declare const DELETE_FILES_TOOL_NAME = "mcp__genie__delete_files";
|
|
31
|
+
export declare const DELETE_FILES_DESCRIPTION: string;
|
|
32
|
+
/**
|
|
33
|
+
* Authoritative argument schema. `.strict()` rejects unknown keys, and the
|
|
34
|
+
* per-item `.min(1)` guards against empty path strings. This is re-parsed
|
|
35
|
+
* inside `deleteFiles` so the same validation holds whether the tool is invoked
|
|
36
|
+
* over MCP or called directly (tests, future in-process callers).
|
|
37
|
+
*/
|
|
38
|
+
declare const deleteFilesArgsSchema: z.ZodObject<{
|
|
39
|
+
planId: z.ZodString;
|
|
40
|
+
paths: z.ZodArray<z.ZodString>;
|
|
41
|
+
}, z.core.$strict>;
|
|
42
|
+
export type DeleteFilesArgs = z.infer<typeof deleteFilesArgsSchema>;
|
|
43
|
+
export interface DeleteFilesResult extends Record<string, unknown> {
|
|
44
|
+
/** Paths that existed and were removed (AC4). */
|
|
45
|
+
deletedPaths: string[];
|
|
46
|
+
/** Authorized paths that did not exist — a non-error (AC5). */
|
|
47
|
+
notFoundPaths: string[];
|
|
48
|
+
}
|
|
49
|
+
export type DeleteFilesErrorCode = "InvalidArguments" | "PathOutsidePlanError" | "DeleteFailed";
|
|
50
|
+
/** Structured, client-facing error for the delete_files tool. */
|
|
51
|
+
export declare class DeleteFilesError extends Error {
|
|
52
|
+
readonly code: DeleteFilesErrorCode;
|
|
53
|
+
/** The offending path, when the error is path-specific. */
|
|
54
|
+
readonly path?: string;
|
|
55
|
+
constructor(code: DeleteFilesErrorCode, message: string, path?: string);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Sort paths so deeper/longer paths come first. Deleting a file before the
|
|
59
|
+
* (shorter) path that names its parent directory avoids ENOTEMPTY. Length ties
|
|
60
|
+
* fall back to `localeCompare` for a deterministic order. Pure — never mutates
|
|
61
|
+
* the caller's array.
|
|
62
|
+
*/
|
|
63
|
+
export declare function sortPathsLongestFirst(paths: string[]): string[];
|
|
64
|
+
/**
|
|
65
|
+
* Delete every `path` in `args.paths` from the plan's kit, subject to the
|
|
66
|
+
* plan's `deletes` allow-list.
|
|
67
|
+
*
|
|
68
|
+
* @param store The injected kit backend (M1-14a-1a / DRO-540). The concrete kit
|
|
69
|
+
* is `plan.kitId`; the physical removal is `store.deleteFile(plan.kitId, path)`
|
|
70
|
+
* — LocalFs unlink or git-host contents-API delete, behind one interface.
|
|
71
|
+
*/
|
|
72
|
+
export declare function deleteFiles(store: KitStore, args: DeleteFilesArgs): Promise<DeleteFilesResult>;
|
|
73
|
+
/**
|
|
74
|
+
* Register the `mcp__genie__delete_files` tool.
|
|
75
|
+
*
|
|
76
|
+
* AC1: Tool name `mcp__genie__delete_files`.
|
|
77
|
+
* AC2: Input `{ planId: string, paths: string[] }`.
|
|
78
|
+
* AC3: Each path must match a plan `deletes` glob, else PathOutsidePlanError.
|
|
79
|
+
* AC4: Returns `deletedPaths` — what was actually removed.
|
|
80
|
+
* AC5: A missing path is a non-error, recorded in `notFoundPaths`.
|
|
81
|
+
* AC6: Other errors (permission denied, directory target, …) fail the call.
|
|
82
|
+
*
|
|
83
|
+
* Plan-boundary validation (planId presence/existence/expiry, path-vs-
|
|
84
|
+
* `deletes`-glob) runs through the M1-13 plan-guard middleware
|
|
85
|
+
* (`../middleware/plan-guard.ts`) so the write/delete verbs share one
|
|
86
|
+
* identical check. Delete-tool-specific validation (InvalidArguments shape,
|
|
87
|
+
* kitId containment, dot-segment rejection, unlink failure taxonomy) stays
|
|
88
|
+
* in `deleteFiles` itself — the guard is intentionally tool-agnostic about
|
|
89
|
+
* those.
|
|
90
|
+
*/
|
|
91
|
+
export declare function registerDeleteFilesTool(server: McpServer, store: KitStore): void;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=delete_files.d.ts.map
|