diff --git a/apps/gittensory-ui/src/lib/command-reference.ts b/apps/gittensory-ui/src/lib/command-reference.ts index 3b366525c9..178c9cfc1d 100644 --- a/apps/gittensory-ui/src/lib/command-reference.ts +++ b/apps/gittensory-ui/src/lib/command-reference.ts @@ -1,7 +1,149 @@ // Generated by scripts/gen-command-reference.mjs. Do not edit manually. // Regenerate via `npm run command-reference`. +export const PUBLIC_COMMAND_ENTRIES = [ + { + "id": "help", + "title": "Gittensory command help", + "description": "Show public-safe @gittensory command help." + }, + { + "id": "ask", + "title": "Gittensory contribution context Q&A", + "description": "Answer contribution-quality questions from connected cached sources with citations." + }, + { + "id": "preflight", + "title": "Gittensory preflight", + "description": "Summarize public PR hygiene and validation readiness." + }, + { + "id": "blockers", + "title": "Gittensory readiness blockers", + "description": "Explain public-safe readiness blockers." + }, + { + "id": "duplicate-check", + "title": "Gittensory duplicate & WIP check", + "description": "Summarize duplicate and in-progress overlap caution." + }, + { + "id": "miner-context", + "title": "Gittensory miner context", + "description": "Confirm public Gittensor miner context when available." + }, + { + "id": "next-action", + "title": "Gittensory next step", + "description": "Suggest the next public-safe action." + }, + { + "id": "reviewability", + "title": "Gittensory PR readiness", + "description": "Summarize maintainer-friendly PR readiness without private review internals." + }, + { + "id": "repo-fit", + "title": "Gittensory repository fit", + "description": "Summarize public-safe repository fit signals." + }, + { + "id": "packet", + "title": "Gittensory public packet", + "description": "Prepare public-safe PR packet guidance." + } +] as const; + export const PUBLIC_COMMAND_LIST = "@gittensory help\n@gittensory ask\n@gittensory preflight\n@gittensory blockers\n@gittensory duplicate-check\n@gittensory miner-context\n@gittensory next-action\n@gittensory reviewability\n@gittensory repo-fit\n@gittensory packet"; +export const MAINTAINER_COMMAND_ENTRIES = [ + { + "id": "queue-summary", + "title": "Gittensory maintainer queue summary", + "description": "Post a maintainer-only queue digest from cached GitHub metadata." + }, + { + "id": "confirmed-miners", + "title": "Gittensory confirmed-miner PRs", + "description": "List open PRs whose authors are confirmed in the official-miner cache." + }, + { + "id": "review-now", + "title": "Gittensory review-now queue", + "description": "List cached PRs that look ready for maintainer review." + }, + { + "id": "needs-author", + "title": "Gittensory needs-author queue", + "description": "List cached PRs that need author cleanup before detailed review." + }, + { + "id": "duplicate-clusters", + "title": "Gittensory duplicate clusters", + "description": "List duplicate or WIP clusters visible from cached GitHub metadata." + }, + { + "id": "burden-forecast", + "title": "Gittensory burden forecast", + "description": "Project maintainer review load and queue-growth risk from cached metadata." + }, + { + "id": "intake-health", + "title": "Gittensory intake health", + "description": "Summarize contributor-intake health from cached queue and config signals." + }, + { + "id": "outcome-patterns", + "title": "Gittensory outcome patterns", + "description": "Summarize what this repo actually merges vs closes from cached PR outcomes." + }, + { + "id": "noise-report", + "title": "Gittensory noise report", + "description": "Highlight queue noise sources maintainers should triage first." + } +] as const; + export const MAINTAINER_COMMAND_LIST = "@gittensory queue-summary\n@gittensory confirmed-miners\n@gittensory review-now\n@gittensory needs-author\n@gittensory duplicate-clusters\n@gittensory burden-forecast\n@gittensory intake-health\n@gittensory outcome-patterns\n@gittensory noise-report"; + +export const ACTION_COMMAND_ENTRIES = [ + { + "id": "gate-override", + "title": "Gate override", + "description": "Record a maintainer override for this commit's gate check only (does not persist across new commits)." + }, + { + "id": "review", + "title": "Request review", + "description": "Request an auto-review run on the current PR head (`@gittensory re-review` is an alias)." + }, + { + "id": "pause", + "title": "Pause auto-review", + "description": "Pause auto-review for this PR with an optional reason; does not change gate enforcement." + }, + { + "id": "resume", + "title": "Resume auto-review", + "description": "Resume auto-review for this PR with an optional reason; does not change gate enforcement." + }, + { + "id": "resolve", + "title": "Resolve finding", + "description": "Mark a review finding as resolved, optionally naming the finding in trailing text." + }, + { + "id": "configuration", + "title": "Show configuration", + "description": "Show the effective resolved review configuration for this repository." + }, + { + "id": "explain", + "title": "Explain finding", + "description": "Explain a specific review finding; supply the finding reference in trailing text." + } +] as const; + +export const ACTION_COMMAND_LIST = + "@gittensory gate-override\n@gittensory review\n@gittensory pause\n@gittensory resume\n@gittensory resolve\n@gittensory configuration\n@gittensory explain"; diff --git a/apps/gittensory-ui/src/routeTree.gen.ts b/apps/gittensory-ui/src/routeTree.gen.ts index 7173d5a8c6..78d409c9f7 100644 --- a/apps/gittensory-ui/src/routeTree.gen.ts +++ b/apps/gittensory-ui/src/routeTree.gen.ts @@ -49,6 +49,7 @@ import { Route as DocsMaintainerWorkflowRouteImport } from './routes/docs.mainta import { Route as DocsMaintainerSelfHostingRouteImport } from './routes/docs.maintainer-self-hosting' import { Route as DocsMaintainerInstallTrustRouteImport } from './routes/docs.maintainer-install-trust' import { Route as DocsHowReviewsWorkRouteImport } from './routes/docs.how-reviews-work' +import { Route as DocsGittensoryCommandsRouteImport } from './routes/docs.gittensory-commands' import { Route as DocsGithubAppRouteImport } from './routes/docs.github-app' import { Route as DocsBranchAnalysisRouteImport } from './routes/docs.branch-analysis' import { Route as DocsBetaOnboardingRouteImport } from './routes/docs.beta-onboarding' @@ -279,6 +280,11 @@ const DocsHowReviewsWorkRoute = DocsHowReviewsWorkRouteImport.update({ path: '/how-reviews-work', getParentRoute: () => DocsRoute, } as any) +const DocsGittensoryCommandsRoute = DocsGittensoryCommandsRouteImport.update({ + id: '/gittensory-commands', + path: '/gittensory-commands', + getParentRoute: () => DocsRoute, +} as any) const DocsGithubAppRoute = DocsGithubAppRouteImport.update({ id: '/github-app', path: '/github-app', @@ -398,6 +404,7 @@ export interface FileRoutesByFullPath { '/docs/beta-onboarding': typeof DocsBetaOnboardingRoute '/docs/branch-analysis': typeof DocsBranchAnalysisRoute '/docs/github-app': typeof DocsGithubAppRoute + '/docs/gittensory-commands': typeof DocsGittensoryCommandsRoute '/docs/how-reviews-work': typeof DocsHowReviewsWorkRoute '/docs/maintainer-install-trust': typeof DocsMaintainerInstallTrustRoute '/docs/maintainer-self-hosting': typeof DocsMaintainerSelfHostingRoute @@ -455,6 +462,7 @@ export interface FileRoutesByTo { '/docs/beta-onboarding': typeof DocsBetaOnboardingRoute '/docs/branch-analysis': typeof DocsBranchAnalysisRoute '/docs/github-app': typeof DocsGithubAppRoute + '/docs/gittensory-commands': typeof DocsGittensoryCommandsRoute '/docs/how-reviews-work': typeof DocsHowReviewsWorkRoute '/docs/maintainer-install-trust': typeof DocsMaintainerInstallTrustRoute '/docs/maintainer-self-hosting': typeof DocsMaintainerSelfHostingRoute @@ -516,6 +524,7 @@ export interface FileRoutesById { '/docs/beta-onboarding': typeof DocsBetaOnboardingRoute '/docs/branch-analysis': typeof DocsBranchAnalysisRoute '/docs/github-app': typeof DocsGithubAppRoute + '/docs/gittensory-commands': typeof DocsGittensoryCommandsRoute '/docs/how-reviews-work': typeof DocsHowReviewsWorkRoute '/docs/maintainer-install-trust': typeof DocsMaintainerInstallTrustRoute '/docs/maintainer-self-hosting': typeof DocsMaintainerSelfHostingRoute @@ -578,6 +587,7 @@ export interface FileRouteTypes { | '/docs/beta-onboarding' | '/docs/branch-analysis' | '/docs/github-app' + | '/docs/gittensory-commands' | '/docs/how-reviews-work' | '/docs/maintainer-install-trust' | '/docs/maintainer-self-hosting' @@ -635,6 +645,7 @@ export interface FileRouteTypes { | '/docs/beta-onboarding' | '/docs/branch-analysis' | '/docs/github-app' + | '/docs/gittensory-commands' | '/docs/how-reviews-work' | '/docs/maintainer-install-trust' | '/docs/maintainer-self-hosting' @@ -695,6 +706,7 @@ export interface FileRouteTypes { | '/docs/beta-onboarding' | '/docs/branch-analysis' | '/docs/github-app' + | '/docs/gittensory-commands' | '/docs/how-reviews-work' | '/docs/maintainer-install-trust' | '/docs/maintainer-self-hosting' @@ -1024,6 +1036,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof DocsHowReviewsWorkRouteImport parentRoute: typeof DocsRoute } + '/docs/gittensory-commands': { + id: '/docs/gittensory-commands' + path: '/gittensory-commands' + fullPath: '/docs/gittensory-commands' + preLoaderRoute: typeof DocsGittensoryCommandsRouteImport + parentRoute: typeof DocsRoute + } '/docs/github-app': { id: '/docs/github-app' path: '/github-app' @@ -1204,6 +1223,7 @@ interface DocsRouteChildren { DocsBetaOnboardingRoute: typeof DocsBetaOnboardingRoute DocsBranchAnalysisRoute: typeof DocsBranchAnalysisRoute DocsGithubAppRoute: typeof DocsGithubAppRoute + DocsGittensoryCommandsRoute: typeof DocsGittensoryCommandsRoute DocsHowReviewsWorkRoute: typeof DocsHowReviewsWorkRoute DocsMaintainerInstallTrustRoute: typeof DocsMaintainerInstallTrustRoute DocsMaintainerSelfHostingRoute: typeof DocsMaintainerSelfHostingRoute @@ -1239,6 +1259,7 @@ const DocsRouteChildren: DocsRouteChildren = { DocsBetaOnboardingRoute: DocsBetaOnboardingRoute, DocsBranchAnalysisRoute: DocsBranchAnalysisRoute, DocsGithubAppRoute: DocsGithubAppRoute, + DocsGittensoryCommandsRoute: DocsGittensoryCommandsRoute, DocsHowReviewsWorkRoute: DocsHowReviewsWorkRoute, DocsMaintainerInstallTrustRoute: DocsMaintainerInstallTrustRoute, DocsMaintainerSelfHostingRoute: DocsMaintainerSelfHostingRoute, diff --git a/apps/gittensory-ui/src/routes/docs.gittensory-commands.tsx b/apps/gittensory-ui/src/routes/docs.gittensory-commands.tsx new file mode 100644 index 0000000000..a65079c500 --- /dev/null +++ b/apps/gittensory-ui/src/routes/docs.gittensory-commands.tsx @@ -0,0 +1,158 @@ +import { createFileRoute, Link } from "@tanstack/react-router"; + +import { DocsPage } from "@/components/site/docs-page"; +import { Callout, CodeBlock } from "@/components/site/primitives"; +import { + ACTION_COMMAND_ENTRIES, + MAINTAINER_COMMAND_ENTRIES, + PUBLIC_COMMAND_ENTRIES, +} from "@/lib/command-reference"; + +export const Route = createFileRoute("/docs/gittensory-commands")({ + head: () => ({ + meta: [ + { title: "@gittensory command reference — Gittensory docs" }, + { + name: "description", + content: + "Every @gittensory PR and issue comment command: syntax, default authorization roles, and the hard boundary between auto-review and the one-shot gate.", + }, + { property: "og:title", content: "@gittensory command reference — Gittensory docs" }, + { + property: "og:description", + content: + "Every @gittensory PR and issue comment command: syntax, default authorization roles, and the hard boundary between auto-review and the one-shot gate.", + }, + { property: "og:url", content: "/docs/gittensory-commands" }, + ], + links: [{ rel: "canonical", href: "/docs/gittensory-commands" }], + }), + component: GittensoryCommandsReference, +}); + +const DEFAULT_ROLE_SUMMARY: Record = { + help: "maintainer, collaborator, confirmed_miner (default policy)", + ask: "maintainer, collaborator, confirmed_miner", + preflight: "maintainer, collaborator, confirmed_miner", + blockers: "maintainer, collaborator, confirmed_miner", + "duplicate-check": "maintainer, collaborator, confirmed_miner", + "miner-context": "maintainer, collaborator, confirmed_miner", + "next-action": "maintainer, collaborator, confirmed_miner", + reviewability: "maintainer, collaborator, confirmed_miner", + "repo-fit": "maintainer, collaborator, confirmed_miner", + packet: "maintainer, collaborator, confirmed_miner", + "queue-summary": "maintainer, collaborator", + "confirmed-miners": "maintainer, collaborator", + "review-now": "maintainer, collaborator", + "needs-author": "maintainer, collaborator", + "duplicate-clusters": "maintainer, collaborator", + "burden-forecast": "maintainer, collaborator", + "intake-health": "maintainer, collaborator", + "outcome-patterns": "maintainer, collaborator", + "noise-report": "maintainer, collaborator", + "gate-override": "maintainer, collaborator", + review: "maintainer, collaborator, confirmed_miner", + pause: "maintainer, collaborator", + resume: "maintainer, collaborator", + resolve: "maintainer, collaborator", + configuration: "maintainer, collaborator", + explain: "maintainer, collaborator", +}; + +function CommandTable({ + title, + entries, +}: { + title: string; + entries: ReadonlyArray<{ id: string; title: string; description: string }>; +}) { + return ( + <> +

