delimit-cli 4.7.1 → 4.7.3
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 +37 -0
- package/README.md +37 -2
- package/gateway/ai/server.py +39 -6
- package/gateway/ai/toolcard_cache.py +64 -1
- package/package.json +1 -1
- package/server.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,43 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
## [4.7.3] - 2026-06-04
|
|
5
|
+
|
|
6
|
+
Docs + metadata release. No functional changes to the package.
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
|
|
10
|
+
- npm README now carries the **"Adopt with minimum privilege"** section
|
|
11
|
+
(phase-1 read-only tool allowlist, Action SHA-pinning, BYOK vault guidance)
|
|
12
|
+
that previously only rendered on GitHub.
|
|
13
|
+
- Detection-engine claims corrected to **28 change types (17 breaking,
|
|
14
|
+
11 non-breaking)** — adds `field_requirement_relaxed` (context-aware
|
|
15
|
+
severity) to the documented table.
|
|
16
|
+
- `server.json` (MCP registry metadata) version brought current.
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
## [4.7.2] - 2026-06-04
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
|
|
23
|
+
- **Tool-usage telemetry now persists.** `record_call` previously only kept an
|
|
24
|
+
in-memory per-session counter that vanished on restart, so tool utilization
|
|
25
|
+
was unobservable. It now appends every call to `~/.delimit/tool_usage.jsonl`
|
|
26
|
+
(crash-safe, append-only), making utilization and dormancy measurable.
|
|
27
|
+
|
|
28
|
+
### Added
|
|
29
|
+
|
|
30
|
+
- **`delimit_toolcard_cache action="usage"`** — durable tool-utilization +
|
|
31
|
+
dormancy report (per-tool call counts, `last_seen`, and registered tools never
|
|
32
|
+
called).
|
|
33
|
+
- Additive next-step suggestions wiring under-surfaced tools into the governance
|
|
34
|
+
loop: `lint → impact`, `agent_dispatch → agent_link`, `deploy_verify →
|
|
35
|
+
seal_verify`, `evidence_collect → seal_verify`, `os_plan → os_gates`.
|
|
36
|
+
|
|
37
|
+
Purely additive — no tool signature or behavior changed. Published via OIDC
|
|
38
|
+
trusted publishing with provenance.
|
|
39
|
+
|
|
40
|
+
|
|
4
41
|
## [4.7.1] - 2026-06-03
|
|
5
42
|
|
|
6
43
|
Release-infrastructure update. No functional changes to the package versus 4.7.0.
|
package/README.md
CHANGED
|
@@ -226,6 +226,40 @@ That's it. Delimit auto-fetches the base branch spec, diffs it, and posts a PR c
|
|
|
226
226
|
|
|
227
227
|
---
|
|
228
228
|
|
|
229
|
+
## Adopt with minimum privilege
|
|
230
|
+
|
|
231
|
+
You don't have to trust a large tool surface on day one. The safe on-ramp:
|
|
232
|
+
|
|
233
|
+
**Phase 1 — read-only governance (free, no account).** Start with the tools that
|
|
234
|
+
only read your repo and write reports: `delimit_lint`, `delimit_diff`,
|
|
235
|
+
`delimit_semver`, `delimit_policy`, `delimit_explain`, `delimit_scan`, and
|
|
236
|
+
`delimit_seal_verify`. If your MCP client supports per-tool allowlists, grant
|
|
237
|
+
exactly those. Nothing in this set executes, deploys, or posts anywhere.
|
|
238
|
+
|
|
239
|
+
**Phase 2 — opt into side effects deliberately.** Tools that write evidence
|
|
240
|
+
bundles, open PR comments, or run deploys (`delimit_security_audit`,
|
|
241
|
+
`delimit_deploy_*`, agent orchestration) are tier-gated; enable them once
|
|
242
|
+
phase 1 has earned its keep in your CI.
|
|
243
|
+
|
|
244
|
+
**Pin the Action to a commit SHA.** `@v1` is a floating tag. For
|
|
245
|
+
supply-chain-sensitive pipelines, pin the exact commit and bump on review:
|
|
246
|
+
|
|
247
|
+
```yaml
|
|
248
|
+
- uses: delimit-ai/delimit-action@<commit-sha> # gh api repos/delimit-ai/delimit-action/git/refs/tags/v1
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
**Keep BYOK keys out of plaintext config.** If you bring your own model keys
|
|
252
|
+
for deliberation, store them with `delimit_secret_store` (encrypted vault,
|
|
253
|
+
access-logged via `delimit_secret_access_log`) rather than in dotfiles.
|
|
254
|
+
|
|
255
|
+
Our own releases ship under the same discipline: every release carries a
|
|
256
|
+
signed, replayable Seal receipt (see the latest
|
|
257
|
+
[release assets](https://github.com/delimit-ai/delimit-mcp-server/releases) —
|
|
258
|
+
verify with `npx delimit-cli seal-verify <receipt.json>` or at its
|
|
259
|
+
`delimit.ai/att/<id>` replay URL), plus SLSA provenance on npm.
|
|
260
|
+
|
|
261
|
+
---
|
|
262
|
+
|
|
229
263
|
## CLI commands
|
|
230
264
|
|
|
231
265
|
```bash
|
|
@@ -290,7 +324,7 @@ When installed into your AI coding assistant, Delimit provides tools across two
|
|
|
290
324
|
|
|
291
325
|
## What It Detects
|
|
292
326
|
|
|
293
|
-
|
|
327
|
+
28 change types (17 breaking, 11 non-breaking) -- deterministic rules, not AI inference. Same input always produces the same result.
|
|
294
328
|
|
|
295
329
|
### Breaking Changes
|
|
296
330
|
|
|
@@ -328,6 +362,7 @@ When installed into your AI coding assistant, Delimit provides tools across two
|
|
|
328
362
|
| 25 | `security_added` | API key security scheme added |
|
|
329
363
|
| 26 | `deprecated_added` | `GET /v1/users` marked as deprecated |
|
|
330
364
|
| 27 | `default_changed` | Default value for `page_size` changed from 10 to 20 |
|
|
365
|
+
| 28 | `field_requirement_relaxed` | Required field `nickname` became optional (context-aware severity) |
|
|
331
366
|
|
|
332
367
|
---
|
|
333
368
|
|
|
@@ -367,7 +402,7 @@ rules:
|
|
|
367
402
|
|
|
368
403
|
**How does this compare to Obsidian Mind?**
|
|
369
404
|
|
|
370
|
-
Obsidian Mind is a great Obsidian vault template for Claude Code users who want persistent memory via markdown files. Delimit takes a different approach: it's an MCP server that works across Claude Code, Codex, Gemini CLI, and Cursor. Your memory, ledger, and governance travel with you when you switch models. Delimit also adds API governance (
|
|
405
|
+
Obsidian Mind is a great Obsidian vault template for Claude Code users who want persistent memory via markdown files. Delimit takes a different approach: it's an MCP server that works across Claude Code, Codex, Gemini CLI, and Cursor. Your memory, ledger, and governance travel with you when you switch models. Delimit also adds API governance (28-type breaking change detection), CI gates, git hooks, and policy enforcement that Obsidian Mind doesn't cover. Use Obsidian Mind if you're all-in on Claude + Obsidian. Use Delimit if you switch between models or need governance.
|
|
371
406
|
|
|
372
407
|
**Does this work without Claude Code?**
|
|
373
408
|
|
package/gateway/ai/server.py
CHANGED
|
@@ -742,6 +742,7 @@ NEXT_STEPS_REGISTRY: Dict[str, List[Dict[str, Any]]] = {
|
|
|
742
742
|
"lint": [
|
|
743
743
|
{"tool": "delimit_explain", "reason": "Get migration guide for breaking changes", "suggested_args": {"template": "migration"}, "is_premium": False},
|
|
744
744
|
{"tool": "delimit_semver", "reason": "Determine the version bump for these changes", "suggested_args": {}, "is_premium": False},
|
|
745
|
+
{"tool": "delimit_impact", "reason": "Enumerate downstream callers affected by a breaking change before it ships", "suggested_args": {}, "is_premium": True},
|
|
745
746
|
],
|
|
746
747
|
"diff": [
|
|
747
748
|
{"tool": "delimit_semver", "reason": "Classify the semver bump for these changes", "suggested_args": {}, "is_premium": False},
|
|
@@ -766,7 +767,9 @@ NEXT_STEPS_REGISTRY: Dict[str, List[Dict[str, Any]]] = {
|
|
|
766
767
|
{"tool": "delimit_diagnose", "reason": "Check environment and tool status", "suggested_args": {}, "is_premium": False},
|
|
767
768
|
],
|
|
768
769
|
# --- Tier 2 Platform (Pro) ---
|
|
769
|
-
"os_plan": [
|
|
770
|
+
"os_plan": [
|
|
771
|
+
{"tool": "delimit_os_gates", "reason": "Check whether the new OS plan passes its governance gates", "suggested_args": {}, "is_premium": True},
|
|
772
|
+
],
|
|
770
773
|
"os_status": [],
|
|
771
774
|
"os_gates": [],
|
|
772
775
|
"gov_health": [
|
|
@@ -799,6 +802,7 @@ NEXT_STEPS_REGISTRY: Dict[str, List[Dict[str, Any]]] = {
|
|
|
799
802
|
# --- Agent Orchestration (Pro) ---
|
|
800
803
|
"agent_dispatch": [
|
|
801
804
|
{"tool": "delimit_agent_status", "reason": "Check the status of your dispatched task", "suggested_args": {}, "is_premium": True},
|
|
805
|
+
{"tool": "delimit_agent_link", "reason": "Link this dispatched task to its ledger item for the replay/audit trail (operating-model mandate)", "suggested_args": {}, "is_premium": True},
|
|
802
806
|
],
|
|
803
807
|
"agent_status": [
|
|
804
808
|
{"tool": "delimit_agent_complete", "reason": "Mark a task as complete when done", "suggested_args": {}, "is_premium": True},
|
|
@@ -871,6 +875,10 @@ NEXT_STEPS_REGISTRY: Dict[str, List[Dict[str, Any]]] = {
|
|
|
871
875
|
"deploy_publish": [
|
|
872
876
|
{"tool": "delimit_deploy_verify", "reason": "Verify deployment health after publish", "suggested_args": {}, "is_premium": True},
|
|
873
877
|
],
|
|
878
|
+
"deploy_verify": [
|
|
879
|
+
{"tool": "delimit_evidence_collect", "reason": "Collect a deploy evidence bundle", "suggested_args": {}, "is_premium": True},
|
|
880
|
+
{"tool": "delimit_seal_verify", "reason": "Verify the signed, replayable attestation produced by the deploy", "suggested_args": {}, "is_premium": False},
|
|
881
|
+
],
|
|
874
882
|
"deploy_rollback": [],
|
|
875
883
|
"deploy_status": [],
|
|
876
884
|
"generate_template": [],
|
|
@@ -880,9 +888,13 @@ NEXT_STEPS_REGISTRY: Dict[str, List[Dict[str, Any]]] = {
|
|
|
880
888
|
],
|
|
881
889
|
"evidence_collect": [
|
|
882
890
|
{"tool": "delimit_evidence_verify", "reason": "Verify evidence bundle integrity", "suggested_args": {}, "is_premium": True},
|
|
891
|
+
{"tool": "delimit_seal_verify", "reason": "Verify the Seal attestation receipt (Ed25519 + content-pin) for this artifact", "suggested_args": {}, "is_premium": False},
|
|
883
892
|
],
|
|
884
893
|
"evidence_verify": [],
|
|
885
|
-
"seal_verify": [
|
|
894
|
+
"seal_verify": [
|
|
895
|
+
{"tool": "delimit_evidence_collect", "reason": "Collect an evidence bundle for the verified attestation", "suggested_args": {}, "is_premium": True},
|
|
896
|
+
{"tool": "delimit_notify", "reason": "Notify stakeholders that the merge attestation verified", "suggested_args": {}, "is_premium": True},
|
|
897
|
+
],
|
|
886
898
|
"security_audit": [
|
|
887
899
|
{"tool": "delimit_security_scan", "reason": "Run deeper security scan on flagged areas", "suggested_args": {}, "is_premium": True},
|
|
888
900
|
{"tool": "delimit_evidence_collect", "reason": "Collect evidence of security findings", "suggested_args": {}, "is_premium": True},
|
|
@@ -13235,7 +13247,7 @@ def delimit_loop_config(session_id: Annotated[str, Field(description="Session to
|
|
|
13235
13247
|
|
|
13236
13248
|
@mcp.tool()
|
|
13237
13249
|
def delimit_toolcard_cache(
|
|
13238
|
-
action: Annotated[str, Field(description="One of \"status\" (default), \"register\", \"delta\", \"clear\", \"estimate\", \"flush\".")] = "status",
|
|
13250
|
+
action: Annotated[str, Field(description="One of \"status\" (default), \"register\", \"delta\", \"clear\", \"estimate\", \"flush\", \"usage\" (durable tool utilization + dormancy report).")] = "status",
|
|
13239
13251
|
tool_schemas: Annotated[Optional[str], Field(description="JSON array of tool schema objects (for register/ estimate).")] = None,
|
|
13240
13252
|
tool_names: Annotated[Optional[str], Field(description="Comma-separated tool names (for delta).")] = None,
|
|
13241
13253
|
) -> Dict[str, Any]:
|
|
@@ -13255,13 +13267,16 @@ def delimit_toolcard_cache(
|
|
|
13255
13267
|
|
|
13256
13268
|
Args:
|
|
13257
13269
|
action: One of "status" (default), "register", "delta",
|
|
13258
|
-
"clear", "estimate", "flush".
|
|
13270
|
+
"clear", "estimate", "flush", "usage". "usage" returns the
|
|
13271
|
+
durable tool-utilization + dormancy report (per-tool call
|
|
13272
|
+
counts, last_seen, and registry tools never called).
|
|
13259
13273
|
tool_schemas: JSON array of tool schema objects (for register/
|
|
13260
13274
|
estimate).
|
|
13261
13275
|
tool_names: Comma-separated tool names (for delta).
|
|
13262
13276
|
|
|
13263
13277
|
Returns:
|
|
13264
|
-
Dict with the action result (stats, delta names, estimate,
|
|
13278
|
+
Dict with the action result (stats, delta names, estimate,
|
|
13279
|
+
usage/dormancy summary, etc).
|
|
13265
13280
|
"""
|
|
13266
13281
|
from ai.license import require_premium
|
|
13267
13282
|
gate = require_premium("toolcard_cache")
|
|
@@ -13306,8 +13321,26 @@ def delimit_toolcard_cache(
|
|
|
13306
13321
|
r = cache.estimate_savings(schemas)
|
|
13307
13322
|
elif action == "flush":
|
|
13308
13323
|
r = cache.flush_session()
|
|
13324
|
+
elif action == "usage":
|
|
13325
|
+
# Durable tool utilization / dormancy report. record_call now persists
|
|
13326
|
+
# every call to ~/.delimit/tool_usage.jsonl; usage_summary aggregates
|
|
13327
|
+
# counts + last_seen and, given the registered-tool list, flags tools
|
|
13328
|
+
# never called as `dormant`. Mechanizes the dormant-tool audit.
|
|
13329
|
+
registry = None
|
|
13330
|
+
try:
|
|
13331
|
+
tm = getattr(mcp, "_tool_manager", None)
|
|
13332
|
+
if tm is not None:
|
|
13333
|
+
if hasattr(tm, "list_tools"):
|
|
13334
|
+
registry = [getattr(t, "name", None) or t.get("name") for t in tm.list_tools()]
|
|
13335
|
+
elif hasattr(tm, "_tools"):
|
|
13336
|
+
registry = list(tm._tools.keys())
|
|
13337
|
+
if registry:
|
|
13338
|
+
registry = sorted(n for n in registry if n)
|
|
13339
|
+
except Exception:
|
|
13340
|
+
registry = None
|
|
13341
|
+
r = cache.usage_summary(registry=registry)
|
|
13309
13342
|
else:
|
|
13310
|
-
r = {"error": "unknown_action", "message": f"Unknown action: {action}. Use: status, register, delta, clear, estimate, flush"}
|
|
13343
|
+
r = {"error": "unknown_action", "message": f"Unknown action: {action}. Use: status, register, delta, clear, estimate, flush, usage"}
|
|
13311
13344
|
|
|
13312
13345
|
return _with_next_steps("toolcard_cache", r)
|
|
13313
13346
|
|
|
@@ -209,8 +209,71 @@ class ToolcardCache:
|
|
|
209
209
|
}
|
|
210
210
|
|
|
211
211
|
def record_call(self, tool_name: str) -> None:
|
|
212
|
-
"""Record that a tool was called in
|
|
212
|
+
"""Record that a tool was called: in-memory (session) AND durably.
|
|
213
|
+
|
|
214
|
+
The prior implementation only kept an in-memory counter that vanished on
|
|
215
|
+
process restart, so tool utilization was never observable across sessions
|
|
216
|
+
(usage.json stayed empty and dormancy was unmeasurable). We now also
|
|
217
|
+
append to a crash-safe, append-only JSONL event log so utilization is
|
|
218
|
+
measurable over time. Append is O(1) with no read-modify-write race.
|
|
219
|
+
Analytics must never break a tool call, so all failures are swallowed.
|
|
220
|
+
"""
|
|
213
221
|
self._session_calls[tool_name] = self._session_calls.get(tool_name, 0) + 1
|
|
222
|
+
try:
|
|
223
|
+
usage_log = self._cache_file.parent / "tool_usage.jsonl"
|
|
224
|
+
usage_log.parent.mkdir(parents=True, exist_ok=True)
|
|
225
|
+
with open(usage_log, "a") as f:
|
|
226
|
+
f.write(json.dumps({
|
|
227
|
+
"ts": datetime.now(timezone.utc).isoformat(),
|
|
228
|
+
"tool": tool_name,
|
|
229
|
+
}) + "\n")
|
|
230
|
+
except Exception:
|
|
231
|
+
pass
|
|
232
|
+
|
|
233
|
+
def usage_summary(self, registry: Optional[List[str]] = None) -> Dict[str, Any]:
|
|
234
|
+
"""Aggregate the durable usage log into per-tool counts + dormancy.
|
|
235
|
+
|
|
236
|
+
Reads tool_usage.jsonl (written by record_call). When `registry` (the
|
|
237
|
+
full list of registered tool names) is provided, tools that appear in
|
|
238
|
+
the registry but never in the log are reported as `dormant`.
|
|
239
|
+
"""
|
|
240
|
+
usage_log = self._cache_file.parent / "tool_usage.jsonl"
|
|
241
|
+
counts: Dict[str, int] = {}
|
|
242
|
+
last_seen: Dict[str, str] = {}
|
|
243
|
+
total = 0
|
|
244
|
+
try:
|
|
245
|
+
with open(usage_log, "r") as f:
|
|
246
|
+
for line in f:
|
|
247
|
+
line = line.strip()
|
|
248
|
+
if not line:
|
|
249
|
+
continue
|
|
250
|
+
try:
|
|
251
|
+
rec = json.loads(line)
|
|
252
|
+
except Exception:
|
|
253
|
+
continue
|
|
254
|
+
name = rec.get("tool")
|
|
255
|
+
if not name:
|
|
256
|
+
continue
|
|
257
|
+
counts[name] = counts.get(name, 0) + 1
|
|
258
|
+
ts = rec.get("ts")
|
|
259
|
+
if ts and (name not in last_seen or ts > last_seen[name]):
|
|
260
|
+
last_seen[name] = ts
|
|
261
|
+
total += 1
|
|
262
|
+
except FileNotFoundError:
|
|
263
|
+
pass
|
|
264
|
+
result: Dict[str, Any] = {
|
|
265
|
+
"total_calls": total,
|
|
266
|
+
"distinct_tools_used": len(counts),
|
|
267
|
+
"counts": dict(sorted(counts.items(), key=lambda x: x[1], reverse=True)),
|
|
268
|
+
"last_seen": last_seen,
|
|
269
|
+
"usage_log": str(usage_log),
|
|
270
|
+
}
|
|
271
|
+
if registry is not None:
|
|
272
|
+
used = set(counts)
|
|
273
|
+
result["registry_size"] = len(registry)
|
|
274
|
+
result["dormant"] = sorted(t for t in registry if t not in used)
|
|
275
|
+
result["dormant_count"] = len(result["dormant"])
|
|
276
|
+
return result
|
|
214
277
|
|
|
215
278
|
def get_stats(self) -> Dict[str, Any]:
|
|
216
279
|
"""Return cache stats: total tools, cached, cache hit rate, token savings."""
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "delimit-cli",
|
|
3
3
|
"mcpName": "io.github.delimit-ai/delimit-mcp-server",
|
|
4
|
-
"version": "4.7.
|
|
4
|
+
"version": "4.7.3",
|
|
5
5
|
"description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
|
|
6
6
|
"main": "index.js",
|
|
7
7
|
"files": [
|
package/server.json
CHANGED
|
@@ -7,13 +7,13 @@
|
|
|
7
7
|
"url": "https://github.com/delimit-ai/delimit-mcp-server",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "4.
|
|
10
|
+
"version": "4.7.3",
|
|
11
11
|
"websiteUrl": "https://delimit.ai",
|
|
12
12
|
"packages": [
|
|
13
13
|
{
|
|
14
14
|
"registryType": "npm",
|
|
15
15
|
"identifier": "delimit-cli",
|
|
16
|
-
"version": "4.
|
|
16
|
+
"version": "4.7.3",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|