Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 10 additions & 10 deletions .github/workflows/demo-rule.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@
# this check to the protection rule. That is the same reason the house-style
# scan lives inside `validate.yml` rather than beside it.
#
# **It is not the only place `check.ts` runs in CI.** `test/demo-command.test.ts`
# executes it too, under `Validate`. What this job adds is a different
# assertion: it copies the bytes `taskless demo` would write — the manifest's
# own written-path list, read straight off `assets/demo-*/` — into one bare
# project, and runs the engines over them with no command and no bundle in the
# way. So a break in a shipped asset tree is attributed to that tree, rather
# than to the command that writes it or the imports that embed it.
# **It is the one place these rules' fixtures run.** The rules are the
# conformance corpus published as `@taskless/cli/reference.json`, and
# `test/reference.test.ts` only proves that payload matches the asset trees.
# This job proves the trees themselves hold: it copies the manifest's own
# written-path list, read straight off `assets/demo-*/`, into one bare project
# and runs the engines over it. A corpus rule that fails its own cases would
# hand another team a broken oracle.
name: Demo rules

on:
Expand Down Expand Up @@ -84,9 +84,9 @@ jobs:
pnpm --filter @taskless/cli demo:manifests /tmp/manifests.txt
cat /tmp/manifests.txt
# The trailing field is the manifest's own written-path list, so this
# stages exactly what `taskless demo` writes. A `cp -R` of the asset
# directory would also land `prompt.md`, which the command never
# writes, and this job would be judging a tree no user ever has.
# stages exactly the rule and its cases. A `cp -R` of the asset
# directory would also land `prompt.md`, the generation request, and
# this job would be judging a tree no generator is asked to produce.
while read -r engine ruleId assetDir paths; do
[ -z "$engine" ] && continue
RULE="$PROJECT/.taskless/rules/$engine/$ruleId"
Expand Down
4 changes: 2 additions & 2 deletions eslint.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ export default tseslint.config(
"openspec/",
"**/test/fixtures/",
// The demonstration rule ships as DATA, not as source. Its `check.ts`
// is bytes the CLI writes into a user's project, where it runs under
// `tsx` against a structural contract and imports nothing from this
// is a rule in the published conformance corpus, run under `tsx`
// against a structural contract and importing nothing from this
// repository — so it is deliberately outside every tsconfig, and
// type-aware linting has no project to resolve it against.
"packages/cli/assets/demo-runtime/",
Expand Down
8 changes: 4 additions & 4 deletions packages/cli/scripts/print-demo-manifests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* where a fourth sample would be added to the manifest and silently not staged.
*
* The trailing paths are {@link writtenPaths}, not the asset directory's
* contents, so a staging loop copies exactly what `taskless demo` writes. A
* whole-directory copy would additionally land `prompt.md`, which the command
* never writes, and the job would then be attributing results to a tree no
* user ever has.
* contents, so a staging loop copies exactly the rule and its cases. A
* whole-directory copy would additionally land `prompt.md`, which is the
* generation request rather than part of any rule tree, and the job would then
* be judging a tree a generator is never asked to produce.
*
* Takes an output PATH rather than printing to stdout. Capturing stdout meant
* capturing whatever the runner wrapped around it: `pnpm --filter ... > file`
Expand Down
40 changes: 7 additions & 33 deletions packages/cli/scripts/write-reference.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,50 +5,24 @@
* itself, and the held-out cases — separately, so another team can run their
* rule against our cases and ours against theirs.
*
* Reads the asset files directly rather than importing `rule.ts`, whose `?raw`
* specifiers only resolve under a vite transform. Both read `DEMO_MANIFESTS`,
* and `test/reference.test.ts` asserts they agree.
* `test/reference.test.ts` builds the payload through the same reader and
* asserts the committed file matches it.
*
* Run: pnpm --filter @taskless/cli reference
*/

import { readFile, writeFile } from "node:fs/promises";
import { writeFile } from "node:fs/promises";
import { join } from "node:path";

import { buildReference } from "../src/rules/reference";
import { DEMO_MANIFESTS } from "../src/rules/demo/manifest";
import { readReferenceInputs } from "../src/rules/demo/manifest";

const packageRoot = join(import.meta.dirname, "..");
const assetsRoot = join(import.meta.dirname, "..", "assets");

async function readAll(assetDirectory: string, paths: readonly string[]) {
return Promise.all(
paths.map(async (path) => ({
path,
content: await readFile(
join(packageRoot, "assets", assetDirectory, path),
"utf8"
),
}))
);
}

const rules = await Promise.all(
DEMO_MANIFESTS.map(async (manifest) => ({
engine: manifest.engine,
ruleId: manifest.ruleId,
prompt: await readFile(
join(packageRoot, "assets", manifest.assetDirectory, manifest.promptPath),
"utf8"
),
ruleFiles: await readAll(manifest.assetDirectory, manifest.rulePaths),
testFiles: await readAll(manifest.assetDirectory, manifest.testPaths),
}))
);

const target = join(packageRoot, "assets", "reference.json");
const target = join(assetsRoot, "reference.json");
await writeFile(
target,
`${JSON.stringify(buildReference(rules), undefined, 2)}\n`,
`${JSON.stringify(buildReference(await readReferenceInputs(assetsRoot)), undefined, 2)}\n`,
"utf8"
);
console.log(`Wrote ${target}`);
12 changes: 1 addition & 11 deletions packages/cli/src/commands/agent.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,16 +90,6 @@ async function resolveDescription(
* unrelated reasons and a bare `name === "agent"` recorded none of them.
*
* - `agent` is the index itself, so listing it would be circular.
* - `demo` writes a fixed example rule for someone learning what a rule is. It
* is run deliberately, in a demonstration, and it is not surface we are
* exposing yet. Its description ("Write an example rule into this project")
* is close enough to a real authoring request that an agent asked to "add a
* rule that checks X" could route to it and write the example instead. That
* is the whole reason the demonstration was originally nested under `rule`:
* the property came from not being registered at all. Making it a top-level
* verb gave it discoverability it does not want yet, and this is the cost of
* that choice, paid here.
*
* - `feedback` the COMMAND is reached through a recipe, never directly. The
* index lists the `feedback` and `bug-report` recipes instead (see
* `FEEDBACK_TOPICS`), because they are what show the user the payload and
Expand All @@ -111,7 +101,7 @@ async function resolveDescription(
* Absence from this list is what puts a command in the index, so adding one is
* a decision someone made rather than a step they forgot.
*/
const UNLISTED_COMMANDS = new Set(["agent", "demo", "feedback"]);
const UNLISTED_COMMANDS = new Set(["agent", "feedback"]);

export function createAgentCommand(subCommands: SubCommandsDef) {
return defineCommand({
Expand Down
158 changes: 0 additions & 158 deletions packages/cli/src/commands/demo.ts

This file was deleted.

1 change: 0 additions & 1 deletion packages/cli/src/commands/names.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const SUBCOMMAND_NAMES = [
"agent",
"auth",
"check",
"demo",
"detect",
"feedback",
"info",
Expand Down
2 changes: 0 additions & 2 deletions packages/cli/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { z } from "zod";
import { createAgentCommand } from "./commands/agent";
import { authCommand } from "./commands/auth";
import { checkCommand } from "./commands/check";
import { demoCommand } from "./commands/demo";
import { detectCommand } from "./commands/detect";
import { feedbackCommand } from "./commands/feedback";
import { initCommand, updateCommand } from "./commands/init";
Expand Down Expand Up @@ -53,7 +52,6 @@ const subCommands = {
info: infoCommand,
detect: detectCommand,
check: checkCommand,
demo: demoCommand,
feedback: feedbackCommand,
auth: authCommand,
onboard: onboardCommand,
Expand Down
Loading
Loading