delimit-cli 3.7.1 → 3.8.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/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-action-demo/pull/1)
43
43
 
44
44
  ### Example PR comment
45
45
 
@@ -327,6 +327,25 @@ Run full governance compliance checks. Verify security, policy compliance, evide
327
327
  }
328
328
  }
329
329
 
330
+ // Create instruction files for other platforms
331
+ const instructionContent = getClaudeMdContent();
332
+
333
+ // Codex instructions
334
+ const codexInstructions = path.join(os.homedir(), '.codex', 'instructions.md');
335
+ if (fs.existsSync(path.join(os.homedir(), '.codex'))) {
336
+ if (!fs.existsSync(codexInstructions) || fs.readFileSync(codexInstructions, 'utf-8').includes('# Delimit')) {
337
+ fs.writeFileSync(codexInstructions, instructionContent);
338
+ }
339
+ }
340
+
341
+ // Cursor rules
342
+ const cursorRules = path.join(os.homedir(), '.cursorrules');
343
+ if (fs.existsSync(path.join(os.homedir(), '.cursor'))) {
344
+ if (!fs.existsSync(cursorRules) || fs.readFileSync(cursorRules, 'utf-8').includes('# Delimit')) {
345
+ fs.writeFileSync(cursorRules, instructionContent);
346
+ }
347
+ }
348
+
330
349
  // Silent: auto-detect API keys and configure models.json (no output)
331
350
  const modelsPath = path.join(DELIMIT_HOME, 'models.json');
