-
Notifications
You must be signed in to change notification settings - Fork 4
Add Ultracite and TanStack Intent to init flow #263
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@proofkit/cli": patch | ||
| --- | ||
|
|
||
| Write npm min-release-age config during npm project scaffolding. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@proofkit/cli": patch | ||
| --- | ||
|
|
||
| Run Ultracite and TanStack Intent setup during project scaffolding. |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,7 +3,7 @@ import { Chalk } from "chalk"; | |||||||||||||||||||||||||||||||||||||||||||||||||
| import { Cause, Effect, Exit } from "effect"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getOrUndefined } from "effect/Option"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AGENT_INSTRUCTIONS } from "~/consts.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getAgentInstructions } from "~/consts.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CliContext, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| CodegenService, | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -19,7 +19,14 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||
| import { DirectoryConflictError, FileSystemError, isCliError, UserCancelledError } from "~/core/errors.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { applyPackageJsonMutations } from "~/core/planInit.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { InitPlan } from "~/core/types.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { normalizeImportAlias, replaceTextInFiles, updateTypegenConfig } from "~/utils/projectFiles.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getIntentInstallCommand } from "~/helpers/intent.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { getBrowserOxlintConfig, getUltraciteInitCommand } from "~/helpers/ultracite.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| formatPackageManagerCommand, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| normalizeImportAlias, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| replaceTextInFiles, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| updateTypegenConfig, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } from "~/utils/projectFiles.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| import { sortPackageJson } from "~/utils/sortPackageJson.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| const AGENT_METADATA_DIRS = new Set([".agents", ".claude", ".clawed", ".clinerules", ".cursor", ".windsurf"]); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -78,6 +85,14 @@ function renderNextSteps(plan: InitPlan, additionalSteps: string[] = []) { | |||||||||||||||||||||||||||||||||||||||||||||||||
| return lines.join("\n"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| function getPackageScriptCommand(plan: InitPlan, scriptName: string) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const [command, ...args] = formatPackageManagerCommand(plan.request.packageManager, scriptName).split(" "); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!command) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new Error(`Unable to resolve ${scriptName} command for ${plan.request.packageManager}.`); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return { command, args }; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
+94
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Splitting formatted command on space may fail with complex arguments. Line 89 splits the result of 🛡️ Suggested defensive approachConsider documenting the assumption or using a more robust parser: function getPackageScriptCommand(plan: InitPlan, scriptName: string) {
+ // Note: assumes formatPackageManagerCommand returns space-delimited tokens without quoted args
const [command, ...args] = formatPackageManagerCommand(plan.request.packageManager, scriptName).split(" ");
if (!command) {
throw new Error(`Unable to resolve ${scriptName} command for ${plan.request.packageManager}.`);
}
return { command, args };
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| function getMeaningfulDirectoryEntries(entries: string[]) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| return entries.filter((entry) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (AGENT_METADATA_DIRS.has(entry)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -275,7 +290,7 @@ export const executeInitPlan = (plan: InitPlan) => | |||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* Effect.tryPromise({ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: () => replaceTextInFiles(projectFilesFs, plan.targetDir, "__AGENT_INSTRUCTIONS__", AGENT_INSTRUCTIONS), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| try: () => replaceTextInFiles(projectFilesFs, plan.targetDir, "__AGENT_INSTRUCTIONS__", getAgentInstructions()), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| catch: (cause) => | ||||||||||||||||||||||||||||||||||||||||||||||||||
| new FileSystemError({ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| message: "Unable to rewrite scaffold placeholders.", | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -380,14 +395,6 @@ export const executeInitPlan = (plan: InitPlan) => | |||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.runInstall) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.request.packageManager === "pnpm") { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* processService.run("pnpm", ["self-update", "11"], { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: plan.targetDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| let installArgs: string[] = ["install"]; | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.request.packageManager === "yarn") { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| installArgs = []; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -399,10 +406,61 @@ export const executeInitPlan = (plan: InitPlan) => | |||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.runUltraciteInit) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const ultraciteCommand = getUltraciteInitCommand({ | ||||||||||||||||||||||||||||||||||||||||||||||||||
| appType: plan.request.appType, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| packageManager: plan.request.packageManager, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| skipInstall: plan.request.noInstall, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* processService.run(ultraciteCommand.command, ultraciteCommand.args, { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: plan.targetDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.request.appType === "browser") { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* fs.writeFile(path.join(plan.targetDir, "oxlint.config.ts"), getBrowserOxlintConfig()); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.runIntentInstall) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const intentCommand = getIntentInstallCommand(plan.request.packageManager); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* processService.run(intentCommand.command, intentCommand.args, { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: plan.targetDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.runInitialCodegen) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* codegenService.runInitial(plan.targetDir, plan.request.packageManager); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.runFix) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const fixCommand = getPackageScriptCommand(plan, "fix"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* Effect.either( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| processService.run(fixCommand.command, fixCommand.args, { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: plan.targetDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+439
to
+447
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Don't swallow Line 441 converts the command failure into an Suggested fix if (plan.tasks.runFix) {
const fixCommand = getPackageScriptCommand(plan, "fix");
- yield* Effect.either(
- processService.run(fixCommand.command, fixCommand.args, {
- cwd: plan.targetDir,
- stdout: "pipe",
- stderr: "pipe",
- }),
- );
+ yield* processService.run(fixCommand.command, fixCommand.args, {
+ cwd: plan.targetDir,
+ stdout: "pipe",
+ stderr: "pipe",
+ });
}🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.runLint) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const fixCommand = getPackageScriptCommand(plan, "fix"); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| const result = yield* Effect.either( | ||||||||||||||||||||||||||||||||||||||||||||||||||
| processService.run(fixCommand.command, fixCommand.args, { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| cwd: plan.targetDir, | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stdout: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| stderr: "pipe", | ||||||||||||||||||||||||||||||||||||||||||||||||||
| }), | ||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| if (result._tag === "Left") { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| consoleService.warn("Lint fix did not succeed; continuing setup."); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+450
to
+460
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Run the Line 451 resolves Suggested fix if (plan.tasks.runLint) {
- const fixCommand = getPackageScriptCommand(plan, "fix");
+ const lintCommand = getPackageScriptCommand(plan, "lint");
const result = yield* Effect.either(
- processService.run(fixCommand.command, fixCommand.args, {
+ processService.run(lintCommand.command, lintCommand.args, {
cwd: plan.targetDir,
stdout: "pipe",
stderr: "pipe",
}),
);
if (result._tag === "Left") {
- consoleService.warn("Lint fix did not succeed; continuing setup.");
+ consoleService.warn("Lint did not succeed; continuing setup.");
}
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
| if (plan.tasks.initializeGit) { | ||||||||||||||||||||||||||||||||||||||||||||||||||
| yield* gitService.initialize(plan.targetDir); | ||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Command string splitting may break with complex arguments.
Lines 435 and 444 split
formatPackageManagerCommand()result on space. If the formatted command contains quoted arguments with spaces, this will incorrectly parse the command and args.This is the same issue as in
executeInitPlan.tsline 89 andultracite.tsline 11. Consider extracting a shared helper that handles this splitting consistently, or document the assumption that formatted commands never contain complex arguments.Also applies to: 444-446
🤖 Prompt for AI Agents