@kici-dev/compiler 0.1.22 → 0.1.24

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.
Files changed (61) hide show
  1. package/dist/cli.js +34 -10
  2. package/dist/commands/compile.d.ts +6 -0
  3. package/dist/commands/compile.js +6 -3
  4. package/dist/commands/docs.d.ts +8 -8
  5. package/dist/commands/docs.js +35 -16
  6. package/dist/commands/index.d.ts +4 -2
  7. package/dist/commands/index.js +3 -2
  8. package/dist/commands/init.js +2 -2
  9. package/dist/commands/org.js +2 -2
  10. package/dist/commands/pat.d.ts +27 -0
  11. package/dist/commands/pat.js +76 -0
  12. package/dist/commands/preview.d.ts +88 -0
  13. package/dist/commands/{test.js → preview.js} +15 -14
  14. package/dist/commands/run.d.ts +27 -2
  15. package/dist/commands/run.js +117 -18
  16. package/dist/commands/test.d.ts +4 -0
  17. package/dist/commands/types.d.ts +2 -0
  18. package/dist/commands/types.js +1 -1
  19. package/dist/commands/verify-attestation.d.ts +4 -1
  20. package/dist/commands/verify-attestation.js +26 -10
  21. package/dist/fixtures/describe-event.d.ts +6 -0
  22. package/dist/fixtures/describe-event.js +18 -0
  23. package/dist/fixtures/picker.d.ts +19 -0
  24. package/dist/fixtures/picker.js +64 -0
  25. package/dist/generators/secrets-dts.js +2 -0
  26. package/dist/index.d.ts +2 -2
  27. package/dist/index.js +2 -2
  28. package/dist/llm-context/llms-architecture.txt +1440 -0
  29. package/dist/llm-context/llms-cli.txt +2509 -0
  30. package/dist/llm-context/llms-features.txt +2491 -0
  31. package/dist/llm-context/llms-full.txt +1364 -361
  32. package/dist/llm-context/llms-getting-started.txt +519 -0
  33. package/dist/llm-context/llms-patterns.txt +1324 -0
  34. package/dist/llm-context/llms-providers.txt +805 -0
  35. package/dist/llm-context/llms-sdk.txt +3844 -0
  36. package/dist/llm-context/llms.txt +16 -1
  37. package/dist/local-executor/index.js +42 -4
  38. package/dist/local-executor/job-runner.d.ts +2 -0
  39. package/dist/local-executor/job-runner.js +38 -6
  40. package/dist/local-executor/types.d.ts +2 -0
  41. package/dist/lockfile/generator.d.ts +10 -2
  42. package/dist/lockfile/generator.js +112 -49
  43. package/dist/remote/history.d.ts +1 -1
  44. package/dist/remote/history.js +1 -1
  45. package/dist/remote/local-repo-identity.d.ts +32 -0
  46. package/dist/remote/local-repo-identity.js +74 -0
  47. package/dist/remote/platform-client.d.ts +6 -0
  48. package/dist/remote/prod-defaults.d.ts +8 -0
  49. package/dist/remote/prod-defaults.js +9 -1
  50. package/dist/remote/uploader.js +1 -0
  51. package/dist/templates/agents-md.d.ts +1 -1
  52. package/dist/templates/agents-md.js +2 -2
  53. package/dist/templates/package-json.js +1 -1
  54. package/dist/test-runner/rule-evaluator.d.ts +1 -1
  55. package/dist/test-runner/rule-evaluator.js +2 -1
  56. package/dist/test-runner/step-context.d.ts +1 -1
  57. package/dist/test-runner/step-context.js +8 -2
  58. package/dist/types.d.ts +38 -7
  59. package/dist/types.js +5 -1
  60. package/package.json +4 -7
  61. package/sbom.spdx.json +35 -35
package/dist/cli.js CHANGED
@@ -7,7 +7,7 @@ import { realpathSync } from "node:fs";
7
7
  import { Argument, Command, Option } from "commander";
8
8
  import pc from "picocolors";
9
9
  //#region src/cli.ts
