clinkx 1.1.0 → 1.1.1

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.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,15 @@
1
1
  # Changelog
2
2
 
3
- ## Unreleased
3
+ ## 1.1.1 — 2026-10-05
4
+
5
+ ### Changed
6
+
7
+ - The npm package now ships `docs/configuration.md` and `docs/security.md`, and the README is rewritten as a self-contained, install-facing page in Simplified Technical English. `package.json` `homepage` points at the npm package page and `bugs` is removed, because the source repository is private.
8
+
9
+ ### Fixed
10
+
11
+ - `context_builder` secret scanning (`allow_secrets:false`) on large bodies is 6-8x faster on Node 24: comment masking now returns the input string when nothing is masked, so memo lookups no longer compare a distinct but equal copy of the whole body. Output is unchanged.
12
+ - npm publish confirmation now waits up to about eight minutes for npm to expose the new version instead of failing after a few seconds; 1.1.0's automated publish succeeded but failed confirmation and needed a manual re-run to go green.
4
13
 
5
14
  ## 1.1.0 — 2026-10-03
6
15
 
package/README.md CHANGED
@@ -1,146 +1,171 @@
1
1
  # ClinkX
2
2
 
3
- An MCP STDIO server with local tools for CLI delegation and workflow orchestration.
3
+ ClinkX is an MCP server that connects your AI coding assistant to other command-line AI agents. It runs Codex, Claude Code, agy, GLM, or Grok as child processes and returns their output as MCP tool results. You can send one task to one agent. You can run a council of many agents on one question. You can also build a context bundle for a later task.
4
4
 
5
- - `clink` runs a configured CLI adapter (agy, Codex, Claude, Grok, or anything you add) as a subprocess and returns the output.
6
- - `context_builder` writes a persisted context bundle from allowed local roots. Selection is deterministic by default. You can also run explicit curator and finalizer adapters, then the server validates and materializes locally.
7
- - `list_workflows`, `resume_workflow`, and `run_workflow` run multi-stage workflows.
5
+ ClinkX uses MCP over STDIO. It works with any MCP client that can start a local command.
8
6
 
9
- Adapters come from config. There is no hardcoded CLI allowlist. Field-level PAL compatibility is intentional.
7
+ ## Requirements
10
8
 
