@thinkingsage/kanon 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +410 -0
- package/LICENSE +21 -0
- package/README.md +168 -0
- package/bridge/mcp-server.cjs +14171 -0
- package/package.json +98 -0
- package/src/adapters/capabilities.ts +178 -0
- package/src/adapters/claude-code.ts +110 -0
- package/src/adapters/cline.ts +98 -0
- package/src/adapters/codex.ts +173 -0
- package/src/adapters/copilot.ts +106 -0
- package/src/adapters/cursor.ts +97 -0
- package/src/adapters/degradation.ts +95 -0
- package/src/adapters/index.ts +324 -0
- package/src/adapters/kiro-frontmatter.ts +139 -0
- package/src/adapters/kiro-inclusion.ts +86 -0
- package/src/adapters/kiro.ts +412 -0
- package/src/adapters/qdeveloper.ts +115 -0
- package/src/adapters/types.ts +81 -0
- package/src/adapters/windsurf.ts +96 -0
- package/src/admin.ts +283 -0
- package/src/asset-conventions.ts +118 -0
- package/src/attribution-backfill.ts +319 -0
- package/src/attribution-report.ts +95 -0
- package/src/attribution.ts +239 -0
- package/src/backends/github.ts +194 -0
- package/src/backends/http.ts +122 -0
- package/src/backends/index.ts +39 -0
- package/src/backends/local.ts +47 -0
- package/src/backends/s3.ts +157 -0
- package/src/backends/types.ts +59 -0
- package/src/base-cache.ts +270 -0
- package/src/browse-ui.ts +3754 -0
- package/src/browse.ts +1038 -0
- package/src/build.ts +1108 -0
- package/src/catalog.ts +204 -0
- package/src/cli-deprecated.ts +29 -0
- package/src/cli.ts +773 -0
- package/src/collection-admin.ts +287 -0
- package/src/collection-builder.ts +464 -0
- package/src/collections.ts +116 -0
- package/src/compatibility.ts +105 -0
- package/src/config.ts +743 -0
- package/src/eval/rubrics/kiro-progressive-steering.ts +841 -0
- package/src/eval.ts +1169 -0
- package/src/file-writer.ts +61 -0
- package/src/format-registry.ts +141 -0
- package/src/guild/auto-updater.ts +163 -0
- package/src/guild/backend-resolver.ts +49 -0
- package/src/guild/cli.ts +592 -0
- package/src/guild/collection-expander.ts +47 -0
- package/src/guild/global-cache.ts +247 -0
- package/src/guild/hook-generator.ts +100 -0
- package/src/guild/manifest.ts +154 -0
- package/src/guild/path-utils.ts +12 -0
- package/src/guild/sync.ts +622 -0
- package/src/guild/version-resolver.ts +42 -0
- package/src/help/metadata.ts +445 -0
- package/src/help/renderer.ts +265 -0
- package/src/help/typo-suggester.ts +25 -0
- package/src/hooks/expression.ts +493 -0
- package/src/hooks/pipeline.ts +141 -0
- package/src/import.ts +773 -0
- package/src/importers/claude-code.ts +134 -0
- package/src/importers/cline.ts +103 -0
- package/src/importers/codex.ts +140 -0
- package/src/importers/copilot.ts +103 -0
- package/src/importers/cursor.ts +105 -0
- package/src/importers/index.ts +390 -0
- package/src/importers/kiro.ts +110 -0
- package/src/importers/qdeveloper.ts +103 -0
- package/src/importers/types.ts +54 -0
- package/src/importers/windsurf.ts +104 -0
- package/src/install.ts +1005 -0
- package/src/manifest-admin.ts +306 -0
- package/src/mcp-bridge.ts +240 -0
- package/src/mutation/delta.ts +50 -0
- package/src/mutation/history.ts +66 -0
- package/src/mutation/operators.ts +524 -0
- package/src/mutation/runner.ts +332 -0
- package/src/new.ts +106 -0
- package/src/outcomes/collision.ts +127 -0
- package/src/outcomes/normalize.ts +208 -0
- package/src/outcomes/registry.ts +173 -0
- package/src/parser.ts +446 -0
- package/src/provenance-backfill-cli.ts +319 -0
- package/src/provenance-backfill.ts +520 -0
- package/src/publish.ts +354 -0
- package/src/reconcile-orchestrator.ts +502 -0
- package/src/reconcile-report-renderer.ts +176 -0
- package/src/resolve-body.ts +15 -0
- package/src/rosetta/builtins/compatibility-profiles.ts +297 -0
- package/src/rosetta/builtins/contracts.ts +1033 -0
- package/src/rosetta/builtins/pretty-printers/claude-code-native.ts +122 -0
- package/src/rosetta/builtins/pretty-printers/cline-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/codex-native.ts +127 -0
- package/src/rosetta/builtins/pretty-printers/copilot-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/cursor-native.ts +50 -0
- package/src/rosetta/builtins/pretty-printers/index.ts +81 -0
- package/src/rosetta/builtins/pretty-printers/kiro-native.ts +166 -0
- package/src/rosetta/builtins/pretty-printers/kiro-power.ts +108 -0
- package/src/rosetta/builtins/pretty-printers/kiro-skill.ts +88 -0
- package/src/rosetta/builtins/pretty-printers/qdeveloper-native.ts +51 -0
- package/src/rosetta/builtins/pretty-printers/superpowers.ts +97 -0
- package/src/rosetta/builtins/pretty-printers/windsurf-native.ts +50 -0
- package/src/rosetta/builtins/sources/claude-code-native.ts +348 -0
- package/src/rosetta/builtins/sources/cline-native.ts +176 -0
- package/src/rosetta/builtins/sources/codex-native.ts +343 -0
- package/src/rosetta/builtins/sources/copilot-native.ts +178 -0
- package/src/rosetta/builtins/sources/cursor-native.ts +176 -0
- package/src/rosetta/builtins/sources/index.ts +95 -0
- package/src/rosetta/builtins/sources/kiro-native.ts +462 -0
- package/src/rosetta/builtins/sources/kiro-power.ts +285 -0
- package/src/rosetta/builtins/sources/kiro-skill.ts +230 -0
- package/src/rosetta/builtins/sources/qdeveloper-native.ts +181 -0
- package/src/rosetta/builtins/sources/superpowers.ts +240 -0
- package/src/rosetta/builtins/sources/windsurf-native.ts +176 -0
- package/src/rosetta/builtins/targets/claude-code.ts +181 -0
- package/src/rosetta/builtins/targets/cline.ts +87 -0
- package/src/rosetta/builtins/targets/codex.ts +226 -0
- package/src/rosetta/builtins/targets/copilot.ts +103 -0
- package/src/rosetta/builtins/targets/cursor.ts +87 -0
- package/src/rosetta/builtins/targets/index.ts +60 -0
- package/src/rosetta/builtins/targets/kiro.ts +278 -0
- package/src/rosetta/builtins/targets/qdeveloper.ts +103 -0
- package/src/rosetta/builtins/targets/windsurf.ts +87 -0
- package/src/rosetta/canonical.ts +729 -0
- package/src/rosetta/compatibility.ts +432 -0
- package/src/rosetta/contracts.ts +329 -0
- package/src/rosetta/detector.ts +724 -0
- package/src/rosetta/diagnostics.ts +630 -0
- package/src/rosetta/engine-bootstrap.ts +103 -0
- package/src/rosetta/engine.ts +744 -0
- package/src/rosetta/index.ts +381 -0
- package/src/rosetta/inspection.ts +530 -0
- package/src/rosetta/plan.ts +448 -0
- package/src/rosetta/provenance-digest.ts +369 -0
- package/src/rosetta/reconcile.ts +812 -0
- package/src/rosetta/redaction.ts +467 -0
- package/src/rosetta/registry.ts +712 -0
- package/src/rosetta/renderers.ts +571 -0
- package/src/rosetta/request-guard.ts +335 -0
- package/src/rosetta/resolution.ts +419 -0
- package/src/rosetta/source-accounting.ts +233 -0
- package/src/rosetta/templates.ts +129 -0
- package/src/rosetta-cli.ts +717 -0
- package/src/rosetta-docs-generator.ts +793 -0
- package/src/rosetta-profiles-cli.ts +367 -0
- package/src/schemas.ts +1712 -0
- package/src/spec-coordination.ts +1141 -0
- package/src/temper.ts +747 -0
- package/src/template-bundle-loader.ts +312 -0
- package/src/template-engine.ts +53 -0
- package/src/translation-application-policy.ts +496 -0
- package/src/translation-orchestrator.ts +1013 -0
- package/src/translation-plan-applier.ts +473 -0
- package/src/tutorial.ts +305 -0
- package/src/validate.ts +1093 -0
- package/src/versioning.ts +553 -0
- package/src/wizard.ts +660 -0
- package/src/workspace.ts +237 -0
- package/templates/eval-contexts/claude-code.md.njk +6 -0
- package/templates/eval-contexts/cline.md.njk +6 -0
- package/templates/eval-contexts/copilot.md.njk +6 -0
- package/templates/eval-contexts/cursor.md.njk +6 -0
- package/templates/eval-contexts/kiro.md.njk +10 -0
- package/templates/eval-contexts/qdeveloper.md.njk +6 -0
- package/templates/eval-contexts/windsurf.md.njk +6 -0
- package/templates/harness-adapters/_base/attribution-footer.md.njk +17 -0
- package/templates/harness-adapters/_base/base.md.njk +16 -0
- package/templates/harness-adapters/claude-code/claude.md.njk +1 -0
- package/templates/harness-adapters/claude-code/mcp.json.njk +1 -0
- package/templates/harness-adapters/claude-code/settings.json.njk +1 -0
- package/templates/harness-adapters/claude-code/skill-library-index.md.njk +13 -0
- package/templates/harness-adapters/claude-code/skill.md.njk +19 -0
- package/templates/harness-adapters/cline/hook.sh.njk +4 -0
- package/templates/harness-adapters/cline/mcp.json.njk +1 -0
- package/templates/harness-adapters/cline/rule.md.njk +1 -0
- package/templates/harness-adapters/codex/agents-md.md.njk +6 -0
- package/templates/harness-adapters/codex/agents-pointer.md.njk +16 -0
- package/templates/harness-adapters/codex/skill.md.njk +27 -0
- package/templates/harness-adapters/copilot/agents.md.njk +1 -0
- package/templates/harness-adapters/copilot/instructions.md.njk +1 -0
- package/templates/harness-adapters/copilot/scoped.md.njk +6 -0
- package/templates/harness-adapters/cursor/mcp.json.njk +1 -0
- package/templates/harness-adapters/cursor/rule.md.njk +6 -0
- package/templates/harness-adapters/kiro/hook.json.njk +1 -0
- package/templates/harness-adapters/kiro/mcp.json.njk +1 -0
- package/templates/harness-adapters/kiro/power-steering.md.njk +3 -0
- package/templates/harness-adapters/kiro/power.md.njk +12 -0
- package/templates/harness-adapters/kiro/steering.md.njk +16 -0
- package/templates/harness-adapters/qdeveloper/agent.md.njk +1 -0
- package/templates/harness-adapters/qdeveloper/mcp.json.njk +1 -0
- package/templates/harness-adapters/qdeveloper/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/mcp.json.njk +1 -0
- package/templates/harness-adapters/windsurf/rule.md.njk +1 -0
- package/templates/harness-adapters/windsurf/workflow.md.njk +1 -0
- package/templates/knowledge/hooks.yaml.njk +4 -0
- package/templates/knowledge/knowledge.md.njk +53 -0
- package/templates/knowledge/mcp-servers.yaml.njk +2 -0
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { exists, readdir, readFile, unlink, writeFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import * as yaml from "js-yaml";
|
|
4
|
+
import {
|
|
5
|
+
type CatalogEntry,
|
|
6
|
+
type Collection,
|
|
7
|
+
CollectionSchema,
|
|
8
|
+
} from "./schemas";
|
|
9
|
+
|
|
10
|
+
const KEBAB_CASE_PATTERN = /^[a-z0-9]+(-[a-z0-9]+)*$/;
|
|
11
|
+
|
|
12
|
+
/** Input shape for collection create/update requests */
|
|
13
|
+
export interface CollectionInput {
|
|
14
|
+
name: string;
|
|
15
|
+
displayName: string;
|
|
16
|
+
description: string;
|
|
17
|
+
version: string;
|
|
18
|
+
trust: string;
|
|
19
|
+
tags: string[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Validates CollectionInput against CollectionSchema and the kebab-case name pattern.
|
|
24
|
+
* Returns either the parsed Collection data or an array of field-level errors.
|
|
25
|
+
*/
|
|
26
|
+
export function validateCollectionInput(
|
|
27
|
+
input: CollectionInput,
|
|
28
|
+
):
|
|
29
|
+
| { success: true; data: Collection }
|
|
30
|
+
| { success: false; errors: Array<{ field: string; message: string }> } {
|
|
31
|
+
const errors: Array<{ field: string; message: string }> = [];
|
|
32
|
+
|
|
33
|
+
// Validate name against kebab-case pattern
|
|
34
|
+
if (!KEBAB_CASE_PATTERN.test(input.name)) {
|
|
35
|
+
errors.push({
|
|
36
|
+
field: "name",
|
|
37
|
+
message:
|
|
38
|
+
"Name must be kebab-case (lowercase alphanumeric segments separated by hyphens)",
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Build the collection object for schema validation
|
|
43
|
+
const collectionData: Record<string, unknown> = {
|
|
44
|
+
name: input.name,
|
|
45
|
+
displayName: input.displayName,
|
|
46
|
+
description: input.description,
|
|
47
|
+
version: input.version,
|
|
48
|
+
tags: input.tags,
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
if (input.trust) {
|
|
52
|
+
collectionData.trust = input.trust;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const result = CollectionSchema.safeParse(collectionData);
|
|
56
|
+
|
|
57
|
+
if (!result.success) {
|
|
58
|
+
for (const issue of result.error.issues) {
|
|
59
|
+
errors.push({
|
|
60
|
+
field: issue.path.join(".") || "unknown",
|
|
61
|
+
message: issue.message,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (errors.length > 0) {
|
|
67
|
+
return { success: false, errors };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return { success: true, data: result.data as Collection };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Serializes a Collection record to a YAML string using js-yaml.
|
|
75
|
+
* Accepts Record<string, unknown> to preserve unknown keys beyond the schema.
|
|
76
|
+
*/
|
|
77
|
+
export function serializeCollection(
|
|
78
|
+
collection: Record<string, unknown>,
|
|
79
|
+
): string {
|
|
80
|
+
return yaml.dump(collection, {
|
|
81
|
+
lineWidth: -1,
|
|
82
|
+
noRefs: true,
|
|
83
|
+
quoteStyle: "single",
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Parses a YAML string into a validated Collection, preserving unknown keys.
|
|
89
|
+
* Returns both the validated Collection object and the raw parsed object
|
|
90
|
+
* (which may contain keys beyond the schema).
|
|
91
|
+
*/
|
|
92
|
+
export function parseCollectionFile(yamlContent: string): {
|
|
93
|
+
collection: Collection;
|
|
94
|
+
raw: Record<string, unknown>;
|
|
95
|
+
} {
|
|
96
|
+
const parsed = yaml.load(yamlContent) as Record<string, unknown>;
|
|
97
|
+
|
|
98
|
+
if (!parsed || typeof parsed !== "object") {
|
|
99
|
+
throw new Error("Invalid YAML: expected an object");
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const result = CollectionSchema.safeParse(parsed);
|
|
103
|
+
|
|
104
|
+
if (!result.success) {
|
|
105
|
+
const messages = result.error.issues
|
|
106
|
+
.map((i) => `${i.path.join(".")}: ${i.message}`)
|
|
107
|
+
.join("; ");
|
|
108
|
+
throw new Error(`Collection validation failed: ${messages}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return { collection: result.data, raw: parsed };
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Lists all collections from the collections/ directory.
|
|
116
|
+
* Scans for .yaml files, parses each one, and returns an array of collection objects.
|
|
117
|
+
*/
|
|
118
|
+
export async function listCollections(
|
|
119
|
+
collectionsDir: string,
|
|
120
|
+
): Promise<Array<{ collection: Collection; raw: Record<string, unknown> }>> {
|
|
121
|
+
const entries = await readdir(collectionsDir);
|
|
122
|
+
const yamlFiles = entries.filter((f) => f.endsWith(".yaml"));
|
|
123
|
+
|
|
124
|
+
const results: Array<{
|
|
125
|
+
collection: Collection;
|
|
126
|
+
raw: Record<string, unknown>;
|
|
127
|
+
}> = [];
|
|
128
|
+
|
|
129
|
+
for (const file of yamlFiles) {
|
|
130
|
+
const filePath = join(collectionsDir, file);
|
|
131
|
+
const content = await readFile(filePath, "utf-8");
|
|
132
|
+
const parsed = parseCollectionFile(content);
|
|
133
|
+
results.push(parsed);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return results;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Gets a single collection by name, including member artifact names.
|
|
141
|
+
* Reads {collectionsDir}/{name}.yaml, parses it, then finds member artifacts
|
|
142
|
+
* by checking catalogEntries where entry.collections includes the collection name.
|
|
143
|
+
* Throws a not-found error if the collection file does not exist.
|
|
144
|
+
*/
|
|
145
|
+
export async function getCollection(
|
|
146
|
+
collectionsDir: string,
|
|
147
|
+
name: string,
|
|
148
|
+
catalogEntries: CatalogEntry[],
|
|
149
|
+
): Promise<{
|
|
150
|
+
collection: Collection;
|
|
151
|
+
raw: Record<string, unknown>;
|
|
152
|
+
members: string[];
|
|
153
|
+
}> {
|
|
154
|
+
const filePath = join(collectionsDir, `${name}.yaml`);
|
|
155
|
+
|
|
156
|
+
let content: string;
|
|
157
|
+
try {
|
|
158
|
+
content = await readFile(filePath, "utf-8");
|
|
159
|
+
} catch (_err) {
|
|
160
|
+
const error: Error & { type?: string } = new Error(
|
|
161
|
+
`Collection '${name}' not found`,
|
|
162
|
+
);
|
|
163
|
+
error.type = "not-found";
|
|
164
|
+
throw error;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const { collection, raw } = parseCollectionFile(content);
|
|
168
|
+
|
|
169
|
+
const members = catalogEntries
|
|
170
|
+
.filter((entry) => entry.collections.includes(name))
|
|
171
|
+
.map((entry) => entry.name);
|
|
172
|
+
|
|
173
|
+
return { collection, raw, members };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Creates a new collection YAML file.
|
|
178
|
+
* Validates input, checks for existing file (throws conflict error if so),
|
|
179
|
+
* writes new YAML file to collectionsDir, and returns the validated Collection.
|
|
180
|
+
*/
|
|
181
|
+
export async function createCollection(
|
|
182
|
+
collectionsDir: string,
|
|
183
|
+
input: CollectionInput,
|
|
184
|
+
): Promise<Collection> {
|
|
185
|
+
// Validate input
|
|
186
|
+
const validation = validateCollectionInput(input);
|
|
187
|
+
if (!validation.success) {
|
|
188
|
+
const error: Error & { type?: string; details?: unknown } = new Error(
|
|
189
|
+
"Validation failed",
|
|
190
|
+
);
|
|
191
|
+
error.type = "validation";
|
|
192
|
+
error.details = validation.errors;
|
|
193
|
+
throw error;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
const filePath = join(collectionsDir, `${input.name}.yaml`);
|
|
197
|
+
|
|
198
|
+
// Check for existing file
|
|
199
|
+
if (await exists(filePath)) {
|
|
200
|
+
const error: Error & { type?: string } = new Error(
|
|
201
|
+
`Collection '${input.name}' already exists`,
|
|
202
|
+
);
|
|
203
|
+
error.type = "conflict";
|
|
204
|
+
throw error;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Serialize and write the new collection file
|
|
208
|
+
const yamlContent = serializeCollection(
|
|
209
|
+
validation.data as Record<string, unknown>,
|
|
210
|
+
);
|
|
211
|
+
await writeFile(filePath, yamlContent, "utf-8");
|
|
212
|
+
|
|
213
|
+
return validation.data;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Updates an existing collection YAML file, preserving unknown keys.
|
|
218
|
+
* Validates input, reads existing file to get raw object with unknown keys,
|
|
219
|
+
* merges the new validated fields into the raw object, writes back, and returns the validated Collection.
|
|
220
|
+
*/
|
|
221
|
+
export async function updateCollection(
|
|
222
|
+
collectionsDir: string,
|
|
223
|
+
name: string,
|
|
224
|
+
input: CollectionInput,
|
|
225
|
+
): Promise<Collection> {
|
|
226
|
+
// Validate input
|
|
227
|
+
const validation = validateCollectionInput(input);
|
|
228
|
+
if (!validation.success) {
|
|
229
|
+
const error: Error & { type?: string; details?: unknown } = new Error(
|
|
230
|
+
"Validation failed",
|
|
231
|
+
);
|
|
232
|
+
error.type = "validation";
|
|
233
|
+
error.details = validation.errors;
|
|
234
|
+
throw error;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const filePath = join(collectionsDir, `${name}.yaml`);
|
|
238
|
+
|
|
239
|
+
// Read existing file to preserve unknown keys
|
|
240
|
+
let existingRaw: Record<string, unknown>;
|
|
241
|
+
try {
|
|
242
|
+
const content = await readFile(filePath, "utf-8");
|
|
243
|
+
const parsed = parseCollectionFile(content);
|
|
244
|
+
existingRaw = parsed.raw;
|
|
245
|
+
} catch (_err) {
|
|
246
|
+
const error: Error & { type?: string } = new Error(
|
|
247
|
+
`Collection '${name}' not found`,
|
|
248
|
+
);
|
|
249
|
+
error.type = "not-found";
|
|
250
|
+
throw error;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Merge new validated fields into the raw object (preserving unknown keys)
|
|
254
|
+
const merged: Record<string, unknown> = {
|
|
255
|
+
...existingRaw,
|
|
256
|
+
...(validation.data as Record<string, unknown>),
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
// Serialize and write the updated collection file
|
|
260
|
+
const yamlContent = serializeCollection(merged);
|
|
261
|
+
await writeFile(filePath, yamlContent, "utf-8");
|
|
262
|
+
|
|
263
|
+
return validation.data;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Deletes a collection YAML file.
|
|
268
|
+
* Checks if the file exists (throws not-found error if missing), then removes it.
|
|
269
|
+
*/
|
|
270
|
+
export async function deleteCollection(
|
|
271
|
+
collectionsDir: string,
|
|
272
|
+
name: string,
|
|
273
|
+
): Promise<void> {
|
|
274
|
+
const filePath = join(collectionsDir, `${name}.yaml`);
|
|
275
|
+
|
|
276
|
+
// Check if file exists
|
|
277
|
+
if (!(await exists(filePath))) {
|
|
278
|
+
const error: Error & { type?: string } = new Error(
|
|
279
|
+
`Collection '${name}' not found`,
|
|
280
|
+
);
|
|
281
|
+
error.type = "not-found";
|
|
282
|
+
throw error;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
// Remove the YAML file
|
|
286
|
+
await unlink(filePath);
|
|
287
|
+
}
|