10
- const version = "0.1.22";
10
+ const version = "0.1.24";
11
11
  /**
12
12
  * Build the kici Commander program with every command registered. Exported so
13
13
  * the surface registry can walk the real command tree without parsing argv (no
@@ -44,7 +44,7 @@ function buildProgram() {
44
44
  await fixtureCommand(event, options);
45
45
  });
46
46
  const runCommand = program.command("run").description("Execute workflows locally or remotely");
47
- runCommand.command("local").argument("[event]", "Event type (e.g., push, pr:open, schedule) — optional with --pick").description("Execute workflows locally without orchestrator infrastructure").option("-p, --pick", "Interactively pick a workflow and trigger to simulate", false).option("--workflow <name>", "Run only the specified workflow").option("--job <name>", "Run only the specified job (and its dependencies)").option("--branch <name>", "Override detected git branch").option("--sha <hash>", "Override detected git SHA").option("--payload <path>", "Path to explicit event payload JSON file").option("--concurrency <n>", "Max parallel jobs (default: CPU cores)", parseInt).option("--keep-going", "Continue after job failure", false).option("--container", "Use Podman container isolation", false).option("--env <KEY=VALUE>", "Environment variable override (repeatable)", (val, prev) => [...prev, val], []).option("--quiet", "Suppress streaming output", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--files <path>", "Override changed file paths (repeatable, default: git diff)", (val, prev) => [...prev, val], []).option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--in-place", "Run against the real working directory instead of an isolated tmp checkout", false).option("--keep", "Always retain the isolated tmp checkout (default: keep only on failure)", false).option("--check", "Run in check mode: report drift, change nothing", false).option("--fail-on-drift", "In check mode, exit non-zero if any step reports drift", false).action(async (event, options) => {
47
+ runCommand.command("local").argument("[event]", "Event type (e.g., push, pr:open, schedule) — optional with --pick").description("Execute workflows locally without orchestrator infrastructure").option("-p, --pick", "Interactively pick a workflow and trigger to simulate", false).option("--workflow <name>", "Run only the specified workflow").option("--job <name>", "Run only the specified job (and its dependencies)").option("--branch <name>", "Override detected git branch").option("--sha <hash>", "Override detected git SHA").option("--payload <path>", "Path to explicit event payload JSON file").option("--concurrency <n>", "Max parallel jobs (default: CPU cores)", parseInt).option("--keep-going", "Continue after job failure", false).option("--container", "Use Podman container isolation", false).option("--env <KEY=VALUE>", "Environment variable override (repeatable)", (val, prev) => [...prev, val], []).option("--input <KEY=VALUE>", "Typed workflow-dispatch input (repeatable)", (val, prev) => [...prev, val], []).option("--quiet", "Suppress streaming output", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--files <path>", "Override changed file paths (repeatable, default: git diff)", (val, prev) => [...prev, val], []).option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--in-place", "Run against the real working directory instead of an isolated tmp checkout", false).option("--keep", "Always retain the isolated tmp checkout (default: keep only on failure)", false).option("--check", "Run in check mode: report drift, change nothing", false).option("--fail-on-drift", "In check mode, exit non-zero if any step reports drift", false).action(async (event, options) => {
48
48
  if (options.pick && options.workflow) {
49
49
  console.error("Error: --pick is mutually exclusive with --workflow.");
50
50
  process.exit(2);
@@ -78,6 +78,7 @@ function buildProgram() {
78
78
  keepGoing: options.keepGoing,
79
79
  container: options.container,
80
80
  env: options.env,
81
+ inputs: options.input,
81
82
  quiet: options.quiet,
82
83
  json: options.json,
83
84
  junit: options.junit,
@@ -89,7 +90,19 @@ function buildProgram() {
89
90
  });
90
91
  process.exit(success ? 0 : 1);
91
92
  });
92
- runCommand.command("remote").argument("[fixture]", "Fixture name or glob pattern (omit to list available)").description("Execute fixtures remotely via orchestrator").option("--workflow <name>", "Run a specific workflow directly (bypass triggers)").option("--all", "Run all available fixtures", false).option("--parallel", "Run matching fixtures concurrently", false).option("--no-wait", "Fire and forget (print runIds, don't stream)").option("--quiet", "Suppress output except final result", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--history", "Show recent run history", false).option("--routing-key <key>", "Override routing key for this run").option("--org <id>", "Target organization (overrides the active org)").option("--orchestrator <name>", "Target orchestrator cluster (overrides the per-org default)").option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--context <ctx.key=value>", "Inject a namespaced context secret, uploaded encrypted to the orchestrator (repeatable)", (val, prev) => [...prev, val], []).option("--env <KEY=VALUE>", "Provide a per-run secret (repeatable); uploaded encrypted to the orchestrator", (val, prev) => [...prev, val], []).option("--check", "Run in check mode: report drift, change nothing", false).option("--fail-on-drift", "In check mode, exit non-zero if any step reports drift", false).option("--target <selector>", "Narrow runsOnAll jobs to hosts matching this label selector (repeatable, AND-combined)", (val, prev) => [...prev, val], []).option("--target-allow-empty", "A --target that narrows a runsOnAll job to zero hosts skips it instead of failing", false).option("--approve-all, --yes", "Auto-approve every approval gate this run holds on (run-scoped; eligibility still enforced)", false).action(async (fixture, options) => {
93
+ runCommand.command("remote").argument("[fixture]", "Fixture name or glob pattern (omit to list available)").description("Execute fixtures remotely via orchestrator").option("--workflow <name>", "Run a specific workflow directly (bypass triggers)").option("--all", "Run all available fixtures", false).option("-p, --pick", "Interactively pick fixtures to run", false).option("--parallel", "Run matching fixtures concurrently", false).option("--no-wait", "Fire and forget (print runIds, don't stream)").option("--quiet", "Suppress output except final result", false).option("--json", "Output structured JSON result", false).option("--junit <path>", "Output JUnit XML result").option("--history", "Show recent run history", false).option("--routing-key <key>", "Override routing key for this run").option("--org <id>", "Target organization (overrides the active org)").option("--orchestrator <name>", "Target orchestrator cluster (overrides the per-org default)").option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--context <ctx.key=value>", "Inject a namespaced context secret, uploaded encrypted to the orchestrator (repeatable)", (val, prev) => [...prev, val], []).option("--env <KEY=VALUE>", "Provide a per-run secret (repeatable); uploaded encrypted to the orchestrator", (val, prev) => [...prev, val], []).option("--check", "Run in check mode: report drift, change nothing", false).option("--fail-on-drift", "In check mode, exit non-zero if any step reports drift", false).option("--target <selector>", "Narrow runsOnAll jobs to hosts matching this label selector (repeatable, AND-combined)", (val, prev) => [...prev, val], []).option("--target-allow-empty", "A --target that narrows a runsOnAll job to zero hosts skips it instead of failing", false).option("--input <KEY=VALUE>", "Typed workflow-dispatch input (repeatable)", (val, prev) => [...prev, val], []).option("--approve-all, --yes", "Auto-approve every approval gate this run holds on (run-scoped; eligibility still enforced)", false).action(async (fixture, options) => {
94
+ if (options.pick && fixture) {
95
+ console.error("Error: --pick selects fixtures interactively; do not also pass a fixture name.");
96
+ process.exit(2);
97
+ }
98
+ if (options.pick && options.all) {
99
+ console.error("Error: --pick is mutually exclusive with --all.");
100
+ process.exit(2);
101
+ }
102
+ if (options.pick && options.workflow) {
103
+ console.error("Error: --pick is mutually exclusive with --workflow.");
104
+ process.exit(2);
105
+ }
93
106
  const { runRemoteCommand } = await import("./commands/index.js");
94
107
  const { resolveCheckMode } = await import("./commands/check-mode.js");
95
108
  let checkMode;
@@ -108,13 +121,14 @@ function buildProgram() {
108
121
  envFlags: options.env,
109
122
  targets: options.target,
110
123
  targetAllowEmpty: options.targetAllowEmpty,
111
- approveAll: options.approveAll
124
+ approveAll: options.approveAll,
125
+ inputs: options.input
112
126
  });
113
127
  process.exit(success ? 0 : 1);
114
128
  });
115
- program.command("test").argument("[event]", "Event type to preview (e.g., push, pr:open, schedule)").description("Preview which workflows match a trigger event (dry-run)").option("--branch <name>", "Override target branch for trigger matching (default: main)").option("--sha <hash>", "Override commit SHA").option("--workflow <name>", "Filter to specific workflow in display").option("--job <name>", "Filter to specific job in display").option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--files <path>", "Simulate changed file path for trigger matching (repeatable)", (val, prev) => [...prev, val], []).option("--secret <key=value>", "Inject flat secret (repeatable)", (val, prev) => [...prev, val], []).option("--context <ctx.key=value>", "Inject context secret (repeatable)", (val, prev) => [...prev, val], []).action(async (event, options) => {
116
- const { testCommand } = await import("./commands/index.js");
117
- const success = await testCommand(event, options);
129
+ program.command("preview").argument("[event]", "Event type to preview (e.g., push, pr:open, schedule)").description("Preview which workflows match a trigger event (no execution)").option("--branch <name>", "Override target branch for trigger matching (default: main)").option("--sha <hash>", "Override commit SHA").option("--workflow <name>", "Filter to specific workflow in display").option("--job <name>", "Filter to specific job in display").option("--debug", "Verbose internals", false).option("--kici-dir <path>", "Path to .kici directory", ".kici").option("--files <path>", "Simulate changed file path for trigger matching (repeatable)", (val, prev) => [...prev, val], []).option("--secret <key=value>", "Inject flat secret (repeatable)", (val, prev) => [...prev, val], []).option("--context <ctx.key=value>", "Inject context secret (repeatable)", (val, prev) => [...prev, val], []).action(async (event, options) => {
130
+ const { previewCommand } = await import("./commands/index.js");
131
+ const success = await previewCommand(event, options);
118
132
  process.exit(success ? 0 : 1);
119
133
  });
120
134
  program.command("init").description("Initialize .kici/ directory with default workflows").option("--force", "Overwrite existing .kici/ directory", false).option("--skip-install", "Create files without installing dependencies", false).option("--package-manager <npm|pnpm|yarn>", "Force a package manager for the install step (default: auto-detect)").option("--mjs", "JavaScript-only mode (no TypeScript, no dependencies)", false).option("--no-agents-md", "Skip writing .kici/AGENTS.md (LLM authoring context)").option("--private-registry <url>", "Scaffold a workflow registries: entry pointing at <url>").option("--private-registry-scope <scope>", "Optional npm package scope (e.g. @my-org) for the private registry").option("--private-registry-secret <ref>", "Qualified secret reference (env:NAME) the private registry token comes from", "production:NPM_TOKEN").addOption(new Option("--use-verdaccio-local").default(false).hideHelp()).action(async (options) => {
@@ -185,6 +199,16 @@ Environment variables:
185
199
  const success = await secretsListCommand();
186
200
  process.exit(success ? 0 : 1);
187
201
  });
202
+ program.command("pat").description("Manage personal access tokens").command("create").description("Mint a personal access token (use --agent for a coding-agent token)").option("--name <name>", "Token name (defaults to the agent label)").option("--agent", "Mint an agent-kind PAT for the KiCI MCP server", false).option("--expires-in-days <n>", "Custom expiry in days", (v) => parseInt(v, 10)).action(async (options) => {
203
+ const { patCreateCommand } = await import("./commands/index.js");
204
+ const success = await patCreateCommand({
205
+ name: options.name,
206
+ agent: options.agent,
207
+ label: options.name,
208
+ expiresInDays: options.expiresInDays
209
+ });
210
+ process.exit(success ? 0 : 1);
211
+ });
188
212
  const runsCommand = program.command("runs").description("Inspect and manage execution runs");
189
213
  runsCommand.command("list").description("List execution runs (mirrors the dashboard Runs page)").option("--status <s>", "Filter by status").option("--workflow <w>", "Filter by workflow name").option("--branch <b>", "Filter by branch/ref").option("--repo <r>", "Filter by repository").option("--trigger <t>", "Filter by trigger type").option("--source <routingKey>", "Filter by source routing key").option("--since <ts>", "Only runs since (ISO-8601 or epoch ms)").option("--page <n>", "Page number", (v) => parseInt(v, 10)).option("--json", "Output raw JSON", false).action(async (options) => {
190
214
  const { runsListCommand } = await import("./commands/index.js");
@@ -268,10 +292,10 @@ Environment variables:
268
292
  const { docsCommand } = await import("./commands/index.js");
269
293
  const success = await docsCommand({ open: options.open });
270
294
  process.exit(success ? 0 : 1);
271
- }).command("llm").description("Print the bundled LLM context (llms-full.txt) to stdout").option("--index", "Print the curated llms.txt index instead of the full bundle", false).option("--out <path>", "Write the bundle to a file instead of stdout").action(async (options) => {
295
+ }).command("llm [topic]").description("Print KiCI LLM docs bundles. No topic prints the llms.txt index; <topic> prints a task bundle (e.g. sdk, cli, patterns, features, providers, architecture, getting-started); \"full\" prints the complete bundle.").option("--out <path>", "Write the bundle to a file instead of stdout").action(async (topic, options) => {
272
296
  const { docsLlmCommand } = await import("./commands/index.js");
273
297
  const success = await docsLlmCommand({
274
- index: options.index,
298
+ topic,
275
299
  out: options.out
276
300
  });
277
301
  process.exit(success ? 0 : 1);
@@ -281,7 +305,7 @@ Environment variables:
281
305
  const success = await drainWorkerCommand({ url: options.url });
282
306
  process.exit(success ? 0 : 1);
283
307
  });
284
- program.command("verify-attestation").argument("[artifact]", "Artifact path to digest-check against the attestation subject (optional)").description("Verify a KiCI provenance attestation bundle offline").option("--bundle <path>", "Path or URL to the attestation bundle JSON").option("--trust-root <url-or-file>", "Trusted issuer URL, or a self-contained { issuer, jwks } file").option("--audience <aud>", "Expected token audience").option("--json", "Output structured JSON result", false).action(async (artifact, options) => {
308
+ program.command("verify-attestation").argument("[artifact]", "Artifact path to digest-check against the attestation subject (optional)").description("Verify a KiCI provenance attestation bundle offline").option("--bundle <path>", "Path or URL to the attestation bundle JSON").option("--trust-root <url-or-file>", "Trusted issuer URL, or a self-contained { issuer, jwks } file (default: hosted KiCI platform)").option("--audience <aud>", "Expected token audience").option("--json", "Output structured JSON result", false).action(async (artifact, options) => {
285
309
  const { verifyAttestationCommand } = await import("./commands/index.js");
286
310
  const success = await verifyAttestationCommand(artifact, options);
287
311
  process.exit(success ? 0 : 1);
@@ -6,6 +6,12 @@ export interface CompileOptions {
6
6
  check: boolean;
7
7
  /** Verbose output */