11
- ## Quick start
9
+ - Node.js 24.0.0 or later.
10
+ - At least one of these command-line tools on your `PATH`: `codex`, `claude`, `agy`, or `grok`. The `glm` adapter also uses the `claude` command.
11
+ - The `codex` command, if you run workflows. Workflows use Codex to select repository context before the first stage. See [Workflows](#workflows) for the opt-out switch.
12
12
 
13
- Node.js >= 24, plus at least one CLI adapter.
13
+ ## Installation
14
14
 
15
- ```bash
16
- npm install
17
- npm run build:all
18
- node dist/index.js
19
- ```
20
-
21
- stdout is MCP JSON-RPC only (one JSON object per line). Logs go to stderr.
15
+ Add ClinkX to the MCP server list of your client. The exact file and format depend on the client. This is the common JSON shape:
22
16
 
23
- Reviewed version bumps publish through CI after main checks pass. See
24
- [npm releases](docs/npm-releases.md) for trusted publisher setup, release policy,
25
- artifact verification, and recovery.
26
-
27
- Both STDIO servers detect parent death even when stdin never delivers EOF.
28
- Idle shutdown is separately opt-in: `CLINKX_SERVER_IDLE_TIMEOUT_SECONDS=1800`
29
- enables a 30-minute idle lifetime, while the default `0` preserves quiet
30
- sessions. Active work and response delivery prevent idle expiry. Enable it only
31
- for hosts that can reconnect; restart loses process-local session aliases and
32
- logs. See [STDIO server lifetime](docs/configuration.md#stdio-server-lifetime)
33
- for shutdown reasons and parent-wrapper limitations.
17
+ ```json
18
+ {
19
+ "mcpServers": {
20
+ "clinkx": {
21
+ "command": "npx",
22
+ "args": ["-y", "clinkx@latest"],
23
+ "env": {
24
+ "CLINKX_ALLOWED_ROOTS": "/path/to/your/workspace",
25
+ "CLINKX_WORKFLOWS_WORKSPACE_ROOT": "/path/to/your/workspace"
26
+ }
27
+ }
28
+ }
29
+ }
30
+ ```
34
31
 
35
- ## Local workflow dashboard
32
+ Set the two paths to the directory that tool calls can reference. Use absolute paths.
36
33
 
37
- After `npm run build:all`:
34
+ To install ClinkX globally, run this command. Then set `"command": "clinkx"` with no `args`:
38
35
 
39
36
  ```bash
40
- node dist/dashboard-cli.js --port 0
37
+ npm install -g clinkx
41
38
  ```
42
39
 
43
- Passkey auth is the default. The process binds `127.0.0.1` and `::1` on the same port and prints `http://localhost:<port>`. An empty credential store also prints one enrollment code. Register a passkey with that code, then sign in as a separate step. Credentials live under `CLINKX_WORKFLOWS_DASHBOARD_CREDENTIAL_DIR` (default `${CLINKX_WORKFLOWS_STATE_DIR}/dashboard/webauthn`). Sessions die on restart.
40
+ Restart your MCP client after you change its configuration. To control when upgrades happen, replace `clinkx@latest` with a fixed version.
44
41
 
45
- Screens: Runs, Single Calls, Context Runs, Workflows, Adapters, Artifacts. They show retained workflow state, direct-call lifecycle, standalone/refine context-builder lifecycle, persisted pre-dispatch workflow input, final outputs and errors, validated JSON artifacts, and the launch-time workflow/adapter catalogs. Runs, Single Calls, and Context Runs have their own path routes. The persisted-call inspector is an overlay that still shows up as a nested route in history.
42
+ ## First call
46
43
 
47
- Workflow-call, activity, and context-run detail pages also have local session logs (observability only). Timeline and Conversation are chronological; order comes from the log sequence. Each lane is fenced to one immutable parent generation (run, activity, or context) plus the matching attempt, turn, or curator/finalizer execution. Pages are at most 500 events. A lane stops at 100,000 events. Events are normalized and pattern-redacted. They are not raw provider payloads, a session browser, or a live tail.
44
+ Ask your assistant to use the `clink` tool. This is the smallest valid request:
48
45
 
49
- Live tool arguments, sanitized argument-derived summaries, and `redaction.coverage: "pattern_best_effort"` require `CLINKX_SESSION_LOG_TOOL_ARGUMENTS_PERSISTENCE_ENABLED=true` (exact value, default off, read once per producer at creation). Disabled producers omit those additions; Claude keeps `"Tool invocation"` and Codex/agy keep legacy summaries. Deploy capable backend and browser readers first; readers always accept extended events. Rollback disables producers first and keeps capable readers until extended journals age out. Flag-off does not make existing gate-on journals old-reader-safe. Redaction hardening remains unconditional; coverage and match counts do not prove cleanliness. See [rollout configuration](docs/configuration.md#session-log-tool-arguments-rollout) and [detector scope](docs/security.md#dashboard-session-logs).
50
-
51
- The [native projection foundation](clinkx-workflows/src/dashboard/native-transcript/README.md) is unwired and persists nothing, independently of that gate. It handles JSON and text results, preserves source-file/component order, and scopes unverified synthesized identity to the affected call/result pair. Native capture, workers, storage, routes, and UI remain unfinished; #56 stays open.
52
-
53
- The dashboard has no SSE, filesystem watch, worker-health inference, cross-process cancellation, or run-creation form. Session logs cannot replay, approve, cancel, or otherwise control a run. Copied action objects are MCP tool handoffs, not JSON-RPC requests.
54
-
55
- Point the dashboard at the same `CLINKX_WORKFLOWS_STATE_DIR` as the workflow process or you will see a different (often empty) run set. Successful sync and async runs are kept for one day by default so you can inspect them. `CLINKX_WORKFLOWS_RETAIN_SUCCEEDED_DAYS=0` or `CLINKX_WORKFLOWS_RETAIN_ASYNC_DAYS=0` means keep that class forever, which can fill the disk.
56
-
57
- Workflow and activity session logs follow retention of that exact parent generation. Context session logs follow the parent context-run/manual lifecycle. There is no separate context-run pruner yet.
58
-
59
- Persisted inputs, outputs, errors, and artifact bytes can be sensitive. Access is loopback-only and passkey-gated. That is a shared-account boundary, not isolation from other processes as the same OS user. Redaction strips high-confidence credentials and user-home paths before write. Prompts, outputs, tool summaries, and reasoning can still be sensitive after that. Per-run state admission defaults to 16 MiB (configurable 1–64 MiB via `CLINKX_WORKFLOWS_DASHBOARD_MAX_STATE_BYTES`). Raw result/call reads use smaller fixed caps.
46
+ ```json
47
+ {
48
+ "cli_name": "codex",
49
+ "prompt": "Summarize the module boundaries in this project. Do not edit files.",
50
+ "absolute_file_paths": ["/path/to/your/workspace/src/server.ts"]
51
+ }
52
+ ```
60
53
 
61
- For local Docker, `compose.dashboard-local.yml` runs the real UI + API over plain HTTP. Caddy publishes on `127.0.0.1` and `::1`. The browser uses `http://localhost:<port>`. Caddy shares the dashboard network namespace and proxies IPv4 to Node at `127.0.0.1:4180`. The same Node process also listens on `::1:4180`. See `docs/dashboard-local-compose.md`.
54
+ ClinkX starts the Codex CLI, sends the prompt and the file references, and returns the final answer. You can run many `clink` calls at the same time, up to the concurrency limit.
62
55
 
63
- Hosted passkey topology is one UI+API service behind Traefik on a private Docker network. See `deploy/dashboard-cloud/README.md`. The older gateway/companion and split-origin paths exist only as a temporary bearer rollback until local and live cloud passkey ceremonies pass the proof-gated cutover.
56
+ ## Tools
64
57
 
65
- To build the local CLI as one host-native executable, use an official Node 25.5+ runtime with SEA support:
58
+ | Tool | Function |
59
+ |---|---|
60
+ | `clink` | Runs one task on one configured CLI and returns the result. |
61
+ | `context_builder` | Selects a bounded set of files from permitted roots and writes a reusable context bundle to disk. |
62
+ | `refine_context_run` | Makes a new bundle from an earlier one. You can add, remove, promote, or demote files, or set their slices. |
63
+ | `list_workflows` | Lists the available workflows, CLIs, and roster presets. |
64
+ | `run_workflow` | Starts a multi-stage workflow. Runs in blocking mode by default, or in the background with `async: true`. |
65
+ | `workflow_run_status` | Reports the progress and result of a workflow run. |
66
+ | `resume_workflow` | Continues a workflow run that stopped before completion. |
67
+ | `cancel_workflow` | Cancels a workflow run. |
68
+ | `progress_report_start`, `progress_report_update`, `progress_report_get`, `progress_report_list` | Keep a live progress report that the dashboard shows. |
66
69
 
67
- ```bash
68
- npm run build:dashboard:standalone -- \
69
- --node-executable /absolute/path/to/official-node \
70
- --output ./artifacts/clinkx-dashboard
71
- ```
70
+ Two more tool groups are off by default:
72
71
 
73
- The binary embeds the dashboard UI, bundled configuration, workflows, and templates. At runtime it still reads state, context runs, and the inspected workspace from the usual environment paths.
72
+ - `clink_agent_run` and `clink_agent_manage` control a stateful Codex app-server session with approvals. Set `CLINKX_APP_SERVER_ENABLED=true` to publish them.
73
+ - `clink_elaris_run` and `clink_elaris_manage` start remote Elaris agent sessions over HTTPS. Set `CLINKX_ELARIS_ENABLED=true` and the Elaris connection variables to publish them.
74
74
 
75
- ## Configuration
75
+ Each tool publishes its full input schema through MCP. MCP clients can show the schema with the tool description.
76
76
 
77
- Adapter JSON is discovered in this order:
77
+ ## Supported CLIs
78
78
 
79
- 1. `CLINKX_CONFIG_PATH` (file or directory). This replaces the whole search path. Shipped adapters are not loaded.
80
- 2. `${XDG_CONFIG_HOME:-~/.config}/clinkx/adapters/*.json`. First-wins on `name`.
81
- 3. Packaged `conf/adapters/*.json` next to the installed package (`import.meta.url`). Not `process.cwd()`.
79
+ An adapter file tells ClinkX how to start one CLI, how to pass the prompt, and how to read the output. These adapters are included:
82
80
 
83
- A `conf/adapters/*.json` file in the current working directory is ignored unless `CLINKX_CONFIG_PATH` points at it. Full reference: `docs/configuration.md`.
81
+ | `cli_name` | Command (short form) | Notes |
82
+ |---|---|---|
83
+ | `codex` | `codex exec --json` | Default curator and finalizer for workflow context. |
84
+ | `claude` | `claude -p --output-format stream-json` | |
85
+ | `agy` | `agy --output-format stream-json` | Use an agy build that emits stream-json (1.1.8 or later). |
86
+ | `glm` | `claude -p ... --model glm-5.3[1m]` | Runs GLM through the Claude Code CLI. |
87
+ | `grok` | `grok --output-format streaming-messages-json` | |
84
88
 
85
- Per-call model and effort are optional and require support declared by the resolved adapter:
89
+ All included adapters accept the optional `model` and `effort` fields on each call:
86
90
 
87
91
  ```json
88
- {"cli_name":"codex","prompt":"Summarize the module boundaries without editing files.","model":"gpt-5.4","effort":"low"}
92
+ { "cli_name": "codex", "prompt": "...", "model": "gpt-5.4", "effort": "low" }
89
93
  ```
90
94
 
91
- Omit either field to retain that CLI setting. Unsupported overrides fail with MCP `-32602` before spawning; XDG adapter files must declare their own override support.
92
-
93
- Dashboard deployment controls:
94
-
95
- | Setting | Contract |
96
- |---|---|
97
- | `CLINKX_WORKFLOWS_DASHBOARD_CREDENTIAL_DIR` | Optional absolute, non-root credential directory. Host default: `${CLINKX_WORKFLOWS_STATE_DIR}/dashboard/webauthn`. Compose pins the writable `/data/dashboard-credentials` volume. |
98
- | `DASHBOARD_DEPLOYMENT_SLUG` | Required Compose identity, `^[a-z][a-z0-9-]{0,31}$`. Local and cloud names keep distinct stack-kind prefixes. |
99
- | `DASHBOARD_CREDENTIAL_BOOTSTRAP_MODE` | Required Compose state assertion. First start is `bootstrap`. Flip to `existing-enrolled` right after enrollment, before restart. |
100
-
101
- Passkey challenge capacity is 96 login slots and 32 registration/add-passkey slots, with 8 concurrent verifications. Saturation returns 503 `auth_busy` and `Retry-After: 1`. Live challenges are not evicted. Local session and challenge cookie names are port-scoped. WebAuthn still uses the shared bare `localhost` RP ID.
95
+ If you omit a field, ClinkX uses the adapter setting or the CLI default. If the adapter does not declare the field or the value, ClinkX returns MCP error `-32602` and does not start the process.
102
96
 
103
- On browser startup, a valid same-origin passkey status beats provisional bearer state. The only bearer fallback is the split-origin static server's exact no-store 404 `{"error":{"code":"not_found"}}`. Malformed or ambiguous status, and uncertain logout, fail closed. Bearer is the explicit tested rollback.
97
+ The `conf/adapters/hapi/` directory in the package holds optional templates that start each CLI through a `hapi` gateway. These templates are not ready to use. You must set the gateway URL and credentials in your copy.
104
98
 
105
- Dashboard shutdown closes listeners first, then auth/gateway resources. Cleanup failures are aggregated across both.
99
+ ## Configuration
106
100
 
107
- ## Council roster presets
101
+ ### Adapter files
108
102
 
109
- `run_workflow.roster` names a JSON preset instead of passing `members`, `chairman`, or `cb_delegate`. Council workflows only. Combining `roster` with any of those fields (or a non-empty `cb_delegate`) is MCP `-32602` and creates no run.
103
+ ClinkX reads adapter JSON files from these locations, in this order. ClinkX uses the first file with a given `name`.
110
104
 
111
- Discovery is first-wins by file stem, same as adapters:
105
+ 1. `CLINKX_CONFIG_PATH`, a file or a directory. When you set it, ClinkX reads only this location and does not load the included adapters. The location must also contain a `codex` adapter with a `json` role, or you must set `CLINKX_WORKFLOWS_CONTEXT_PREFLIGHT_ENABLED=false`.
106
+ 2. `${XDG_CONFIG_HOME:-~/.config}/clinkx/adapters/*.json`.
107
+ 3. The `conf/adapters/` directory inside the installed package.
112
108
 
113
- 1. `CLINKX_ROSTERS_PATH` (file or directory). Replaces the rest when set.
114
- 2. `${XDG_CONFIG_HOME:-~/.config}/clinkx/rosters/*.json`
115
- 3. Packaged `conf/rosters/*.json` on the root server only. Standalone `createWorkflowServer` loads env/user only.
109
+ To change an included adapter, copy its file to `~/.config/clinkx/adapters/` and edit the copy. The copy replaces the included file completely. ClinkX does not merge the two files. After you upgrade ClinkX, compare your copy with the new included file. Copy the new file again if necessary.
116
110
 
117
- Put a user preset at `~/.config/clinkx/rosters/<name>.json`. The file stem is the name (`^[a-z0-9][a-z0-9_-]{0,63}$`). Schema is Zod-strict `clinkx-roster-v1`; at least one of `members`, `chairman`, or `delegate` is required. Shipped `conf/rosters/default-council.json`:
111
+ To add a CLI that is not included, write a new file in the same directory. This example adapter returns the prompt unchanged:
118
112
 
119
113
  ```json
120
114
  {
121
- "schema_version": "clinkx-roster-v1",
122
- "description": "Three-member council with a Codex chairman and an enabled curator/finalizer delegate.",
123
- "members": [
124
- { "cli_name": "codex" },
125
- { "cli_name": "claude" },
126
- { "cli_name": "glm" }
127
- ],
128
- "chairman": { "cli_name": "codex" },
129
- "delegate": {
130
- "enabled": true,
131
- "curator": { "cli_name": "codex", "role": "json" },
132
- "finalizer": {
133
- "cli_name": "codex",
134
- "role": "json-codereviewer",
135
- "enabled": true,
136
- "min_score": 8.5
137
- },
138
- "failure_policy": "fail"
139
- }
115
+ "name": "echo",
116
+ "command": "node",
117
+ "args": ["-e", "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>process.stdout.write(d));"],
118
+ "parser": "text",
119
+ "prompt_mode": "stdin",
120
+ "timeout_seconds": 60
140
121
  }
141
122
  ```
142
123
 
143
- Call `run_workflow` with the preset name:
124
+ ### Environment variables
125
+
126
+ These are the variables that most installations set. Every variable has a default.
127
+
128
+ | Variable | Default | Function |
129
+ |---|---|---|
130
+ | `CLINKX_ALLOWED_ROOTS` | Current directory of the server process | Comma-separated list of directories that tool calls can reference. Set it so path checks do not depend on the directory in which the client starts ClinkX. |
131
+ | `CLINKX_WORKFLOWS_WORKSPACE_ROOT` | Current directory of the server process | Workspace for workflows. Set it to the same directory as the allowed root. |
132
+ | `CLINKX_WORKFLOWS_STATE_DIR` | `${XDG_STATE_HOME:-~/.local/state}/clinkx-workflows` | Directory for workflow runs, progress reports, and dashboard state. |
133
+ | `CLINKX_CONTEXT_BUILDER_RUNS_DIR` | `${XDG_STATE_HOME:-~/.local/state}/clinkx/context-builder/runs` | Directory for `context_builder` runs and bundles. |
134
+ | `CLINKX_CONFIG_PATH` | not set | Replaces the adapter search path. See [Adapter files](#adapter-files). |
135
+ | `CLINKX_ROSTERS_PATH` | not set | Replaces the roster-preset search path. See [Workflows](#workflows). |
136
+ | `CLINKX_MAX_CONCURRENT` | `5` | Maximum number of `clink` processes at one time. ClinkX returns an error when the limit is reached. It does not queue requests. |
137
+ | `CLINKX_TIMEOUT_SECONDS` | `600` | Default time limit for one child process. |
138
+ | `CLINKX_MAX_RESPONSE_CHARS` | `20000` | Maximum number of characters in one tool result. |
139
+ | `CLINKX_LOG_LEVEL` | `info` | Log level. Logs go to stderr. |
140
+ | `CLINKX_CONTEXT_ALLOW_SECRETS` | `true` | When `true`, `context_builder` copies secret material into bundles as written. See [Safety](#safety). |
141
+ | `CLINKX_WORKFLOWS_CONTEXT_PREFLIGHT_ENABLED` | `true` | Set to `false` to run workflows without the Codex context step. |
142
+ | `CLINKX_WORKFLOWS_RETAIN_SUCCEEDED_DAYS` | `1` | Days to keep completed blocking workflow runs on disk. `0` keeps them without limit. |
143
+ | `CLINKX_WORKFLOWS_RETAIN_ASYNC_DAYS` | `1` | Days to keep completed background workflow runs on disk. `0` keeps them without limit. |
144
+ | `CLINKX_SERVER_IDLE_TIMEOUT_SECONDS` | `0` | Seconds of inactivity after which the server exits. `0` disables the timer. Active tool calls, workflows, and Codex turns reset it. |
145
+ | `CLINKX_APP_SERVER_ENABLED` | `false` | Set to `true` to publish the Codex app-server tools. |
146
+ | `CLINKX_ELARIS_ENABLED` | `false` | Set to `true` to publish the Elaris tools. |
147
+
148
+ ClinkX writes only MCP JSON-RPC to stdout. All logs go to stderr. The server detects the exit of its parent process and stops.
149
+
150
+ ## Workflows
151
+
152
+ A workflow runs many CLI calls in stages and returns one result. A council workflow asks each member for an independent answer. The members then review each other. A chairman writes the final answer. These workflows are included:
153
+
154
+ | Workflow | Purpose |
155
+ |---|---|
156
+ | `council-answer` | Answer one hard question. |
157
+ | `council-code-review` | Review a change set. |
158
+ | `council-debug` | Find the cause of a defect. |
159
+ | `council-discover` | Inspect a codebase or a design space. |
160
+ | `council-plan-review` | Review a plan document. |
161
+ | `council-default` | General-purpose council. |
162
+ | `plan-review-receipts` | Typed plan review that returns receipt candidates for an external approval process. |
163
+
164
+ Before the first stage, the workflow runs `context_builder` with Codex as curator and finalizer. This step selects the repository files that the members receive. The `codex` command must be on your `PATH`. To skip this step, set `CLINKX_WORKFLOWS_CONTEXT_PREFLIGHT_ENABLED=false`.
165
+
166
+ ### Roster presets
167
+
168
+ A roster preset names the members, the chairman, and the context delegate of a council in one file. Pass the preset name as `roster` in place of `members`, `chairman`, and `cb_delegate`:
144
169
 
145
170
  ```json
146
171
  {
@@ -150,106 +175,81 @@ Call `run_workflow` with the preset name:
150
175
  }
151
176
  ```
152
177
 
153
- `list_workflows` still returns `available_cli_names`. The root server also returns `available_clis` (per-adapter model/effort template support, not value validity; `app_server` excluded) and `available_rosters` (`{name, source}` with `source` `env` | `user` | `builtin`). A council run's requested seats and delegate appear as `resolved_roster` on the async ack, a trailing sync content block, and `workflow_run_status`. `provider_spawns` is `2N+1` (ranking is a reducer, not a spawn).
178
+ The included `default-council` preset uses Codex, Claude, and GLM as members and Codex as chairman. ClinkX reads these locations in this order and uses the first file with a given name:
154
179
 
155
- ## Context bundles
180
+ 1. `CLINKX_ROSTERS_PATH`, a file or a directory. When you set it, ClinkX reads only this location.
181
+ 2. `${XDG_CONFIG_HOME:-~/.config}/clinkx/rosters/*.json`.
182
+ 3. The `conf/rosters/` directory inside the installed package.
156
183
 
157
- Use `context_builder` when a later `clink` call needs curated repo context and you do not want that source inlined in the first tool response. You must pass a bounded anchor: at least one `focus_paths` entry or a nonblank `search_terms` entry. `include_globs` only narrows the scan when those anchors exist. It does not select context by itself.
184
+ To write your own preset, copy `conf/rosters/default-council.json` to `~/.config/clinkx/rosters/<name>.json` and edit the copy. The file name is the preset name. The preset name must match `^[a-z0-9][a-z0-9_-]{0,63}$`. A request that gives `roster` together with `members`, `chairman`, `chairman_model`, `chairman_effort`, or a non-empty `cb_delegate` returns error `-32602` and starts no run.
158
185
 
159
- The tool writes a durable run directory (`manifest.json`, `artifacts/bundle.md`) and returns one minified JSON receipt. The receipt includes `recommended_clink.absolute_file_paths`. Pass those as `absolute_file_paths` on a later `clink` call. The handoff files are the persisted `artifacts/bundle.md` and `manifest.json`.
186
+ ### Background runs
160
187
 
161
- Delegate mode needs an explicit request shape and explicit providers:
188
+ Pass `async: true` to `run_workflow` to get a `run_id` immediately. Call `workflow_run_status` with that `run_id` to see the stage progress. Pass `include_result: true` to get the final result. Use `cancel_workflow` to stop a run. Completed runs stay on disk for the retention period given above.
189
+
190
+ ## Context bundles
191
+
192
+ Use `context_builder` when a later call needs a selected part of the repository. Then the first tool result does not contain that content. You must give at least one anchor: a `focus_paths` entry or a non-blank `search_terms` entry.
162
193
 
163
194
  ```json
164
195
  {
165
196
  "task": "Collect context for the server tool registration",
166
- "roots": ["/path/to/workspace"],
197
+ "roots": ["/path/to/your/workspace"],
167
198
  "focus_paths": ["src/server.ts"],
168
- "delegate": {
169
- "enabled": true,
170
- "curator": {
171
- "cli_name": "<curator-cli>",
172
- "role": "<curator-role>"
173
- },
174
- "finalizer": {
175
- "cli_name": "<finalizer-cli>",
176
- "role": "<finalizer-role>",
177
- "min_score": 8.5
178
- },
179
- "failure_policy": "fallback",
180
- "timeout_seconds": 120,
181
- "max_response_chars": 20000
182
- }
199
+ "search_terms": ["ListToolsRequestSchema"]
183
200
  }
184
201
  ```
185
202
 
186
- There are no built-in curator or finalizer provider defaults. Provider output is advisory. The server checks strict JSON, rebuilds accepted content from local candidates, reapplies budgets and secret checks, and does not persist raw provider output. `failure_policy:"fallback"` publishes deterministic local selection as a partial v2 run if delegate curation fails. `failure_policy:"fail"` returns an error and does not publish a manifest.
203
+ The tool writes the bundle to the context-runs directory and returns a short JSON receipt. The receipt contains `recommended_clink.absolute_file_paths`. Pass that list as `absolute_file_paths` on the next `clink` call.
187
204
 
188
- ### Adding an adapter
205
+ By default the selection is deterministic. You can also set `delegate.enabled: true` and name a curator and a finalizer CLI. The curator makes a selection and the finalizer scores it. ClinkX checks the selection and rebuilds the bundle from local files. It applies the budgets again and writes only content that it checked itself. Provider output is an input that ClinkX checks, not a result.
189
206
 
190
- Put the JSON in `${XDG_CONFIG_HOME:-~/.config}/clinkx/adapters/`. Or set `CLINKX_CONFIG_PATH` to a directory that also contains every shipped adapter you still need. That env var replaces the search path.
207
+ ## Dashboard
191
208
 
192
- ```json
193
- {
194
- "name": "echo",
195
- "command": "node",
196
- "args": ["-e", "let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>process.stdout.write(d));"],
197
- "parser": "text",
198
- "prompt_mode": "stdin",
199
- "timeout_seconds": 60
200
- }
209
+ The package includes a local, read-only web dashboard. It shows workflow runs, single calls, context runs, progress reports, the workflow catalog, the adapter catalog, and the saved artifacts. Start it with the second command that the package installs:
210
+
211
+ ```bash
212
+ npx -p clinkx clinkx-workflows-dashboard --port 4180
201
213
  ```
202
214
 
203
- ## MCP client config
215
+ The dashboard listens on `127.0.0.1` and `::1` and prints `http://localhost:<port>`. Passkey sign-in is the default. On the first start, the dashboard prints one enrollment code. Register a passkey with that code, then sign in. For local tests only, `CLINKX_WORKFLOWS_DASHBOARD_AUTH_MODE=none` disables sign-in.
204
216
 
205
- The exact shape depends on the client.
217
+ Point the dashboard at the same `CLINKX_WORKFLOWS_STATE_DIR` as the ClinkX server. If the two directories differ, the dashboard shows a different run set, often an empty one. If you set `CLINKX_CONTEXT_BUILDER_RUNS_DIR` for the server, set the same value for the dashboard.
206
218
 
207
- Via npx (recommended):
219
+ The dashboard does not start, cancel, or approve anything. The data that it shows can contain prompts, outputs, and file contents from your workspace. Access is local and needs a passkey, but any process that runs as the same OS user can read the state directory.
208
220
 
209
- ```json
210
- {
211
- "mcpServers": {
212
- "clinkx": {
213
- "command": "npx",
214
- "args": ["-y", "clinkx@latest"],
215
- "env": {
216
- "CLINKX_ALLOWED_ROOTS": "/path/to/your/workspace",
217
- "CLINKX_WORKFLOWS_WORKSPACE_ROOT": "/path/to/your/workspace"
218
- }
219
- }
220
- }
221
- }
222
- ```
221
+ ## Safety
223
222
 
224
- From a local clone:
223
+ ClinkX checks request paths and passes only a small set of environment variables to the child process. It does not sandbox the child process.
225
224
 
226
- ```json
227
- {
228
- "mcpServers": {
229
- "clinkx": {
230
- "command": "node",
231
- "args": ["/path/to/clink-mcp/dist/index.js"],
232
- "env": {
233
- "CLINKX_ALLOWED_ROOTS": "/path/to/your/workspace",
234
- "CLINKX_WORKFLOWS_WORKSPACE_ROOT": "/path/to/your/workspace"
235
- }
236
- }
237
- }
238
- }
239
- ```
225
+ **WARNING: A child process has the same permissions as the user who started ClinkX.** The included adapters start each CLI with no sandbox and with approval prompts turned off or set to accept file edits. The child process can edit and delete files in your workspace and can run commands. Give each prompt clear limits. Do not run ClinkX as root or as an administrator.
240
226
 
241
- ## Safety
227
+ **WARNING: `context_builder` copies secrets by default.** The default is `allow_secrets: true`. With this default, ClinkX copies API keys, private keys, tokens, and files such as `.env` and `id_rsa` into bundles. The CLIs that receive the bundle also receive the secrets. To make ClinkX omit or redact common secret forms, set one of these:
228
+
229
+ - `allow_secrets: false` on the `context_builder` call.
230
+ - `cb_allow_secrets: false` on the `run_workflow` call.
231
+ - `CLINKX_CONTEXT_ALLOW_SECRETS=false` in the server environment.
232
+
233
+ This filter is a best-effort check, not a security boundary.
242
234
 
243
- ClinkX validates request paths, environment variables, and argument policy. It does not sandbox the spawned process.
235
+ `CLINKX_ALLOWED_ROOTS` limits the paths that tool calls can reference. It does not limit the child process. Set it in all installations so that the limit does not depend on the start directory of the client.
244
236
 
245
- `CLINKX_ALLOWED_ROOTS` limits which file paths the tool accepts. Default is `process.cwd()`. It is required only if the MCP host starts ClinkX with cwd at `/`. Set it anyway so path admission does not depend on whatever cwd the host chose. The subprocess has the same permissions as whoever started ClinkX.
237
+ Only the `council-plan-review` and `plan-review-receipts` prompts tell the members not to edit files. This is an instruction to the model, not a mechanical control. The other workflows give no such instruction.
246
238
 
247
- Set `CLINKX_WORKFLOWS_WORKSPACE_ROOT` to the same workspace. If you omit it, workflows use `process.cwd()` and warn on stderr.
239
+ ## Documentation
248
240
 
249
- `context_builder` validates roots and blocks symlink escapes. Secret material (API keys, private keys, tokens, `.env` / `.pem` / `id_rsa` / `credentials.json`) is included as written by default (`allow_secrets:true`) so delegates can probe. Opt out with `allow_secrets:false`, `cb_allow_secrets:false`, or `CLINKX_CONTEXT_ALLOW_SECRETS=false`. Bundle artifacts are not a sandbox and not an access-control boundary.
241
+ The installed package contains these files. After `npm install clinkx`, find them in `node_modules/clinkx/`.
250
242
 
251
- See `docs/security.md`.
243
+ | File | Content |
244
+ |---|---|
245
+ | `docs/configuration.md` | Reference for adapter files, invocation overrides, and environment variables. |
246
+ | `docs/security.md` | Trust boundaries, redaction scope, and the limits of each safety control. |
247
+ | `CHANGELOG.md` | Changes, upgrade notes, and new flags for each release. |
248
+
249
+ Read the upgrade notes before you upgrade a server that has unfinished workflow runs.
252
250
 
253
251
  ## License
254
252
 
255
- Copyright (c) 2026 jim380. All rights reserved. Proprietary package (`license: UNLICENSED`). See `LICENSE`.
253
+ Copyright (c) 2026 jim380. All rights reserved.
254
+
255
+ ClinkX is free to use but not open source. You may install and run the published npm package for your own use. You may not copy, change, redistribute, or sell it. The `package.json` license field is `UNLICENSED`. See the `LICENSE` file in the package for the full text.
@@ -2359,8 +2359,11 @@ function residualJsValueSpan(text, commentMasked, valueStart) {
2359
2359
  (cursor - valueStart >= 32_768 || lineCount >= 64),
2360
2360
  };
2361
2361
  }
2362
+ // Returns `text` itself when nothing is masked: callers key memo Maps by the
2363
+ // result, and Node 24's V8 compares a distinct-but-equal string per lookup.
2362
2364
  function maskJsCommentTrivia(text) {
2363
2365
  const output = text.split("");
2366
+ let changed = false;
2364
2367
  let quote;
2365
2368
  let escaped = false;
2366
2369
  for (let index = 0; index < text.length; index++) {
@@ -2377,6 +2380,7 @@ function maskJsCommentTrivia(text) {
2377
2380
  continue;
2378
2381
  }
2379
2382
  if (character === "/" && text[index + 1] === "*") {
2383
+ changed = true;
2380
2384
  output[index] = " ";
2381
2385
  output[index + 1] = " ";
2382
2386
  index += 2;
@@ -2395,6 +2399,7 @@ function maskJsCommentTrivia(text) {
2395
2399
  continue;
2396
2400
  }
2397
2401
  if (character === "/" && text[index + 1] === "/") {
2402
+ changed = true;
2398
2403
  output[index] = " ";
2399
2404
  output[index + 1] = " ";
2400
2405
  index += 2;
@@ -2405,7 +2410,7 @@ function maskJsCommentTrivia(text) {
2405
2410
  index--;
2406
2411
  }
2407
2412
  }
2408
- return output.join("");
2413
+ return changed ? output.join("") : text;
2409
2414
  }
2410
2415
  function shannonEntropy(value) {
2411
2416
  const counts = new Map();