@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
@@ -1,6 +1,6 @@
1
- import type { HostTargetSelector } from '@kici-dev/engine';
1
+ import { type HostTargetSelector, type InputsDescriptorMap } from '@kici-dev/engine';
2
2
  import type { RunLocalOptions } from '../local-executor/types.js';
3
- import type { RemoteRunOptions } from './test.js';
3
+ import type { RemoteRunOptions } from './preview.js';
4
4
  /**
5
5
  * Compile `--target` selector strings into a {@link HostTargetSelector}. Each
6
6
  * string becomes one AND value (its own include set); repeated values
@@ -8,6 +8,31 @@ import type { RemoteRunOptions } from './test.js';
8
8
  * `--target-allow-empty` is set without at least one `--target`.
9
9
  */
10
10
  export declare function buildTargetSelector(targets: string[] | undefined, allowEmpty: boolean): HostTargetSelector | undefined;
11
+ /**
12
+ * Look up the dispatch-trigger `inputs` descriptor for a workflow from a parsed
13
+ * inline lock file. When `workflowName` is given, only that workflow's dispatch
14
+ * triggers are considered; otherwise descriptors across all workflows are merged
15
+ * (best-effort fast-fail — the orchestrator re-validates against the matched
16
+ * workflow authoritatively). Returns undefined when no dispatch inputs declared.
17
+ */
18
+ export declare function lookupDispatchInputsDescriptor(inlineLockFile: string | undefined, workflowName: string | undefined): InputsDescriptorMap | undefined;
19
+ /**
20
+ * Validate raw `--input KEY=VALUE` pairs against the (optional) lock descriptor
21
+ * and return the raw operator pairs verbatim. The CLI fast-fails on malformed /
22
+ * invalid input for UX, but forwards the **raw** strings — the orchestrator is
23
+ * authoritative and applies coercion + defaults exactly once.
24
+ */
25
+ export declare function buildDispatchInputs(pairs: string[], descriptor: InputsDescriptorMap | undefined): Record<string, string>;
26
+ /**
27
+ * Run `fn` with everything written to `process.stdout` redirected to
28
+ * `process.stderr`, then restore the original writer. Compiling a repo
29
+ * evaluates every user workflow / fixture module, and a `console.log` at a
30
+ * module's top level writes straight to stdout. On a machine-readable
31
+ * (`--json`) run that stdout MUST carry only the final JSON result, so the
32
+ * compile phases run inside this guard — workflow log output belongs on
33
+ * stderr regardless.
34
+ */
35
+ export declare function withStdoutOnStderr<T>(fn: () => Promise<T>): Promise<T>;
11
36
  /**
12
37
  * Run a workflow locally using the local executor.
13
38
  * Thin wrapper that delegates to executeLocal from local-executor.
@@ -3,19 +3,24 @@ import { resolveKiciDir } from "../execution/executor.js";
3
3
  import "../execution/index.js";
4
4
  import { loadGlobalConfig } from "../remote/config.js";
5
5
  import { RunHistory } from "../remote/history.js";
6
+ import { buildLocalRepoIdentity } from "../remote/local-repo-identity.js";
6
7
  import { AccessDeniedError, AmbiguousClusterError, AuthenticationError, ConnectionError, NoClusterError, PlatformRunClient } from "../remote/platform-client.js";
7
8
  import { buildEncryptedSecrets } from "../remote/secret-upload.js";
8
9
  import { createOverlayTarball, getSizeWarning, uploadTarball } from "../remote/uploader.js";
9
10
  import { formatJsonResult } from "../remote/output/json.js";
10
11
  import { formatJunitResult } from "../remote/output/junit.js";
11
12
  import { formatErrorHighlight, formatMultiFixtureSummary, formatSummary } from "../remote/output/summary.js";
13
+ import { compileCommand } from "./compile.js";
12
14
  import { compileFixtures, filterFixtures } from "../fixtures/compiler.js";
15
+ import { describeEvent } from "../fixtures/describe-event.js";
16
+ import { FixturePickerCancelledError, runFixturePicker } from "../fixtures/picker.js";
13
17
  import { listHeldRunsForRun, resolveHeldRunContext } from "./held-run-client.js";
14
18
  import { handleNewHolds } from "./run-hold-watch.js";
15
19
  import path from "node:path";
16
20
  import pc from "picocolors";
17
21
  import { readFile, writeFile } from "node:fs/promises";
18
22
  import { formatBytes, logger, toErrorMessage } from "@kici-dev/core";
23
+ import { coerceDispatchInputs, parseInputPairs } from "@kici-dev/engine";
19
24
  import { normalizeRunsOnToMatchers } from "@kici-dev/engine/labels/compile";
20
25
  import { confirm } from "@inquirer/prompts";
21
26
  //#region src/commands/run.ts
@@ -42,9 +47,85 @@ function buildTargetSelector(targets, allowEmpty) {
42
47
  allowEmpty
43
48
  };
44
49
  }
50
+ /**
51
+ * Look up the dispatch-trigger `inputs` descriptor for a workflow from a parsed
52
+ * inline lock file. When `workflowName` is given, only that workflow's dispatch
53
+ * triggers are considered; otherwise descriptors across all workflows are merged
54
+ * (best-effort fast-fail — the orchestrator re-validates against the matched
55
+ * workflow authoritatively). Returns undefined when no dispatch inputs declared.
56
+ */
57
+ function lookupDispatchInputsDescriptor(inlineLockFile, workflowName) {
58
+ if (!inlineLockFile) return void 0;
59
+ let lock;
60
+ try {
61
+ lock = JSON.parse(inlineLockFile);
62
+ } catch {
63
+ return;
64
+ }
65
+ const merged = {};
66
+ let found = false;
67
+ for (const wf of lock.workflows ?? []) {
68
+ if (workflowName && wf.name !== workflowName) continue;
69
+ for (const trigger of wf.triggers ?? []) if (trigger._type === "dispatch" && trigger.inputs) {
70
+ Object.assign(merged, trigger.inputs);
71
+ found = true;
72
+ }
73
+ }
74
+ return found ? merged : void 0;
75
+ }
76
+ /**
77
+ * Validate raw `--input KEY=VALUE` pairs against the (optional) lock descriptor
78
+ * and return the raw operator pairs verbatim. The CLI fast-fails on malformed /
79
+ * invalid input for UX, but forwards the **raw** strings — the orchestrator is
80
+ * authoritative and applies coercion + defaults exactly once.
81
+ */
82
+ function buildDispatchInputs(pairs, descriptor) {
83
+ if (!pairs.length) return {};
84
+ const raw = parseInputPairs(pairs);
85
+ if (descriptor) {
86
+ const r = coerceDispatchInputs(raw, descriptor);
87
+ if ("error" in r) throw r.error;
88
+ }
89
+ return raw;
90
+ }
45
91
  /** Interval between status/log polls while a run is active. */