8
8
  verbose: boolean;
9
+ /**
10
+ * Suppress the success line on stdout (and the auto-types success line) so a
11
+ * caller emitting machine-readable output keeps stdout pure. Validation
12
+ * errors are still reported.
13
+ */
14
+ quiet?: boolean;
9
15
  }
10
16
  /**
11
17
  * Execute the compile command.
@@ -76,7 +76,7 @@ async function compileCommand(options) {
76
76
  const lockJson = serializeLockFile(generateLockFile(workflowsWithSource));
77
77
  if (!options.check) {
78
78
  await fs.writeFile(lockPath, lockJson, "utf-8");
79
- logger.info(pc.green("✓") + ` Compiled workflows → .kici/kici.lock.json` + pc.dim(` (${workflowsWithSource.length} workflow${workflowsWithSource.length !== 1 ? "s" : ""})`));
79
+ if (!options.quiet) logger.info(pc.green("✓") + ` Compiled workflows → .kici/kici.lock.json` + pc.dim(` (${workflowsWithSource.length} workflow${workflowsWithSource.length !== 1 ? "s" : ""})`));
80
80
  try {
81
81
  const { loadGlobalConfig } = await import("../remote/config.js");
82
82
  const config = await loadGlobalConfig();
@@ -84,12 +84,15 @@ async function compileCommand(options) {
84
84
  const hasEndpoint = Boolean(config.platformEndpoint ?? config.endpoint);
85
85
  if (hasToken && hasEndpoint && config.activeOrgId) {
86
86
  const { typesCommand } = await import("./types.js");
87
- await typesCommand({ kiciDir });
87
+ await typesCommand({
88
+ kiciDir,
89
+ quiet: options.quiet
90
+ });
88
91
  }
89
92
  } catch {
90
93
  logger.warn(pc.yellow("Could not refresh types (Platform unreachable). Compilation succeeded."));
91
94
  }
92
- } else logger.info(pc.green("✓") + ` Workflows are valid` + pc.dim(` (${workflowsWithSource.length} workflow${workflowsWithSource.length !== 1 ? "s" : ""})`));
95
+ } else if (!options.quiet) logger.info(pc.green("✓") + ` Workflows are valid` + pc.dim(` (${workflowsWithSource.length} workflow${workflowsWithSource.length !== 1 ? "s" : ""})`));
93
96
  return true;
94
97
  } catch (error) {
95
98
  if (isCompilerError(error)) logger.error(formatError(error));
@@ -3,11 +3,11 @@ export interface DocsOptions {
3
3
  open?: boolean;
4
4
  }
5
5
  export interface DocsLlmOptions {
6
- /** Print only the llms.txt index instead of the full markdown bundle. */
7
- index?: boolean;
6
+ /** Task bundle id to print. Undefined → the llms.txt index; 'full' llms-full.txt. */
7
+ topic?: string;
8
8
  /** Override the bundled output destination (overrides default stdout). */
