@chanom/cli 0.1.0 → 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.
- package/README.md +1 -1
- package/dist/index.js +123 -67
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ Based on your answers, `brew` will:
|
|
|
46
46
|
|
|
47
47
|
- Initialize a git repo if one doesn't exist yet (with a `.gitignore`)
|
|
48
48
|
- Detect your package manager (`pnpm`, `npm`, `yarn`, or `bun`) - the `packageManager` field in `package.json` wins over the invoking user agent, falling back to `pnpm`
|
|
49
|
-
- Install the packages needed for your selected toppings as dev dependencies
|
|
49
|
+
- Install the packages needed for your selected toppings as dev dependencies - when run at a workspace root (a `pnpm-workspace.yaml` or a `workspaces` field in `package.json`), the install gets the flag your package manager needs: `-w` for pnpm, `-W` for yarn classic, none for npm, bun, and yarn berry
|
|
50
50
|
- Write config files and wire up `package.json` scripts
|
|
51
51
|
- If `medium` sweetness is selected, run `husky init` and add `pre-commit` / `commit-msg` hooks
|
|
52
52
|
- Stage and commit the changes it made
|
package/dist/index.js
CHANGED
|
@@ -102,20 +102,82 @@ var Git = class extends Effect.Service()("cli/Git", { effect: Effect.gen(functio
|
|
|
102
102
|
"--quiet"
|
|
103
103
|
], cwd).pipe(Effect.map((clean) => !clean)),
|
|
104
104
|
isRepo: (cwd) => fs.exists(path.join(cwd, ".git")),
|
|
105
|
-
writeGitignore: (cwd, contents = "node_modules\n") => fs.writeFileString(path.join(cwd, ".gitignore"), contents)
|
|
105
|
+
writeGitignore: (cwd, contents = "node_modules\n") => fs.writeFileString(path.join(cwd, ".gitignore"), contents),
|
|
106
|
+
readGitignore: (cwd) => fs.readFileString(path.join(cwd, ".gitignore"))
|
|
106
107
|
};
|
|
107
108
|
}) }) {};
|
|
108
109
|
//#endregion
|
|
110
|
+
//#region src/domain/package-manager.ts
|
|
111
|
+
/** How to run a locally installed binary with each package manager. */
|
|
112
|
+
const PM_EXEC = {
|
|
113
|
+
pnpm: ["pnpm", "exec"],
|
|
114
|
+
npm: [
|
|
115
|
+
"npx",
|
|
116
|
+
"--no",
|
|
117
|
+
"--"
|
|
118
|
+
],
|
|
119
|
+
yarn: ["yarn"],
|
|
120
|
+
bun: ["bunx"]
|
|
121
|
+
};
|
|
122
|
+
const KNOWN_PMS = [
|
|
123
|
+
"pnpm",
|
|
124
|
+
"yarn",
|
|
125
|
+
"bun",
|
|
126
|
+
"npm"
|
|
127
|
+
];
|
|
128
|
+
/** Parses the `packageManager` field of package.json, e.g. `pnpm@11.9.0`. */
|
|
129
|
+
function parsePmField(field) {
|
|
130
|
+
if (!field) return void 0;
|
|
131
|
+
const name = field.split("@")[0];
|
|
132
|
+
return KNOWN_PMS.find((pm) => pm === name);
|
|
133
|
+
}
|
|
134
|
+
/** Parses the `npm_config_user_agent` environment variable, e.g. `pnpm/11.9.0 npm/? node/v22`. */
|
|
135
|
+
function parsePmUserAgent(agent) {
|
|
136
|
+
if (!agent) return void 0;
|
|
137
|
+
return KNOWN_PMS.find((pm) => agent.startsWith(pm));
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Extra `add` flags needed when installing at a workspace root: pnpm and yarn
|
|
141
|
+
* classic refuse root installs without `-w` / `-W`. npm, bun, and yarn berry
|
|
142
|
+
* install to the root with no flag (npm's `-w` targets a child workspace instead).
|
|
143
|
+
*/
|
|
144
|
+
function workspaceRootFlags(pm, hints) {
|
|
145
|
+
switch (pm) {
|
|
146
|
+
case "pnpm": return hints.hasPnpmWorkspaceFile ? ["-w"] : [];
|
|
147
|
+
case "yarn": return hints.hasWorkspacesField && !hints.hasYarnBerryConfig ? ["-W"] : [];
|
|
148
|
+
case "npm":
|
|
149
|
+
case "bun": return [];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/** The `packageManager` field wins over the user agent, which wins over the fallback. */
|
|
153
|
+
function resolvePm(hints) {
|
|
154
|
+
return parsePmField(hints.packageManagerField) ?? parsePmUserAgent(hints.userAgent) ?? hints.fallback ?? "pnpm";
|
|
155
|
+
}
|
|
156
|
+
//#endregion
|
|
109
157
|
//#region src/services/package-installer.ts
|
|
110
158
|
var InstallFailed = class extends Data.TaggedError("InstallFailed") {};
|
|
111
|
-
/**
|
|
159
|
+
/**
|
|
160
|
+
* Installs npm packages with the detected package manager, adding the
|
|
161
|
+
* workspace-root flag (pnpm `-w`, yarn classic `-W`) when the target
|
|
162
|
+
* directory is a workspace root.
|
|
163
|
+
*/
|
|
112
164
|
var PackageInstaller = class extends Effect.Service()("cli/PackageInstaller", { effect: Effect.gen(function* () {
|
|
113
165
|
const runner = yield* CommandRunner;
|
|
114
|
-
|
|
166
|
+
const fs = yield* FileSystem.FileSystem;
|
|
167
|
+
const path = yield* Path.Path;
|
|
168
|
+
const has = (cwd, file) => fs.exists(path.join(cwd, file)).pipe(Effect.orElseSucceed(() => false));
|
|
169
|
+
const hasWorkspacesField = (cwd) => fs.readFileString(path.join(cwd, "package.json")).pipe(Effect.flatMap((contents) => Effect.try(() => JSON.parse(contents).workspaces !== void 0)), Effect.orElseSucceed(() => false));
|
|
170
|
+
const workspaceHints = (cwd) => Effect.all({
|
|
171
|
+
hasPnpmWorkspaceFile: has(cwd, "pnpm-workspace.yaml"),
|
|
172
|
+
hasWorkspacesField: hasWorkspacesField(cwd),
|
|
173
|
+
hasYarnBerryConfig: has(cwd, ".yarnrc.yml")
|
|
174
|
+
}, { concurrency: 3 });
|
|
175
|
+
return { installDev: (pm, cwd, packages) => workspaceHints(cwd).pipe(Effect.flatMap((hints) => runner.execInherit(pm, [
|
|
115
176
|
"add",
|
|
116
177
|
"-D",
|
|
178
|
+
...workspaceRootFlags(pm, hints),
|
|
117
179
|
...packages
|
|
118
|
-
], cwd)
|
|
180
|
+
], cwd)), Effect.filterOrFail((code) => code === 0, () => new InstallFailed({ pm })), Effect.asVoid) };
|
|
119
181
|
}) }) {};
|
|
120
182
|
//#endregion
|
|
121
183
|
//#region src/services/prompter.ts
|
|
@@ -147,40 +209,6 @@ var Prompter = class extends Effect.Service()("cli/Prompter", { succeed: {
|
|
|
147
209
|
})
|
|
148
210
|
} }) {};
|
|
149
211
|
//#endregion
|
|
150
|
-
//#region src/domain/package-manager.ts
|
|
151
|
-
/** How to run a locally installed binary with each package manager. */
|
|
152
|
-
const PM_EXEC = {
|
|
153
|
-
pnpm: ["pnpm", "exec"],
|
|
154
|
-
npm: [
|
|
155
|
-
"npx",
|
|
156
|
-
"--no",
|
|
157
|
-
"--"
|
|
158
|
-
],
|
|
159
|
-
yarn: ["yarn"],
|
|
160
|
-
bun: ["bunx"]
|
|
161
|
-
};
|
|
162
|
-
const KNOWN_PMS = [
|
|
163
|
-
"pnpm",
|
|
164
|
-
"yarn",
|
|
165
|
-
"bun",
|
|
166
|
-
"npm"
|
|
167
|
-
];
|
|
168
|
-
/** Parses the `packageManager` field of package.json, e.g. `pnpm@11.9.0`. */
|
|
169
|
-
function parsePmField(field) {
|
|
170
|
-
if (!field) return void 0;
|
|
171
|
-
const name = field.split("@")[0];
|
|
172
|
-
return KNOWN_PMS.find((pm) => pm === name);
|
|
173
|
-
}
|
|
174
|
-
/** Parses the `npm_config_user_agent` environment variable, e.g. `pnpm/11.9.0 npm/? node/v22`. */
|
|
175
|
-
function parsePmUserAgent(agent) {
|
|
176
|
-
if (!agent) return void 0;
|
|
177
|
-
return KNOWN_PMS.find((pm) => agent.startsWith(pm));
|
|
178
|
-
}
|
|
179
|
-
/** The `packageManager` field wins over the user agent, which wins over the fallback. */
|
|
180
|
-
function resolvePm(hints) {
|
|
181
|
-
return parsePmField(hints.packageManagerField) ?? parsePmUserAgent(hints.userAgent) ?? hints.fallback ?? "pnpm";
|
|
182
|
-
}
|
|
183
|
-
//#endregion
|
|
184
212
|
//#region src/utils/detect-pm.ts
|
|
185
213
|
const detectPm = (cwd, userAgent = process.env["npm_config_user_agent"], fallback = "pnpm") => Effect.gen(function* () {
|
|
186
214
|
const fs = yield* FileSystem.FileSystem;
|
|
@@ -285,7 +313,7 @@ function commitMsgHook(pm) {
|
|
|
285
313
|
}
|
|
286
314
|
//#endregion
|
|
287
315
|
//#region src/commands/add-commitlint/index.ts
|
|
288
|
-
const apply$5 =
|
|
316
|
+
const apply$5 = Effect.fn("add-commitlint.apply")(function* (cwd, pm) {
|
|
289
317
|
const fs = yield* FileSystem.FileSystem;
|
|
290
318
|
const path = yield* Path.Path;
|
|
291
319
|
const prompter = yield* Prompter;
|
|
@@ -298,7 +326,7 @@ const apply$5 = (cwd, pm) => Effect.gen(function* () {
|
|
|
298
326
|
if (yield* fs.exists(hookPath)) yield* prompter.warn("`.husky/commit-msg` already exists - skipping");
|
|
299
327
|
else yield* fs.writeFileString(hookPath, commitMsgHook(pm));
|
|
300
328
|
}
|
|
301
|
-
})
|
|
329
|
+
});
|
|
302
330
|
//#endregion
|
|
303
331
|
//#region src/commands/add-husky/logic.ts
|
|
304
332
|
function getPackages$4(pkg) {
|
|
@@ -307,7 +335,7 @@ function getPackages$4(pkg) {
|
|
|
307
335
|
//#endregion
|
|
308
336
|
//#region src/commands/add-husky/index.ts
|
|
309
337
|
var HuskyInitFailed = class extends Data.TaggedError("HuskyInitFailed") {};
|
|
310
|
-
const apply$4 =
|
|
338
|
+
const apply$4 = Effect.fn("add-husky.apply")(function* (cwd, pm) {
|
|
311
339
|
const fs = yield* FileSystem.FileSystem;
|
|
312
340
|
const path = yield* Path.Path;
|
|
313
341
|
const prompter = yield* Prompter;
|
|
@@ -324,7 +352,7 @@ const apply$4 = (cwd, pm) => Effect.gen(function* () {
|
|
|
324
352
|
"init"
|
|
325
353
|
], cwd);
|
|
326
354
|
if (exitCode !== 0) return yield* new HuskyInitFailed({ exitCode });
|
|
327
|
-
})
|
|
355
|
+
});
|
|
328
356
|
//#endregion
|
|
329
357
|
//#region src/domain/scripts.ts
|
|
330
358
|
/**
|
|
@@ -363,7 +391,7 @@ function getScriptPlan$2(scripts) {
|
|
|
363
391
|
//#endregion
|
|
364
392
|
//#region src/commands/add-knip/index.ts
|
|
365
393
|
/** Returns a copy of `pkg` with missing scripts added; the caller owns writing package.json. */
|
|
366
|
-
const apply$3 = (cwd, esm, pkg)
|
|
394
|
+
const apply$3 = Effect.fn("add-knip.apply")(function* (cwd, esm, pkg) {
|
|
367
395
|
const fs = yield* FileSystem.FileSystem;
|
|
368
396
|
const path = yield* Path.Path;
|
|
369
397
|
const prompter = yield* Prompter;
|
|
@@ -379,7 +407,7 @@ const apply$3 = (cwd, esm, pkg) => Effect.gen(function* () {
|
|
|
379
407
|
...pkg,
|
|
380
408
|
scripts: plan.scripts
|
|
381
409
|
};
|
|
382
|
-
})
|
|
410
|
+
});
|
|
383
411
|
//#endregion
|
|
384
412
|
//#region src/commands/add-lint-staged/logic.ts
|
|
385
413
|
function getPackages$2(pkg) {
|
|
@@ -400,7 +428,7 @@ function buildConfig(linters, formatters) {
|
|
|
400
428
|
}
|
|
401
429
|
//#endregion
|
|
402
430
|
//#region src/commands/add-lint-staged/index.ts
|
|
403
|
-
const apply$2 = (cwd, linters = [], formatters = [], overwritePreCommit = false)
|
|
431
|
+
const apply$2 = Effect.fn("add-lint-staged.apply")(function* (cwd, linters = [], formatters = [], overwritePreCommit = false) {
|
|
404
432
|
const fs = yield* FileSystem.FileSystem;
|
|
405
433
|
const path = yield* Path.Path;
|
|
406
434
|
const prompter = yield* Prompter;
|
|
@@ -413,7 +441,7 @@ const apply$2 = (cwd, linters = [], formatters = [], overwritePreCommit = false)
|
|
|
413
441
|
if ((yield* fs.exists(hookPath)) && !overwritePreCommit) yield* prompter.warn("`.husky/pre-commit` already exists - skipping");
|
|
414
442
|
else yield* fs.writeFileString(hookPath, "lint-staged\n");
|
|
415
443
|
}
|
|
416
|
-
})
|
|
444
|
+
});
|
|
417
445
|
//#endregion
|
|
418
446
|
//#region src/commands/add-oxfmt/logic.ts
|
|
419
447
|
function getPackages$1(pkg, versions) {
|
|
@@ -434,7 +462,7 @@ function getScriptPlan$1(scripts) {
|
|
|
434
462
|
//#endregion
|
|
435
463
|
//#region src/commands/add-oxfmt/index.ts
|
|
436
464
|
/** Returns a copy of `pkg` with missing scripts added; the caller owns writing package.json. */
|
|
437
|
-
const apply$1 = (cwd, esm, pkg)
|
|
465
|
+
const apply$1 = Effect.fn("add-oxfmt.apply")(function* (cwd, esm, pkg) {
|
|
438
466
|
const fs = yield* FileSystem.FileSystem;
|
|
439
467
|
const path = yield* Path.Path;
|
|
440
468
|
const prompter = yield* Prompter;
|
|
@@ -450,7 +478,7 @@ const apply$1 = (cwd, esm, pkg) => Effect.gen(function* () {
|
|
|
450
478
|
...pkg,
|
|
451
479
|
scripts: plan.scripts
|
|
452
480
|
};
|
|
453
|
-
})
|
|
481
|
+
});
|
|
454
482
|
//#endregion
|
|
455
483
|
//#region src/commands/add-oxlint/logic.ts
|
|
456
484
|
function getPackages(pkg, versions) {
|
|
@@ -471,7 +499,7 @@ function getScriptPlan(scripts) {
|
|
|
471
499
|
//#endregion
|
|
472
500
|
//#region src/commands/add-oxlint/index.ts
|
|
473
501
|
/** Returns a copy of `pkg` with missing scripts added; the caller owns writing package.json. */
|
|
474
|
-
const apply = (cwd, esm, pkg)
|
|
502
|
+
const apply = Effect.fn("add-oxlint.apply")(function* (cwd, esm, pkg) {
|
|
475
503
|
const fs = yield* FileSystem.FileSystem;
|
|
476
504
|
const path = yield* Path.Path;
|
|
477
505
|
const prompter = yield* Prompter;
|
|
@@ -487,7 +515,7 @@ const apply = (cwd, esm, pkg) => Effect.gen(function* () {
|
|
|
487
515
|
...pkg,
|
|
488
516
|
scripts: plan.scripts
|
|
489
517
|
};
|
|
490
|
-
})
|
|
518
|
+
});
|
|
491
519
|
//#endregion
|
|
492
520
|
//#region src/commands/brew/logic.ts
|
|
493
521
|
/** Deduplicated list of packages to install for the chosen toppings and sweetness. */
|
|
@@ -499,6 +527,21 @@ function planPackages(pkg, toppings, sweetness, versions) {
|
|
|
499
527
|
if (sweetness === "medium") packages.push(...getPackages$4(pkg), ...getPackages$2(pkg), ...getPackages$5(pkg));
|
|
500
528
|
return [...new Set(packages)];
|
|
501
529
|
}
|
|
530
|
+
/** True when the .gitignore content already has the entry as its own line. */
|
|
531
|
+
function hasGitignoreEntry(content, entry) {
|
|
532
|
+
const forms = /* @__PURE__ */ new Set([
|
|
533
|
+
entry,
|
|
534
|
+
`${entry}/`,
|
|
535
|
+
`/${entry}`,
|
|
536
|
+
`/${entry}/`
|
|
537
|
+
]);
|
|
538
|
+
return content.split(/\r?\n/).some((line) => forms.has(line.trim()));
|
|
539
|
+
}
|
|
540
|
+
/** .gitignore content with the entry appended, preserving existing lines and newline termination. */
|
|
541
|
+
function appendGitignoreEntry(content, entry) {
|
|
542
|
+
if (content === "") return `${entry}\n`;
|
|
543
|
+
return content.endsWith("\n") ? `${content}${entry}\n` : `${content}\n${entry}\n`;
|
|
544
|
+
}
|
|
502
545
|
function selectedLinters(toppings) {
|
|
503
546
|
return toppings.filter((t) => t === "oxlint");
|
|
504
547
|
}
|
|
@@ -507,7 +550,7 @@ function selectedFormatters(toppings) {
|
|
|
507
550
|
}
|
|
508
551
|
//#endregion
|
|
509
552
|
//#region src/commands/brew/index.ts
|
|
510
|
-
const ensureGitRepo =
|
|
553
|
+
const ensureGitRepo = Effect.fn("brew.ensureGitRepo")(function* (cwd) {
|
|
511
554
|
const git = yield* Git;
|
|
512
555
|
if (yield* git.isRepo(cwd)) {
|
|
513
556
|
yield* Effect.logDebug("git repository already present");
|
|
@@ -515,10 +558,21 @@ const ensureGitRepo = (cwd) => Effect.gen(function* () {
|
|
|
515
558
|
}
|
|
516
559
|
const s = yield* (yield* Prompter).spinner("Git not found, initializing repository...");
|
|
517
560
|
yield* git.init(cwd);
|
|
518
|
-
yield* git.writeGitignore(cwd);
|
|
519
561
|
yield* s.stop("Git initialized");
|
|
520
|
-
})
|
|
521
|
-
const
|
|
562
|
+
});
|
|
563
|
+
const ensureGitIgnore = Effect.fn("brew.ensureGitIgnore")(function* (cwd) {
|
|
564
|
+
const git = yield* Git;
|
|
565
|
+
const prompter = yield* Prompter;
|
|
566
|
+
const existing = yield* git.readGitignore(cwd).pipe(Effect.catchTag("SystemError", (e) => e.reason === "NotFound" ? Effect.succeed(void 0) : Effect.fail(e)));
|
|
567
|
+
if (existing !== void 0 && hasGitignoreEntry(existing, "node_modules")) {
|
|
568
|
+
yield* Effect.logDebug("node_modules already present in .gitignore");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
const s = yield* prompter.spinner(existing === void 0 ? "Creating .gitignore..." : "Adding node_modules to .gitignore...");
|
|
572
|
+
yield* git.writeGitignore(cwd, appendGitignoreEntry(existing ?? "", "node_modules")).pipe(Effect.tapError(() => s.stop(pc.red("Could not update .gitignore"))));
|
|
573
|
+
yield* s.stop(existing === void 0 ? ".gitignore created" : "node_modules added to .gitignore");
|
|
574
|
+
});
|
|
575
|
+
const askRecipe = Effect.fn("brew.askRecipe")(function* () {
|
|
522
576
|
const prompter = yield* Prompter;
|
|
523
577
|
return {
|
|
524
578
|
toppings: yield* prompter.multiselect({
|
|
@@ -555,8 +609,8 @@ const askRecipe = () => Effect.gen(function* () {
|
|
|
555
609
|
}]
|
|
556
610
|
})
|
|
557
611
|
};
|
|
558
|
-
})
|
|
559
|
-
const installPackages = (pm, cwd, packages)
|
|
612
|
+
});
|
|
613
|
+
const installPackages = Effect.fn("brew.installPackages")(function* (pm, cwd, packages) {
|
|
560
614
|
if (packages.length === 0) {
|
|
561
615
|
yield* Effect.logDebug("no packages to install");
|
|
562
616
|
return;
|
|
@@ -566,21 +620,22 @@ const installPackages = (pm, cwd, packages) => Effect.gen(function* () {
|
|
|
566
620
|
const s = yield* prompter.spinner(`Installing ${packages.join(", ")}...`);
|
|
567
621
|
yield* installer.installDev(pm, cwd, packages).pipe(Effect.tapError(() => s.stop(pc.red("Package installation failed"))));
|
|
568
622
|
yield* s.stop("Packages installed");
|
|
569
|
-
})
|
|
570
|
-
const applyToppings = (cwd, esm, pkg, toppings)
|
|
623
|
+
});
|
|
624
|
+
const applyToppings = Effect.fn("brew.applyToppings")(function* (cwd, esm, pkg, toppings) {
|
|
571
625
|
let updated = pkg;
|
|
572
626
|
if (toppings.includes("oxlint")) updated = yield* apply(cwd, esm, updated);
|
|
573
627
|
if (toppings.includes("oxfmt")) updated = yield* apply$1(cwd, esm, updated);
|
|
574
628
|
if (toppings.includes("knip")) updated = yield* apply$3(cwd, esm, updated);
|
|
575
629
|
return updated;
|
|
576
|
-
})
|
|
577
|
-
const persistScripts = (pkgPath, pkg, updated)
|
|
578
|
-
if (JSON.stringify(updated.scripts ?? {})
|
|
630
|
+
});
|
|
631
|
+
const persistScripts = Effect.fn("brew.persistScripts")(function* (pkgPath, pkg, updated) {
|
|
632
|
+
if (JSON.stringify(updated.scripts ?? {}) === JSON.stringify(pkg.scripts ?? {})) yield* Effect.logDebug("scripts unchanged, skipping package.json write");
|
|
633
|
+
else {
|
|
579
634
|
yield* Effect.logDebug(`writing updated scripts to ${pkgPath}`);
|
|
580
635
|
yield* writePkg(pkgPath, updated);
|
|
581
|
-
}
|
|
582
|
-
})
|
|
583
|
-
const setupCommitGate = (cwd, pm, toppings, sweetness)
|
|
636
|
+
}
|
|
637
|
+
});
|
|
638
|
+
const setupCommitGate = Effect.fn("brew.setupCommitGate")(function* (cwd, pm, toppings, sweetness) {
|
|
584
639
|
if (sweetness !== "medium") return;
|
|
585
640
|
const fs = yield* FileSystem.FileSystem;
|
|
586
641
|
const path = yield* Path.Path;
|
|
@@ -588,8 +643,8 @@ const setupCommitGate = (cwd, pm, toppings, sweetness) => Effect.gen(function* (
|
|
|
588
643
|
yield* apply$4(cwd, pm);
|
|
589
644
|
yield* apply$2(cwd, selectedLinters(toppings), selectedFormatters(toppings), !huskyExisted);
|
|
590
645
|
yield* apply$5(cwd, pm);
|
|
591
|
-
})
|
|
592
|
-
const commitChanges =
|
|
646
|
+
});
|
|
647
|
+
const commitChanges = Effect.fn("brew.commitChanges")(function* (cwd) {
|
|
593
648
|
const git = yield* Git;
|
|
594
649
|
const prompter = yield* Prompter;
|
|
595
650
|
const s = yield* prompter.spinner("Committing changes...");
|
|
@@ -607,11 +662,12 @@ const commitChanges = (cwd) => Effect.gen(function* () {
|
|
|
607
662
|
yield* s.stop(pc.yellow("Could not commit changes automatically"));
|
|
608
663
|
const detail = result.stderr || result.stdout;
|
|
609
664
|
yield* prompter.warn(`${detail ? detail + "\n" : ""}Your files are staged - commit them manually when ready.`);
|
|
610
|
-
})
|
|
665
|
+
});
|
|
611
666
|
const brew = (cwd = process.cwd()) => Effect.gen(function* () {
|
|
612
667
|
const prompter = yield* Prompter;
|
|
613
668
|
yield* prompter.intro(pc.magenta("🧋 Chanom - Let's brew your project!"));
|
|
614
669
|
yield* ensureGitRepo(cwd);
|
|
670
|
+
yield* ensureGitIgnore(cwd);
|
|
615
671
|
const pm = yield* detectPm(cwd);
|
|
616
672
|
yield* Effect.logDebug(`detected package manager: ${pm}`);
|
|
617
673
|
const { pkg, pkgPath } = yield* readPkg(cwd);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chanom/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"keywords": [],
|
|
6
6
|
"repository": {
|
|
@@ -28,6 +28,7 @@
|
|
|
28
28
|
"@tsconfig/node-lts": "24.0.0",
|
|
29
29
|
"@tsconfig/node-ts": "23.6.4",
|
|
30
30
|
"@types/node": "26.0.1",
|
|
31
|
+
"@vitest/coverage-v8": "3.2.6",
|
|
31
32
|
"tsdown": "0.22.3",
|
|
32
33
|
"typescript": "6.0.3",
|
|
33
34
|
"vitest": "3.2.6",
|
|
@@ -37,6 +38,7 @@
|
|
|
37
38
|
"build": "tsdown",
|
|
38
39
|
"dev": "tsdown --watch",
|
|
39
40
|
"test": "vitest run",
|
|
41
|
+
"test:coverage": "vitest run --coverage",
|
|
40
42
|
"test:watch": "vitest"
|
|
41
43
|
}
|
|
42
44
|
}
|