46
92
  const POLL_INTERVAL_MS = 750;
47
93
  /**
94
+ * Run `fn` with everything written to `process.stdout` redirected to
95
+ * `process.stderr`, then restore the original writer. Compiling a repo
96
+ * evaluates every user workflow / fixture module, and a `console.log` at a
97
+ * module's top level writes straight to stdout. On a machine-readable
98
+ * (`--json`) run that stdout MUST carry only the final JSON result, so the
99
+ * compile phases run inside this guard — workflow log output belongs on
100
+ * stderr regardless.
101
+ */
102
+ async function withStdoutOnStderr(fn) {
103
+ const realWrite = process.stdout.write.bind(process.stdout);
104
+ process.stdout.write = process.stderr.write.bind(process.stderr);
105
+ try {
106
+ return await fn();
107
+ } finally {
108
+ process.stdout.write = realWrite;
109
+ }
110
+ }
111
+ /**
112
+ * Recompile `.kici/workflows` → `kici.lock.json` before a remote run, mirroring
113
+ * `kici run local`. The orchestrator matches triggers and dispatches against the
114
+ * inline lock, so a stale lock would route an edited or newly-added workflow
115
+ * incorrectly. Returns false on a compile/validation error so the caller can
116
+ * abort before any upload or dispatch.
117
+ */
118
+ async function compileBeforeRemoteRun(options) {
119
+ const pureStdout = Boolean(options.json || options.quiet);
120
+ const compile = () => compileCommand({
121
+ kiciDir: options.kiciDir ?? ".kici",
122
+ check: false,
123
+ verbose: options.debug ?? false,
124
+ quiet: pureStdout
125
+ });
126
+ return pureStdout ? withStdoutOnStderr(compile) : compile();
127
+ }
128
+ /**
48
129
  * Run a workflow locally using the local executor.
49
130
  * Thin wrapper that delegates to executeLocal from local-executor.
50
131
  *
@@ -88,10 +169,22 @@ async function runRemoteCommand(fixture, options) {
88
169
  console.log(history.formatTable(entries));
89
170
  return true;
90
171
  }
91
- const fixtures = await compileFixtures(path.join(kiciDir, "tests"));
92
- if (!fixture && !options.all) return listFixtures(fixtures);
172
+ const testsDir = path.join(kiciDir, "tests");
173
+ const fixtures = options.json ? await withStdoutOnStderr(() => compileFixtures(testsDir)) : await compileFixtures(testsDir);
93
174
  let selected;
94
- if (options.all) selected = fixtures;
175
+ if (options.pick) {
176
+ if (fixtures.length === 0) return listFixtures(fixtures);
177
+ try {
178
+ selected = await runFixturePicker(fixtures);
179
+ } catch (err) {
180
+ if (err instanceof FixturePickerCancelledError) {
181
+ logger.info(pc.gray(err.message));
182
+ return false;
183
+ }
184
+ throw err;
185
+ }
186
+ } else if (!fixture && !options.all) return listFixtures(fixtures);
187
+ else if (options.all) selected = fixtures;
95
188
  else selected = filterFixtures(fixtures, fixture);
96
189
  if (selected.length === 0) {
97
190
  logger.info(pc.yellow(`No fixtures matched: ${fixture ?? "(none)"}`));
@@ -132,17 +225,6 @@ function listFixtures(fixtures) {
132
225
  return true;
133
226
  }
134
227
  /**
135
- * Describe the event type from a fixture's trigger config.
136
- */
137
- function describeEvent(event) {
138
- if (!event || typeof event !== "object") return "unknown";
139
- const e = event;
140
- if (e._type === "push") return "push";
141
- if (e._type === "pr") return `pr:${e.action ?? "open"}`;
142
- if (typeof e._type === "string") return String(e._type);
143
- return "custom";
144
- }
145
- /**
146
228
  * Resolve the authenticated Platform client and the run target (org + cluster).
147
229
  *
148
230
  * Org resolution: `--org` → `config.activeOrgId` → error.
@@ -182,6 +264,7 @@ function resolvePlatformContext(config, options) {
182
264
  * Run fixtures remotely against the Platform.
183
265
  */