9
9
  out?: string;
10
- /** Override the directory holding llms.txt + llms-full.txt (test seam). */
10
+ /** Override the directory holding the llms*.txt files (test seam). */
11
11
  bundleDir?: string;
12
12
  }
13
13
  /**
@@ -15,12 +15,12 @@ export interface DocsLlmOptions {
15
15
  */
16
16
  export declare function docsCommand(options?: DocsOptions): Promise<boolean>;
17
17
  /**
18
- * Print the bundled llms.txt or llms-full.txt content to stdout (or a file).
18
+ * Print a KiCI LLM docs bundle to stdout (or a file).
19
19
  *
20
- * The bundle is generated at build time by hack/postbuild.mjs and shipped at
21
- * dist/llm-context/{llms.txt,llms-full.txt}. Customer-facing LLM tools can
22
- * pipe `kici docs llm` straight into an Anthropic / OpenAI context buffer to
23
- * brief the agent on KiCI authoring conventions without an internet round-trip.
20
+ * No topic prints the llms.txt index (a router listing every task bundle).
21
+ * A topic prints that task bundle (e.g. `sdk`, `cli`, `patterns`); `full`
22
+ * prints the everything-bundle. Pipe straight into a coding agent's context
23
+ * to brief it on KiCI authoring without an internet round-trip.
24
24
  */
