delimit-cli 3.7.1 → 3.8.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.
package/README.md CHANGED
@@ -39,7 +39,7 @@ That's it. Delimit auto-fetches the base branch spec, diffs it, and posts a PR c
39
39
  - Step-by-step migration guide
40
40
  - Policy violations
41
41
 
42
- [View on GitHub Marketplace →](https://github.com/marketplace/actions/delimit-api-governance) · [See a live PR comment →](https://github.com/delimit-ai/delimit-quickstart/pull/1)
42
+ [View on GitHub Marketplace →](https://github.com/marketplace/actions/delimit-api-governance) · [See a live demo (6 breaking changes) →](https://github.com/delimit-ai/delimit-demo/pull/1)
43
43
 
44
44
  ### Example PR comment
45
45
 
@@ -365,12 +365,12 @@ Run full governance compliance checks. Verify security, policy compliance, evide
365
365
  log(' Try it now:');
366
366
  log(` ${bold('$ claude')}`);
367
367
  log('');
368
- log(` Then say: ${blue('"check this project\'s health"')}`);
368
+ log(` Then say: ${blue('"scan this project"')}`);
369
369
  log('');
370
370
  log(' Or try:');
371
- log(` ${dim('-')} "add to ledger: set up CI pipeline" ${dim('— start tracking tasks')}`);
372
- log(` ${dim('-')} "what\'s on the ledger?" ${dim('— see what\'s pending')}`);
373
- log(` ${dim('-')} "delimit help" ${dim('— see all capabilities')}`);
371
+ log(` ${dim('-')} "lint my API spec" ${dim('— catch breaking changes')}`);
372
+ log(` ${dim('-')} "add to ledger: set up CI pipeline" ${dim('— track tasks across sessions')}`);
373
+ log(` ${dim('-')} "deliberate [question]" ${dim('— multi-model AI consensus')}`);
374
374
  log('');
375
375
  log(` ${dim('Config:')} ${MCP_CONFIG}`);
376
376
  log(` ${dim('Server:')} ${actualServer}`);
@@ -387,10 +387,11 @@ function getClaudeMdContent() {
387
387
  One workspace for every AI coding assistant.
388
388
 
389
389
  ## Try these:
390
+ - "scan this project" -- discover what Delimit can do here
390
391
  - "lint my API spec" -- catch breaking changes in your OpenAPI spec
391
392
  - "add to ledger: [anything]" -- track tasks across sessions
392
393
  - "what's on the ledger?" -- pick up where you left off
393
- - "check this project's health" -- run governance diagnostics
394
+ - "deliberate [question]" -- get multi-model AI consensus
394
395
 
395
396
  ## What Delimit does:
396
397
  - **API governance** -- lint, diff, semver classification, migration guides
@@ -2244,6 +2244,135 @@ def delimit_release_sync(action: str = "audit") -> Dict[str, Any]:
2244
2244
  return _with_next_steps("release_sync", audit())
2245
2245
 
2246
2246
 
2247
+ @mcp.tool()
2248
+ def delimit_scan(project_path: str = ".") -> Dict[str, Any]:
2249
+ """Scan a project and show what Delimit can do for it.
2250
+
2251
+ First-run discovery tool. Finds OpenAPI specs, checks for security issues,
2252
+ detects frameworks, and suggests what to track. Use this when you first
2253
+ install Delimit or open a new project.
2254
+
2255
+ Args:
2256
+ project_path: Path to the project to scan.
2257
+ """
2258
+ import glob as _glob
2259
+ p = Path(project_path).resolve()
2260
+ findings = []
2261
+ suggestions = []
2262
+
2263
+ # 1. Find OpenAPI specs
2264
+ spec_patterns = ["**/openapi.yaml", "**/openapi.yml", "**/openapi.json",
2265
+ "**/swagger.yaml", "**/swagger.yml", "**/swagger.json",
2266
+ "**/*api*.yaml", "**/*api*.yml"]
2267
+ specs_found = []
2268
+ for pattern in spec_patterns:
2269
+ for match in p.glob(pattern):
2270
+ rel = str(match.relative_to(p))
2271
+ if "node_modules" not in rel and ".next" not in rel and "venv" not in rel:
2272
+ specs_found.append(rel)
2273
+ specs_found = list(set(specs_found))[:10]
2274
+
2275
+ if specs_found:
2276
+ findings.append({"type": "openapi_specs", "count": len(specs_found), "files": specs_found})
2277
+ suggestions.append({"action": "lint", "detail": f"Run delimit_lint on {specs_found[0]} to check for issues"})
2278
+ suggestions.append({"action": "github_action", "detail": "Add the Delimit GitHub Action to catch breaking changes on PRs"})
2279
+ else:
2280
+ # Check for framework that could generate a spec
2281
+ framework = None
2282
+ if (p / "requirements.txt").exists() or (p / "pyproject.toml").exists():
2283
+ for py_file in p.rglob("*.py"):
2284
+ if "node_modules" in str(py_file):
2285
+ continue
2286
+ try:
2287
+ content = py_file.read_text(errors="ignore")[:2000]
2288
+ if "FastAPI" in content or "fastapi" in content:
2289
+ framework = "FastAPI"
2290
+ break
2291
+ except Exception:
2292
+ pass
2293
+ if (p / "package.json").exists():
2294
+ try:
2295
+ pkg = json.loads((p / "package.json").read_text())
2296
+ deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
2297
+ if "@nestjs/core" in deps:
2298
+ framework = "NestJS"
2299
+ elif "express" in deps:
2300
+ framework = "Express"
2301
+ except Exception:
2302
+ pass
2303
+
2304
+ if framework:
2305
+ findings.append({"type": "framework_detected", "framework": framework, "has_spec": False})
2306
+ suggestions.append({"action": "zero_spec", "detail": f"Run delimit_zero_spec to extract an OpenAPI spec from your {framework} code"})
2307
+ else:
2308
+ findings.append({"type": "no_api_detected", "note": "No OpenAPI spec or supported framework found"})
2309
+
2310
+ # 2. Check for security patterns (quick scan)
2311
+ security_issues = []
2312
+ for pattern_name, pattern_glob, check in [
2313
+ ("env_file_in_git", ".env", lambda f: True),
2314
+ ("hardcoded_key", "**/*.py", lambda f: "API_KEY" in f.read_text(errors="ignore")[:5000] and "os.environ" not in f.read_text(errors="ignore")[:5000]),
2315
+ ("hardcoded_key_js", "**/*.js", lambda f: "apiKey" in f.read_text(errors="ignore")[:5000] and "process.env" not in f.read_text(errors="ignore")[:5000]),
2316
+ ]:
2317
+ try:
2318
+ for match in p.glob(pattern_glob):
2319
+ rel = str(match.relative_to(p))
2320
+ if "node_modules" in rel or ".next" in rel or "venv" in rel or "__pycache__" in rel:
2321
+ continue
2322
+ if check(match):
2323
+ security_issues.append({"issue": pattern_name, "file": rel})
2324
+ break # One per pattern is enough
2325
+ except Exception:
2326
+ pass
2327
+
2328
+ if security_issues:
2329
+ findings.append({"type": "security_concerns", "count": len(security_issues), "issues": security_issues})
2330
+ suggestions.append({"action": "security_audit", "detail": "Run delimit_security_audit for a full scan"})
2331
+
2332
+ # 3. Check git status
2333
+ try:
2334
+ import subprocess
2335
+ result = subprocess.run(["git", "log", "--oneline", "-1"], capture_output=True, text=True, timeout=5, cwd=str(p))
2336
+ if result.returncode == 0:
2337
+ findings.append({"type": "git_repo", "latest_commit": result.stdout.strip()})
2338
+ except Exception:
2339
+ pass
2340
+
2341
+ # 4. Check for existing tests
2342
+ test_files = list(p.glob("**/test_*.py")) + list(p.glob("**/*.test.js")) + list(p.glob("**/*.test.ts")) + list(p.glob("**/*.spec.js"))
2343
+ test_files = [f for f in test_files if "node_modules" not in str(f)]
2344
+ if test_files:
2345
+ findings.append({"type": "tests_found", "count": len(test_files)})
2346
+ suggestions.append({"action": "test_coverage", "detail": "Run delimit_test_smoke to verify tests pass and measure coverage"})
2347
+
2348
+ # 5. Check ledger
2349
+ from ai.ledger_manager import list_items
2350
+ ledger = list_items(project_path=str(p))
2351
+ open_items = [i for i in ledger.get("items", []) if isinstance(i, dict) and i.get("status") == "open"]
2352
+ if open_items:
2353
+ findings.append({"type": "ledger_active", "open_items": len(open_items), "top": [i.get("title", "") for i in open_items[:3]]})
2354
+ else:
2355
+ suggestions.append({"action": "ledger", "detail": "Say 'add to ledger: [task]' to start tracking work across sessions"})
2356
+
2357
+ # 6. Check deliberation models
2358
+ from ai.deliberation import get_models_config
2359
+ models = get_models_config()
2360
+ enabled = [v.get("name", k) for k, v in models.items() if v.get("enabled")]
2361
+ if len(enabled) >= 2:
2362
+ findings.append({"type": "deliberation_ready", "models": enabled})
2363
+ elif len(enabled) == 1:
2364
+ suggestions.append({"action": "models", "detail": f"Add 1 more AI model for multi-model deliberation (have {enabled[0]})"})
2365
+ else:
2366
+ suggestions.append({"action": "models", "detail": "Configure AI models for deliberation: say 'configure delimit models'"})
2367
+
2368
+ return _with_next_steps("scan", {
2369
+ "project": str(p),
2370
+ "findings": findings,
2371
+ "suggestions": suggestions,
2372
+ "summary": f"Found {len(findings)} things, {len(suggestions)} suggestions",
2373
+ })
2374
+
2375
+
2247
2376
  # ═══════════════════════════════════════════════════════════════════════
2248
2377
  # ENTRY POINT
2249
2378
  # ═══════════════════════════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
- "version": "3.7.1",
3
+ "version": "3.8.0",
4
4
  "description": "One workspace for every AI coding assistant. Tasks, memory, and governance carry between Claude Code, Codex, and Gemini CLI.",
5
5
  "main": "index.js",
6
6
  "files": [