{title}

+
+ + + + + + + + + + {entries.map((entry) => ( + + + + + + ))} + +
SyntaxEffectDefault roles
+ @gittensory {entry.id} + {entry.description}{DEFAULT_ROLE_SUMMARY[entry.id] ?? "see policy"}
+
+ + ); +} + +function GittensoryCommandsReference() { + return ( + + + Commands never flip the gate to advisory and never bypass the one-shot disposition.{" "} + pause and resume affect only auto-review scheduling — not gate enforcement. + See How reviews work for the gate/review split. + + +

Syntax

+

+ Post a comment on a pull request (or issue thread) mentioning @gittensory followed by a + verb. Trailing free text becomes the command argument where noted (for example{" "} + @gittensory ask what should I fix first?). +

+ [argument or reason]`} /> + + + + + +

Per-command authorization overrides

+

+ Default allowed roles ship in the worker configuration. A maintainer can tighten or widen a single verb + via commandAuthorization in .gittensory.yml (resolved in the same order as + other per-repo settings: manifest → database → defaults). +

+ +

+ Maintainer-only digest verbs ignore a plain pr_author role even when widened — only + maintainer, collaborator, and confirmed_miner survive the clamp for those commands. +

+ +

Related docs

+
    +
  • + Maintainer workflow — when to invoke commands in a PR + thread +
  • +
  • + How reviews work — gate, dual-AI review, and unified comment +
  • +
  • + Tuning your reviews — per-repo review and agent execution modes +
  • +
+
+ ); +} diff --git a/apps/gittensory-ui/src/routes/docs.index.tsx b/apps/gittensory-ui/src/routes/docs.index.tsx index f030bf1632..d57d64eeed 100644 --- a/apps/gittensory-ui/src/routes/docs.index.tsx +++ b/apps/gittensory-ui/src/routes/docs.index.tsx @@ -74,6 +74,7 @@ const AUDIENCES: Audience[] = [ { to: "/docs/maintainer-install-trust", label: "Install & trust guide" }, { to: "/docs/github-app", label: "GitHub App configuration" }, { to: "/docs/maintainer-workflow", label: "Maintainer workflow" }, + { to: "/docs/gittensory-commands", label: "@gittensory commands" }, { to: "/docs/self-hosting-rees-analyzers", label: "REES analyzers" }, { to: "/docs/upstream-drift", label: "Upstream drift" }, { to: "/docs/ai-summaries", label: "AI summaries policy" }, diff --git a/apps/gittensory-ui/src/routes/docs.maintainer-workflow.tsx b/apps/gittensory-ui/src/routes/docs.maintainer-workflow.tsx index 2e0eeedbf6..0000ce2f8a 100644 --- a/apps/gittensory-ui/src/routes/docs.maintainer-workflow.tsx +++ b/apps/gittensory-ui/src/routes/docs.maintainer-workflow.tsx @@ -158,6 +158,10 @@ GET /v1/repos/:owner/:repo/registration-readiness`} only:

+

+ For syntax, default roles, PR action verbs, and the gate vs auto-review boundary, see the{" "} + @gittensory command reference. +

Public-facing comments are sanitized before they leave the Worker. Private scoring, reward, diff --git a/scripts/check-docs-drift.mjs b/scripts/check-docs-drift.mjs index df60673f22..c9f6346a5b 100644 --- a/scripts/check-docs-drift.mjs +++ b/scripts/check-docs-drift.mjs @@ -109,7 +109,7 @@ export function checkDocsDrift({ root, readFile = defaultReadFile }) { if (allCommandIds.length < 15) { failures.push(`src/github/commands.ts: extraction found only ${allCommandIds.length} unique @gittensory command ids -- expected 15+; the extraction regex may be broken`); } else { - const commandDocsPages = ["docs.maintainer-workflow.tsx", "docs.maintainer-install-trust.tsx"]; + const commandDocsPages = ["docs.maintainer-workflow.tsx", "docs.maintainer-install-trust.tsx", "docs.gittensory-commands.tsx"]; for (const page of commandDocsPages) { const pageText = read(`${DOCS_ROUTES_DIR}/${page}`); if (pageText.includes("@/lib/command-reference")) continue; diff --git a/scripts/gen-command-reference.d.mts b/scripts/gen-command-reference.d.mts index d7984b4969..990803fb95 100644 --- a/scripts/gen-command-reference.d.mts +++ b/scripts/gen-command-reference.d.mts @@ -24,11 +24,13 @@ export declare function renderCommandList(entries: CommandCatalogEntry[]): strin export declare function collectCommandCatalogs(options?: CommandCatalogOptions): { publicCommands: CommandCatalogEntry[]; maintainerCommands: CommandCatalogEntry[]; + actionCommands: CommandCatalogEntry[]; }; export declare function renderCommandReferenceModule(catalogs: { publicCommands: CommandCatalogEntry[]; maintainerCommands: CommandCatalogEntry[]; + actionCommands: CommandCatalogEntry[]; }): string; export declare function writeCommandReference(options?: WriteCommandReferenceOptions): { @@ -36,4 +38,5 @@ export declare function writeCommandReference(options?: WriteCommandReferenceOpt outputPath: string; publicCommands: CommandCatalogEntry[]; maintainerCommands: CommandCatalogEntry[]; + actionCommands: CommandCatalogEntry[]; }; diff --git a/scripts/gen-command-reference.mjs b/scripts/gen-command-reference.mjs index 23604f34df..f39b89d0cf 100644 --- a/scripts/gen-command-reference.mjs +++ b/scripts/gen-command-reference.mjs @@ -13,6 +13,7 @@ export const DEFAULT_OUTPUT_PATH = "apps/gittensory-ui/src/lib/command-reference const PUBLIC_CATALOG_NAME = "PUBLIC_MENTION_COMMAND_CATALOG"; const MAINTAINER_CATALOG_NAME = "MAINTAINER_QUEUE_DIGEST_COMMAND_CATALOG"; +const ACTION_CATALOG_NAME = "GITTENSORY_ACTION_COMMAND_CATALOG"; // Self-defense floor: 10 public + 9 maintainer-only commands exist today. If extraction ever finds fewer // than 15 total entries across both catalogs, the extraction regex is almost certainly broken -- fail @@ -47,23 +48,38 @@ export function collectCommandCatalogs({ rootDir = process.cwd(), sourcePath = D const sourceText = readFileSync(resolve(rootDir, sourcePath), "utf8"); const publicCommands = extractCatalogEntries(sourceText, PUBLIC_CATALOG_NAME); const maintainerCommands = extractCatalogEntries(sourceText, MAINTAINER_CATALOG_NAME); + const actionCommands = extractCatalogEntries(sourceText, ACTION_CATALOG_NAME); const total = publicCommands.length + maintainerCommands.length; if (total < MIN_TOTAL_COMMANDS) { throw new Error( `gen-command-reference: extraction found only ${total} total @gittensory command(s) across both catalogs in ${sourcePath} -- expected ${MIN_TOTAL_COMMANDS}+; the extraction regex may be broken`, ); } - return { publicCommands, maintainerCommands }; + if (actionCommands.length < 7) { + throw new Error( + `gen-command-reference: extraction found only ${actionCommands.length} PR action command(s) in ${sourcePath} -- expected 7; the extraction regex may be broken`, + ); + } + return { publicCommands, maintainerCommands, actionCommands }; } -export function renderCommandReferenceModule({ publicCommands, maintainerCommands }) { +export function renderCommandReferenceModule({ publicCommands, maintainerCommands, actionCommands }) { return `// Generated by scripts/gen-command-reference.mjs. Do not edit manually. // Regenerate via \`npm run command-reference\`. +export const PUBLIC_COMMAND_ENTRIES = ${JSON.stringify(publicCommands, null, 2)} as const; + export const PUBLIC_COMMAND_LIST = ${JSON.stringify(renderCommandList(publicCommands))}; +export const MAINTAINER_COMMAND_ENTRIES = ${JSON.stringify(maintainerCommands, null, 2)} as const; + export const MAINTAINER_COMMAND_LIST = ${JSON.stringify(renderCommandList(maintainerCommands))}; + +export const ACTION_COMMAND_ENTRIES = ${JSON.stringify(actionCommands, null, 2)} as const; + +export const ACTION_COMMAND_LIST = + ${JSON.stringify(renderCommandList(actionCommands))}; `; } @@ -73,8 +89,8 @@ export function writeCommandReference({ outputPath = DEFAULT_OUTPUT_PATH, check = false, } = {}) { - const { publicCommands, maintainerCommands } = collectCommandCatalogs({ rootDir, sourcePath }); - const output = renderCommandReferenceModule({ publicCommands, maintainerCommands }); + const { publicCommands, maintainerCommands, actionCommands } = collectCommandCatalogs({ rootDir, sourcePath }); + const output = renderCommandReferenceModule({ publicCommands, maintainerCommands, actionCommands }); const absOutput = resolve(rootDir, outputPath); const current = existsSync(absOutput) ? readFileSync(absOutput, "utf8") : null; const changed = current !== output; @@ -82,7 +98,7 @@ export function writeCommandReference({ mkdirSync(dirname(absOutput), { recursive: true }); writeFileSync(absOutput, output); } - return { changed, outputPath, publicCommands, maintainerCommands }; + return { changed, outputPath, publicCommands, maintainerCommands, actionCommands }; } function main(argv) { @@ -93,7 +109,7 @@ function main(argv) { process.exit(1); } process.stdout.write( - `gen-command-reference: ${check ? "checked" : "wrote"} ${result.publicCommands.length} public + ${result.maintainerCommands.length} maintainer-only command references in ${result.outputPath}\n`, + `gen-command-reference: ${check ? "checked" : "wrote"} ${result.publicCommands.length} public + ${result.maintainerCommands.length} maintainer-only + ${result.actionCommands.length} action command references in ${result.outputPath}\n`, ); } diff --git a/src/github/commands.ts b/src/github/commands.ts index e8c4bcff7f..5888db0d22 100644 --- a/src/github/commands.ts +++ b/src/github/commands.ts @@ -4,7 +4,7 @@ import { suggestCommand as suggestCommandFromCatalog, type CommandSuggestCatalog, } from "./command-suggest"; -import { gittensoryFooter } from "./footer"; +import { gittensoryFooter, GITTENSORY_SITE_URL } from "./footer"; import type { AgentRunBundle } from "../services/agent-orchestrator"; import type { GittensorContributorSnapshot, OfficialGittensorMinerDetection } from "../gittensor/api"; import type { AgentActionRecord, RepositoryCommandAuthorizationPolicy } from "../types"; @@ -780,6 +780,8 @@ function helpSections(unknownVerb?: string | undefined): string[] { "- `@gittensory noise-report` highlights queue noise to triage first.", "", ...actionCommandHelpSections(), + "", + `- Full command reference (syntax, roles, gate boundary): ${GITTENSORY_SITE_URL}/docs/gittensory-commands`, ]; } diff --git a/test/unit/gen-command-reference-script.test.ts b/test/unit/gen-command-reference-script.test.ts index af8b1717bb..216100fe35 100644 --- a/test/unit/gen-command-reference-script.test.ts +++ b/test/unit/gen-command-reference-script.test.ts @@ -121,16 +121,19 @@ describe("gen-command-reference script (#3046)", () => { }); describe("renderCommandReferenceModule", () => { - it("renders a generated-file header plus both exported string constants", () => { + it("renders a generated-file header plus exported string constants", () => { const output = renderCommandReferenceModule({ publicCommands: [{ id: "help", title: "Help", description: "Show help." }], maintainerCommands: [{ id: "queue-summary", title: "Queue summary", description: "Post a digest." }], + actionCommands: [{ id: "review", title: "Review", description: "Request review." }], }); expect(output).toContain("// Generated by scripts/gen-command-reference.mjs. Do not edit manually."); expect(output).toContain("npm run command-reference"); expect(output).toContain('export const PUBLIC_COMMAND_LIST =\n "@gittensory help";'); expect(output).toContain('export const MAINTAINER_COMMAND_LIST =\n "@gittensory queue-summary";'); + expect(output).toContain('export const ACTION_COMMAND_LIST =\n "@gittensory review";'); + expect(output).toContain("export const ACTION_COMMAND_ENTRIES"); }); }); @@ -141,8 +144,8 @@ describe("gen-command-reference script (#3046)", () => { // generated file must not be hand-edited to make this test pass. it("the committed generated file matches what the generator would produce right now (regression guard)", () => { const rootDir = process.cwd(); - const { publicCommands, maintainerCommands } = collectCommandCatalogs({ rootDir }); - const expected = renderCommandReferenceModule({ publicCommands, maintainerCommands }); + const { publicCommands, maintainerCommands, actionCommands } = collectCommandCatalogs({ rootDir }); + const expected = renderCommandReferenceModule({ publicCommands, maintainerCommands, actionCommands }); const actual = readFileSync(resolve(rootDir, DEFAULT_OUTPUT_PATH), "utf8"); @@ -152,6 +155,6 @@ describe("gen-command-reference script (#3046)", () => { it("prints a clean summary and exits 0 for the real repo state when run as a subprocess with --check", () => { const output = execFileSync("node", ["scripts/gen-command-reference.mjs", "--check"], { encoding: "utf8" }); - expect(output).toMatch(/gen-command-reference: checked \d+ public \+ \d+ maintainer-only command references in/); + expect(output).toMatch(/gen-command-reference: checked \d+ public \+ \d+ maintainer-only \+ \d+ action command references in/); }); }); diff --git a/test/unit/github-commands.test.ts b/test/unit/github-commands.test.ts index e30cbf3f82..c2c059ecab 100644 --- a/test/unit/github-commands.test.ts +++ b/test/unit/github-commands.test.ts @@ -174,6 +174,12 @@ describe("GitHub mention commands", () => { expect(helpCard).toContain("Additional safe details"); }); + it("helpSections links to the public command reference doc (#2171)", () => { + const body = githubCommandsInternals.helpSections().join("\n"); + expect(body).toContain("https://gittensory.aethereal.dev/docs/gittensory-commands"); + expect(body).toContain("Full command reference"); + }); + it("isGittensoryActionCommand distinguishes action verbs from Q&A commands", () => { for (const action of ["gate-override", "review", "pause", "resume", "resolve", "configuration", "explain"] as const) { expect(isGittensoryActionCommand(action)).toBe(true);