@taskless/cli 0.11.0 → 0.11.1

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.
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Public entry for `@taskless/cli/layout`.
3
+ *
4
+ * The rule layout table, as data: which engines exist, what file *is* the rule
5
+ * for each of them, where its per-engine config and capture rules live, and the
6
+ * directory names the whole tree hangs off. A rule is one directory,
7
+ * `.taskless/rules/<engine>/<id>/`, and this describes its contents.
8
+ *
9
+ * These are the same values the CLI itself dispatches on — not a copy kept in
10
+ * step, the actual module. Nothing here reaches the filesystem, the network,
11
+ * telemetry, or the command tree, so a Worker can import it; the build fails
12
+ * rather than emitting an entry whose graph reaches a host capability.
13
+ *
14
+ * ## Why this is published
15
+ *
16
+ * A service that builds a rule payload has to know what a complete rule is for
17
+ * a given engine. The alternative to importing it is transcribing it, and
18
+ * transcription drifts: the same layout was written into seven code comments
19
+ * and one specification here, all naming a path two migrations had already
20
+ * moved, and one of those stale comments carried the wrong layout into a
21
+ * cross-team design document before anyone noticed.
22
+ *
23
+ * ## What it does not tell you
24
+ *
25
+ * Only the shape. Whether a rule *runs* is a separate question with separate
26
+ * answers — a runtime rule executes only against a server-blessed signature
27
+ * over its `check.ts`, and a Vale rule fires only where its own `.vale.ini`
28
+ * scopes it. A file set that satisfies this table is well-formed, not
29
+ * necessarily live.
30
+ */
31
+ export { ENGINES, ENGINE_LAYOUTS, RULES_DIRECTORY, RULE_TESTS_DIRECTORY, TASKLESS_DIRECTORY, isKnownEngine, type EngineExecutor, type EngineLayout, type EngineName, type FixtureLayout, } from "../rules/layout.js";
package/dist/layout.js ADDED
@@ -0,0 +1,43 @@
1
+ const t = ["sg", "vale", "runtime"], r = "rules", i = ".taskless", n = ".tests", s = {
2
+ sg: {
3
+ engine: "sg",
4
+ ruleFile: (e) => `${e}.yml`,
5
+ ruleConfigFile: void 0,
6
+ capturesDirectory: void 0,
7
+ fixtureLayout: "ast-grep-test",
8
+ executor: "ast-grep"
9
+ },
10
+ vale: {
11
+ engine: "vale",
12
+ ruleFile: (e) => `${e}.yml`,
13
+ ruleConfigFile: ".vale.ini",
14
+ capturesDirectory: void 0,
15
+ fixtureLayout: "case-documents",
16
+ executor: "vale-runner"
17
+ },
18
+ runtime: {
19
+ engine: "runtime",
20
+ ruleFile: () => "check.ts",
21
+ ruleConfigFile: void 0,
22
+ // `captures/` rather than `matchers/`: "matcher" denotes a Vale `[<glob>]`
23
+ // config section elsewhere in this tree, and one word for two unrelated
24
+ // concepts is a cost paid at every future reading.
25
+ capturesDirectory: "captures",
26
+ // Directories, not documents. A runtime rule exists because its evidence
27
+ // spans more than one file, so a one-file case could not express the rules
28
+ // this tier is for.
29
+ fixtureLayout: "case-directories",
30
+ executor: "runtime-harness"
31
+ }
32
+ };
33
+ function u(e) {
34
+ return t.includes(e);
35
+ }
36
+ export {
37
+ t as ENGINES,
38
+ s as ENGINE_LAYOUTS,
39
+ r as RULES_DIRECTORY,
40
+ n as RULE_TESTS_DIRECTORY,
41
+ i as TASKLESS_DIRECTORY,
42
+ u as isKnownEngine
43
+ };
@@ -0,0 +1,102 @@
1
+ /**
2
+ * Public entry for `@taskless/cli/node/runtimes`.
3
+ *
4
+ * Where the engine binaries this CLI executes actually live on this host, and
5
+ * how to find them. It is the CLI's own resolver and the CLI's own package
6
+ * specs — not a copy kept in step, the actual modules — so a consumer that
7
+ * installs this package resolves the same ast-grep and the same Vale that
8
+ * `taskless check` would run.
9
+ *
10
+ * ## Why this is published
11
+ *
12
+ * A service that verifies a generated rule has to execute it, and a rule's
13
+ * behaviour is a property of the engine version that runs it, not of the
14
+ * version it was written against. ast-grep in particular changes what a capture
15
+ * matches across minors: a rule verified under one and executed under another
16
+ * can verify clean and then match nothing, which is a rule that passes its gate
17
+ * and never fires. Nothing reports that, because both sides succeeded.
18
+ *
19
+ * The alternative to importing this is pinning the engine version by hand
20
+ * alongside the CLI version, in a second place, and keeping the two in step.
21
+ * They drift, and the drift is silent in exactly the direction that matters.
22
+ * Installing this package and asking it where the binary is makes the engine
23
+ * version a consequence of the CLI version rather than a parallel fact: the
24
+ * pinned platform packages arrive as this package's own optional dependencies,
25
+ * and they move when the pin moves.
26
+ *
27
+ * ## This entry requires Node, and says so in its path
28
+ *
29
+ * `node/` is in the specifier deliberately. Resolution spawns each candidate to
30
+ * make it identify itself, reads the filesystem, and consults `PATH`, so this
31
+ * graph reaches `node:child_process`, `node:fs`, `node:module`, `node:path` and
32
+ * `node:url`. It will not run on a Worker or in a browser, and the import path
33
+ * says that before a build error has to.
34
+ *
35
+ * This is the difference between this entry and `@taskless/cli/prompts` and
36
+ * `@taskless/cli/layout`, which are host-free and whose builds fail if their
37
+ * graphs ever reach a host capability. Every published entry is classified as
38
+ * one or the other in `vite.config.ts`, and the build fails on an export
39
+ * belonging to neither, so this exemption is a declaration rather than an
40
+ * omission.
41
+ *
42
+ * ## Resolution reports a miss as a value, and each side decides what it means
43
+ *
44
+ * {@link resolvePlatformBinary} returns `{ path: undefined, tried }` when no
45
+ * candidate verified. It does not throw, and that is deliberate rather than
46
+ * incidental: `taskless check` runs several engines, and a host without Vale
47
+ * installed must lose the Vale rules rather than the whole run. Encoding "not
48
+ * found" as a value is what lets a caller choose.
49
+ *
50
+ * A verifier should choose the opposite. A sandbox whose optional dependency
51
+ * did not install resolves `undefined` for the same reason a developer's laptop
52
+ * does — nothing is there — but the consequence is not a skipped engine, it is
53
+ * a verification that ran no engine and reported success. Fail closed on
54
+ * `undefined`, and use `tried` to say where you looked:
55
+ *
56
+ * ```ts
57
+ * import {
58
+ * AST_GREP_BINARY,
59
+ * resolvePlatformBinary,
60
+ * } from "@taskless/cli/node/runtimes";
61
+ *
62
+ * const { path, tried } = resolvePlatformBinary(AST_GREP_BINARY);
63
+ * if (path === undefined) {
64
+ * throw new Error(`ast-grep not resolved. Looked in: ${tried.join(", ")}`);
65
+ * }
66
+ * ```
67
+ *
68
+ * ## What a resolved path is, and is not
69
+ *
70
+ * It is a path that exists and that answered `--version` with output matching
71
+ * the spec's `identity`, so it is the binary and not a placeholder a failed
72
+ * install left behind.
73
+ *
74
+ * **It is not a promise about which version answered.** The first candidate
75
+ * tried is the pinned platform package, but a host that only has the engine on
76
+ * `PATH` resolves that instead, and that binary can be any version.
77
+ *
78
+ * `resolution.source` says which tier answered. Only `"platform-package"` is
79
+ * the pinned install, so only that value tells a caller the binary is the one
80
+ * this CLI ships:
81
+ *
82
+ * ```ts
83
+ * const { path, source } = resolvePlatformBinary(AST_GREP_BINARY);
84
+ * if (path === undefined) throw new Error("no engine");
85
+ * if (source !== "platform-package") throw new Error("not the pinned engine");
86
+ * // The binary at `path` is now known to be the pinned build.
87
+ * ```
88
+ *
89
+ * {@link isPlatformBinary} does NOT answer this. It checks that a file exists
90
+ * and answers `--version` as the right tool, which every tier satisfies by the
91
+ * time a path is returned. Identity is not provenance, and an earlier draft of
92
+ * this note said otherwise.
93
+ *
94
+ * **The pinned versions are deliberately not published here.** A consumer
95
+ * wanting to know it has the pinned engine should read `source`, which answers
96
+ * that directly and at resolution time. A version constant would answer it
97
+ * later and less well, by inviting a comparison against a number rather than a
98
+ * check of where the binary came from.
99
+ */
100
+ export { findOnPath, isPlatformBinary, pathCommandName, platformPackageName, resolvePlatformBinary, type PlatformBinaryResolution, type PlatformBinarySource, type PlatformBinarySpec, } from "../../rules/platform-binary.js";
101
+ export { AST_GREP_BINARY } from "../../rules/ast-grep-binary.js";
102
+ export { VALE_BINARY } from "../../rules/vale/binary.js";
@@ -0,0 +1,10 @@
1
+ import { A as s, V as m, f as o, i as e, p as f, a as t, r as i } from "../binary-DSACXL6n.js";
2
+ export {
3
+ s as AST_GREP_BINARY,
4
+ m as VALE_BINARY,
5
+ o as findOnPath,
6
+ e as isPlatformBinary,
7
+ f as pathCommandName,
8
+ t as platformPackageName,
9
+ i as resolvePlatformBinary
10
+ };
@@ -11,7 +11,7 @@ import { type RecipeOptions, type RecipeText } from "./recipes.js";
11
11
  /**
12
12
  * Topics exported as public API. Hand-maintained rather than derived from the
13
13
  * recipe files, because an exported name is a promise held for a major version
14
- * and a new `agent/*.txt` must not be able to publish one by existing. The
14
+ * and a new `agent/*.md` must not be able to publish one by existing. The
15
15
  * completeness check in `test/prompts.test.ts` asserts this list plus
16
16
  * {@link INTERNAL_TOPICS} accounts for every canonical recipe on disk.
17
17
  *
@@ -23,13 +23,28 @@ import { type RecipeOptions, type RecipeText } from "./recipes.js";
23
23
  * remove.
24
24
  *
25
25
  * `engine-selection` used to be exported alongside them. It no longer exists:
26
- * the criterion it carried now lives in `route`, stated once. `route` is not
27
- * exported yet because it still contains local mechanics (`taskless detect`,
28
- * on-device authoring) a Worker cannot run; until it is, a consumer gets each
29
- * destination's own scope from these three and adjudicates genuinely ambiguous
30
- * calls itself.
26
+ * the criterion it carried now lives in `route`, stated once.
27
+ *
28
+ * `route` was withheld on the grounds that it still contains local mechanics
29
+ * (`taskless detect`, on-device authoring) a Worker cannot run, and that a
30
+ * consumer could adjudicate ambiguous calls from the three destinations. Both
31
+ * halves were wrong in the same way.
32
+ *
33
+ * The mechanics are true and do not defeat it: a service consumer ignores
34
+ * them, which is a smaller adaptation than restating the criteria. And
35
+ * adjudicating from the destinations is what the platform generator actually
36
+ * tried. It hand-wrote the same judgement, arrived at `static | runtime` with
37
+ * nowhere to put `vale`, and generated every prose rule as an ast-grep rule
38
+ * while its own delivery layer could already serve a Vale one. `route` warns
39
+ * about exactly that conflation: whether a rule is static or runtime is one
40
+ * axis, and which engine authors it is another.
41
+ *
42
+ * That is the dead end named two paragraphs up, mirrored. Exporting a chooser
43
+ * without its destinations strands a consumer that can route but not author;
44
+ * exporting destinations without the chooser strands one that can author but
45
+ * not route, and it will write its own chooser rather than stop.
31
46
  */