25
25
  export declare function docsLlmCommand(options?: DocsLlmOptions): Promise<boolean>;
26
26
  //# sourceMappingURL=docs.d.ts.map
@@ -2,12 +2,24 @@ import "../chunk-BTugEXQM.js";
2
2
  import { fileURLToPath } from "node:url";
3
3
  import path from "node:path";
4
4
  import pc from "picocolors";
5
- import { readFile, writeFile } from "node:fs/promises";
5
+ import { readFile, readdir, writeFile } from "node:fs/promises";
6
6
  import { logger, toErrorMessage } from "@kici-dev/core";
7
7
  import open from "open";
8
8
  //#region src/commands/docs.ts
9
9
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
10
10
  const DOCS_HOME_URL = "https://kici.dev/docs/";
11
+ function bundleFilenameForTopic(topic) {
12
+ if (!topic) return "llms.txt";
13
+ if (topic === "full") return "llms-full.txt";
14
+ return `llms-${topic}.txt`;
15
+ }
16
+ async function availableTopics(bundleDir) {
17
+ try {
18
+ return [...(await readdir(bundleDir)).filter((n) => n.startsWith("llms-") && n.endsWith(".txt") && n !== "llms-full.txt").map((n) => n.slice(5, -4)).sort(), "full"];
19
+ } catch {
20
+ return ["full"];
21
+ }
22
+ }
11
23
  /**
12
24
  * Open the published documentation site in the user's default browser.
13
25
  */
@@ -27,32 +39,39 @@ async function docsCommand(options = {}) {
27
39
  }
28
40
  }
29
41
  /**
30
- * Print the bundled llms.txt or llms-full.txt content to stdout (or a file).
42
+ * Print a KiCI LLM docs bundle to stdout (or a file).
31
43
  *
32
- * The bundle is generated at build time by hack/postbuild.mjs and shipped at
33
- * dist/llm-context/{llms.txt,llms-full.txt}. Customer-facing LLM tools can
34
- * pipe `kici docs llm` straight into an Anthropic / OpenAI context buffer to
35
- * brief the agent on KiCI authoring conventions without an internet round-trip.
44
+ * No topic prints the llms.txt index (a router listing every task bundle).
45
+ * A topic prints that task bundle (e.g. `sdk`, `cli`, `patterns`); `full`
46
+ * prints the everything-bundle. Pipe straight into a coding agent's context
47
+ * to brief it on KiCI authoring without an internet round-trip.
36
48
  */
37
49
  async function docsLlmCommand(options = {}) {
38
- const filename = options.index ? "llms.txt" : "llms-full.txt";
50
+ const filename = bundleFilenameForTopic(options.topic);
39
51
  const bundleDir = options.bundleDir ?? path.join(__dirname, "..", "llm-context");
40
52
  const bundlePath = path.join(bundleDir, filename);
53
+ let content;
41
54
  try {
42
- const content = await readFile(bundlePath, "utf-8");
43
- if (options.out) {
44
- await writeFile(options.out, content, "utf-8");
45
- logger.info(pc.gray(`Wrote ${filename} to ${options.out}`));
46
- return true;
47
- }
48
- process.stdout.write(content);
49
- if (!content.endsWith("\n")) process.stdout.write("\n");
50
- return true;
55
+ content = await readFile(bundlePath, "utf-8");
51
56
  } catch (error) {
57
+ if (options.topic && options.topic !== "full") {
58
+ const topics = await availableTopics(bundleDir);
59
+ logger.error(pc.red(`Unknown docs bundle "${options.topic}".`));
60
+ logger.info(pc.gray(`Available topics: ${topics.join(", ")} (no topic prints the index).`));
61
+ return false;
62
+ }
52
63
  logger.error(pc.red(`Error reading bundled ${filename}: ${toErrorMessage(error)}`));
53
64
  logger.info(pc.gray("The bundle ships with the @kici-dev/compiler package. If you built the package locally, run `pnpm build` in packages/compiler/."));
54
65
  return false;
55
66
  }
67
+ if (options.out) {
68
+ await writeFile(options.out, content, "utf-8");
69
+ logger.info(pc.gray(`Wrote ${filename} to ${options.out}`));
70
+ return true;
71
+ }
72
+ process.stdout.write(content);
73
+ if (!content.endsWith("\n")) process.stdout.write("\n");
74
+ return true;
56
75
  }
57
76
  //#endregion
58
77
  export { docsCommand, docsLlmCommand };
@@ -3,8 +3,8 @@ export type { CompileOptions } from './compile.js';
3
3
  export { watchCommand } from './watch.js';
4
4
  export { fixtureCommand } from './fixture.js';
5
5
  export type { FixtureOptions } from './fixture.js';
6
- export { testCommand, testDryRun } from './test.js';
7
- export type { TestOptions, RemoteRunOptions, RemoteRunResult } from './test.js';
6
+ export { previewCommand, previewEvent } from './preview.js';
7
+ export type { PreviewOptions, RemoteRunOptions, RemoteRunResult } from './preview.js';
8
8
  export { runLocalCommand, runRemoteCommand } from './run.js';
9
9
  export { initCommand } from './init.js';
10
10
  export type { InitOptions } from './init.js';
@@ -14,6 +14,8 @@ export { loginCommand } from './login.js';
14
14
  export type { LoginOptions } from './login.js';
15
15
  export { secretsListCommand } from './secrets-list.js';
16
16
  export type { SecretsListOptions } from './secrets-list.js';
