@kici-dev/compiler 0.1.23 → 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 (41) hide show
  1. package/dist/cli.js +15 -5
  2. package/dist/commands/index.d.ts +4 -2
  3. package/dist/commands/index.js +3 -2
  4. package/dist/commands/init.js +2 -2
  5. package/dist/commands/pat.d.ts +27 -0
  6. package/dist/commands/pat.js +76 -0
  7. package/dist/commands/preview.d.ts +88 -0
  8. package/dist/commands/{test.js → preview.js} +15 -14
  9. package/dist/commands/run.d.ts +11 -1
  10. package/dist/commands/run.js +34 -7
  11. package/dist/commands/verify-attestation.d.ts +4 -1
  12. package/dist/commands/verify-attestation.js +26 -10
  13. package/dist/generators/secrets-dts.js +2 -0
  14. package/dist/index.d.ts +2 -2
  15. package/dist/index.js +2 -2
  16. package/dist/llm-context/llms-architecture.txt +3 -3
  17. package/dist/llm-context/llms-cli.txt +149 -26
  18. package/dist/llm-context/llms-features.txt +107 -5
  19. package/dist/llm-context/llms-full.txt +390 -46
  20. package/dist/llm-context/llms-getting-started.txt +6 -6
  21. package/dist/llm-context/llms-sdk.txt +125 -6
  22. package/dist/llm-context/llms.txt +7 -5
  23. package/dist/local-executor/index.js +2 -1
  24. package/dist/local-executor/job-runner.js +3 -3
  25. package/dist/lockfile/generator.d.ts +10 -2
  26. package/dist/lockfile/generator.js +106 -52
  27. package/dist/remote/history.d.ts +1 -1
  28. package/dist/remote/history.js +1 -1
  29. package/dist/remote/local-repo-identity.d.ts +32 -0
  30. package/dist/remote/local-repo-identity.js +74 -0
  31. package/dist/remote/prod-defaults.d.ts +8 -0
  32. package/dist/remote/prod-defaults.js +9 -1
  33. package/dist/templates/agents-md.d.ts +1 -1
  34. package/dist/templates/agents-md.js +2 -2
  35. package/dist/templates/package-json.js +1 -1
  36. package/dist/test-runner/step-context.d.ts +1 -1
  37. package/dist/test-runner/step-context.js +2 -1
  38. package/dist/types.d.ts +33 -6
  39. package/dist/types.js +5 -1
  40. package/package.json +4 -7
  41. package/sbom.spdx.json +35 -35
