Skip to content

Commit 36fa856

Browse files
committed
fix(cli): print remedy commands through getCliPrefix()
1 parent 057683c commit 36fa856

15 files changed

Lines changed: 58 additions & 31 deletions
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@taskless/cli": patch
3+
---
4+
5+
Messages that tell you what to run now print a command you can run as shown. They used to hard-code `taskless`, which is usually not on `PATH` when the CLI was launched with `npx @taskless/cli` or `pnpm dlx`. They now use the same launcher-aware prefix the authentication and rule-recovery errors already used, for example `npx @taskless/cli@latest auth login`. This covers `auth`, `check`, `rule create`/`improve`/`meta`, `onboard`, `agent`, `feedback send`, the install wizard, the non-interactive banner, and the Vale and missing-fixture notices. `check` with no rules now also names the `--from` that `rule create` requires.

‎packages/cli/src/commands/agent.ts‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { withSurveyInvite } from "../survey/invite";
1919
import { applyCliInvocation } from "../util/invocation";
2020
import {
2121
detectCliInvocation,
22+
getCliPrefix,
2223
processLauncherContext,
2324
} from "../util/package-manager";
2425

@@ -162,8 +163,9 @@ export function createAgentCommand(subCommands: SubCommandsDef) {
162163
"\nAppend `--anonymous` to any rule/check command to skip the Taskless API"
163164
);
164165
console.log("and use local-only behavior.");
166+
const prefix = getCliPrefix();
165167
console.log(
166-
"\nRun `taskless agent <topic>` for the full recipe (e.g. `taskless agent create-sg-rule`)."
168+
`\nRun \`${prefix} agent <topic>\` for the full recipe (e.g. \`${prefix} agent create-sg-rule\`).`
167169
);
168170
return;
169171
}
@@ -177,7 +179,7 @@ export function createAgentCommand(subCommands: SubCommandsDef) {
177179
telemetry.capture("cli_agent", { topic: positionals.join(" ") });
178180
console.error(`Too many arguments: ${positionals.join(" ")}`);
179181
console.error(
180-
"A topic is a single token. Run `taskless agent` for the topic index."
182+
`A topic is a single token. Run \`${getCliPrefix()} agent\` for the topic index.`
181183
);
182184
process.exitCode = 1;
183185
return;
@@ -238,7 +240,7 @@ export function createAgentCommand(subCommands: SubCommandsDef) {
238240
// cli_agent for an unknown topic — still the attempted topic string.
239241
telemetry.capture("cli_agent", { topic: key });
240242
console.error(`Unknown command: ${key}`);
241-
console.error("Run `taskless agent` for available topics.");
243+
console.error(`Run \`${getCliPrefix()} agent\` for available topics.`);
242244
process.exitCode = 1;
243245
}
244246
},

‎packages/cli/src/commands/auth.ts‎

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { fetchWhoami } from "../auth/whoami";
88
import { getTelemetry } from "../telemetry";
99
import { type CLIErrorCode, writeJsonError } from "../types/errors";
1010
import { splitRawArguments } from "../util/argv";
11+
import { getCliPrefix } from "../util/package-manager";
1112