17
+ export { patCreateCommand } from './pat.js';
18
+ export type { PatCreateOptions } from './pat.js';
17
19
  export { runsListCommand } from './runs/list.js';
18
20
  export type { RunsListOptions } from './runs/list.js';
19
21
  export { runsShowCommand } from './runs/show.js';
@@ -8,11 +8,12 @@ import { endpointsCommand } from "./endpoints.js";
8
8
  import { fixtureCommand } from "./fixture.js";
9
9
  import { hookInstallCommand } from "./hook.js";
10
10
  import { watchCommand } from "./watch.js";
11
- import { testCommand, testDryRun } from "./test.js";
11
+ import { previewCommand, previewEvent } from "./preview.js";
12
12
  import { runLocalCommand, runRemoteCommand } from "./run.js";
13
13
  import { initCommand } from "./init.js";
14
14
  import { loginCommand } from "./login.js";
15
15
  import { secretsListCommand } from "./secrets-list.js";
16
+ import { patCreateCommand } from "./pat.js";
16
17
  import { runsListCommand } from "./runs/list.js";
17
18
  import { runsShowCommand } from "./runs/show.js";
18
19
  import { runsLogsCommand } from "./runs/logs.js";
@@ -25,4 +26,4 @@ import { logoutCommand } from "./logout.js";
25
26
  import { rejectCommand } from "./reject.js";
26
27
  import { workflowsListCommand } from "./workflows.js";
27
28
  import { verifyAttestationCommand } from "./verify-attestation.js";
