@kici-dev/compiler 0.3.0 → 0.5.0

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 (36) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/commands/compile.js +1 -13
  3. package/dist/commands/preview.js +1 -8
  4. package/dist/commands/types.js +1 -2
  5. package/dist/errors/formatter.d.ts +2 -4
  6. package/dist/errors/formatter.js +1 -3
  7. package/dist/errors/index.d.ts +1 -1
  8. package/dist/errors/index.js +2 -2
  9. package/dist/generators/secrets-dts.d.ts +0 -1
  10. package/dist/generators/secrets-dts.js +1 -2
  11. package/dist/llm-context/llms-architecture.txt +30 -14
  12. package/dist/llm-context/llms-cli.txt +26 -6
  13. package/dist/llm-context/llms-features.txt +272 -91
  14. package/dist/llm-context/llms-full.txt +652 -236
  15. package/dist/llm-context/llms-getting-started.txt +20 -20
  16. package/dist/llm-context/llms-patterns.txt +10 -6
  17. package/dist/llm-context/llms-providers.txt +4 -6
  18. package/dist/llm-context/llms-sdk-runtime.txt +37 -36
  19. package/dist/llm-context/llms-sdk.txt +253 -57
  20. package/dist/llm-context/llms.txt +6 -6
  21. package/dist/local-plane/plane-manager.js +2 -2
  22. package/dist/lockfile/generator.js +137 -42
  23. package/dist/lockfile/index.d.ts +0 -2
  24. package/dist/lockfile/index.js +1 -2
  25. package/dist/templates/package-json.js +1 -1
  26. package/dist/test-runner/dry-run.d.ts +1 -2
  27. package/dist/test-runner/dry-run.js +1 -18
  28. package/dist/types.d.ts +32 -8
  29. package/dist/types.js +7 -1
  30. package/dist/validation/validator.js +40 -0
  31. package/package.json +6 -6
  32. package/sbom.spdx.json +126 -121
  33. package/dist/lockfile/purity-analyzer.d.ts +0 -25
  34. package/dist/lockfile/purity-analyzer.js +0 -204
  35. package/dist/lockfile/purity-diagnostics.d.ts +0 -31
  36. package/dist/lockfile/purity-diagnostics.js +0 -52
@@ -18,22 +18,24 @@ function workflow(name: string, options: WorkflowOptions): Workflow;
18
18
 
19
19
  **Parameters:**
20
20
 