1213
const loginCommand = defineCommand({
1314
meta: {
@@ -70,7 +71,9 @@ const loginCommand = defineCommand({
7071
case "already_logged_in": {
7172
if (!args.json) {
7273
console.log("You are already logged in.");
73-
console.log("Run `taskless auth logout` first to re-authenticate.");
74+
console.log(
75+
`Run \`${getCliPrefix()} auth logout\` first to re-authenticate.`
76+
);
7477
}
7578
return;
7679
}
@@ -174,15 +177,15 @@ export const authCommand = defineCommand({
174177
const token = await getToken(cwd);
175178
if (!token) {
176179
console.log("Not logged in.");
177-
console.log("Run `taskless auth login` to authenticate.");
180+
console.log(`Run \`${getCliPrefix()} auth login\` to authenticate.`);
178181
return;
179182
}
180183

181184
const whoami = await fetchWhoami(token);
182185
if (!whoami) {
183186
console.log("Logged in, but unable to verify identity.");
184187
console.log(
185-
"Your token may be invalid or expired. Run `taskless auth login` to re-authenticate."
188+
`Your token may be invalid or expired. Run \`${getCliPrefix()} auth login\` to re-authenticate.`
186189
);
187190
return;
188191
}

‎packages/cli/src/commands/check.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import { planCheck } from "../rules/plan-check";
1919
import { openRun } from "../rules/run-directory";
2020
import { fromProjectRoot } from "../rules/snapshot";
2121
import { markNotice } from "../util/notices";
22+
import { getCliPrefix } from "../util/package-manager";
2223

2324
async function pathExists(absolutePath: string): Promise<boolean> {
2425
try {
@@ -334,7 +335,7 @@ export const checkCommand = defineCommand({
334335
);
335336
} else {
336337
console.log(
337-
"No rules configured. Create one with `taskless rule create`."
338+
`No rules configured. Create one with \`${getCliPrefix()} rule create --from <file>\`.`
338339
);
339340
}
340341
return;

‎packages/cli/src/commands/feedback.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import {
1515
import { getTelemetry, isTelemetryEnabled } from "../telemetry";
1616
import { type CLIErrorCode, writeJsonError } from "../types/errors";
1717
import { CLIError } from "../util/cli-error";
18+
import { getCliPrefix } from "../util/package-manager";
1819

1920
/**
2021
* What both verbs say under the telemetry opt-out. An agent should never
@@ -111,7 +112,7 @@ const sendCommand = defineCommand({
111112
// whether or not anything would be sent, and the agent should hear so.
112113
if (!args.from) {
113114
fail(
114-
"--from is required. Provide a path to a JSON file.\n Example: taskless feedback send --from .taskless/.tmp-feedback.json",
115+
`--from is required. Provide a path to a JSON file.\n Example: ${getCliPrefix()} feedback send --from .taskless/.tmp-feedback.json`,
115116
"INVALID_INPUT"
116117
);
117118
}

‎packages/cli/src/commands/onboard.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import { getTelemetry } from "../telemetry";
1212
import { CLIError } from "../util/cli-error";
1313
import {
1414
detectCliInvocation,
15+
getCliPrefix,
1516
processLauncherContext,
1617
} from "../util/package-manager";
1718

@@ -31,9 +32,9 @@ export function getOnboardTrailer(args: {
3132
commandsInstalled: boolean;
3233
}): string {
3334
if (args.commandsInstalled) {
34-
return "Next: in your AI tool, run /tskl onboard or ask it to use the Taskless skill (or run `taskless onboard` from your terminal) to discover rule candidates from your codebase.";
35+
return `Next: in your AI tool, run /tskl onboard or ask it to use the Taskless skill (or run \`${getCliPrefix()} onboard\` from your terminal) to discover rule candidates from your codebase.`;
3536
}
36-
return "Next: in your AI tool, ask it to use the Taskless skill (or run `taskless onboard` from your terminal) to discover rule candidates from your codebase.";
37+
return `Next: in your AI tool, ask it to use the Taskless skill (or run \`${getCliPrefix()} onboard\` from your terminal) to discover rule candidates from your codebase.`;
3738
}
3839

3940
export const onboardCommand = defineCommand({
@@ -97,7 +98,7 @@ export const onboardCommand = defineCommand({
9798
if (alreadyOnboarded && !args.force) {
9899
console.log("Taskless onboarding is already marked complete.");
99100
console.log(
100-
"Run `taskless onboard --force` to re-run the discovery recipe."
101+
`Run \`${getCliPrefix()} onboard --force\` to re-run the discovery recipe.`
101102
);
102103
return;
103104
}

‎packages/cli/src/commands/rules.ts‎

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ import {
4343
import { getTelemetry } from "../telemetry";
4444
import { CLIError } from "../util/cli-error";
4545
import { type CLIErrorCode, writeJsonError } from "../types/errors";
46+
import { getCliPrefix } from "../util/package-manager";
4647

4748
/** Why submitting a request or an iteration failed, as a message and a code. */
4849
function describeSubmitFailure(
@@ -251,8 +252,7 @@ const createCommand = defineCommand({
251252
// `create-sg-rule`: authoring an ast-grep rule on-device with no service
252253
// call is exactly what anonymous mode asks for, so it is the destination
253254
// rather than an `--anonymous` variant of the service recipe.
254-
const message =
255-
"Anonymous rule generation runs in the agent. Run `taskless agent create-sg-rule` to fetch the local-only recipe.";
255+
const message = `Anonymous rule generation runs in the agent. Run \`${getCliPrefix()} agent create-sg-rule\` to fetch the local-only recipe.`;
256256
if (args.json) {
257257
writeJsonError("INVALID_INPUT", message);
258258
} else {
@@ -269,7 +269,7 @@ const createCommand = defineCommand({
269269
// 1. Read and validate --from file
270270
if (!args.from) {
271271
fail(
272-
"--from is required. Provide a path to a JSON file.\n Example: taskless rule create --from request.json",
272+
`--from is required. Provide a path to a JSON file.\n Example: ${getCliPrefix()} rule create --from request.json`,
273273
"INVALID_INPUT"
274274
);
275275
}
@@ -398,8 +398,7 @@ const improveCommand = defineCommand({
398398
}
399399

400400
if (args.anonymous) {
401-
const message =
402-
"Anonymous rule improvement runs in the agent. Run `taskless agent improve-rule --anonymous` to fetch the local-only recipe.";
401+
const message = `Anonymous rule improvement runs in the agent. Run \`${getCliPrefix()} agent improve-rule --anonymous\` to fetch the local-only recipe.`;
403402
if (args.json) {
404403
writeJsonError("INVALID_INPUT", message);
405404
} else {
@@ -416,7 +415,7 @@ const improveCommand = defineCommand({
416415
// 1. Read and validate --from file
417416
if (!args.from) {
418417
fail(
419-
"--from is required. Provide a path to a JSON file.\n Example: taskless rule improve --from request.json",
418+
`--from is required. Provide a path to a JSON file.\n Example: ${getCliPrefix()} rule improve --from request.json`,
420419
"INVALID_INPUT"
421420
);
422421
}
@@ -558,7 +557,7 @@ const metaCommand = defineCommand({
558557
`the rule service does not return the metadata block that ` +
559558
`.taskless/rule-metadata/ is written from. This is not specific to "${args.id}". ` +
560559
`To iterate on a rule, pass its id (the rule's directory name under .taskless/rules/<engine>/) ` +
561-
`to \`taskless rule improve --from <file>\`, or use the local-only improve flow.`,
560+
`to \`${getCliPrefix()} rule improve --from <file>\`, or use the local-only improve flow.`,
562561
"RULE_META_UNAVAILABLE"
563562
);
564563
}

‎packages/cli/src/index.ts‎

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
} from "./util/argv";
3232
import { shouldLaunchWizard } from "./util/interactive";
3333
import { showResolvedUsage } from "./util/help";
34+
import { getCliPrefix } from "./util/package-manager";
3435
import { CLIError } from "./util/cli-error";
3536

3637
// `satisfies` against the name list (minus `agent`, which is constructed from
@@ -140,11 +141,12 @@ const main = defineCommand({
140141
return;
141142
}
142143

144+
const prefix = getCliPrefix();
143145
console.error(
144146
"Taskless CLI — non-interactive context detected.\n" +
145147
" For interactive install, run from a terminal.\n" +
146-
" For scripted install, run `taskless init`.\n" +
147-
" For agent recipes, run `taskless agent` (no args) for the topic index.\n"
148+
` For scripted install, run \`${prefix} init\`.\n` +
149+
` For agent recipes, run \`${prefix} agent\` (no args) for the topic index.\n`
148150
);
149151
// Forward the parent's rawArgs (e.g. `-d <path>`) so the agent command
150152
// doesn't mis-parse them as positional topic names.

‎packages/cli/src/rules/deliver.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import {
88
} from "./layout";
99
import { ruleDirectory } from "./engines";
1010
import { isMissingDirectory } from "./errno";
11+
import { getCliPrefix } from "../util/package-manager";
1112

1213
/** One file of a delivered rule, its path relative to the rule directory. */
1314
export interface DeliveredFile {
@@ -140,7 +141,7 @@ export function describeMissingFixtures(
140141
if (hasFixture) return undefined;
141142
return (
142143
`was delivered with no ${prefix} fixtures, so nothing exercises it: ` +
143-
"`taskless test` reports a pass over zero cases, which is " +
144+
`\`${getCliPrefix()} test\` reports a pass over zero cases, which is ` +
144145
"indistinguishable from a rule proven to work"
145146
);
146147
}

‎packages/cli/src/rules/dispatch.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { RuntimeRule } from "./runtime/discover";
1010
import { runAstGrepScan } from "./scan";
1111
import { runVale } from "./vale/run";
1212
import { collectNotices } from "../util/notices";
13+
import { getCliPrefix } from "../util/package-manager";
1314

1415
/**
1516
* Whether `.taskless/rules/vale/` holds anything to run.
@@ -286,7 +287,7 @@ function describeValeRefusal(refused: RefusedValeConfig): string {
286287
);
287288
return [
288289
`Vale did not run: the config of ${rules} was rejected by the config schema. ` +
289-
`Run \`taskless verify\` for the constraint behind each line.`,
290+
`Run \`${getCliPrefix()} verify\` for the constraint behind each line.`,
290291
...lines,
291292
].join("\n");
292293
}

0 commit comments

Comments
 (0)