184
266
  async function runFixturesRemotely(fixtures, options) {
267
+ if (!await compileBeforeRemoteRun(options)) return false;
185
268
  const config = await loadGlobalConfig();
186
269
  const ctx = resolvePlatformContext(config, options);
187
270
  if (!ctx) return false;
@@ -294,11 +377,12 @@ async function runSingleFixture(fixture, ctx, options, config, history) {
294
377
  const uploaded = await initAndUpload(ctx, overlay, options);
295
378
  const event = buildEventFromFixture(opts);
296
379
  {
297
- const repoName = path.basename(overlay.repoRoot);
380
+ const identity = buildLocalRepoIdentity(overlay.repoRoot);
298
381
  const repo = event.payload.repository;
299
382
  event.payload.repository = {
300
383
  ...repo ?? {},
301
- full_name: `local/${repoName}`
384
+ full_name: identity.repoIdentifier,
385
+ provider: identity.provider
302
386
  };
303
387
  }
304
388
  if (!options.quiet) logger.info(pc.gray("Triggering test run..."));
@@ -320,6 +404,11 @@ async function runSingleFixture(fixture, ctx, options, config, history) {
320
404
  ...(() => {
321
405
  const target = buildTargetSelector(options.targets, options.targetAllowEmpty ?? false);
322
406
  return target ? { target } : {};
407
+ })(),
408
+ ...(() => {
409
+ const descriptor = lookupDispatchInputsDescriptor(overlay.inlineLockFile, opts.workflowName);
410
+ const dispatchInputs = buildDispatchInputs(options.inputs ?? [], descriptor);
411
+ return Object.keys(dispatchInputs).length ? { dispatchInputs } : {};
323
412
  })()
324
413
  });
325
414
  if (triggerResult.status === "rejected") {
@@ -513,6 +602,7 @@ function buildEventFromFixture(opts) {
513
602
  * Platform.
514
603
  */
515
604
  async function runDirectWorkflow(workflowName, options) {
605
+ if (!await compileBeforeRemoteRun(options)) return false;
516
606
  const ctx = resolvePlatformContext(await loadGlobalConfig(), options);
517
607
  if (!ctx) return false;
518
608
  if (options.json) options.quiet = true;
@@ -522,7 +612,11 @@ async function runDirectWorkflow(workflowName, options) {
522
612
  try {
523
613
  const overlay = await prepareOverlay(options);
524
614
  const uploaded = await initAndUpload(ctx, overlay, options);
525
- const payload = { repository: { full_name: `local/${path.basename(overlay.repoRoot)}` } };
615
+ const identity = buildLocalRepoIdentity(overlay.repoRoot);
616
+ const payload = { repository: {
617
+ full_name: identity.repoIdentifier,
618
+ provider: identity.provider
619
+ } };
526
620
  const encrypted = await buildEncryptedSecrets(overlay.kiciDir, options.envFlags, options.context, uploaded.publicKey);
527
621
  const triggerResult = await ctx.client.trigger(ctx.orgId, ctx.target, {
528
622
  fixtureId,
@@ -544,6 +638,11 @@ async function runDirectWorkflow(workflowName, options) {
544
638
  ...(() => {
545
639
  const target = buildTargetSelector(options.targets, options.targetAllowEmpty ?? false);
546
640
  return target ? { target } : {};
641
+ })(),
642
+ ...(() => {
643
+ const descriptor = lookupDispatchInputsDescriptor(overlay.inlineLockFile, workflowName);
644
+ const dispatchInputs = buildDispatchInputs(options.inputs ?? [], descriptor);
645
+ return Object.keys(dispatchInputs).length ? { dispatchInputs } : {};
547
646
  })()
548
647
  });
549
648
  if (!options.quiet) logger.info(pc.green(`Run started: ${triggerResult.runId}`));
@@ -572,6 +671,6 @@ function displayRemoteResults(results) {
572
671
  logger.info("");
573
672
  }
574
673
  //#endregion
575
- export { buildTargetSelector, runLocalCommand, runRemoteCommand };
674
+ export { buildDispatchInputs, buildTargetSelector, lookupDispatchInputsDescriptor, runLocalCommand, runRemoteCommand, withStdoutOnStderr };
576
675
 
577
676
  //# sourceMappingURL=run.js.map
@@ -19,6 +19,8 @@ export interface TestOptions extends PayloadOptions {
19
19
  export interface RemoteRunOptions extends TestOptions {
20
20
  /** Run all available fixtures */
21
21
  all?: boolean;
22
+ /** Interactively pick fixtures to run (multi-select checkbox). */
23
+ pick?: boolean;
22
24
  /** Run matching fixtures concurrently */
23
25
  parallel?: boolean;
24
26
  /** Fire and forget (print runIds, don't stream) */
@@ -55,6 +57,8 @@ export interface RemoteRunOptions extends TestOptions {
55
57
  * operator must still be clause-eligible per hold; an ineligible hold blocks.
56
58
  */
57
59
  approveAll?: boolean;
60
+ /** `--input KEY=VALUE` values (repeatable): typed workflow-dispatch inputs. */
61
+ inputs?: string[];
58
62
  }
59
63
  /** Result of a single remote fixture run */
60
64
  export interface RemoteRunResult {
@@ -1,6 +1,8 @@
1
1
  export interface TypesOptions {
2
2
  /** Path to .kici directory (defaults to .kici) */
3
3
  kiciDir?: string;
4
+ /** Suppress the success line on stdout (so machine-readable output stays pure). */
5
+ quiet?: boolean;
4
6
  }
5
7
  /**
6
8
  * Generate TypeScript declarations for environment secrets.
@@ -33,7 +33,7 @@ async function typesCommand(options = {}) {
33
33
  await fs.mkdir(typesDir, { recursive: true });
34
34
  const outputPath = path.join(typesDir, "secrets.d.ts");
35
35
  await fs.writeFile(outputPath, dtsContent, "utf-8");
36
- console.log(pc.green("Types generated") + pc.dim(` ${outputPath}`));
36
+ if (!options.quiet) console.log(pc.green("Types generated") + pc.dim(` ${outputPath}`));
37
37
  return true;
38
38
  } catch (err) {
39
39
  if (err instanceof DashboardClientError) {
@@ -1,7 +1,10 @@
1
1
  export interface VerifyAttestationOptions {
2
2
  /** Path or `http(s)` URL to the attestation bundle JSON. Required. */
3
3
  bundle?: string;
4
- /** Trusted issuer URL (online discovery) or a self-contained `{ issuer, jwks }` file. Required. */
4
+ /**
5
+ * Trusted issuer URL (online discovery) or a self-contained `{ issuer, jwks }`
6
+ * file. Optional — defaults to the hosted KiCI Platform's provenance issuer.
7
+ */
5
8
  trustRoot?: string;
6
9
  /** Expected token audience (defaults to the KiCI provenance audience). */
7
10
  audience?: string;
@@ -1,5 +1,6 @@
1
1
  import "../chunk-BTugEXQM.js";
2
2
  import { resolveTrustRoot } from "../provenance-trust-root.js";
3
+ import "../remote/prod-defaults.js";
3
4
  import pc from "picocolors";
4
5
  import { readFile } from "node:fs/promises";
5
6
  import { logger, sha256File, toErrorMessage } from "@kici-dev/core";
@@ -7,13 +8,18 @@ import { KICI_PROVENANCE_AUDIENCE } from "@kici-dev/engine/provenance/bundle";
7
8
  import { verifyKiciBundle } from "@kici-dev/engine/provenance/verify";
8
9
  //#region src/commands/verify-attestation.ts
9
10
  /**
10
- * `kici verify-attestation [artifact] --bundle <path|url> --trust-root <url|file>`
11
+ * `kici verify-attestation [artifact] --bundle <path|url> [--trust-root <url|file>]`
11
12
  *
12
13
  * Offline verification of a KiCI-signed provenance bundle: read the bundle,
13
- * resolve the trusted issuer + JWKS out-of-band (`--trust-root`), optionally
14
- * digest the artifact, and hand everything to the shared browser-safe
15
- * `verifyKiciBundle` core in `@kici-dev/engine`. The engine owns all crypto;
16
- * this command is the thin Node wrapper (fs / fetch / artifact digest / output).
14
+ * resolve the trusted issuer + JWKS out-of-band, optionally digest the artifact,
15
+ * and hand everything to the shared browser-safe `verifyKiciBundle` core in
16
+ * `@kici-dev/engine`. The engine owns all crypto; this command is the thin Node
17
+ * wrapper (fs / fetch / artifact digest / output).
18
+ *
19
+ * `--trust-root` is optional: when omitted it defaults to the hosted KiCI
20
+ * Platform's provenance issuer, so the common case (verifying a bundle attested
21
+ * on the hosted platform) needs no flag. Pass `--trust-root` to verify against a
22
+ * different environment (e.g. staging) or an offline `{ issuer, jwks }` file.
17
23
  *
18
24
  * Returns a boolean (verified) so `cli.ts` can map it to an exit code (0/1).
19
25
  */
@@ -23,12 +29,22 @@ async function verifyAttestationCommand(artifact, options = {}) {
23
29
  logger.error(pc.red("Error: --bundle <path|url> is required"));
24
30
  return false;
25
31
  }
26
- if (!options.trustRoot) {
27
- logger.error(pc.red("Error: --trust-root <url|file> is required"));
28
- return false;
29
- }
32
+ const trustRoot = options.trustRoot ?? "https://api.kici.dev";
33
+ const usingDefault = !options.trustRoot;
34
+ if (usingDefault) logger.info(pc.gray(`Using default trust root ${trustRoot} (pass --trust-root to override)`));
30
35
  const bundle = JSON.parse(await readBundle(options.bundle));
31
- const { issuer, jwks } = await resolveTrustRoot(options.trustRoot);
36
+ let resolved;
37
+ try {
38
+ resolved = await resolveTrustRoot(trustRoot);
39
+ } catch (error) {
40
+ const msg = toErrorMessage(error);
41
+ if (usingDefault && /\b503\b/.test(msg)) {
42
+ logger.error(pc.red(`Error: build provenance is not enabled on the hosted KiCI platform yet (${trustRoot} returned 503). Pass --trust-root to verify against another environment (e.g. staging) or an offline { issuer, jwks } file.`));
43
+ return false;
44
+ }
45
+ throw error;
46
+ }
47
+ const { issuer, jwks } = resolved;
32
48
  const expectedDigest = artifact ? {
33
49
  alg: "sha256",
34
50
  hex: await sha256File(artifact)
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Describe the event type from a fixture's trigger config, for the fixtures
3
+ * table and the interactive picker.
4
+ */
5
+ export declare function describeEvent(event: unknown): string;
6
+ //# sourceMappingURL=describe-event.d.ts.map
@@ -0,0 +1,18 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ //#region src/fixtures/describe-event.ts
3
+ /**
4
+ * Describe the event type from a fixture's trigger config, for the fixtures
5
+ * table and the interactive picker.
6
+ */
7
+ function describeEvent(event) {
8
+ if (!event || typeof event !== "object") return "unknown";
9
+ const e = event;
10
+ if (e._type === "push") return "push";
11
+ if (e._type === "pr") return `pr:${e.action ?? "open"}`;
12
+ if (typeof e._type === "string") return String(e._type);
13
+ return "custom";
14
+ }
15
+ //#endregion
16
+ export { describeEvent };
17
+
18
+ //# sourceMappingURL=describe-event.js.map
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Interactive multi-select fixture picker for `kici run remote --pick`.
3
+ *
4
+ * Lists every available fixture with its source and event type, lets the user
5
+ * toggle one or more via a checkbox prompt, and returns the chosen fixtures.
6
+ * The caller feeds them into the standard remote-dispatch pipeline, so a picked
7
+ * run is identical to one selected by name / glob / --all.
8
+ */
9
+ import type { CompiledFixture } from './compiler.js';
10
+ export declare class FixturePickerCancelledError extends Error {
11
+ constructor(message: string);
12
+ }
13
+ /**
14
+ * Run the interactive multi-select fixture picker.
15
+ *
16
+ * @throws FixturePickerCancelledError when stdin is not a TTY or the user aborts.
17
+ */
18
+ export declare function runFixturePicker(fixtures: CompiledFixture[]): Promise<CompiledFixture[]>;
19
+ //# sourceMappingURL=picker.d.ts.map
@@ -0,0 +1,64 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ import { describeEvent } from "./describe-event.js";
3
+ import path from "node:path";
4
+ import pc from "picocolors";
5
+ import { logger } from "@kici-dev/core";
6
+ import { checkbox } from "@inquirer/prompts";
7
+ //#region src/fixtures/picker.ts
8
+ /**
9
+ * Interactive multi-select fixture picker for `kici run remote --pick`.
10
+ *
11
+ * Lists every available fixture with its source and event type, lets the user
12
+ * toggle one or more via a checkbox prompt, and returns the chosen fixtures.
13
+ * The caller feeds them into the standard remote-dispatch pipeline, so a picked
14
+ * run is identical to one selected by name / glob / --all.
15
+ */
16
+ var FixturePickerCancelledError = class extends Error {
17
+ constructor(message) {
18
+ super(message);
19
+ this.name = "FixturePickerCancelledError";
20
+ }
21
+ };
22
+ function isStdinTty() {
23
+ return Boolean(process.stdin.isTTY);
24
+ }
25
+ /** Build a single fixture's display row: `id <source> <event type>`. */
26
+ function fixtureRow(f) {
27
+ const opts = typeof f.fixture.options === "function" ? null : f.fixture.options;
28
+ const eventType = opts?.event ? describeEvent(opts.event) : "(async)";
29
+ const source = path.relative(process.cwd(), f.sourceFile);
30
+ return `${f.id} ${pc.gray(source)} ${eventType}`;
31
+ }
32
+ function printFixtureList(fixtures) {
33
+ logger.info(pc.bold("Available fixtures:"));
34
+ for (const f of fixtures) logger.info(` ${pc.cyan(f.id)} — ${fixtureRow(f)}`);
35
+ }
36
+ /**
37
+ * Run the interactive multi-select fixture picker.
38
+ *
39
+ * @throws FixturePickerCancelledError when stdin is not a TTY or the user aborts.
40
+ */
41
+ async function runFixturePicker(fixtures) {
42
+ if (!isStdinTty()) {
43
+ printFixtureList(fixtures);
44
+ throw new FixturePickerCancelledError("--pick requires an interactive terminal. Pass a fixture name instead.");
45
+ }
46
+ let chosenIds;
47
+ try {
48
+ chosenIds = await checkbox({
49
+ message: "Select fixtures to run",
50
+ choices: fixtures.map((f) => ({
51
+ name: fixtureRow(f),
52
+ value: f.id
53
+ })),
54
+ required: true
55
+ });
56
+ } catch (err) {
57
+ throw new FixturePickerCancelledError(`Picker cancelled: ${err instanceof Error ? err.message : String(err)}`);
58
+ }
59
+ return fixtures.filter((f) => chosenIds.includes(f.id));
60
+ }
61
+ //#endregion
62
+ export { FixturePickerCancelledError, runFixturePicker };
63
+
64
+ //# sourceMappingURL=picker.js.map
@@ -59,6 +59,8 @@ function generateSecretsDts(options) {
59
59
  lines.push(" }");
60
60
  lines.push("}");
61
61
  lines.push("");
62
+ lines.push("export {};");
63
+ lines.push("");
62
64
  return lines.join("\n");
63
65
  }
64
66
  //#endregion
package/dist/index.d.ts CHANGED
@@ -3,8 +3,8 @@ export type { ExecutionResult } from './execution/index.js';
3
3
  export { validateConfig } from './validation/index.js';
4
4
  export type { ValidationResult } from './validation/index.js';
5
5
  export { generateLockFile, serializeLockFile } from './lockfile/index.js';
6
- export { SCHEMA_VERSION, isLockStaticJob } from './types.js';
7
- export type { LockFile, LockWorkflow, LockJob, LockDynamicJobFn, LockJobOrFactory, LockTrigger, LockPrTrigger, LockPushTrigger, LockMatrix, LockRule, LockStep, LockApproval, LockSource, LockBranchPattern, } from './types.js';
6
+ export { SCHEMA_VERSION, isLockStaticJob, isLockParallelStep } from './types.js';
7
+ export type { LockFile, LockWorkflow, LockJob, LockDynamicJobFn, LockJobOrFactory, LockTrigger, LockPrTrigger, LockPushTrigger, LockMatrix, LockRule, LockStep, LockParallelStep, LockStepEntry, LockApproval, LockSource, LockBranchPattern, } from './types.js';
8
8
  export { formatError, compilerError, isCompilerError } from './errors/index.js';
9
9
  export type { SourceLocation, CompilerError } from './errors/index.js';
10
10
  export { CapabilityGapError, formatCapabilityGapError } from './errors/index.js';
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { executeConfig } from "./execution/executor.js";
6
6
  import "./execution/index.js";
7
7
  import { validateConfig } from "./validation/validator.js";
8
8
  import "./validation/index.js";
9
- import { SCHEMA_VERSION, isLockStaticJob } from "./types.js";
9
+ import { SCHEMA_VERSION, isLockParallelStep, isLockStaticJob } from "./types.js";
10
10
  import { generateLockFile, serializeLockFile } from "./lockfile/generator.js";
11
11
  import "./lockfile/index.js";
12
- export { CapabilityGapError, SCHEMA_VERSION, compilerError, executeConfig, formatCapabilityGapError, formatError, generateLockFile, isCompilerError, isLockStaticJob, serializeLockFile, validateConfig };
12
+ export { CapabilityGapError, SCHEMA_VERSION, compilerError, executeConfig, formatCapabilityGapError, formatError, generateLockFile, isCompilerError, isLockParallelStep, isLockStaticJob, serializeLockFile, validateConfig };