@procedures/cue 0.0.0-stage → 0.2.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/CHANGELOG.md +27 -0
  2. package/LICENSE +21 -0
  3. package/README.md +71 -2
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/index.d.ts +13 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +16 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/module.d.ts +22 -0
  10. package/dist/module.d.ts.map +1 -0
  11. package/dist/module.js +127 -0
  12. package/dist/module.js.map +1 -0
  13. package/dist/procedures/cue/add.d.ts +13 -0
  14. package/dist/procedures/cue/add.d.ts.map +1 -0
  15. package/dist/procedures/cue/add.js +63 -0
  16. package/dist/procedures/cue/add.js.map +1 -0
  17. package/dist/procedures/cue/generate.d.ts +14 -0
  18. package/dist/procedures/cue/generate.d.ts.map +1 -0
  19. package/dist/procedures/cue/generate.js +189 -0
  20. package/dist/procedures/cue/generate.js.map +1 -0
  21. package/dist/procedures/cue/index.d.ts +6 -0
  22. package/dist/procedures/cue/index.d.ts.map +1 -0
  23. package/dist/procedures/cue/index.js +6 -0
  24. package/dist/procedures/cue/index.js.map +1 -0
  25. package/dist/procedures/cue/init.d.ts +13 -0
  26. package/dist/procedures/cue/init.d.ts.map +1 -0
  27. package/dist/procedures/cue/init.js +79 -0
  28. package/dist/procedures/cue/init.js.map +1 -0
  29. package/dist/procedures/cue/remove.d.ts +13 -0
  30. package/dist/procedures/cue/remove.d.ts.map +1 -0
  31. package/dist/procedures/cue/remove.js +52 -0
  32. package/dist/procedures/cue/remove.js.map +1 -0
  33. package/dist/procedures/cue/validate.d.ts +13 -0
  34. package/dist/procedures/cue/validate.d.ts.map +1 -0
  35. package/dist/procedures/cue/validate.js +76 -0
  36. package/dist/procedures/cue/validate.js.map +1 -0
  37. package/dist/register.d.ts +7 -0
  38. package/dist/register.d.ts.map +1 -0
  39. package/dist/register.js +9 -0
  40. package/dist/register.js.map +1 -0
  41. package/dist/shared.d.ts +39 -0
  42. package/dist/shared.d.ts.map +1 -0
  43. package/dist/shared.js +148 -0
  44. package/dist/shared.js.map +1 -0
  45. package/dist/types.d.ts +80 -0
  46. package/dist/types.d.ts.map +1 -0
  47. package/dist/types.js +81 -0
  48. package/dist/types.js.map +1 -0
  49. package/package.json +81 -3
  50. package/src/index.ts +18 -0
  51. package/src/module.ts +158 -0
  52. package/src/procedures/cue/add.ts +80 -0
  53. package/src/procedures/cue/generate.ts +245 -0
  54. package/src/procedures/cue/index.ts +5 -0
  55. package/src/procedures/cue/init.ts +98 -0
  56. package/src/procedures/cue/remove.ts +64 -0
  57. package/src/procedures/cue/validate.ts +96 -0
  58. package/src/register.ts +11 -0
  59. package/src/shared.ts +216 -0
  60. package/src/types.ts +181 -0