332
351
  if (!fs.existsSync(modelsPath)) {
@@ -365,12 +384,12 @@ Run full governance compliance checks. Verify security, policy compliance, evide
365
384
  log(' Try it now:');
366
385
  log(` ${bold('$ claude')}`);
367
386
  log('');
368
- log(` Then say: ${blue('"check this project\'s health"')}`);
387
+ log(` Then say: ${blue('"scan this project"')}`);
369
388
  log('');
370
389
  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')}`);
390
+ log(` ${dim('-')} "lint my API spec" ${dim('— catch breaking changes')}`);
391
+ log(` ${dim('-')} "add to ledger: set up CI pipeline" ${dim('— track tasks across sessions')}`);
392
+ log(` ${dim('-')} "deliberate [question]" ${dim('— multi-model AI consensus')}`);
374
393
  log('');
375
394
  log(` ${dim('Config:')} ${MCP_CONFIG}`);
376
395
  log(` ${dim('Server:')} ${actualServer}`);
@@ -386,11 +405,17 @@ function getClaudeMdContent() {
386
405
 
387
406
  One workspace for every AI coding assistant.
388
407
 
408
+ ## On every session start:
409
+ 1. Call \`delimit_ledger_context\` to check for open tasks
410
+ 2. If returning user, summarize what's pending
411
+ 3. If new project, call \`delimit_scan\` to discover what Delimit can do
412
+
389
413
  ## Try these:
414
+ - "scan this project" -- discover what Delimit can do here
390
415
  - "lint my API spec" -- catch breaking changes in your OpenAPI spec
391
416
  - "add to ledger: [anything]" -- track tasks across sessions
392
417
  - "what's on the ledger?" -- pick up where you left off
393
- - "check this project's health" -- run governance diagnostics
418
+ - "deliberate [question]" -- get multi-model AI consensus
394
419
 
395
420
  ## What Delimit does:
396
421
  - **API governance** -- lint, diff, semver classification, migration guides
@@ -34,7 +34,11 @@ from fastmcp import FastMCP
34
34
  logger = logging.getLogger("delimit.ai")
35
35
 
36
36
  mcp = FastMCP("delimit")
37
- mcp.description = "Delimit — The smart lint engine for OpenAPI. Unified agent surface."
37
+ mcp.description = (
38
+ "Delimit — One workspace for every AI coding assistant. "
39
+ "On session start, call delimit_ledger_context to check for open tasks. "
40
+ "Use delimit_scan on new projects. Track all work via the ledger."
41
+ )
38
42
 
39
43
  VERSION = "3.2.0"
40
44
 
@@ -2244,6 +2248,135 @@ def delimit_release_sync(action: str = "audit") -> Dict[str, Any]:
2244
2248
  return _with_next_steps("release_sync", audit())
2245
2249
 
2246
2250
 
2251
+ @mcp.tool()
2252
+ def delimit_scan(project_path: str = ".") -> Dict[str, Any]:
2253
+ """Scan a project and show what Delimit can do for it.
2254
+
2255
+ First-run discovery tool. Finds OpenAPI specs, checks for security issues,
2256
+ detects frameworks, and suggests what to track. Use this when you first
2257
+ install Delimit or open a new project.
2258
+
2259
+ Args:
2260
+ project_path: Path to the project to scan.
2261
+ """
2262
+ import glob as _glob
2263
+ p = Path(project_path).resolve()
2264
+ findings = []
2265
+ suggestions = []
2266
+
2267
+ # 1. Find OpenAPI specs
2268
+ spec_patterns = ["**/openapi.yaml", "**/openapi.yml", "**/openapi.json",
2269
+ "**/swagger.yaml", "**/swagger.yml", "**/swagger.json",
2270
+ "**/*api*.yaml", "**/*api*.yml"]
2271
+ specs_found = []
2272
+ for pattern in spec_patterns:
2273
+ for match in p.glob(pattern):
2274
+ rel = str(match.relative_to(p))
2275
+ if "node_modules" not in rel and ".next" not in rel and "venv" not in rel:
2276
+ specs_found.append(rel)
2277
+ specs_found = list(set(specs_found))[:10]
2278
+
2279
+ if specs_found:
2280
+ findings.append({"type": "openapi_specs", "count": len(specs_found), "files": specs_found})
2281
+ suggestions.append({"action": "lint", "detail": f"Run delimit_lint on {specs_found[0]} to check for issues"})
2282
+ suggestions.append({"action": "github_action", "detail": "Add the Delimit GitHub Action to catch breaking changes on PRs"})
2283
+ else:
2284
+ # Check for framework that could generate a spec
2285
+ framework = None
2286
+ if (p / "requirements.txt").exists() or (p / "pyproject.toml").exists():
2287
+ for py_file in p.rglob("*.py"):
2288
+ if "node_modules" in str(py_file):
2289
+ continue
2290
+ try:
2291
+ content = py_file.read_text(errors="ignore")[:2000]
2292
+ if "FastAPI" in content or "fastapi" in content:
2293
+ framework = "FastAPI"
2294
+ break
2295
+ except Exception:
2296
+ pass
2297
+ if (p / "package.json").exists():
2298
+ try:
2299
+ pkg = json.loads((p / "package.json").read_text())
2300
+ deps = {**pkg.get("dependencies", {}), **pkg.get("devDependencies", {})}
2301
+ if "@nestjs/core" in deps:
2302
+ framework = "NestJS"
2303
+ elif "express" in deps:
2304
+ framework = "Express"
2305
+ except Exception:
2306
+ pass
2307
+
2308
+ if framework:
2309
+ findings.append({"type": "framework_detected", "framework": framework, "has_spec": False})
2310
+ suggestions.append({"action": "zero_spec", "detail": f"Run delimit_zero_spec to extract an OpenAPI spec from your {framework} code"})
2311
+ else:
2312
+ findings.append({"type": "no_api_detected", "note": "No OpenAPI spec or supported framework found"})
2313
+
2314
+ # 2. Check for security patterns (quick scan)
2315
+ security_issues = []
2316
+ for pattern_name, pattern_glob, check in [
2317
+ ("env_file_in_git", ".env", lambda f: True),
2318
+ ("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]),
2319
+ ("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]),
2320
+ ]:
2321
+ try:
2322
+ for match in p.glob(pattern_glob):
2323
+ rel = str(match.relative_to(p))
2324
+ if "node_modules" in rel or ".next" in rel or "venv" in rel or "__pycache__" in rel:
2325
+ continue
2326
+ if check(match):
2327
+ security_issues.append({"issue": pattern_name, "file": rel})
2328
+ break # One per pattern is enough
2329
+ except Exception:
2330
+ pass
2331
+
2332
+ if security_issues:
2333
+ findings.append({"type": "security_concerns", "count": len(security_issues), "issues": security_issues})
2334
+ suggestions.append({"action": "security_audit", "detail": "Run delimit_security_audit for a full scan"})
2335
+
2336
+ # 3. Check git status
2337
+ try:
2338
+ import subprocess
2339
+ result = subprocess.run(["git", "log", "--oneline", "-1"], capture_output=True, text=True, timeout=5, cwd=str(p))
2340
+ if result.returncode == 0:
2341
+ findings.append({"type": "git_repo", "latest_commit": result.stdout.strip()})
2342
+ except Exception:
2343
+ pass
2344
+
2345
+ # 4. Check for existing tests
2346
+ test_files = list(p.glob("**/test_*.py")) + list(p.glob("**/*.test.js")) + list(p.glob("**/*.test.ts")) + list(p.glob("**/*.spec.js"))
2347
+ test_files = [f for f in test_files if "node_modules" not in str(f)]
2348
+ if test_files:
2349
+ findings.append({"type": "tests_found", "count": len(test_files)})
2350
+ suggestions.append({"action": "test_coverage", "detail": "Run delimit_test_smoke to verify tests pass and measure coverage"})
2351
+
2352
+ # 5. Check ledger
2353
+ from ai.ledger_manager import list_items
2354
+ ledger = list_items(project_path=str(p))
2355
+ open_items = [i for i in ledger.get("items", []) if isinstance(i, dict) and i.get("status") == "open"]
2356
+ if open_items:
2357
+ findings.append({"type": "ledger_active", "open_items": len(open_items), "top": [i.get("title", "") for i in open_items[:3]]})
2358
+ else:
2359
+ suggestions.append({"action": "ledger", "detail": "Say 'add to ledger: [task]' to start tracking work across sessions"})
2360
+
2361
+ # 6. Check deliberation models
2362
+ from ai.deliberation import get_models_config
2363
+ models = get_models_config()
2364
+ enabled = [v.get("name", k) for k, v in models.items() if v.get("enabled")]
2365
+ if len(enabled) >= 2:
2366
+ findings.append({"type": "deliberation_ready", "models": enabled})
2367
+ elif len(enabled) == 1:
2368
+ suggestions.append({"action": "models", "detail": f"Add 1 more AI model for multi-model deliberation (have {enabled[0]})"})
2369
+ else:
2370
+ suggestions.append({"action": "models", "detail": "Configure AI models for deliberation: say 'configure delimit models'"})
2371
+
2372
+ return _with_next_steps("scan", {
2373
+ "project": str(p),
2374
+ "findings": findings,
2375
+ "suggestions": suggestions,
2376
+ "summary": f"Found {len(findings)} things, {len(suggestions)} suggestions",
2377
+ })
2378
+
2379
+
2247
2380
  # ═══════════════════════════════════════════════════════════════════════
2248
2381
  # ENTRY POINT
2249
2382
  # ═══════════════════════════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
- "version": "3.7.1",
3
+ "version": "3.8.1",
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": [