21
- | Parameter | Type | Required | Description |
22
- | --------------------- | ---------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
- | `name` | `string` | yes | Unique workflow name |
24
- | `options.jobs` | `JobOrFactory[]` | yes | Static jobs and/or dynamic job generators |
25
- | `options.on` | `Trigger \| Trigger[]` | no | When the workflow should trigger |
26
- | `options.rules` | `Rule[]` | no | Conditions that must pass for execution |
27
- | `options.description` | `string` | no | Human-readable description |
28
- | `options.hashFiles` | `string[]` | no | Extra repo-relative paths or globs mixed into the workflow content hash. Changes invalidate the source cache. |
29
- | `options.registries` | `Registry[]` | no | Private npm registries the agent authenticates against before `npm install`. Each `tokenSecret` uses qualified `<context>:<secret>` syntax. |
30
- | `options.installEnv` | `string[]` | no | Qualified `<context>:<secret>` refs projected as env vars onto the install subprocess (used with a customer-committed `.kici/.npmrc`). |
31
- | `options.onCancel` | `HookInput` | no | Runs when the workflow is cancelled |
32
- | `options.cleanup` | `HookInput` | no | Always runs after the workflow (success, failure, or cancel) |
33
- | `options.onSuccess` | `HookInput` | no | Runs on workflow success |
34
- | `options.onFailure` | `HookInput` | no | Runs on workflow failure |
35
- | `options.concurrency` | `{ group: (ctx) => string; cancelInProgress?: boolean; max?: number }` | no | Workflow-scoped concurrency. See [Concurrency](https://docs.kici.dev/user/concurrency/). |
36
- | `options.timeout` | `number` | no | Whole-run wall-clock timeout in milliseconds across all jobs. On breach the orchestrator cancels the run and marks it timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
21
+ | Parameter | Type | Required | Description |
22
+ | --------------------- | ---------------------------------------------------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
23
+ | `name` | `string` | yes | Unique workflow name |
24
+ | `options.jobs` | `JobOrFactory[]` | yes | Static jobs and/or dynamic job generators |
25
+ | `options.on` | `Trigger \| Trigger[]` | no | When the workflow should trigger |
26
+ | `options.rules` | `Rule[]` | no | Conditions that must pass for execution |
27
+ | `options.filter` | `FilterFn` | no | Pre-dispatch predicate deciding whether the workflow applies to the event's source repo. A `false` result suppresses the workflow's jobs -- none is dispatched and none is reported as skipped. See [Global workflows](https://docs.kici.dev/user/global-workflows/). |
28
+ | `options.description` | `string` | no | Human-readable description |
29
+ | `options.hashFiles` | `string[]` | no | Extra repo-relative paths or globs mixed into the workflow content hash. Changes invalidate the source cache. |
30
+ | `options.registries` | `Registry[]` | no | Private npm registries the agent authenticates against before `npm install`. Each `tokenSecret` uses qualified `<context>:<secret>` syntax. |
31
+ | `options.installEnv` | `string[]` | no | Qualified `<context>:<secret>` refs projected as env vars onto the install subprocess (used with a customer-committed `.kici/.npmrc`). |
32
+ | `options.onCancel` | `HookInput` | no | Runs when the workflow is cancelled |
33
+ | `options.cleanup` | `HookInput` | no | Always runs after the workflow (success, failure, or cancel) |
34
+ | `options.onSuccess` | `HookInput` | no | Runs on workflow success |
35
+ | `options.onFailure` | `HookInput` | no | Runs on workflow failure |
36
+ | `options.concurrency` | `{ group: (ctx) => string; cancelInProgress?: boolean; max?: number }` | no | Workflow-scoped concurrency. See [Concurrency](https://docs.kici.dev/user/concurrency/). |
37
+ | `options.timeout` | `number` | no | Whole-run wall-clock timeout in milliseconds across all jobs. On breach the orchestrator cancels the run and marks it timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
38
+ | `options.approval` | `ApprovalConfig` | no | Pause for a manual human approval before the whole workflow dispatches. See [Approval gates](https://docs.kici.dev/user/approvals/). |
37
39
 
38
40
  **Returns:** `Workflow` -- an immutable workflow definition.
39
41
 
@@ -59,34 +61,42 @@ function job(options: JobOptions): Job;
59
61
 
60
62
  **Parameters:**
61
63
 
62
- | Parameter | Type | Required | Description |
63
- | -------------------------- | ---------------------------------------------------------------------- | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
64
- | `name` | `string` | no | Job name (auto-generated UUID if omitted) |
65
- | `options.runsOn` | `RunsOn` | yes | Runner label(s) and optional exclusions (see below) |
66
- | `options.steps` | `StepInput[]` | yes (or use `run`) | Steps to execute in order. Mutually exclusive with `run`. |
67
- | `options.run` | `(ctx) => Promise<unknown>` | yes (or use `steps`) | Single-step shorthand -- see [Single-step job shorthand](https://docs.kici.dev/user/sdk/core/#single-step-job-shorthand). Mutually exclusive with `steps`. |
68
- | `options.needs` | `NeedsEntry[]` | no | Job dependencies (must complete first) -- see [Job dependencies (`needs`)](https://docs.kici.dev/user/sdk/core/#job-dependencies-needs) |
69
- | `options.rules` | `Rule[]` | no | Conditions for conditional execution |
70
- | `options.description` | `string` | no | Human-readable description |
71
- | `options.matrix` | `Matrix` | no | Matrix configuration for job expansion |
72
- | `options.include` | `MatrixInclude[]` | no | Additional matrix combinations |
73
- | `options.exclude` | `MatrixExclude[]` | no | Matrix combinations to remove |
74
- | `options.checkout` | `boolean` | no (default: `true`) | When `false`, agent skips git clone. Useful for deploy/notify jobs. |
75
- | `options.container` | `string \| ContainerConfig` | no | Docker image for job execution. String form is the image name; object form adds `env`. All steps run inside the container. |
76
- | `options.sandbox` | `{ capabilities?: string[]; network?: 'default' \| 'none' \| 'host' }` | no | Per-job container sandbox escape hatch (container jobs only). Request extra Linux capabilities / host networking; granted only within your operator's allow-list, else the run fails at dispatch. See below. |
77
- | `options.environment` | `string \| ((event) => string \| Promise<string>)` | no | Deployment environment for this job. Static string or async/dynamic function -- see [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
78
- | `options.env` | `Record<string, string> \| ((event) => Record<string, string>)` | no | Environment variables. Static object or async/dynamic function -- see [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
79
- | `options.concurrencyGroup` | `string \| ((event) => string \| Promise<string>)` | no | Concurrency group name (defaults to environment name) -- see [Concurrency](https://docs.kici.dev/user/concurrency/). |
80
- | `options.onCancel` | `HookInput` | no | Hook that runs when the job is cancelled |
81
- | `options.cleanup` | `HookInput` | no | Hook that always runs after completion |
82
- | `options.onSuccess` | `HookInput` | no | Hook that runs when the job succeeds |
83
- | `options.onFailure` | `HookInput` | no | Hook that runs when the job fails |
84
- | `options.beforeStep` | `HookInput` | no | Hook that runs before each step |
85
- | `options.afterStep` | `HookInput` | no | Hook that runs after each step |
86
- | `options.gracePeriod` | `number` | no | Seconds before SIGKILL after SIGTERM during cancellation -- see [Hooks](https://docs.kici.dev/user/hooks/#hook-timeout). |
87
- | `options.timeout` | `number` | no | Total job wall-clock timeout in milliseconds (init + all steps + hooks). On breach the job is aborted and reported timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
88
- | `options.resources` | `ResourceRequest` | no | Per-job CPU / memory request and limit. See [Per-job resources](https://docs.kici.dev/user/sdk/core/#per-job-resources) below. |
89
- | `options.init` | `InitConfig` | no | Per-job initialization run after clone, before steps -- provisions a toolchain. A generic config, a typed preset (`'mise'` / `{ mise }`), `'auto'`, or `false`. See [Per-job init](https://docs.kici.dev/user/sdk/core/#per-job-init) below. |
64
+ | Parameter | Type | Required | Description |
65
+ | ------------------------------ | -------------------------------------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
66
+ | `name` | `string` | no | Job name (auto-generated UUID if omitted) |
67
+ | `options.runsOn` | `RunsOn` | yes (or `runsOnAll`) | Single-agent targeting -- runner label(s) and optional exclusions (see below). Mutually exclusive with `runsOnAll`. |
68
+ | `options.runsOnAll` | `RunsOnAllInput` | yes (or `runsOn`) | Host fan-out -- one pinned execution per roster host matching the predicate. Mutually exclusive with `runsOn`. See [runsOnAll host fan-out](https://docs.kici.dev/user/sdk/runs-on-all/). |
69
+ | `options.onUnreachable` | `'skip' \| 'fail' \| 'hold'` | no (default: `hold`) | Failure policy for unreachable durable hosts. `skip` omits them, `fail` fails the run, `hold` queues a pinned child and waits. Only meaningful alongside `runsOnAll`. |
70
+ | `options.includeUninitialized` | `boolean` | no (default: `false`) | Widen a `runsOnAll` fan-out to declared-but-un-agented hosts -- a matching host with no live agent gets a temporary init-runner brought up over SSH. Only meaningful alongside `runsOnAll`. |
71
+ | `options.steps` | `StepInput[]` | yes (or use `run`) | Steps to execute in order. Mutually exclusive with `run`. |
72
+ | `options.run` | `(ctx) => Promise<unknown>` | yes (or use `steps`) | Single-step shorthand -- see [Single-step job shorthand](https://docs.kici.dev/user/sdk/core/#single-step-job-shorthand). Mutually exclusive with `steps`. |
73
+ | `options.needs` | `Array<Job \| string \| DynamicGroupRef \| { name; when? } \| { group; when? }>` | no | Job dependencies (must complete first) -- see [Job dependencies (`needs`)](https://docs.kici.dev/user/sdk/core/#job-dependencies-needs) |
74
+ | `options.rules` | `Rule[]` | no | Conditions for conditional execution |
75
+ | `options.description` | `string` | no | Human-readable description |
76
+ | `options.matrix` | `Matrix` | no | Matrix configuration for job expansion |
77
+ | `options.include` | `MatrixInclude[]` | no | Additional matrix combinations |
78
+ | `options.exclude` | `MatrixExclude[]` | no | Matrix combinations to remove |
79
+ | `options.maxParallel` | `number` | no | Fan-out concurrency width -- the maximum number of fan-out children (matrix combinations or `runsOnAll` hosts) running at once. A sliding window; `1` is strictly serial. Must be `>= 1`. |
80
+ | `options.failFast` | `boolean` | no (default: `false`) | Halt the fan-out on the first child failure: stop releasing new children and skip the ones still held. Applies to both matrix and `runsOnAll` fan-out. |
81
+ | `options.checkout` | `boolean` | no (default: `true`) | When `false`, agent skips git clone. Useful for deploy/notify jobs. |
82
+ | `options.container` | `string \| ContainerConfig` | no | Docker image for job execution. String form is the image name; object form adds `env`. All steps run inside the container. |
83
+ | `options.sandbox` | `{ capabilities?: string[]; network?: 'default' \| 'none' \| 'host' }` | no | Per-job container sandbox escape hatch (container jobs only). Request extra Linux capabilities / host networking; granted only within your operator's allow-list, else the run fails at dispatch. See below. |
84
+ | `options.context` | `string \| ((event) => string \| Promise<string>)` | no | Bound context for this job -- the secret / variable scope it resolves against. Static string or async/dynamic function -- see [Contexts](https://docs.kici.dev/user/contexts/) and [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
85
+ | `options.contexts` | `(string \| ((event) => string \| Promise<string>))[]` | no | Bound contexts in merge order (later entries override earlier on name collisions). Mutually exclusive with `context`. |
86
+ | `options.env` | `Record<string, string> \| ((event) => Record<string, string>)` | no | Environment variables. Static object or async/dynamic function -- see [Dynamic values](https://docs.kici.dev/user/dynamic-values/). |
87
+ | `options.concurrencyGroup` | `string \| ((event) => string \| Promise<string>)` | no | Concurrency group name. Defaults to the first bound context's name -- see [Concurrency](https://docs.kici.dev/user/concurrency/). |
88
+ | `options.onCancel` | `HookInput` | no | Hook that runs when the job is cancelled |
89
+ | `options.cleanup` | `HookInput` | no | Hook that always runs after completion |
90
+ | `options.onSuccess` | `HookInput` | no | Hook that runs when the job succeeds |
91
+ | `options.onFailure` | `HookInput` | no | Hook that runs when the job fails |
92
+ | `options.beforeStep` | `HookInput` | no | Hook that runs before each step |
93
+ | `options.afterStep` | `HookInput` | no | Hook that runs after each step |
94
+ | `options.gracePeriod` | `number` | no | Seconds before SIGKILL after SIGTERM during cancellation -- see [Hooks](https://docs.kici.dev/user/hooks/#hook-timeout). |
95
+ | `options.timeout` | `number` | no | Total job wall-clock timeout in milliseconds (init + all steps + hooks). On breach the job is aborted and reported timed out. See [Timeouts](https://docs.kici.dev/user/sdk/core/#timeouts). |
96
+ | `options.resources` | `ResourceRequest` | no | Per-job CPU / memory request and limit. See [Per-job resources](https://docs.kici.dev/user/sdk/core/#per-job-resources) below. |
97
+ | `options.init` | `InitConfig` | no | Per-job initialization run after clone, before steps -- provisions a toolchain. A generic config, a typed preset (`'mise'` / `{ mise }`), `'auto'`, or `false`. See [Per-job init](https://docs.kici.dev/user/sdk/core/#per-job-init) below. |
98
+ | `options.cache` | `CacheInput` | no | Declarative cache: restored before steps, saved after the job on a key miss. See [Caching](https://docs.kici.dev/user/sdk/caching/). |
99
+ | `options.approval` | `ApprovalConfig` | no | Pause for a manual human approval before this job dispatches. See [Approval gates](https://docs.kici.dev/user/approvals/). |
90
100
 
91
101
  **Returns:** `Job` -- an immutable job definition.
92
102
 
@@ -1002,13 +1012,15 @@ type RuleCheckFn = (ctx: RuleContext) => Promise<boolean> | boolean;
1002
1012
 
1003
1013
  Can be sync or async. Receives a `RuleContext`:
1004
1014
 
1005
- | Property | Type | Description |
1006
- | -------------------- | ----------------------------------------- | --------------------------------------------------------------------- |
1007
- | `event` | `EventPayload` | The triggering event payload (discriminated union — narrow on `type`) |
1008
- | `changedFiles` | `string[]` | Files changed in this event (see availability note below) |
1009
- | `changedFilesStatus` | `'fetched' \| 'unavailable' \| 'skipped'` | Whether `changedFiles` is available |
1010
- | `env` | `Record<string, string\|undefined>` | Environment variables |
1011
- | `$` | zx shell | Shell executor for running commands |
1015
+ | Property | Type | Description |
1016
+ | -------------------- | ----------------------------------------- | ----------------------------------------------------------------------- |
1017
+ | `event` | `EventPayload` | The triggering event payload (discriminated union — narrow on `type`) |
1018
+ | `changedFiles` | `string[]` | Files changed in this event (see availability note below) |
1019
+ | `changedFilesStatus` | `'fetched' \| 'unavailable' \| 'skipped'` | Whether `changedFiles` is available |
1020
+ | `sourceRepo` | `RepoInfo \| undefined` | Repo whose event triggered the run, when the evaluation has a checkout |
1021
+ | `workflowRepo` | `RepoInfo \| undefined` | Repo that registered the workflow (same repo outside a global workflow) |
1022
+ | `env` | `Record<string, string\|undefined>` | Environment variables |
1023
+ | `$` | zx shell | Shell executor for running commands |
1012
1024
 
1013
1025
  `changedFiles` is available on `push` and `pull_request` events — the agent computes the diff from its checkout, so no `paths:` trigger is required. It is `unavailable` for events with no diff (`schedule`, `tag`, `manual_schedule`), and in the rare case where the diff cannot be computed (e.g. a history deeper than the agent's bounded fetch). Reading `changedFiles` when it is unavailable throws and fails the job, so guard with `changedFilesStatus` first when a rule can run on such events:
1014
1026
 
@@ -1019,6 +1031,8 @@ rule('has source changes', (ctx) => {
1019
1031
  });
1020
1032
  ```
1021
1033
 
1034
+ The throw is a `ChangedFilesUnavailableError` (exported from `@kici-dev/sdk`, carrying the `changedFilesStatus` and `eventType` that produced it). `evaluateRules()` deliberately re-throws it rather than folding it into a `passed=false` skip, so a path-based gate fails loudly instead of silently mis-evaluating.
1035
+
1022
1036
  ### evaluateRules(rules, context, label, onRuleResult?)
1023
1037
 
1024
1038
  Evaluate an array of rules sequentially with fail-fast behavior. Stops on the first failure.
@@ -1291,12 +1305,18 @@ type DynamicJobFn = (context: DynamicJobContext) => Promise<Job[]>;
1291
1305
 
1292
1306
  Receives a `DynamicJobContext`:
1293
1307
 
1294
- | Property | Type | Description |
1295
- | -------- | ----------------------------------- | --------------------------- |
1296
- | `$` | zx shell | Shell executor |
1297
- | `ctx` | `{ workflow, event? }` | Workflow metadata and event |
1298
- | `log` | `Logger` | Structured logger |
1299
- | `env` | `Record<string, string\|undefined>` | Environment variables |
1308
+ | Property | Type | Description |
1309
+ | -------------- | ----------------------------------- | ----------------------------------------------------------------------- |
1310
+ | `$` | zx shell | Shell executor |
1311
+ | `ctx` | `{ workflow, event? }` | Workflow metadata and event |
1312
+ | `log` | `Logger` | Structured logger |
1313
+ | `env` | `Record<string, string\|undefined>` | Environment variables |
1314
+ | `sourceRepo` | `RepoInfo \| undefined` | Repo whose event triggered the run, when the evaluation has a checkout |
1315
+ | `workflowRepo` | `RepoInfo \| undefined` | Repo that registered the workflow (same repo outside a global workflow) |
1316
+
1317
+ `RepoInfo` carries `path` — an absolute path to that repo's checkout — plus optional `ref` and `sha`; guard before reading either, since an event that carries no single ref leaves them undefined. In a [global workflow](https://docs.kici.dev/user/global-workflows/) `sourceRepo` and `workflowRepo` are different repos, so one generator can produce a different job set per source repo.
1318
+
1319
+ **`sourceRepo.path` is not stable across calls.** A generator is invoked once to discover the job set and again to extract the step closures of the job being run; both see the same tree at the same commit, but not necessarily the same path or even the same machine. Read _through_ it, and derive job names from what the tree contains — never from the path itself, or the second call produces different names and the run fails the determinism check.
1300
1320
 
1301
1321
  ```typescript
1302
1322
  const discoverJobs: DynamicJobFn = async ({ $ }) => {
@@ -1489,6 +1509,182 @@ workflow runs rather than being silently dropped, and the delivery is recorded
1489
1509
  as degraded. GitHub always provides an exact list; a transient API error fails
1490
1510
  loudly, not as empty.
1491
1511
 
1512
+ ### Content requirements (`requires`)
1513
+
1514
+ Where `paths` filters on **which files changed**, `requires` filters on **what
1515
+ those files contain**. It is a declarative filter on the `pr()`, `push()`, and
1516
+ `tag()` triggers: a list of queries over the bytes of named source files, read
1517
+ at the event's commit. The orchestrator evaluates it as pure data before
1518
+ dispatching — it reads only the referenced files, never clones the whole
1519
+ repository, and never runs any of your workflow code. A workflow whose `requires`
1520
+ does not pass is simply not dispatched.
1521
+
1522
+ Each entry is a `ContentRequirement`:
1523
+
1524
+ ```typescript
1525
+ interface ContentRequirement {
1526
+ file: string; // repo-relative path to query
1527
+ format?: 'auto' | 'json' | 'yaml' | 'text'; // how to parse the file (default: 'auto')
1528
+ exists?: string[]; // JSONPath expressions that must each resolve to ≥1 node (json/yaml)
1529
+ match?: Record<string, unknown>; // JSONPath → expected value; every one must match (json/yaml)
1530
+ not?: Record<string, unknown>; // JSONPath → value; passes only when NONE match (json/yaml)
1531
+ contains?: string | string[]; // literal substrings, all of which must appear (text only)
1532
+ notContains?: string | string[]; // literal substrings, none of which may appear (text only)
1533
+ matches?: string | RegExp | (string | RegExp)[]; // regexes, all of which must match (text only)
1534
+ notMatches?: string | RegExp | (string | RegExp)[]; // regexes, none of which may match (text only)
1535
+ ignoreCase?: boolean; // applies to contains/notContains only (default: false)
1536
+ absent?: boolean; // passes only when the file does NOT exist
1537
+ }
1538
+ ```
1539
+
1540
+ **Format.** `format: 'auto'` (the default) picks the parser by extension:
1541
+ `.json` → JSON, `.yaml` / `.yml` → YAML, everything else → text. Set `format`
1542
+ explicitly to override — e.g. treat an extensionless file as JSON, or read a
1543
+ `.json` file as raw text. JSON and YAML both parse to an object, so the JSONPath
1544
+ keys (`exists` / `match` / `not`) work identically over either; `text` files are
1545
+ queried by `contains`, `notContains`, `matches`, and `notMatches` over the raw
1546
+ bytes.
1547
+
1548
+ **Query keys.**
1549
+
1550
+ - **`exists`** — an array of JSONPath expressions; each must resolve to at least
1551
+ one node in the parsed document.
1552
+ - **`match`** — a JSONPath → expected-value map; every expression must match. An
1553
+ expected value is an exact value, a regex string in `/pattern/flags` form
1554
+ (against a string node), or an array of acceptable values (any one matches).
1555
+ - **`not`** — the same map shape, inverted: the entry passes only when **none** of
1556
+ the expressions match.
1557
+ - **`contains` / `notContains`** — literal substrings tested against the raw file
1558
+ text. Every entry must be present (`contains`) or absent (`notContains`). No
1559
+ escaping needed (text format only).
1560
+ - **`matches`** — one or several regexes (a `RegExp` or `/pattern/flags` string),
1561
+ each of which must match the raw file text (text format only).
1562
+ - **`notMatches`** — the inverse of `matches`: the entry passes only when none of
1563
+ the regexes match (text format only).
1564
+ - **`ignoreCase`** — case-insensitive `contains` / `notContains` only; a regex
1565
+ carries its own flags. Default false.
1566
+ - **`absent: true`** — passes only when the file does **not** exist at the event's
1567
+ commit. It is mutually exclusive with the query keys above.
1568
+ - A bare `{ file }` with no query key requires the file to **exist**.
1569
+
1570
+ The keys inside one entry are AND-ed, and the entries in a `requires` list are
1571
+ AND-ed with each other. An empty or absent `requires` matches everything, exactly
1572
+ like `paths`.
1573
+
1574
+ **Examples:**
1575
+
1576
+ ```typescript
1577
+ // Only run CI when package.json declares a `ci` script.
1578
+ push({ branches: 'main', requires: [{ file: 'package.json', exists: ['$.scripts.ci'] }] });
1579
+
1580
+ // Deploy only when the service config enables it (YAML, matched by value).
1581
+ push({
1582
+ branches: 'main',
1583
+ requires: [{ file: 'service.yaml', match: { '$.deploy.enabled': true } }],
1584
+ });
1585
+
1586
+ // Only run when the Dockerfile builds from a Node base image (raw-text regex).
1587
+ pr({ requires: [{ file: 'Dockerfile', format: 'text', matches: '/^FROM node:/m' }] });
1588
+
1589
+ // Skip the workflow whenever a repo carries an opt-out marker file.
1590
+ push({ requires: [{ file: '.skip-ci', absent: true }] });
1591
+
1592
+ // Combine filters: a tag build that requires a version file AND forbids a draft flag.
1593
+ tag({
1594
+ patterns: ['v*'],
1595
+ requires: [
1596
+ { file: 'VERSION', matches: '/^\\d+\\.\\d+\\.\\d+$/' },
1597
+ { file: 'release.json', not: { '$.draft': true } },
1598
+ ],
1599
+ });
1600
+ ```
1601
+
1602
+ **Fail-visible evaluation.** Files are read at the event's commit. If a
1603
+ referenced file is larger than **1 MiB**, or fails to parse for its format, the
1604
+ requirement is **indeterminate** — the candidate workflow is dropped and does
1605
+ **not** run. A `requires` that cannot be evaluated never silently passes.
1606
+
1607
+ **Compile-time validation.** `kici compile` rejects a malformed requirement before
1608
+ it ever reaches the orchestrator: a raw-text key that is invalid or catastrophic
1609
+ (ReDoS-prone) is rejected by a safe-regex check; a text file cannot carry a
1610
+ JSON/YAML query key (`exists` / `match` / `not`) and a JSON/YAML file cannot carry a
1611
+ raw-text key; `absent` cannot be combined with a query key; and an explicit
1612
+ `format` with no query key is rejected as having nothing to check.
1613
+
1614
+ ### Commit-message filters (`commitMessage`)
1615
+
1616
+ Where `requires` filters on what the repository's **files** contain,
1617
+ `commitMessage` filters on what the **event** says. It is a declarative filter on
1618
+ the `pr()`, `push()`, and `tag()` triggers. The orchestrator evaluates it
1619
+ directly from the webhook payload: no file is fetched, and no repository is
1620
+ cloned. For an organization-wide workflow it dispatches no evaluation job. It is
1621
+ the cheapest gate available.
1622
+
1623
+ The text it tests is the **full head-commit message** — subject and body — for
1624
+ `push` and `tag`, and the **title plus body** for pull-request events.
1625
+
1626
+ ```typescript
1627
+ interface TextMatch {
1628
+ contains?: string | string[]; // every needle must be present
1629
+ notContains?: string | string[]; // no needle may be present
1630
+ matches?: string | RegExp | (string | RegExp)[]; // every regex must match
1631
+ notMatches?: string | RegExp | (string | RegExp)[]; // no regex may match
1632
+ ignoreCase?: boolean; // applies to contains/notContains only (default: false)
1633
+ }
1634
+ ```
1635
+
1636
+ **Every entry in a list is a conjunct.** `contains: ['a', 'b']` passes only when
1637
+ the text contains both, and the keys AND together. To express OR, declare two
1638
+ triggers — a workflow's trigger list already matches on the first one that fits:
1639
+
1640
+ ```typescript
1641
+ // AND — one trigger.
1642
+ push({ commitMessage: { contains: ['release:', 'approved'] } });
1643
+
1644
+ // OR — two triggers.
1645
+ on: [
1646
+ push({ branches: 'main', commitMessage: { contains: 'deploy:' } }),
1647
+ push({ branches: 'main', commitMessage: { contains: 'release:' } }),
1648
+ ];
1649
+ ```
1650
+
1651
+ Needles are **literal substrings** — no glob, no regex, no escaping, so a needle
1652
+ containing `.*` matches only the literal `.*`. Use `matches` / `notMatches` for a
1653
+ pattern; both accept a `RegExp` literal, and the `m` flag reaches the body:
1654
+
1655
+ ```typescript
1656
+ // The single most common use: skip marker commits.
1657
+ push({ branches: 'main', commitMessage: { notContains: ['[skip ci]', '[ci skip]'] } });
1658
+
1659
+ // Ignore dependency-bump noise across an organization.
1660
+ push({ commitMessage: { notMatches: /^chore\(deps\):/ } });
1661
+
1662
+ // Require a conventional-commit prefix and forbid a WIP marker.
1663
+ pr({ target: 'main', commitMessage: { matches: /^(feat|fix)\(/, notContains: 'WIP' } });
1664
+
1665
+ // Match a trailer in the commit body.
1666
+ push({ commitMessage: { matches: /^Fixes: #\d+$/m } });
1667
+ ```
1668
+
1669
+ `ignoreCase` affects `contains` and `notContains` only — a regex already carries
1670
+ its own flags, so write `/^feat:/i` rather than expecting `ignoreCase` to reach
1671
+ it.
1672
+
1673
+ **Fail-visible evaluation.** Some events carry no message at all. A
1674
+ branch-deletion push has no head commit, and a self-hosted forge may publish
1675
+ none. The trigger then does **not** match, and the decision trace records it as
1676
+ `indeterminate` rather than as an exclusion. A `commitMessage` filter that cannot
1677
+ be evaluated never silently passes.
1678
+
1679
+ **Compile-time validation.** `kici compile` rejects a malformed matcher. It
1680
+ refuses:
1681
+
1682
+ - a matcher with no query key;
1683
+ - an `ignoreCase` that would affect nothing;
1684
+ - an empty needle list;
1685
+ - an empty-string needle (it would match every text);
1686
+ - a regex that is invalid or catastrophic (ReDoS-prone).
1687
+
1492
1688
  ### tag()
1493
1689
 
1494
1690
  Create a tag trigger. Returns a frozen `TagTriggerConfig`.
@@ -9,13 +9,13 @@ The full markdown bundle of every page indexed here is available at https://docs
9
9
  Each bundle below is a self-contained markdown file for one authoring task. Fetch only the one your task needs instead of the full bundle:
10
10
 
11
11
  - [getting-started](https://docs.kici.dev/llms-getting-started.txt) (51 KB) — Adopt KiCI: why it exists, how workflows execute, migrating from GitHub Actions, installing the SDK, and writing/compiling/testing your first workflow
12
- - [patterns](https://docs.kici.dev/llms-patterns.txt) (49 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
13
- - [sdk](https://docs.kici.dev/llms-sdk.txt) (110 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
12
+ - [patterns](https://docs.kici.dev/llms-patterns.txt) (50 KB) — Copy-paste workflow recipes: triggers, conditionals, matrix, scheduling, integrations
13
+ - [sdk](https://docs.kici.dev/llms-sdk.txt) (125 KB) — Core authoring API: workflow/job/step factories, triggers, rules, matrix, dynamic jobs, cross-job outputs
14
14
  - [sdk-runtime](https://docs.kici.dev/llms-sdk-runtime.txt) (112 KB) — Runtime and advanced authoring: step runtime context, event payloads, host fan-out, idempotent steps, caching, artifacts, waiting
15
- - [cli](https://docs.kici.dev/llms-cli.txt) (185 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
16
- - [features](https://docs.kici.dev/llms-features.txt) (135 KB) — Workflow features: concurrency, contexts, secrets, approvals, provenance, events
15
+ - [cli](https://docs.kici.dev/llms-cli.txt) (187 KB) — Running the CLI: compile, test, run local/remote, auth, hooks, lock-file drift
16
+ - [features](https://docs.kici.dev/llms-features.txt) (158 KB) — Workflow features: concurrency, contexts, secrets, approvals, provenance, events
17
17
  - [providers](https://docs.kici.dev/llms-providers.txt) (37 KB) — Connecting sources: GitHub App, universal-git (Forgejo/Gitea/GitLab), local file://
18
- - [architecture](https://docs.kici.dev/llms-architecture.txt) (92 KB) — How the runtime works: three-tier relay model, data flows, configuration
18
+ - [architecture](https://docs.kici.dev/llms-architecture.txt) (95 KB) — How the runtime works: three-tier relay model, data flows, configuration
19
19
 
20
20
  ## Getting started
21
21
 
@@ -77,7 +77,7 @@ Each bundle below is a self-contained markdown file for one authoring task. Fetc
77
77
  - [Concurrency groups](https://docs.kici.dev/user/concurrency/): Control parallel execution with auto-cancel and queue modes
78
78
  - [Contexts](https://docs.kici.dev/user/contexts/): Configure deployment contexts with variables, secrets, and protection rules
79
79
  - [Dashboard](https://docs.kici.dev/user/dashboard/): Web UI for monitoring workflow runs, managing sources, secrets, and organization settings.
80
- - [Dynamic values](https://docs.kici.dev/user/dynamic-values/)
80
+ - [Dynamic values](https://docs.kici.dev/user/dynamic-values/): Compute a job's context, env, and concurrencyGroup at runtime from the incoming event
81
81
  - [Environment variables](https://docs.kici.dev/user/env-vars/): Environment variable reference for the CLI
82
82
  - [Event system](https://docs.kici.dev/user/events/): How KiCI's event model works -- event types, the registration model, event matching, and circuit breaker protection
83
83
  - [Global workflows](https://docs.kici.dev/user/global-workflows/): Cross-repo workflows that run on events from any repo in the same org
@@ -19,8 +19,8 @@ import { generateKeyPairSync, randomBytes } from "node:crypto";
19
19
  */
20
20
  function currentBuildIdentity() {
21
21
  return {
22
- version: "0.3.0",
23
- buildCommit: "2e7dec998"
22
+ version: "0.5.0",
23
+ buildCommit: "cb51c7d1e"
24
24
  };
25
25
  }
26
26
  /**