package/src/module.ts ADDED
@@ -0,0 +1,158 @@
1
+ /**
2
+ * Module (no side effects): the procedures of this package as data, for `createRegistry()`.
3
+ *
4
+ * Procedure Registration for cue operations
5
+ *
6
+ * Registers cue.* procedures with the client system.
7
+ * This file is referenced by package.json's client.procedures field.
8
+ */
9
+
10
+ import { createProcedure, zodAdapter, defineModule, type ProcedureModule, PROCEDURE_REGISTRY, registerModules, type ProcedureRegistry } from "@procedures/client";
11
+ import fsModule from "@procedures/fs/module";
12
+ import { cueInit } from "./procedures/cue/init.js";
13
+ import { cueAdd } from "./procedures/cue/add.js";
14
+ import { cueRemove } from "./procedures/cue/remove.js";
15
+ import { cueGenerate } from "./procedures/cue/generate.js";
16
+ import { cueValidate } from "./procedures/cue/validate.js";
17
+ import {
18
+ CueInitInputSchema,
19
+ CueAddInputSchema,
20
+ CueRemoveInputSchema,
21
+ CueGenerateInputSchema,
22
+ CueValidateInputSchema,
23
+ type CueInitInput,
24
+ type CueInitOutput,
25
+ type CueAddInput,
26
+ type CueAddOutput,
27
+ type CueRemoveInput,
28
+ type CueRemoveOutput,
29
+ type CueGenerateInput,
30
+ type CueGenerateOutput,
31
+ type CueValidateInput,
32
+ type CueValidateOutput,
33
+ CueInitOutputSchema,
34
+ CueAddOutputSchema,
35
+ CueRemoveOutputSchema,
36
+ CueGenerateOutputSchema,
37
+ CueValidateOutputSchema,
38
+ } from "./types.js";
39
+
40
+ // =============================================================================
41
+ // Procedure Definitions
42
+ // =============================================================================
43
+
44
+ const cueInitProcedure = createProcedure()
45
+ .path(["cue", "init"])
46
+ .input(zodAdapter<CueInitInput>(CueInitInputSchema))
47
+ .output(zodAdapter<CueInitOutput>(CueInitOutputSchema))
48
+ .meta({
49
+ description: "Initialize dependencies.json with a preset",
50
+ calls: ["fs.exists", "fs.read", "fs.write", "fs.mkdir"],
51
+ // With `force`, it overwrites dependencies.json. A second call writes the same preset again.
52
+ effects: { reads: ["fs"], writes: ["fs"], idempotent: true },
53
+ args: [],
54
+ shorts: { preset: "p", force: "f", cwd: "C" },
55
+ output: "json",
56
+ })
57
+ .handler(async (input: CueInitInput, ctx): Promise<CueInitOutput> => {
58
+ return cueInit(input, ctx);
59
+ })
60
+ .build();
61
+
62
+ const cueAddProcedure = createProcedure()
63
+ .path(["cue", "add"])
64
+ .input(zodAdapter<CueAddInput>(CueAddInputSchema))
65
+ .output(zodAdapter<CueAddOutput>(CueAddOutputSchema))
66
+ .meta({
67
+ description: "Add a feature to dependencies.json",
68
+ calls: ["fs.exists", "fs.read", "fs.write"],
69
+ // It writes dependencies.json again with only `$schema` and `dependencies`. Other fields go.
70
+ effects: { reads: ["fs"], writes: ["fs"], idempotent: true },
71
+ args: ["feature"],
72
+ shorts: { cwd: "C" },
73
+ output: "json",
74
+ })
75
+ .handler(async (input: CueAddInput, ctx): Promise<CueAddOutput> => {
76
+ return cueAdd(input, ctx);
77
+ })
78
+ .build();
79
+
80
+ const cueRemoveProcedure = createProcedure()
81
+ .path(["cue", "remove"])
82
+ .input(zodAdapter<CueRemoveInput>(CueRemoveInputSchema))
83
+ .output(zodAdapter<CueRemoveOutput>(CueRemoveOutputSchema))
84
+ .meta({
85
+ description: "Remove a feature from dependencies.json",
86
+ calls: ["fs.exists", "fs.read", "fs.write"],
87
+ effects: { reads: ["fs"], writes: ["fs"], idempotent: true },
88
+ args: ["feature"],
89
+ shorts: { cwd: "C" },
90
+ output: "json",
91
+ })
92
+ .handler(async (input: CueRemoveInput, ctx): Promise<CueRemoveOutput> => {
93
+ return cueRemove(input, ctx);
94
+ })
95
+ .build();
96
+
97
+ const cueGenerateProcedure = createProcedure()
98
+ .path(["cue", "generate"])
99
+ .input(zodAdapter<CueGenerateInput>(CueGenerateInputSchema))
100
+ .output(zodAdapter<CueGenerateOutput>(CueGenerateOutputSchema))
101
+ .meta({
102
+ description: "Generate config files from dependencies.json",
103
+ calls: ["fs.exists", "fs.read", "fs.write", "fs.mkdir"],
104
+ // The cue program evaluates the CUE files of @mark1russell7/cue, not code of the project.
105
+ // The procedure rewrites package.json, tsconfig.json and .gitignore.
106
+ effects: { reads: ["fs"], writes: ["fs"], idempotent: true },
107
+ args: [],
108
+ shorts: { cwd: "C" },
109
+ output: "json",
110
+ })
111
+ .handler(async (input: CueGenerateInput, ctx): Promise<CueGenerateOutput> => {
112
+ return cueGenerate(input, ctx);
113
+ })
114
+ .build();
115
+
116
+ const cueValidateProcedure = createProcedure()
117
+ .path(["cue", "validate"])
118
+ .input(zodAdapter<CueValidateInput>(CueValidateInputSchema))
119
+ .output(zodAdapter<CueValidateOutput>(CueValidateOutputSchema))
120
+ .meta({
121
+ description: "Validate dependencies.json",
122
+ calls: ["fs.exists", "fs.read"],
123
+ effects: { reads: ["fs"], writes: [] },
124
+ args: [],
125
+ shorts: { cwd: "C" },
126
+ output: "json",
127
+ })
128
+ .handler(async (input: CueValidateInput, ctx): Promise<CueValidateOutput> => {
129
+ return cueValidate(input, ctx);
130
+ })
131
+ .build();
132
+
133
+ // =============================================================================
134
+ // Registration
135
+ // =============================================================================
136
+
137
+ /**
138
+ * The module of this package, for `createRegistry()`: its procedures as data. Importing this
139
+ * file registers nothing. (`register.ts` registers the same procedures in the global registry.)
140
+ */
141
+ export const cueModule: ProcedureModule = defineModule("@procedures/cue", [
142
+ cueInitProcedure,
143
+ cueAddProcedure,
144
+ cueRemoveProcedure,
145
+ cueGenerateProcedure,
146
+ cueValidateProcedure,
147
+ ], [fsModule]);
148
+
149
+ export default cueModule;
150
+
151
+ /**
152
+ * This function registers the procedures of @procedures/cue, and of the modules that it requires, in
153
+ * a registry. The default is the global registry. Importing this file registers nothing: the
154
+ * `./register` entry starts this function (ARCHITECTURE-PROPOSALS-2026-10, P9).
155
+ */
156
+ export function registerCueProcedures(registry: ProcedureRegistry = PROCEDURE_REGISTRY): void {
157
+ registerModules(registry, [cueModule]);
158
+ }
@@ -0,0 +1,80 @@
1
+ /**
2
+ * cue.add procedure
3
+ *
4
+ * Add a feature to dependencies.json.
5
+ * Uses ctx.client.call() for file system operations (dogfooding).
6
+ */
7
+
8
+ import type { ProcedureContext } from "@procedures/client";
9
+ import type { CueAddInput, CueAddOutput } from "../../types.js";
10
+ import {
11
+ loadFeatures,
12
+ loadDependencies,
13
+ saveDependencies,
14
+ } from "../../shared.js";
15
+
16
+ /**
17
+ * Add a feature to dependencies.json
18
+ */
19
+ export async function cueAdd(
20
+ input: CueAddInput,
21
+ ctx: ProcedureContext
22
+ ): Promise<CueAddOutput> {
23
+ const projectPath = input.cwd ?? process.cwd();
24
+ const feature = input.feature;
25
+
26
+ // Load features manifest
27
+ const manifest = await loadFeatures(ctx);
28
+ if (!manifest) {
29
+ return {
30
+ success: false,
31
+ feature,
32
+ added: false,
33
+ error: "Could not load features.json from @mark1russell7/cue package",
34
+ };
35
+ }
36
+
37
+ // Validate feature exists
38
+ if (!manifest.features[feature]) {
39
+ const available = Object.keys(manifest.features).join(", ");
40
+ return {
41
+ success: false,
42
+ feature,
43
+ added: false,
44
+ error: `Unknown feature: ${feature}. Available: ${available}`,
45
+ };
46
+ }
47
+
48
+ // Load current dependencies
49
+ const deps = await loadDependencies(projectPath, ctx) ?? [];
50
+
51
+ // Check if already present
52
+ if (deps.includes(feature)) {
53
+ return {
54
+ success: true,
55
+ feature,
56
+ added: false,
57
+ message: `Feature '${feature}' is already in dependencies`,
58
+ };
59
+ }
60
+
61
+ // Add feature and save
62
+ deps.push(feature);
63
+ const saved = await saveDependencies(deps, projectPath, ctx);
64
+
65
+ if (!saved) {
66
+ return {
67
+ success: false,
68
+ feature,
69
+ added: false,
70
+ error: "Failed to write dependencies.json",
71
+ };
72
+ }
73
+
74
+ return {
75
+ success: true,
76
+ feature,
77
+ added: true,
78
+ message: `Added '${feature}' to dependencies.json`,
79
+ };
80
+ }
@@ -0,0 +1,245 @@
1
+ /**
2
+ * cue.generate procedure
3
+ *
4
+ * Generate config files (package.json, tsconfig.json, .gitignore).
5
+ * Uses ctx.client.call() for file system operations (dogfooding).
6
+ * Runs the cue program through runCommand of client-shell (async, no shell).
7
+ */
8
+
9
+ import { resolve } from "node:path";
10
+ import type { ProcedureContext } from "@procedures/client";
11
+ import type { CueGenerateInput, CueGenerateOutput } from "../../types.js";
12
+ import {
13
+ loadFeatures,
14
+ loadDependencies,
15
+ resolveFeatures,
16
+ checkCue,
17
+ runCue,
18
+ determineTsconfig,
19
+ featureToFieldName,
20
+ packageRoot,
21
+ fileExists,
22
+ readJson,
23
+ writeJson,
24
+ writeFile,
25
+ mkdir,
26
+ } from "../../shared.js";
27
+
28
+ interface PackageJson {
29
+ $schema?: string;
30
+ name?: string;
31
+ version?: string;
32
+ [key: string]: unknown;
33
+ }
34
+
35
+ /**
36
+ * Keys that describe a package's own entry points. The package owns them: a generated value
37
+ * is only a default for a package that has none. (BUGS-2026-07 H33: generate replaced a
38
+ * multi-entry "exports" with the generated "." entry, and dropped bin and sideEffects.)
39
+ * Policy keys, for example "engines", still come from the generator.
40
+ */
41
+ const PACKAGE_OWNED_KEYS: ReadonlySet<string> = new Set(["exports", "main", "types", "bin", "sideEffects"]);
42
+
43
+ /**
44
+ * Generate package.json via CUE evaluation
45
+ */
46
+ async function generatePackageJson(
47
+ resolvedFeatures: string[],
48
+ existingPkg: PackageJson | null,
49
+ ctx: ProcedureContext
50
+ ): Promise<PackageJson | null> {
51
+ const configDir = resolve(packageRoot, "npm/package");
52
+
53
+ // Build list of CUE files to evaluate
54
+ const files: string[] = ["base.cue"];
55
+ for (const feature of resolvedFeatures) {
56
+ const fieldName = featureToFieldName(feature);
57
+ const cuePath = resolve(configDir, `${fieldName}.cue`);
58
+ if (await fileExists(cuePath, ctx)) {
59
+ files.push(`${fieldName}.cue`);
60
+ }
61
+ }
62
+
63
+ // Run cue eval
64
+ const result = await runCue(["eval", ...files, "-e", "output", "--out", "json"], { cwd: configDir, signal: ctx.signal });
65
+
66
+ if (!result.success) {
67
+ throw new Error(`cue eval failed for package.json: ${(result.stderr || result.stdout || "").trim()}`);
68
+ }
69
+
70
+ const generated = JSON.parse(result.stdout) as PackageJson;
71
+
72
+ // Merge with existing package.json
73
+ const pkg: PackageJson = { ...existingPkg };
74
+
75
+ for (const [key, value] of Object.entries(generated)) {
76
+ if (key === "name" || key === "version" || key === "description") {
77
+ if (!pkg[key]) pkg[key] = value as string;
78
+ } else if (key === "devDependencies" || key === "peerDependencies" || key === "scripts") {
79
+ pkg[key] = { ...(value as Record<string, unknown>), ...(pkg[key] as Record<string, unknown> ?? {}) };
80
+ } else if (key === "files" && Array.isArray(value) && Array.isArray(pkg[key])) {
81
+ const merged = [...(value as string[])];
82
+ for (const item of pkg[key] as string[]) {
83
+ if (!merged.includes(item)) merged.push(item);
84
+ }
85
+ pkg[key] = merged;
86
+ } else if (PACKAGE_OWNED_KEYS.has(key)) {
87
+ if (pkg[key] === undefined) pkg[key] = value;
88
+ } else {
89
+ pkg[key] = value;
90
+ }
91
+ }
92
+
93
+ // Ensure $schema is first
94
+ const ordered: PackageJson = { $schema: "https://json.schemastore.org/package" };
95
+ for (const [key, value] of Object.entries(pkg)) {
96
+ if (key !== "$schema") ordered[key] = value;
97
+ }
98
+
99
+ return ordered;
100
+ }
101
+
102
+ /**
103
+ * Generate .gitignore via CUE evaluation
104
+ */
105
+ async function generateGitignore(
106
+ resolvedFeatures: string[],
107
+ ctx: ProcedureContext
108
+ ): Promise<string | null> {
109
+ const configDir = resolve(packageRoot, "git/ignore");
110
+
111
+ const files: string[] = ["base.cue"];
112
+ for (const feature of resolvedFeatures) {
113
+ const cuePath = resolve(configDir, `${feature}.cue`);
114
+ if (await fileExists(cuePath, ctx)) {
115
+ files.push(`${feature}.cue`);
116
+ }
117
+ }
118
+
119
+ const result = await runCue(["eval", ...files, "-e", "patterns", "--out", "json"], { cwd: configDir, signal: ctx.signal });
120
+
121
+ if (!result.success) {
122
+ throw new Error(`cue eval failed for .gitignore: ${(result.stderr || result.stdout || "").trim()}`);
123
+ }
124
+
125
+ const patterns = JSON.parse(result.stdout) as string[];
126
+ return patterns.join("\n") + "\n";
127
+ }
128
+
129
+ /**
130
+ * Setup cue.mod directory
131
+ */
132
+ async function setupCueMod(projectPath: string, ctx: ProcedureContext): Promise<void> {
133
+ const cueModPath = resolve(projectPath, "cue.mod");
134
+ if (!(await fileExists(cueModPath, ctx))) {
135
+ await mkdir(cueModPath, ctx);
136
+ }
137
+
138
+ await writeFile(
139
+ resolve(cueModPath, "module.cue"),
140
+ `module: "project.local"
141
+ language: {
142
+ \tversion: "v0.15.1"
143
+ }
144
+ `,
145
+ ctx
146
+ );
147
+ }
148
+
149
+ /**
150
+ * Generate config files from dependencies.json
151
+ */
152
+ export async function cueGenerate(
153
+ input: CueGenerateInput,
154
+ ctx: ProcedureContext
155
+ ): Promise<CueGenerateOutput> {
156
+ const projectPath = input.cwd ?? process.cwd();
157
+ const generated: string[] = [];
158
+
159
+ // Check CUE is installed
160
+ if (!(await checkCue(ctx.signal))) {
161
+ return {
162
+ success: false,
163
+ resolvedFeatures: [],
164
+ generated: [],
165
+ error: "CUE is not installed. Install from: https://cuelang.org/docs/install/",
166
+ };
167
+ }
168
+
169
+ // Load features manifest
170
+ const manifest = await loadFeatures(ctx);
171
+ if (!manifest) {
172
+ return {
173
+ success: false,
174
+ resolvedFeatures: [],
175
+ generated: [],
176
+ error: "Could not load features.json from @mark1russell7/cue package",
177
+ };
178
+ }
179
+
180
+ // Load dependencies
181
+ const deps = await loadDependencies(projectPath, ctx);
182
+ if (!deps) {
183
+ return {
184
+ success: false,
185
+ resolvedFeatures: [],
186
+ generated: [],
187
+ error: "No dependencies.json found. Run cue.init first.",
188
+ };
189
+ }
190
+
191
+ // Resolve transitive dependencies
192
+ const resolvedFeatures = resolveFeatures(deps, manifest);
193
+
194
+ // Load existing package.json
195
+ const existingPkg = await readJson<PackageJson>(resolve(projectPath, "package.json"), ctx);
196
+
197
+ // A failed cue eval is an error of the whole call, not a silently skipped file
198
+ // (BUGS-2026-07 M36: the procedure reported success with nothing generated).
199
+ try {
200
+ // Generate package.json
201
+ const packageJson = await generatePackageJson(resolvedFeatures, existingPkg, ctx);
202
+ if (packageJson) {
203
+ await writeJson(resolve(projectPath, "package.json"), packageJson, ctx);
204
+ generated.push("package.json");
205
+ }
206
+
207
+ // Generate tsconfig.json if ts feature is present
208
+ if (resolvedFeatures.includes("ts")) {
209
+ const tsconfigName = determineTsconfig(resolvedFeatures);
210
+ const tsconfig = {
211
+ $schema: "https://json.schemastore.org/tsconfig",
212
+ extends: `@mark1russell7/cue/ts/config/${tsconfigName}.json`,
213
+ };
214
+ await writeJson(resolve(projectPath, "tsconfig.json"), tsconfig, ctx);
215
+ generated.push("tsconfig.json");
216
+ }
217
+
218
+ // Generate .gitignore
219
+ const gitignoreContent = await generateGitignore(resolvedFeatures, ctx);
220
+ if (gitignoreContent) {
221
+ await writeFile(resolve(projectPath, ".gitignore"), gitignoreContent, ctx);
222
+ generated.push(".gitignore");
223
+ }
224
+
225
+ // Setup cue.mod if cue feature is present
226
+ if (resolvedFeatures.includes("cue")) {
227
+ await setupCueMod(projectPath, ctx);
228
+ generated.push("cue.mod/");
229
+ }
230
+ } catch (error) {
231
+ return {
232
+ success: false,
233
+ resolvedFeatures,
234
+ generated,
235
+ error: error instanceof Error ? error.message : String(error),
236
+ };
237
+ }
238
+
239
+ return {
240
+ success: true,
241
+ resolvedFeatures,
242
+ generated,
243
+ message: `Generated ${generated.length} files`,
244
+ };
245
+ }
@@ -0,0 +1,5 @@
1
+ export { cueInit } from "./init.js";
2
+ export { cueAdd } from "./add.js";
3
+ export { cueRemove } from "./remove.js";
4
+ export { cueGenerate } from "./generate.js";
5
+ export { cueValidate } from "./validate.js";
@@ -0,0 +1,98 @@
1
+ /**
2
+ * cue.init procedure
3
+ *
4
+ * Initialize dependencies.json with a preset.
5
+ * Uses ctx.client.call() for file system operations (dogfooding).
6
+ */
7
+
8
+ import { resolve } from "node:path";
9
+ import type { ProcedureContext } from "@procedures/client";
10
+ import type { CueInitInput, CueInitOutput } from "../../types.js";
11
+ import {
12
+ loadFeatures,
13
+ saveDependencies,
14
+ fileExists,
15
+ mkdir,
16
+ writeFile,
17
+ } from "../../shared.js";
18
+
19
+ /**
20
+ * Initialize a project with dependencies.json
21
+ */
22
+ export async function cueInit(
23
+ input: CueInitInput,
24
+ ctx: ProcedureContext
25
+ ): Promise<CueInitOutput> {
26
+ const projectPath = input.cwd ?? process.cwd();
27
+ const presetName = input.preset ?? "lib";
28
+ const created: string[] = [];
29
+
30
+ // Load features manifest
31
+ const manifest = await loadFeatures(ctx);
32
+ if (!manifest) {
33
+ return {
34
+ success: false,
35
+ preset: presetName,
36
+ created: [],
37
+ error: "Could not load features.json from @mark1russell7/cue package",
38
+ };
39
+ }
40
+
41
+ // Validate preset
42
+ const preset = manifest.presets[presetName];
43
+ if (!preset) {
44
+ const available = Object.keys(manifest.presets).join(", ");
45
+ return {
46
+ success: false,
47
+ preset: presetName,
48
+ created: [],
49
+ error: `Unknown preset: ${presetName}. Available: ${available}`,
50
+ };
51
+ }
52
+
53
+ // Check if dependencies.json exists
54
+ const depsPath = resolve(projectPath, "dependencies.json");
55
+ if (await fileExists(depsPath, ctx)) {
56
+ if (!input.force) {
57
+ return {
58
+ success: true,
59
+ preset: presetName,
60
+ created: [],
61
+ message: "dependencies.json already exists (use force: true to overwrite)",
62
+ };
63
+ }
64
+ }
65
+
66
+ // Create dependencies.json
67
+ const saved = await saveDependencies(preset, projectPath, ctx);
68
+ if (!saved) {
69
+ return {
70
+ success: false,
71
+ preset: presetName,
72
+ created: [],
73
+ error: "Failed to write dependencies.json",
74
+ };
75
+ }
76
+ created.push("dependencies.json");
77
+
78
+ // Create src directory if needed
79
+ const srcPath = resolve(projectPath, "src");
80
+ if (!(await fileExists(srcPath, ctx))) {
81
+ await mkdir(srcPath, ctx);
82
+ created.push("src/");
83
+ }
84
+
85
+ // Create src/index.ts if needed
86
+ const indexPath = resolve(projectPath, "src", "index.ts");
87
+ if (!(await fileExists(indexPath, ctx))) {
88
+ await writeFile(indexPath, "// Entry point\nexport {};\n", ctx);
89
+ created.push("src/index.ts");
90
+ }
91
+
92
+ return {
93
+ success: true,
94
+ preset: presetName,
95
+ created,
96
+ message: `Initialized with preset '${presetName}'. Run cue.generate to create config files.`,
97
+ };
98
+ }
@@ -0,0 +1,64 @@
1
+ /**
2
+ * cue.remove procedure
3
+ *
4
+ * Remove a feature from dependencies.json.
5
+ * Uses ctx.client.call() for file system operations (dogfooding).
6
+ */
7
+
8
+ import type { ProcedureContext } from "@procedures/client";
9
+ import type { CueRemoveInput, CueRemoveOutput } from "../../types.js";
10
+ import { loadDependencies, saveDependencies } from "../../shared.js";
11
+
12
+ /**
13
+ * Remove a feature from dependencies.json
14
+ */
15
+ export async function cueRemove(
16
+ input: CueRemoveInput,
17
+ ctx: ProcedureContext
18
+ ): Promise<CueRemoveOutput> {
19
+ const projectPath = input.cwd ?? process.cwd();
20
+ const feature = input.feature;
21
+
22
+ // Load current dependencies
23
+ const deps = await loadDependencies(projectPath, ctx);
24
+
25
+ if (!deps) {
26
+ return {
27
+ success: false,
28
+ feature,
29
+ removed: false,
30
+ error: "No dependencies.json found. Run cue.init first.",
31
+ };
32
+ }
33
+
34
+ // Check if feature is present
35
+ const idx = deps.indexOf(feature);
36
+ if (idx === -1) {
37
+ return {
38
+ success: true,
39
+ feature,
40
+ removed: false,
41
+ message: `Feature '${feature}' is not in dependencies`,
42
+ };
43
+ }
44
+
45
+ // Remove feature and save
46
+ deps.splice(idx, 1);
47
+ const saved = await saveDependencies(deps, projectPath, ctx);
48
+
49
+ if (!saved) {
50
+ return {
51
+ success: false,
52
+ feature,
53
+ removed: false,
54
+ error: "Failed to write dependencies.json",
55
+ };
56
+ }
57
+
58
+ return {
59
+ success: true,
60
+ feature,
61
+ removed: true,
62
+ message: `Removed '${feature}' from dependencies.json`,
63
+ };
64
+ }