28
- export { approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, drainWorkerCommand, endpointsCommand, fixtureCommand, hookInstallCommand, initCommand, loginCommand, logoutCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, rejectCommand, runLocalCommand, runRemoteCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, testCommand, testDryRun, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
29
+ export { approveCommand, compileCommand, diagnosticsCommand, docsCommand, docsLlmCommand, drainWorkerCommand, endpointsCommand, fixtureCommand, hookInstallCommand, initCommand, loginCommand, logoutCommand, orchestratorsListCommand, orchestratorsUseCommand, orgCurrentCommand, orgListCommand, orgUseCommand, patCreateCommand, previewCommand, previewEvent, rejectCommand, runLocalCommand, runRemoteCommand, runsCancelCommand, runsListCommand, runsLogsCommand, runsRerunCommand, runsShowCommand, secretsListCommand, typesCommand, verifyAttestationCommand, watchCommand, workflowsListCommand };
@@ -117,10 +117,10 @@ async function initCommand(options = {}) {
117
117
  logger.info(pc.gray(" 1. Edit workflows in .kici/workflows/"));
118
118
  if (options.mjs || options.skipInstall) {
119
119
  logger.info(pc.gray(" 2. Run your package manager install in .kici/ to generate a lockfile"));
120
- logger.info(pc.gray(" 3. Test locally: kici test push"));
120
+ logger.info(pc.gray(" 3. Preview matching: kici preview push"));
121
121
  logger.info(pc.gray(" 4. Commit .kici/ to your repository\n"));
122
122
  } else {
123
- logger.info(pc.gray(" 2. Test locally: kici test push"));
123
+ logger.info(pc.gray(" 2. Preview matching: kici preview push"));
124
124
  logger.info(pc.gray(" 3. Commit .kici/ to your repository\n"));
125
125
  }
126
126
  return true;
@@ -65,9 +65,9 @@ async function orgListCommand() {
65
65
  const isActive = org.id === config.activeOrgId;
66
66
  const marker = isActive ? pc.green("* ") : " ";
67
67
  const name = isActive ? pc.bold(org.displayName) : org.displayName;
68
- const role = pc.gray(`(${org.role})`);
68
+ const ownership = pc.gray(`(${org.isOwner ? "owner" : "member"})`);
69
69
  const id = pc.gray(org.id);
70
- console.log(` ${marker}${name.padEnd(nameWidth)} ${role} ${id}`);
70
+ console.log(` ${marker}${name.padEnd(nameWidth)} ${ownership} ${id}`);
71
71
  }
72
72
  console.log("");
73
73
  return true;
@@ -0,0 +1,27 @@
1
+ export interface PatCreateOptions {
2
+ /** Token name shown in the dashboard PAT list. Defaults to the agent label. */
3
+ name?: string;
4
+ /** Mint an agent-kind PAT (the only credential the developer MCP server accepts). */
5
+ agent?: boolean;
6
+ /**
7
+ * Agent label (required with `--agent`) — the human-set name recorded on every
8
+ * audit row the agent produces. Also used as the token name when `--name` is
9
+ * omitted.
10
+ */
11
+ label?: string;
12
+ /** Custom expiry in days (server default: 120). */
13
+ expiresInDays?: number;
14
+ /** Injected fetch for tests. Defaults to the global fetch. */
15
+ fetchImpl?: typeof fetch;
16
+ }
17
+ /**
18
+ * Mint a personal access token under the logged-in user's identity.
19
+ *
20
+ * `kici pat create --agent --name <label>` mints an agent-kind PAT: it inherits
21
+ * the user's permissions (provenance only, no authority change), carries its
22
+ * label into every audit row, and is the credential a coding agent points the
23
+ * KiCI developer MCP server at. The token is printed once — there is no way to
24
+ * retrieve it later.
25
+ */
26
+ export declare function patCreateCommand(options?: PatCreateOptions): Promise<boolean>;
27
+ //# sourceMappingURL=pat.d.ts.map
@@ -0,0 +1,76 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ import { loadGlobalConfig } from "../remote/config.js";
3
+ import pc from "picocolors";
4
+ import { toErrorMessage } from "@kici-dev/core";
5
+ import { PatKind } from "@kici-dev/engine";
6
+ //#region src/commands/pat.ts
7
+ /**
8
+ * Mint a personal access token under the logged-in user's identity.
9
+ *
10
+ * `kici pat create --agent --name <label>` mints an agent-kind PAT: it inherits
11
+ * the user's permissions (provenance only, no authority change), carries its
12
+ * label into every audit row, and is the credential a coding agent points the
13
+ * KiCI developer MCP server at. The token is printed once — there is no way to
14
+ * retrieve it later.
15
+ */
16
+ async function patCreateCommand(options = {}) {
17
+ const doFetch = options.fetchImpl ?? fetch;
18
+ try {
19
+ const config = await loadGlobalConfig();
20
+ const token = config.pat ?? config.token;
21
+ const endpoint = config.platformEndpoint ?? config.endpoint;
22
+ if (!token || !endpoint) {
23
+ console.error(pc.red("Not logged in. Run `kici login` first."));
24
+ return false;
25
+ }
26
+ const kind = options.agent ? PatKind.enum.agent : PatKind.enum.user;
27
+ const label = options.label;
28
+ if (kind === PatKind.enum.agent && !label) {
29
+ console.error(pc.red("An agent PAT requires a label. Pass --name <label>."));
30
+ return false;
31
+ }
32
+ const name = options.name ?? label;
33
+ if (!name) {
34
+ console.error(pc.red("A token name is required. Pass --name <name>."));
35
+ return false;
36
+ }
37
+ const body = {
38
+ name,
39
+ kind
40
+ };
41
+ if (kind === PatKind.enum.agent) body.agentLabel = label;
42
+ if (options.expiresInDays !== void 0) body.expiresInDays = options.expiresInDays;
43
+ const res = await doFetch(`${endpoint.replace(/\/$/, "")}/api/v1/pats`, {
44
+ method: "POST",
45
+ headers: {
46
+ "Content-Type": "application/json",
47
+ Authorization: `Bearer ${token}`
48
+ },
49
+ body: JSON.stringify(body)
50
+ });
51
+ if (!res.ok) {
52
+ let detail;
53
+ try {
54
+ detail = (await res.json()).error;
55
+ } catch {}
56
+ console.error(pc.red(`Failed to create token (${res.status}): ${detail ?? "request failed"}`));
57
+ return false;
58
+ }
59
+ const created = await res.json();
60
+ console.log(pc.bold(kind === PatKind.enum.agent ? "\nAgent PAT created.\n" : "\nPAT created.\n"));
61
+ console.log(`${pc.gray("Name: ")}${created.name}`);
62
+ if (kind === PatKind.enum.agent) console.log(`${pc.gray("Agent: ")}${label}`);
63
+ console.log(`${pc.gray("Expires:")} ${created.expiresAt}`);
64
+ console.log(`\n${pc.gray("Token (shown once — save it now):")}`);
65
+ console.log(pc.cyan(created.token));
66
+ if (kind === PatKind.enum.agent) console.log(pc.gray("\nPoint your coding agent at the KiCI MCP server with this token as the Bearer credential."));
67
+ return true;
68
+ } catch (err) {
69
+ console.error(pc.red(`Failed to create token: ${toErrorMessage(err)}`));
70
+ return false;
71
+ }
72
+ }
73
+ //#endregion
74
+ export { patCreateCommand };
75
+
76
+ //# sourceMappingURL=pat.js.map
@@ -0,0 +1,88 @@
1
+ import { type PayloadOptions } from '../test-runner/payload-builder.js';
2
+ import { type CheckMode } from '@kici-dev/engine';
3
+ /** Options for the kici preview command (dry-run trigger preview) */
4
+ export interface PreviewOptions extends PayloadOptions {
5
+ /** Filter to specific workflow */
6
+ workflow?: string;
7
+ /** Filter to specific job */
8
+ job?: string;
9
+ /** Enable debug output */
10
+ debug?: boolean;
11
+ /** Path to .kici directory (defaults to .kici) */
12
+ kiciDir?: string;
13
+ /** Flat secret overrides: KEY=VALUE */
14
+ secret?: string[];
15
+ /** Context secret overrides: contextName.KEY=VALUE */
16
+ context?: string[];
17
+ }
18
+ /** Options for the kici run remote command */
19
+ export interface RemoteRunOptions extends PreviewOptions {
20
+ /** Run all available fixtures */
21
+ all?: boolean;
22
+ /** Interactively pick fixtures to run (multi-select checkbox). */
23
+ pick?: boolean;
24
+ /** Run matching fixtures concurrently */
25
+ parallel?: boolean;
26
+ /** Fire and forget (print runIds, don't stream) */
27
+ wait?: boolean;
28
+ /** Suppress output except final result */
29
+ quiet?: boolean;
30
+ /** Output structured JSON result */
31
+ json?: boolean;
32
+ /** Output JUnit XML result */
33
+ junit?: string;
34
+ /** Override routing key for this run */
35
+ routingKey?: string;
36
+ /** Show recent run history */
37
+ history?: boolean;
38
+ /** --env KEY=VALUE flag values, uploaded as per-run secrets. */
39
+ envFlags?: string[];
40
+ /** Target organization id (overrides config.activeOrgId). */
41
+ org?: string;
42
+ /** Target orchestrator cluster name (overrides the per-org default). */
43
+ orchestrator?: string;
44
+ /**
45
+ * Run mode resolved from --check / --fail-on-drift, threaded onto the dispatch
46
+ * payload so the orchestrator runs the agent step loop in the requested mode.
47
+ * Defaults to `apply`.
48
+ */
49
+ checkMode?: CheckMode;
50
+ /** `--target <selector>` values (repeatable), AND-combined into host narrowing. */
51
+ targets?: string[];
52
+ /** `--target-allow-empty`: a target that zeroes a runsOnAll job skips it instead of failing. */
53
+ targetAllowEmpty?: boolean;
54
+ /**
55
+ * `--approve-all` (alias `--yes`): auto-approve every approval gate this run
56
+ * holds on (run-scoped only — the run id this invocation dispatched). The
57
+ * operator must still be clause-eligible per hold; an ineligible hold blocks.
58
+ */
59
+ approveAll?: boolean;
60
+ /** `--input KEY=VALUE` values (repeatable): typed workflow-dispatch inputs. */
61
+ inputs?: string[];
62
+ }
63
+ /** Result of a single remote fixture run */
64
+ export interface RemoteRunResult {
65
+ fixtureId: string;
66
+ runId: string;
67
+ status: 'accepted' | 'rejected' | 'success' | 'failed' | 'cancelled' | 'error';
68
+ reason?: string;
69
+ observeUrl?: string;
70
+ durationMs?: number;
71
+ jobs?: Array<{
72
+ name: string;
73
+ status: string;
74
+ durationMs?: number;
75
+ }>;
76
+ }
77
+ /**
78
+ * Main preview command entry point.
79
+ *
80
+ * `kici preview <event>` is a dry-run trigger preview only — it executes nothing.
81
+ * If the argument looks like a fixture name (not a known event type), prints a migration message.
82
+ */
83
+ export declare function previewCommand(event: string | undefined, options: PreviewOptions): Promise<boolean>;
84
+ /**
85
+ * Local-only dry-run mode: compile workflows, match triggers, display what would execute.
86
+ */
87
+ export declare function previewEvent(event: string, options: PreviewOptions): Promise<boolean>;
88
+ //# sourceMappingURL=preview.d.ts.map
@@ -9,30 +9,31 @@ import { loadSecretsFile } from "../test-runner/secrets-file.js";
9
9
  import path from "node:path";
10
10
  import pc from "picocolors";
11
11
  import { readFile } from "node:fs/promises";
12
+ import { flattenStepInputs } from "@kici-dev/sdk";
12
13
  import { logger, toErrorMessage } from "@kici-dev/core";
13
14
  import { matchAllWorkflows } from "@kici-dev/engine";
14
15
  import { normalizeRunsOnToMatchers } from "@kici-dev/engine/labels/compile";
15
- //#region src/commands/test.ts
16
+ //#region src/commands/preview.ts
16
17
  /**
17
- * Main test command entry point.
18
+ * Main preview command entry point.
18
19
  *
19
- * `kici test <event>` is now dry-run trigger preview only.
20
+ * `kici preview <event>` is a dry-run trigger preview only — it executes nothing.
20
21
  * If the argument looks like a fixture name (not a known event type), prints a migration message.
21
22
  */
22
- async function testCommand(event, options) {
23
+ async function previewCommand(event, options) {
23
24
  if (options.debug) {
24
25
  process.env.KICI_DEBUG = "true";
25
26
  logger.info(pc.gray("Debug mode enabled"));
26
27
  }
27
28
  try {
28
29
  if (!event) {
29
- logger.info(pc.bold("\nUsage: kici test <event>\n"));
30
+ logger.info(pc.bold("\nUsage: kici preview <event>\n"));
30
31
  logger.info(pc.gray("Preview which workflows and jobs would run for a given event.\n"));
31
32
  logger.info(pc.gray("Examples:"));
32
- logger.info(pc.gray(" kici test push"));
33
- logger.info(pc.gray(" kici test pr:open"));
34
- logger.info(pc.gray(" kici test schedule"));
35
- logger.info(pc.gray(" kici test lifecycle:workflow_complete\n"));
33
+ logger.info(pc.gray(" kici preview push"));
34
+ logger.info(pc.gray(" kici preview pr:open"));
35
+ logger.info(pc.gray(" kici preview schedule"));
36
+ logger.info(pc.gray(" kici preview lifecycle:workflow_complete\n"));
36
37
  logger.info(pc.gray("For remote fixture execution, use: kici run remote [fixture] [options]"));
37
38
  logger.info(pc.gray("For local workflow execution, use: kici run local <event> [options]\n"));
38
39
  return true;
@@ -41,7 +42,7 @@ async function testCommand(event, options) {
41
42
  logger.info(pc.yellow(`\nFixture-based testing has moved to \`kici run remote\`.\nRun \`kici run remote ${event}\` instead.\n`));
42
43
  return false;
43
44
  }
44
- return await testDryRun(event, options);
45
+ return await previewEvent(event, options);
45
46
  } catch (error) {
46
47
  const message = toErrorMessage(error);
47
48
  logger.error(pc.red(`\nError: ${message}\n`));
@@ -64,7 +65,7 @@ function isKnownEventArg(arg) {
64
65
  /**
65
66
  * Local-only dry-run mode: compile workflows, match triggers, display what would execute.
66
67
  */
67
- async function testDryRun(event, options) {
68
+ async function previewEvent(event, options) {
68
69
  try {
69
70
  const kiciDir = resolveKiciDir(options.kiciDir);
70
71
  logger.info(pc.gray(`KiCI directory: ${kiciDir}`));
@@ -144,7 +145,7 @@ function workflowsToLockFormat(workflows) {
144
145
  if ("group" in n) return `__group:${n.group}`;
145
146
  return n.name;
146
147
  }) ?? [],
147
- steps: j.steps.map((s) => {
148
+ steps: flattenStepInputs(j.steps).map((s) => {
148
149
  if (typeof s === "function") return {
149
150
  name: "",
150
151
  hasOutputs: false
@@ -213,6 +214,6 @@ async function loadTestSecrets(kiciDir, secretFlags, contextFlags) {
213
214
  return secrets;
214
215
  }
215
216
  //#endregion
216
- export { testCommand, testDryRun };
217
+ export { previewCommand, previewEvent };
217
218
 
218
- //# sourceMappingURL=test.js.map
219
+ //# sourceMappingURL=preview.js.map