32
- export declare const TOPICS: readonly ["create-sg-rule", "create-vale-rule", "create-runtime-rule"];
47
+ export declare const TOPICS: readonly ["create-sg-rule", "create-vale-rule", "create-runtime-rule", "route"];
33
48
  /**
34
49
  * Recipes deliberately withheld from the export, recorded so they stay visible
35
50
  * decisions rather than oversights. Two groups:
@@ -37,13 +52,12 @@ export declare const TOPICS: readonly ["create-sg-rule", "create-vale-rule", "cr
37
52
  * - Command recipes (`auth` … `update`) walk an agent through running a CLI
38
53
  * subcommand on a developer's machine. There is no caller for them outside
39
54
  * the CLI that hosts those commands.
40
- * - Authoring recipes are unreachable server-side: `route` picks an authoring
41
- * destination before the service is involved, `create-remote-rule` states
55
+ * - Authoring recipes are unreachable server-side: `create-remote-rule` states
42
56
  * the boundary from the client's side, `detect` documents a CLI subprocess a
43
57
  * Worker cannot spawn, `create-legacy-rule` targets a local toolchain, and
44
- * `rule-meta` reads an `improve-rule` sidecar file.
58
+ * `rule-meta` describes a local sidecar file the CLI never writes.
45
59
  */