@@ -0,0 +1,74 @@
1
+ import "../chunk-BTugEXQM.js";
2
+ import path from "node:path";
3
+ import { execSync } from "node:child_process";
4
+ import { z } from "zod";
5
+ //#region src/remote/local-repo-identity.ts
6
+ /** Git hosts whose origin URL we can turn into a real `owner/repo` + web link. */
7
+ const RecognizedProvider = z.enum([
8
+ "github",
9
+ "gitlab",
10
+ "bitbucket"
11
+ ]);
12
+ /** Host → provider map. The keys are the canonical public hostnames. */
13
+ const HOST_PROVIDER = {
14
+ "github.com": "github",
15
+ "gitlab.com": "gitlab",
16
+ "bitbucket.org": "bitbucket"
17
+ };
18
+ /** The literal provider value stamped when no recognized git origin exists. */
19
+ const LOCAL_PROVIDER = "local";
20
+ /**
21
+ * Parse a git remote URL into `{ provider, owner/repo }` for a recognized host.
22
+ * Handles scp-style ssh (`git@host:owner/repo.git`), `ssh://`, and `https://`
23
+ * forms; strips a trailing `.git`. Returns null for any unrecognized host or
24
+ * unparseable input.
25
+ */
26
+ function parseGitOrigin(url) {
27
+ const trimmed = url.trim();
28
+ const scp = /^[^@]+@([^:]+):(.+?)(?:\.git)?$/.exec(trimmed);
29
+ const urlStyle = /^[a-z]+:\/\/(?:[^@/]+@)?([^/]+)\/(.+?)(?:\.git)?$/.exec(trimmed);
30
+ const match = scp ?? urlStyle;
31
+ if (!match) return null;
32
+ const provider = HOST_PROVIDER[match[1].toLowerCase()];
33
+ if (!provider) return null;
34
+ const repoIdentifier = match[2].replace(/^\/+|\/+$/g, "");
35
+ if (!repoIdentifier.includes("/")) return null;
36
+ return {
37
+ provider,
38
+ repoIdentifier
39
+ };
40
+ }
41
+ /** Read the `origin` remote URL of the working tree at `repoRoot`, or null. */
42
+ function detectGitOrigin(repoRoot) {
43
+ try {
44
+ return execSync("git remote get-url origin", {
45
+ cwd: repoRoot,
46
+ encoding: "utf-8",
47
+ stdio: [
48
+ "pipe",
49
+ "pipe",
50
+ "pipe"
51
+ ]
52
+ }).trim();
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+ /**
58
+ * Build the repo identity stamped on a `kici run remote` run. A recognized git
59
+ * origin yields the real `owner/repo` + provider so the dashboard can link to
60
+ * it; otherwise a synthetic `local/<basename>` identity with provider `local`.
61
+ */
62
+ function buildLocalRepoIdentity(repoRoot) {
63
+ const origin = detectGitOrigin(repoRoot);
64
+ const parsed = origin ? parseGitOrigin(origin) : null;
65
+ if (parsed) return parsed;
66
+ return {
67
+ repoIdentifier: `local/${path.basename(repoRoot)}`,
68
+ provider: LOCAL_PROVIDER
69
+ };
70
+ }
71
+ //#endregion
72
+ export { LOCAL_PROVIDER, RecognizedProvider, buildLocalRepoIdentity, detectGitOrigin, parseGitOrigin };
73
+
74
+ //# sourceMappingURL=local-repo-identity.js.map
@@ -15,4 +15,12 @@ export declare const PROD_PLATFORM_URL = "https://api.kici.dev";
15
15
  export declare const PROD_OIDC_ISSUER = "https://auth.kici.dev/realms/kici-internal";
16
16
  /** Public OIDC client id registered for the `kici` CLI. */
17
17
  export declare const PROD_OIDC_CLIENT_ID = "kici-cli";
18
+ /**
19
+ * Provenance trust root for `kici verify-attestation` when `--trust-root` is
20
+ * omitted: the hosted KiCI Platform's provenance issuer. The verifier appends
21
+ * `/.well-known/openid-configuration` and pins the token issuer to it. Pass
22
+ * `--trust-root` to verify against a different environment (e.g. staging) or an
23
+ * offline `{ issuer, jwks }` file.
24
+ */
25
+ export declare const PROD_PROVENANCE_ISSUER = "https://api.kici.dev";
18
26
  //# sourceMappingURL=prod-defaults.d.ts.map
@@ -17,7 +17,15 @@ const PROD_PLATFORM_URL = "https://api.kici.dev";
17
17
  const PROD_OIDC_ISSUER = "https://auth.kici.dev/realms/kici-internal";
18
18
  /** Public OIDC client id registered for the `kici` CLI. */
19
19
  const PROD_OIDC_CLIENT_ID = "kici-cli";
20
+ /**
21
+ * Provenance trust root for `kici verify-attestation` when `--trust-root` is
22
+ * omitted: the hosted KiCI Platform's provenance issuer. The verifier appends
23
+ * `/.well-known/openid-configuration` and pins the token issuer to it. Pass
24
+ * `--trust-root` to verify against a different environment (e.g. staging) or an
25
+ * offline `{ issuer, jwks }` file.
26
+ */
27
+ const PROD_PROVENANCE_ISSUER = "https://api.kici.dev";
20
28
  //#endregion
21
- export { PROD_OIDC_CLIENT_ID, PROD_OIDC_ISSUER, PROD_PLATFORM_URL };
29
+ export { PROD_OIDC_CLIENT_ID, PROD_OIDC_ISSUER, PROD_PLATFORM_URL, PROD_PROVENANCE_ISSUER };
22
30
 
23
31
  //# sourceMappingURL=prod-defaults.js.map
@@ -4,5 +4,5 @@
4
4
  * picked up by Claude Code, Cursor, Aider, and other coding agents that scan
5
5
  * the working tree for an authoring context file.
6
6
  */
7
- export declare const agentsMdTemplate = "# KiCI workflow authoring guide\n\nThis project uses KiCI \u2014 a TypeScript-native CI/CD workflow engine \u2014 instead\nof YAML-based CI. Workflows live in `.kici/workflows/*.ts`, are compiled\ninto a portable lock file, and executed by self-hosted agents.\n\n## Where the API surface lives\n\n- Public SDK types: `node_modules/@kici-dev/sdk/dist/index.d.ts` \u2014 read this\n for the canonical signatures of `workflow`, `job`, `step`, `pr`,\n `push`, `schedule`, `matrix`, `rule`, `dynamicJob`, etc.\n- Bundled offline reference for coding agents: `kici docs llm` prints the\n full markdown documentation bundle to stdout. `kici docs llm --index`\n prints just the curated link index (llms.txt format).\n- Online docs:\n - <https://kici.dev/docs/> \u2014 published docs site.\n - <https://kici.dev/llms.txt> \u2014 curated index for LLM consumers.\n - <https://kici.dev/llms-full.txt> \u2014 full markdown bundle.\n - Key pages: `user/sdk-reference`, `user/workflow-patterns`,\n `user/testing-guide`, `user/hooks`, `user/secrets`.\n\n## The five core patterns\n\n1. **Push trigger** \u2014 `on: push({ branches: 'main' })`. Pair with `paths`\n to scope to subtrees.\n\n ```ts\n import { workflow, job, step, push } from '@kici-dev/sdk';\n\n export default workflow('build', {\n on: push({ branches: 'main' }),\n jobs: [\n job('build', {\n runsOn: 'kici:os:linux',\n steps: [step('install', async ({ $ }) => { await $`pnpm install`; })],\n }),\n ],\n });\n ```\n\n `kici:os:linux` targets any agent reporting that OS \u2014 every agent\n self-reports `kici:os:` / `kici:arch:` / `kici:host:`. Use a custom label\n (e.g. `'gpu'`, `'prod-pool'`) to target a specific agent pool your scaler\n defines.\n\n2. **PR + matrix** \u2014 `pr({ target: 'main' })` plus a matrix over node\n versions. The matrix expands at dispatch time.\n\n ```ts\n import { workflow, job, step, pr, matrix } from '@kici-dev/sdk';\n\n export default workflow('test-matrix', {\n on: pr({ target: 'main' }),\n jobs: [\n job('test', {\n runsOn: 'kici:os:linux',\n strategy: { matrix: matrix({ node: ['20', '22', '24'] }) },\n steps: [\n step('test', async ({ $, matrix }) => {\n await $`node --version`;\n await $`pnpm install`;\n await $`pnpm test`;\n return { node: matrix.node };\n }),\n ],\n }),\n ],\n });\n ```\n\n3. **Lifecycle hooks** \u2014 `onFailure` / `onSuccess` / `onCancel` on a\n job or workflow run after the main steps in their own scope.\n\n4. **Secrets** \u2014 declared scopes resolve at dispatch:\n\n ```ts\n step('deploy', async ({ $, secrets }) => {\n await $`./scripts/deploy.sh`.env({ DEPLOY_TOKEN: secrets.production.DEPLOY_TOKEN });\n });\n ```\n\n Run `kici secrets list` to enumerate the contexts available for testing.\n\n5. **Dynamic jobs** \u2014 `dynamicJob` and `dynamicGroup` build the DAG at\n runtime from a step's outputs. Don't try to compute job names at top level;\n the lock file would be wrong.\n\n## Anti-patterns\n\n- **Do NOT write `.yml` / `.yaml` CI files** \u2014 KiCI replaces that entire\n layer. There is no compatibility shim.\n- **Do NOT `import` from any `@kici-dev/*` package's `/dist/...`\n subpath** \u2014 those are not part of the public API and break across versions.\n Import from the package root.\n- **Do NOT `await` outside step bodies.** The top-level workflow file is\n loaded by the compiler synchronously; async I/O at module scope means the\n lock file emits before it resolves and the workflow appears empty.\n- **Do NOT mutate shared variables between jobs.** Each job runs in its own\n agent process. Use `needs` + step outputs to thread values.\n- **Do NOT hand-edit `kici.lock.json`.** Regenerate it via `kici compile`.\n\n## Local commands a coding agent should run\n\n| Command | Purpose |\n| ------------------------------- | ------------------------------------------- |\n| `pnpm kici compile --check` | Validate workflow source without writing. |\n| `pnpm kici test pr:open --debug`| Preview which workflows match an event. |\n| `pnpm kici run local push` | Execute workflows locally with no orchestrator. |\n| `pnpm kici docs llm` | Print the full LLM documentation bundle. |\n| `pnpm kici docs llm --index` | Print the curated link index. |\n\nIf `pnpm kici` isn't in scripts, fall back to `npx kici`.\n\n## Loop\n\n1. Read the SDK types from `node_modules/@kici-dev/sdk/dist/index.d.ts`.\n2. Pipe `kici docs llm` into the agent's context if it doesn't already have\n the full bundle.\n3. Edit a workflow under `.kici/workflows/`.\n4. Run `kici compile --check` (zero exit means valid).\n5. Run `kici test <event>` to preview matching.\n6. Run `kici run local <event>` to execute locally before pushing.\n";
7
+ export declare const agentsMdTemplate = "# KiCI workflow authoring guide\n\nThis project uses KiCI \u2014 a TypeScript-native CI/CD workflow engine \u2014 instead\nof YAML-based CI. Workflows live in `.kici/workflows/*.ts`, are compiled\ninto a portable lock file, and executed by self-hosted agents.\n\n## Where the API surface lives\n\n- Public SDK types: `node_modules/@kici-dev/sdk/dist/index.d.ts` \u2014 read this\n for the canonical signatures of `workflow`, `job`, `step`, `pr`,\n `push`, `schedule`, `matrix`, `rule`, `dynamicJob`, etc.\n- Bundled offline reference for coding agents: `kici docs llm` prints the\n full markdown documentation bundle to stdout. `kici docs llm --index`\n prints just the curated link index (llms.txt format).\n- Online docs:\n - <https://kici.dev/docs/> \u2014 published docs site.\n - <https://kici.dev/llms.txt> \u2014 curated index for LLM consumers.\n - <https://kici.dev/llms-full.txt> \u2014 full markdown bundle.\n - Key pages: `user/sdk-reference`, `user/workflow-patterns`,\n `user/testing-guide`, `user/hooks`, `user/secrets`.\n\n## The five core patterns\n\n1. **Push trigger** \u2014 `on: push({ branches: 'main' })`. Pair with `paths`\n to scope to subtrees.\n\n ```ts\n import { workflow, job, step, push } from '@kici-dev/sdk';\n\n export default workflow('build', {\n on: push({ branches: 'main' }),\n jobs: [\n job('build', {\n runsOn: 'kici:os:linux',\n steps: [step('install', async ({ $ }) => { await $`pnpm install`; })],\n }),\n ],\n });\n ```\n\n `kici:os:linux` targets any agent reporting that OS \u2014 every agent\n self-reports `kici:os:` / `kici:arch:` / `kici:host:`. Use a custom label\n (e.g. `'gpu'`, `'prod-pool'`) to target a specific agent pool your scaler\n defines.\n\n2. **PR + matrix** \u2014 `pr({ target: 'main' })` plus a matrix over node\n versions. The matrix expands at dispatch time.\n\n ```ts\n import { workflow, job, step, pr, matrix } from '@kici-dev/sdk';\n\n export default workflow('test-matrix', {\n on: pr({ target: 'main' }),\n jobs: [\n job('test', {\n runsOn: 'kici:os:linux',\n strategy: { matrix: matrix({ node: ['20', '22', '24'] }) },\n steps: [\n step('test', async ({ $, matrix }) => {\n await $`node --version`;\n await $`pnpm install`;\n await $`pnpm test`;\n return { node: matrix.node };\n }),\n ],\n }),\n ],\n });\n ```\n\n3. **Lifecycle hooks** \u2014 `onFailure` / `onSuccess` / `onCancel` on a\n job or workflow run after the main steps in their own scope.\n\n4. **Secrets** \u2014 declared scopes resolve at dispatch:\n\n ```ts\n step('deploy', async ({ $, secrets }) => {\n await $`./scripts/deploy.sh`.env({ DEPLOY_TOKEN: secrets.production.DEPLOY_TOKEN });\n });\n ```\n\n Run `kici secrets list` to enumerate the contexts available for testing.\n\n5. **Dynamic jobs** \u2014 `dynamicJob` and `dynamicGroup` build the DAG at\n runtime from a step's outputs. Don't try to compute job names at top level;\n the lock file would be wrong.\n\n## Anti-patterns\n\n- **Do NOT write `.yml` / `.yaml` CI files** \u2014 KiCI replaces that entire\n layer. There is no compatibility shim.\n- **Do NOT `import` from any `@kici-dev/*` package's `/dist/...`\n subpath** \u2014 those are not part of the public API and break across versions.\n Import from the package root.\n- **Do NOT `await` outside step bodies.** The top-level workflow file is\n loaded by the compiler synchronously; async I/O at module scope means the\n lock file emits before it resolves and the workflow appears empty.\n- **Do NOT mutate shared variables between jobs.** Each job runs in its own\n agent process. Use `needs` + step outputs to thread values.\n- **Do NOT hand-edit `kici.lock.json`.** Regenerate it via `kici compile`.\n\n## Local commands a coding agent should run\n\n| Command | Purpose |\n| ------------------------------- | ------------------------------------------- |\n| `pnpm kici compile --check` | Validate workflow source without writing. |\n| `pnpm kici preview pr:open --debug` | Preview which workflows match an event. |\n| `pnpm kici run local push` | Execute workflows locally with no orchestrator. |\n| `pnpm kici docs llm` | Print the full LLM documentation bundle. |\n| `pnpm kici docs llm --index` | Print the curated link index. |\n\nIf `pnpm kici` isn't in scripts, fall back to `npx kici`.\n\n## Loop\n\n1. Read the SDK types from `node_modules/@kici-dev/sdk/dist/index.d.ts`.\n2. Pipe `kici docs llm` into the agent's context if it doesn't already have\n the full bundle.\n3. Edit a workflow under `.kici/workflows/`.\n4. Run `kici compile --check` (zero exit means valid).\n5. Run `kici preview <event>` to preview matching.\n6. Run `kici run local <event>` to execute locally before pushing.\n";
8
8
  //# sourceMappingURL=agents-md.d.ts.map
@@ -112,7 +112,7 @@ into a portable lock file, and executed by self-hosted agents.
112
112
  | Command | Purpose |
113
113
  | ------------------------------- | ------------------------------------------- |
114
114
  | \`pnpm kici compile --check\` | Validate workflow source without writing. |
115
- | \`pnpm kici test pr:open --debug\`| Preview which workflows match an event. |
115
+ | \`pnpm kici preview pr:open --debug\` | Preview which workflows match an event. |
116
116
  | \`pnpm kici run local push\` | Execute workflows locally with no orchestrator. |
117
117
  | \`pnpm kici docs llm\` | Print the full LLM documentation bundle. |
118
118
  | \`pnpm kici docs llm --index\` | Print the curated link index. |
@@ -126,7 +126,7 @@ If \`pnpm kici\` isn't in scripts, fall back to \`npx kici\`.
126
126
  the full bundle.
127
127
  3. Edit a workflow under \`.kici/workflows/\`.
128
128
  4. Run \`kici compile --check\` (zero exit means valid).
129
- 5. Run \`kici test <event>\` to preview matching.
129
+ 5. Run \`kici preview <event>\` to preview matching.
130
130
  6. Run \`kici run local <event>\` to execute locally before pushing.
131
131
  `;
132
132
  //#endregion
@@ -1,6 +1,6 @@
1
1
  import "../chunk-BTugEXQM.js";
2
2
  //#region src/templates/package-json.ts
3
- const sdkVersion = "0.1.23";
3
+ const sdkVersion = "0.1.24";
4
4
  /**
5
5
  * Generate package.json content for .kici/ directory
6
6
  *
@@ -11,5 +11,5 @@ import type { StepContext, WorkflowInfo, JobInfo, MatrixValues } from '@kici-dev
11
11
  export declare function createStepContext(workflowInfo: WorkflowInfo, jobInfo: JobInfo, repoRoot: string, inputs?: Record<string, unknown>, matrix?: MatrixValues, testSecrets?: {
12
12
  flat: Record<string, string>;
13
13
  contexts: Record<string, Record<string, string>>;
14
- }, environment?: string, rawPayload?: Record<string, unknown>, provider?: string, dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>): StepContext;
14
+ }, environment?: string, rawPayload?: Record<string, unknown>, provider?: string, dispatchInputs?: Readonly<Record<string, string | number | boolean | null>>, signal?: AbortSignal): StepContext;
15
15
  //# sourceMappingURL=step-context.d.ts.map
@@ -42,7 +42,7 @@ function createTestLogger(jobName) {
42
42
  * `ctx.$` would inherit `process.cwd()` — i.e. wherever the user invoked
43
43
  * `kici` — which silently breaks any step that uses relative paths.
44
44
  */
45
- function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix, testSecrets, environment, rawPayload, provider, dispatchInputs = {}) {
45
+ function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix, testSecrets, environment, rawPayload, provider, dispatchInputs = {}, signal = new AbortController().signal) {
46
46
  const flat = testSecrets?.flat ?? {};
47
47
  const namespacedSecrets = testSecrets?.contexts ?? {};
48
48
  const mergedFlat = { ...flat };
@@ -99,6 +99,7 @@ function createStepContext(workflowInfo, jobInfo, repoRoot, inputs = {}, matrix,
99
99
  return {
100
100
  $: scoped$,
101
101
  log: createTestLogger(jobInfo.name),
102
+ signal,
102
103
  env,
103
104
  setEnv: (key, value) => {
104
105
  env[key] = value;
package/dist/types.d.ts CHANGED
@@ -28,7 +28,7 @@ export interface LockApproval {
28
28
  readonly when: 'always' | 'drift';
29
29
  }
30
30
  /** Schema version - re-exported from engine as single source of truth */
31
- export declare const SCHEMA_VERSION: 26;
31
+ export declare const SCHEMA_VERSION: 29;
32
32
  /**
33
33
  * Source file reference with meaningful path.
34
34
  * Format: file is relative path from git root, export uses hash syntax.
@@ -275,6 +275,8 @@ export interface LockScheduleTrigger {
275
275
  readonly cronExpression: string;
276
276
  readonly timezone: string;
277
277
  readonly description?: string;
278
+ /** Typed schedule-input descriptors (from `schedule({ inputs })`); defaults-only. */
279
+ readonly inputs?: InputsDescriptorMap;
278
280
  }
279
281
  /**
280
282
  * Lifecycle trigger in lock file.
@@ -319,6 +321,8 @@ export interface LockRule {
319
321
  * Minimal representation - agents load full step functions from source.
320
322
  */
321
323
  export interface LockStep {
324
+ /** Discriminator for the step union; absent/`'sequential'` is an ordinary step. */
325
+ readonly kind?: 'sequential';
322
326
  readonly name: string;
323
327
  readonly hasOutputs: boolean;
324
328
  /** When true, job proceeds even if this step fails. */
@@ -353,6 +357,24 @@ export interface LockStep {
353
357
  /** Normalized approval gate; when set the step pauses for a human approval. */
354
358
  readonly approval?: LockApproval;
355
359
  }
360
+ /**
361
+ * A concurrent group of sequential steps in the lock file. Children run
362
+ * concurrently behind a join barrier; the group wrapper itself consumes no
363
+ * flat step index (its children carry the indices). No nesting is allowed.
364
+ */
365
+ export interface LockParallelStep {
366
+ readonly kind: 'parallel';
367
+ readonly name: string;
368
+ /** Cancel in-flight siblings when the first child fails (default true). */
369
+ readonly failFast: boolean;
370
+ /** Maximum children running at once; queued children report `pending`. */
371
+ readonly maxParallel?: number;
372
+ readonly children: readonly LockStep[];
373
+ }
374
+ /** A lock-file entry in a job's `steps` array: an ordinary step or a parallel group. */
375
+ export type LockStepEntry = LockStep | LockParallelStep;
376
+ /** Type guard distinguishing a parallel group from an ordinary lock step. */
377
+ export declare function isLockParallelStep(entry: LockStepEntry): entry is LockParallelStep;
356
378
  /**
357
379
  * Inline expression value for pure dynamic functions.
358
380
  * The compiler serializes pure functions as { _type: 'inline', expression: '(event) => ...' }
@@ -403,7 +425,7 @@ export interface LockJob {
403
425
  readonly needs: readonly (string | LockNeedsEntry | LockNeedsGroupEntry)[];
404
426
  /** Group names this job depends on (populated from dynamicGroup refs). */
405
427
  readonly dependsOnGroups?: readonly string[];
406
- readonly steps: readonly LockStep[];
428
+ readonly steps: readonly LockStepEntry[];
407
429
  readonly matrix?: LockMatrix;
408
430
  readonly include?: readonly Record<string, string>[];
409
431
  readonly exclude?: readonly Record<string, string>[];
@@ -418,10 +440,15 @@ export interface LockJob {
418
440
  image: string;
419
441
  env?: Record<string, string>;
420
442
  };
421
- /** Deployment environment name (static string) or inline expression (pure function). */
422
- readonly environment?: string | LockInlineValue;
423
- /** When true, environment is dynamic (function) -- resolved at orchestrator two-phase eval or inline. */
424
- readonly dynamicEnvironment?: boolean;
443
+ /**
444
+ * Deployment environments in merge order. Each entry is a static name or inline
445
+ * expression (pure function); `dynamic` is set when it is a function resolved at
446
+ * two-phase eval. Later entries override earlier ones on name collisions.
447
+ */
448
+ readonly environments?: ReadonlyArray<{
449
+ value: string | LockInlineValue;
450
+ dynamic: boolean;
451
+ }>;
425
452
  /** Static environment variables or inline expression (pure function). */
426
453
  readonly env?: Record<string, string> | LockInlineValue;
427
454
  /** When true, env is dynamic (function) -- resolved at orchestrator two-phase eval or inline. */
package/dist/types.js CHANGED
@@ -17,6 +17,10 @@ import { SCHEMA_VERSION as SCHEMA_VERSION$1 } from "@kici-dev/engine";
17
17
  */
18
18
  /** Schema version - re-exported from engine as single source of truth */
19
19
  const SCHEMA_VERSION = SCHEMA_VERSION$1;
20
+ /** Type guard distinguishing a parallel group from an ordinary lock step. */
21
+ function isLockParallelStep(entry) {
22
+ return entry.kind === "parallel";
23
+ }
20
24
  /** Type guard for inline expression values */
21
25
  function isLockInlineValue(value) {
22
26
  return typeof value === "object" && value !== null && value._type === "inline";
@@ -30,6 +34,6 @@ function isLockDynamicJobFn(job) {
30
34
  return job._type === "dynamic";
31
35
  }
32
36
  //#endregion
33
- export { SCHEMA_VERSION, isLockDynamicJobFn, isLockInlineValue, isLockStaticJob };
37
+ export { SCHEMA_VERSION, isLockDynamicJobFn, isLockInlineValue, isLockParallelStep, isLockStaticJob };
34
38
 
35
39
  //# sourceMappingURL=types.js.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kici-dev/compiler",
3
- "version": "0.1.23",
3
+ "version": "0.1.24",
4
4
  "description": "Compiler and CLI for KiCI workflows. Compiles `.kici/workflows/*.ts` to a `kici.lock.json` file consumed by the orchestrator and agents, and runs workflows locally or against a remote orchestrator.",
5
5
  "keywords": [
6
6
  "ci",
@@ -30,9 +30,6 @@
30
30
  "access": "public"
31
31
  },
32
32
  "type": "module",
33
- "bin": {
34
- "kici": "./dist/cli.js"
35
- },
36
33
  "files": [
37
34
  "dist",
38
35
  "!dist/**/*.map",
@@ -63,11 +60,11 @@
63
60
  "yaml": "^2.9.0",
64
61
  "zod": "^4.4.3",
65
62
  "zx": "^8.8.5",
66
- "@kici-dev/core": "0.1.23",
67
- "@kici-dev/engine": "0.1.23"
63
+ "@kici-dev/core": "0.1.24",
64
+ "@kici-dev/engine": "0.1.24"
68
65
  },
69
66
  "peerDependencies": {
70
- "@kici-dev/sdk": "0.1.23"
67
+ "@kici-dev/sdk": "0.1.24"
71
68
  },
72
69
  "devDependencies": {
73
70
  "@types/proper-lockfile": "^4.1.4"
package/sbom.spdx.json CHANGED
@@ -2,10 +2,10 @@
2
2
  "spdxVersion": "SPDX-2.3",
3
3
  "dataLicense": "CC0-1.0",
4
4
  "SPDXID": "SPDXRef-DOCUMENT",
5
- "name": "@kici-dev/compiler@0.1.23",
6
- "documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fcompiler/0.1.23/d56984d9-446e-4527-842c-a90a4a7c9a78",
5
+ "name": "@kici-dev/compiler@0.1.24",
6
+ "documentNamespace": "https://kici.dev/sbom/%40kici-dev%2Fcompiler/0.1.24/5972016c-419a-4b37-a318-6beead684099",
7
7
  "creationInfo": {
8
- "created": "2026-06-25T11:12:53Z",
8
+ "created": "2026-06-28T16:24:34Z",
9
9
  "creators": [
10
10
  "Tool: kici-sbom-generator"
11
11
  ]
@@ -484,7 +484,7 @@
484
484
  {
485
485
  "SPDXID": "SPDXRef-RootPackage",
486
486
  "name": "@kici-dev/compiler",
487
- "versionInfo": "0.1.23",
487
+ "versionInfo": "0.1.24",
488
488
  "downloadLocation": "NOASSERTION",
489
489
  "filesAnalyzed": false,
490
490
  "licenseConcluded": "NOASSERTION",
@@ -495,16 +495,16 @@
495
495
  {
496
496
  "referenceCategory": "PACKAGE-MANAGER",
497
497
  "referenceType": "purl",
498
- "referenceLocator": "pkg:npm/%40kici-dev/compiler@0.1.23"
498
+ "referenceLocator": "pkg:npm/%40kici-dev/compiler@0.1.24"
499
499
  }
500
500
  ],
501
501
  "description": "Compiler and CLI for KiCI workflows. Compiles `.kici/workflows/*.ts` to a `kici.lock.json` file consumed by the orchestrator and agents, and runs workflows locally or against a remote orchestrator.",
502
502
  "homepage": "https://kici.dev"
503
503
  },
504
504
  {
505
- "SPDXID": "SPDXRef-Package--kici-dev-core-0.1.23",
505
+ "SPDXID": "SPDXRef-Package--kici-dev-core-0.1.24",
506
506
  "name": "@kici-dev/core",
507
- "versionInfo": "0.1.23",
507
+ "versionInfo": "0.1.24",
508
508
  "downloadLocation": "NOASSERTION",
509
509
  "filesAnalyzed": false,
510
510
  "licenseConcluded": "NOASSERTION",
@@ -515,16 +515,16 @@
515
515
  {
516
516
  "referenceCategory": "PACKAGE-MANAGER",
517
517
  "referenceType": "purl",
518
- "referenceLocator": "pkg:npm/%40kici-dev/core@0.1.23"
518
+ "referenceLocator": "pkg:npm/%40kici-dev/core@0.1.24"
519
519
  }
520
520
  ],
521
521
  "description": "Light shared utilities for the KiCI stack (logging, errors, formatting, crypto, zx init, the TypeScript ESM loader hook). No server-side dependencies.",
522
522
  "homepage": "https://kici.dev"
523
523
  },
524
524
  {
525
- "SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.23",
525
+ "SPDXID": "SPDXRef-Package--kici-dev-engine-0.1.24",
526
526
  "name": "@kici-dev/engine",
527
- "versionInfo": "0.1.23",
527
+ "versionInfo": "0.1.24",
528
528
  "downloadLocation": "NOASSERTION",
529
529
  "filesAnalyzed": false,
530
530
  "licenseConcluded": "NOASSERTION",
@@ -535,16 +535,16 @@
535
535
  {
536
536
  "referenceCategory": "PACKAGE-MANAGER",
537
537
  "referenceType": "purl",
538
- "referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.23"
538
+ "referenceLocator": "pkg:npm/%40kici-dev/engine@0.1.24"
539
539
  }
540
540
  ],
541
541
  "description": "Shared business logic for the KiCI CI/CD stack: protocol, triggers, state machine, and provider interfaces used by the Platform relay, orchestrator, and compiler.",
542
542
  "homepage": "https://kici.dev"
543
543
  },
544
544
  {
545
- "SPDXID": "SPDXRef-Package--kici-dev-sdk-0.1.23",
545
+ "SPDXID": "SPDXRef-Package--kici-dev-sdk-0.1.24",
546
546
  "name": "@kici-dev/sdk",
547
- "versionInfo": "0.1.23",
547
+ "versionInfo": "0.1.24",
548
548
  "downloadLocation": "NOASSERTION",
549
549
  "filesAnalyzed": false,
550
550
  "licenseConcluded": "NOASSERTION",
@@ -555,7 +555,7 @@
555
555
  {
556
556
  "referenceCategory": "PACKAGE-MANAGER",
557
557
  "referenceType": "purl",
558
- "referenceLocator": "pkg:npm/%40kici-dev/sdk@0.1.23"
558
+ "referenceLocator": "pkg:npm/%40kici-dev/sdk@0.1.24"
559
559
  }
560
560
  ],
561
561
  "description": "TypeScript SDK for defining KiCI workflows. Import into `.kici/workflows/*.ts` to declare workflows, jobs, steps, triggers, rules, and matrix configurations.",
@@ -3222,17 +3222,17 @@
3222
3222
  },
3223
3223
  {
3224
3224
  "spdxElementId": "SPDXRef-RootPackage",
3225
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.23",
3225
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.24",
3226
3226
  "relationshipType": "DEPENDS_ON"
3227
3227
  },
3228
3228
  {
3229
3229
  "spdxElementId": "SPDXRef-RootPackage",
3230
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.23",
3230
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.24",
3231
3231
  "relationshipType": "DEPENDS_ON"
3232
3232
  },
3233
3233
  {
3234
3234
  "spdxElementId": "SPDXRef-RootPackage",
3235
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-sdk-0.1.23",
3235
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-sdk-0.1.24",
3236
3236
  "relationshipType": "DEPENDS_ON"
3237
3237
  },
3238
3238
  {
@@ -3301,82 +3301,82 @@
3301
3301
  "relationshipType": "DEPENDS_ON"
3302
3302
  },
3303
3303
  {
3304
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.23",
3304
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
3305
3305
  "relatedSpdxElement": "SPDXRef-Package-oxc-transform-0.135.0",
3306
3306
  "relationshipType": "DEPENDS_ON"
3307
3307
  },
3308
3308
  {
3309
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.23",
3309
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
3310
3310
  "relatedSpdxElement": "SPDXRef-Package-picocolors-1.1.1",
3311
3311
  "relationshipType": "DEPENDS_ON"
3312
3312
  },
3313
3313
  {
3314
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.23",
3314
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
3315
3315
  "relatedSpdxElement": "SPDXRef-Package-winston-daily-rotate-file-5.0.0",
3316
3316
  "relationshipType": "DEPENDS_ON"
3317
3317
  },
3318
3318
  {
3319
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.23",
3319
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
3320
3320
  "relatedSpdxElement": "SPDXRef-Package-winston-3.19.0",
3321
3321
  "relationshipType": "DEPENDS_ON"
3322
3322
  },
3323
3323
  {
3324
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.23",
3324
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
3325
3325
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
3326
3326
  "relationshipType": "DEPENDS_ON"
3327
3327
  },
3328
3328
  {
3329
- "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.23",
3329
+ "spdxElementId": "SPDXRef-Package--kici-dev-core-0.1.24",
3330
3330
  "relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
3331
3331
  "relationshipType": "DEPENDS_ON"
3332
3332
  },
3333
3333
  {
3334
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.23",
3334
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
3335
3335
  "relatedSpdxElement": "SPDXRef-Package-jose-6.2.3",
3336
3336
  "relationshipType": "DEPENDS_ON"
3337
3337
  },
3338
3338
  {
3339
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.23",
3339
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
3340
3340
  "relatedSpdxElement": "SPDXRef-Package-jsonpath-plus-10.4.0",
3341
3341
  "relationshipType": "DEPENDS_ON"
3342
3342
  },
3343
3343
  {
3344
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.23",
3344
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
3345
3345
  "relatedSpdxElement": "SPDXRef-Package-picomatch-4.0.4",
3346
3346
  "relationshipType": "DEPENDS_ON"
3347
3347
  },
3348
3348
  {
3349
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.23",
3349
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
3350
3350
  "relatedSpdxElement": "SPDXRef-Package-safe-regex-2.1.1",
3351
3351
  "relationshipType": "DEPENDS_ON"
3352
3352
  },
3353
3353
  {
3354
- "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.23",
3354
+ "spdxElementId": "SPDXRef-Package--kici-dev-engine-0.1.24",
3355
3355
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
3356
3356
  "relationshipType": "DEPENDS_ON"
3357
3357
  },
3358
3358
  {
3359
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.23",
3360
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.23",
3359
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
3360
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-core-0.1.24",
3361
3361
  "relationshipType": "DEPENDS_ON"
3362
3362
  },
3363
3363
  {
3364
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.23",
3365
- "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.23",
3364
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
3365
+ "relatedSpdxElement": "SPDXRef-Package--kici-dev-engine-0.1.24",
3366
3366
  "relationshipType": "DEPENDS_ON"
3367
3367
  },
3368
3368
  {
3369
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.23",
3369
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
3370
3370
  "relatedSpdxElement": "SPDXRef-Package-micromatch-4.0.8",
3371
3371
  "relationshipType": "DEPENDS_ON"
3372
3372
  },
3373
3373
  {
3374
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.23",
3374
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
3375
3375
  "relatedSpdxElement": "SPDXRef-Package-zod-4.4.3",
3376
3376
  "relationshipType": "DEPENDS_ON"
3377
3377
  },
3378
3378
  {
3379
- "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.23",
3379
+ "spdxElementId": "SPDXRef-Package--kici-dev-sdk-0.1.24",
3380
3380
  "relatedSpdxElement": "SPDXRef-Package-zx-8.8.5",
3381
3381
  "relationshipType": "DEPENDS_ON"
3382
3382
  },