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 +1 -1
- package/bin/delimit-setup.js +6 -5
- package/gateway/ai/server.py +129 -0
- package/package.json +1 -1
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
|
|
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
|
|
package/bin/delimit-setup.js
CHANGED
|
@@ -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('"
|
|
368
|
+
log(` Then say: ${blue('"scan this project"')}`);
|
|
369
369
|
log('');
|
|
370
370
|
log(' Or try:');
|
|
371
|
-
log(` ${dim('-')} "
|
|
372
|
-
log(` ${dim('-')} "
|
|
373
|
-
log(` ${dim('-')} "
|
|
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
|
-
- "
|
|
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
|
package/gateway/ai/server.py
CHANGED
|
@@ -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