46
- export declare const INTERNAL_TOPICS: readonly ["auth", "check", "ci", "create-legacy-rule", "create-remote-rule", "delete-rule", "detect", "improve-rule", "info", "init", "onboard", "route", "rule", "rule-meta", "update", "verify-rule"];
60
+ export declare const INTERNAL_TOPICS: readonly ["auth", "check", "ci", "create-legacy-rule", "create-remote-rule", "delete-rule", "detect", "improve-rule", "info", "init", "onboard", "rule", "rule-meta", "update", "verify-rule"];
47
61
  /** A topic name the package exports. Unknown names fail to type-check. */
48
62
  export type PromptTopic = (typeof TOPICS)[number];
49
63
  /** Options accepted by every prompt render function. */
@@ -1,4 +1,4 @@
1
- /** The canonical `<topic>.txt` recipe names present in the build. */
1
+ /** The canonical `<topic>.md` recipe names present in the build. */
2
2
  export declare function canonicalRecipeTopics(): string[];
3
3
  /** Options accepted by the shared render path. */
4
4
  export interface RecipeOptions {
@@ -37,6 +37,24 @@ export interface RecipeOptions {
37
37
  * @default "<taskless-cli>"
38
38
  */
39
39
  invocation?: string;
40
+ /**
41
+ * Render the steps that gather evidence by running this CLI.
42
+ *
43
+ * `false` replaces them with a statement of what the caller must supply
44
+ * instead. It exists because `invocation` cannot do this job: that option
45
+ * substitutes the BINARY NAME inside a command, so a consumer with no CLI
46
+ * setting it to a phrase renders `Run: <no CLI available> detect --json` — a
47
+ * malformed instruction rather than a clean absence, and worse than either
48
+ * honest answer.
49
+ *
50
+ * The evidence itself is not optional. A consumer that cannot run the
51
+ * commands still needs the linters, languages, rule styles, login state and
52
+ * owner, because the routing criteria are stated in terms of them. So this
53
+ * names what to supply rather than dropping the steps.
54
+ *
55
+ * @default true
56
+ */
57
+ mechanics?: boolean;
40
58
  /**
41
59
  * Include the `# Topic: <name> (CLI v<version> / topic vN)` first line.
42
60
  * Suppressing it drops the CLI version from the text, which matters to
@@ -47,17 +65,6 @@ export interface RecipeOptions {
47
65
  */
48
66
  header?: boolean;
49
67
  }
50
- /**
51
- * Render a recipe by interpolating sprintf-js named arguments. The recipe
52
- * source uses `%(KEY)s` placeholders; the variable table built here resolves
53
- * each known placeholder to its rendered string. Recipes that contain a
54
- * literal `%` character must escape it as `%%` per sprintf-js conventions.
55
- *
56
- * Two flavors of substitution coexist in the variables table:
57
- * - System-resolved values (e.g. `CLI_VERSION`) — rendered to a real value.
58
- * - Agent-fill markers (e.g. `PACKAGE_MANAGER_DLX`) — rendered as
59
- * `<lower-kebab-name>` so the consuming agent knows to substitute.
60
- */
61
68
  export declare function buildVariables(content: string, topic: string, options?: RecipeOptions): Record<string, string>;
62
69
  /** A recipe's text plus the sprintf variables its template contains. */
63
70
  export interface RecipeText {
@@ -87,3 +94,21 @@ export declare function getRecipe(topic: string, options?: RecipeOptions): strin
87
94
  export declare function getRawRecipe(topic: string, options?: RecipeOptions): RecipeText | undefined;
88
95
  /** A topic's rendered text plus the variables its template contains. */
89
96
  export declare function getRenderedRecipe(topic: string, options?: RecipeOptions): RecipeText | undefined;
97
+ /**
98
+ * Remove Vale's in-file directives from a recipe before anyone reads it.
99
+ *
100
+ * A recipe is checked by this repository's own Vale rules, and two of them
101
+ * teach through a worked example that quotes the words a shipped rule flags.
102
+ * `<!-- vale no-hedging.no-hedging = NO -->` marks that example so the rest of
103
+ * the file stays covered, which is only possible because a recipe is markdown.
104
+ *
105
+ * The directives are configuration, not content. Stripping happens HERE, where
106
+ * the embedded files are read into the maps, rather than at any of the three
107
+ * render entry points: one place to be correct, and `getRawRecipe` is covered
108
+ * by the same stroke as the rendered paths.
109
+ *
110
+ * The whole line goes, including its newline. Leaving a blank line behind
111
+ * would change the markdown a reader sees, which would make the exclusion
112
+ * mechanism visible in the output it exists to keep clean.
113
+ */
114
+ export declare function stripValeDirectives(content: string): string;
package/dist/prompts.js CHANGED
@@ -1,8 +1,9 @@
1
- import { H as n, a6 as i, a7 as u } from "./recipes-C_i98mTO.js";
2
- const a = [
1
+ import { g as n, a as i, b as u } from "./recipes-w68swLg4.js";
2
+ const o = [
3
3
  "create-sg-rule",
4
4
  "create-vale-rule",
5
- "create-runtime-rule"
5
+ "create-runtime-rule",
6
+ "route"
6
7
  ], s = [
7
8
  "auth",
8
9
  "check",
@@ -15,13 +16,12 @@ const a = [
15
16
  "info",
16
17
  "init",
17
18
  "onboard",
18
- "route",
19
19
  "rule",
20
20
  "rule-meta",
21
21
  "update",
22
22
  "verify-rule"
23
23
  ];
24
- function o(e, r) {
24
+ function a(e, r) {
25
25
  return t(n(e, r), e);
26
26
  }
27
27
  function l(e, r) {
@@ -33,21 +33,21 @@ function d(e, r) {
33
33
  function t(e, r) {
34
34
  if (e === void 0)
35
35
  throw new Error(
36
- `No recipe is embedded for prompt topic "${r}". This is a packaging fault: TOPICS lists a topic with no agent/${r}.txt behind it.`
36
+ `No recipe is embedded for prompt topic "${r}". This is a packaging fault: TOPICS lists a topic with no agent/${r}.md behind it.`
37
37
  );
38
38
  return e;
39
39
  }
40
- const f = Object.fromEntries(
41
- a.map((e) => [
40
+ const g = Object.fromEntries(
41
+ o.map((e) => [
42
42
  e,
43
- (r) => o(e, r)
43
+ (r) => a(e, r)
44
44
  ])
45
45
  );
46
46
  export {
47
47
  s as INTERNAL_TOPICS,
48
- f as PROMPTS,
49
- a as TOPICS,
48
+ g as PROMPTS,
49
+ o as TOPICS,
50
50
  l as getInstructions,
51
- o as getPrompt,
51
+ a as getPrompt,
52
52
  d as getRawInstructions
53
53
  };