delimit-cli 4.0.1 → 4.0.2

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.
@@ -19,8 +19,9 @@ All tools follow the Adapter Boundary Contract v1.0:
19
19
  - Stateless between calls
20
20
  """
21
21
 
22
- # ── Output Quality Rules ──────────────────────────────────────────────
22
+ # ── Founder Voice Doctrine ──────────────────────────────────────────────
23
23
  # Applies to ALL outward-facing text generated by any tool in this server.
24
+ # Full doctrine: /home/delimit/delimit-private/style/FOUNDER_VOICE_DOCTRINE.md
24
25
  #
25
26
  # Core: serious builder/operator, not a marketer. Credibility over persuasion.
26
27
  # Truth over excitement. Concrete mechanisms, not vague benefits.
@@ -33,7 +34,14 @@ All tools follow the Adapter Boundary Contract v1.0:
33
34
  # attached to decisions? Would this still read well a year from now?
34
35
  # ────────────────────────────────────────────────────────────────────────
35
36
 
36
- # Output quality rules loaded from user config at runtime.
37
+ FOUNDER_VOICE_HYPE_WORDS = {
38
+ "revolutionary", "game-changing", "world-class", "cutting-edge",
39
+ "best-in-class", "seamless", "unlock", "supercharge", "next-generation",
40
+ "magical", "delightful", "effortless", "frictionless", "transformative",
41
+ "paradigm shift", "visionary", "category-defining", "industry-leading",
42
+ "innovative", "reimagine", "future of", "changing the game",
43
+ "empowering teams", "built for everyone",
44
+ }
37
45
 
38
46
  import json
39
47
  import logging
@@ -52,13 +60,13 @@ from fastmcp import FastMCP
52
60
  logger = logging.getLogger("delimit.ai")
53
61
 
54
62
  # ═══════════════════════════════════════════════════════════════════════
55
- # TASK: Agent Identity — session tracking for every tool call
63
+ # STR-046: Agent Identity — session tracking for every tool call
56
64
  # ═══════════════════════════════════════════════════════════════════════
57
65
 
58
66
  _current_session_id = os.environ.get("DELIMIT_SESSION_ID", "")
59
67
 
60
68
  # ═══════════════════════════════════════════════════════════════════════
61
- # TASK: Distributed Tracing — trace ID + span counter for every call
69
+ # STR-053: Distributed Tracing — trace ID + span counter for every call
62
70
  # ═══════════════════════════════════════════════════════════════════════
63
71
 
64
72
  _trace_id = os.environ.get("DELIMIT_TRACE_ID", str(uuid.uuid4())[:8])
@@ -243,7 +251,7 @@ def _coerce_dict_arg(
243
251
 
244
252
 
245
253
  # ═══════════════════════════════════════════════════════════════════════
246
- # TASK: Risk Classification for Approval Gates
254
+ # STR-040: Risk Classification for Approval Gates
247
255
  # ═══════════════════════════════════════════════════════════════════════
248
256
 
249
257
  HIGH_RISK_TOOLS = {
@@ -267,7 +275,7 @@ def _classify_risk(tool_name: str) -> str:
267
275
 
268
276
 
269
277
  # ═══════════════════════════════════════════════════════════════════════
270
- # TASK: Policy Kernel — Inline Enforcement
278
+ # STR-052: Policy Kernel — Inline Enforcement
271
279
  # Checks policy BEFORE/AFTER tool execution to block high-risk actions.
272
280
  # ═══════════════════════════════════════════════════════════════════════
273
281
 
@@ -319,7 +327,7 @@ def _check_policy_gate(tool_name: str, kwargs: dict) -> Optional[Dict]:
319
327
  "action": "Switch to guarded mode or request approval",
320
328
  }
321
329
 
322
- # TASK: Deploy gating — block deploys when unresolved critical findings exist
330
+ # LED-173: Deploy gating — block deploys when unresolved critical findings exist
323
331
  DEPLOY_TOOLS = {"deploy_publish", "deploy_npm", "deploy_site", "deploy_build"}
324
332
  clean = tool_name.replace("delimit_", "")
325
333
  if clean in DEPLOY_TOOLS and mode != "advisory":
@@ -447,7 +455,7 @@ mcp.description = (
447
455
 
448
456
  VERSION = "3.2.1"
449
457
 
450
- # TASK + Consensus 118/119/120: Tool visibility tiers.
458
+ # LED-044 + Consensus 118/119/120: Tool visibility tiers.
451
459
  # Tier cascade: SHOW_EXPERIMENTAL > SHOW_INTERNAL > SHOW_OPS > public (always visible).
452
460
  # Set DELIMIT_SHOW_INTERNAL=1 to see all tiers (founder workflow).
453
461
  SHOW_EXPERIMENTAL = os.environ.get("DELIMIT_SHOW_EXPERIMENTAL", "") == "1"
@@ -832,7 +840,7 @@ NEXT_STEPS_REGISTRY: Dict[str, List[Dict[str, Any]]] = {
832
840
  ],
833
841
  # --- Sensing ---
834
842
  "sensor_github_issue": [],
835
- # --- Context Filesystem (TASK) ---
843
+ # --- Context Filesystem (STR-048) ---
836
844
  "context_init": [
837
845
  {"tool": "delimit_context_write", "reason": "Write an artifact to the new context", "suggested_args": {}, "is_premium": False},
838
846
  ],
@@ -958,10 +966,10 @@ def _emit_event(tool_name: str, result: Dict[str, Any]) -> None:
958
966
  except Exception:
959
967
  pass # Never let cloud sync break tool execution
960
968
 
961
- # TASK: Webhook notifications for governance events
969
+ # LED-183: Webhook notifications for governance events
962
970
  _fire_webhook(event)
963
971
 
964
- # TASK: Write trace span for session replay
972
+ # STR-053: Write trace span for session replay
965
973
  try:
966
974
  from ai.tracing import start_span, end_span
967
975
  span = start_span(_trace_id, tool_name, args={"tool": tool_name})
@@ -973,7 +981,7 @@ def _emit_event(tool_name: str, result: Dict[str, Any]) -> None:
973
981
  except Exception:
974
982
  pass # Tracing is best-effort
975
983
 
976
- # TASK: Write to agent_actions log for session drill-down
984
+ # STR-046: Write to agent_actions log for session drill-down
977
985
  if session_info["session_id"]:
978
986
  actions_dir = Path.home() / ".delimit" / "agent_actions"
979
987
  actions_dir.mkdir(parents=True, exist_ok=True)
@@ -1177,7 +1185,7 @@ def _with_next_steps(tool_name: str, result: Dict[str, Any]) -> Dict[str, Any]:
1177
1185
  except Exception as e:
1178
1186
  logger.warning("Inbox daemon auto-start failed: %s", e)
1179
1187
 
1180
- # TASK: Auto-register tool schemas with toolcard cache on first call
1188
+ # LED-219: Auto-register tool schemas with toolcard cache on first call
1181
1189
  global _toolcard_cache_autoregistered
1182
1190
  if not _toolcard_cache_autoregistered:
1183
1191
  _toolcard_cache_autoregistered = True
@@ -1195,7 +1203,7 @@ def _with_next_steps(tool_name: str, result: Dict[str, Any]) -> Dict[str, Any]:
1195
1203
  except Exception as e:
1196
1204
  logger.warning("Toolcard cache auto-register failed: %s", e)
1197
1205
 
1198
- # TASK: Track every tool call for session analytics
1206
+ # LED-219: Track every tool call for session analytics
1199
1207
  try:
1200
1208
  from ai.toolcard_cache import get_cache as _get_tc
1201
1209
  _get_tc().record_call(tool_name)
@@ -1208,7 +1216,282 @@ def _with_next_steps(tool_name: str, result: Dict[str, Any]) -> Dict[str, Any]:
1208
1216
  result.get("explanation", ""), result.get("changelog", ""),
1209
1217
  result.get("content", "")]
1210
1218
  _all_text = " ".join(str(f) for f in _text_fields if f).lower()
1211
- # Output quality rules loaded from user config at runtime.
1219
+ _found_hype = [w for w in FOUNDER_VOICE_HYPE_WORDS if w in _all_text]
1220
+ if _found_hype:
1221
+ result.setdefault("voice_warnings", []).append(
1222
+ f"VOICE DOCTRINE: Hype words detected: {', '.join(_found_hype)}. "
1223
+ f"Rewrite with concrete mechanisms, not vague benefits."
1224
+ )
1225
+
1226
+ # Emit event for real-time dashboard
1227
+ _emit_event(tool_name, result)
1228
+
1229
+ # STR-052: Policy kernel inline enforcement
1230
+ policy_gate = _check_policy_gate(tool_name, result if isinstance(result, dict) else {})
1231
+ if policy_gate:
1232
+ policy_gate["original_result"] = result
1233
+ policy_gate["governance"] = {"action": "policy_blocked", "reason": policy_gate["reason"]}
1234
+ return policy_gate
1235
+
1236
+ # LED-195: Prompt injection detection on tool inputs
1237
+ if isinstance(result, dict):
1238
+ injection = _detect_prompt_injection(result, tool_name)
1239
+ if injection:
1240
+ result["_security_warning"] = injection
1241
+
1242
+ # Pro license gate — blocks execution for premium tools
1243
+ full_name = f"delimit_{tool_name}" if not tool_name.startswith("delimit_") else tool_name
1244
+ gate = _check_pro(full_name)
1245
+ if gate:
1246
+ return gate
1247
+
1248
+ # Route through governance loop
1249
+ try:
1250
+ from ai.governance import govern
1251
+ return govern(tool_name, result)
1252
+ except Exception:
1253
+ # Fallback: just add next_steps from registry
1254
+ steps = NEXT_STEPS_REGISTRY.get(tool_name, [])
1255
+ result["next_steps"] = steps
1256
+ return result
1257
+
1258
+
1259
+ # ═══════════════════════════════════════════════════════════════════════
1260
+ # TIER 1: CORE — API Lint Engine
1261
+ # ═══════════════════════════════════════════════════════════════════════
1262
+
1263
+
1264
+ @mcp.tool()
1265
+ def delimit_lint(old_spec: str, new_spec: str, policy_file: Optional[str] = None) -> Dict[str, Any]:
1266
+ """Lint two OpenAPI specs for breaking changes and policy violations.
1267
+ Primary CI integration point. Combines diff + policy into pass/fail.
1268
+ Auto-chains: semver classification, governance evaluation on breaking changes.
1269
+
1270
+ Args:
1271
+ old_spec: Path to the old (baseline) OpenAPI spec file.
1272
+ new_spec: Path to the new (proposed) OpenAPI spec file.
1273
+ policy_file: Optional path to a .delimit/policies.yml file.
1274
+ """
1275
+ from backends.gateway_core import run_lint, run_semver
1276
+
1277
+ # Step 1: Core lint
1278
+ lint_result = _safe_call(run_lint, old_spec=old_spec, new_spec=new_spec, policy_file=policy_file)
1279
+ chain: Dict[str, Any] = {"id": "lint_chain", "steps": []}
1280
+
1281
+ if lint_result.get("error"):
1282
+ lint_result["chain"] = chain
1283
+ return _with_next_steps("lint", lint_result)
1284
+
1285
+ # Step 2: Auto-classify semver bump (non-blocking on failure)
1286
+ semver_result = _chain_call("lint", "semver", run_semver,
1287
+ required=False, old_spec=old_spec, new_spec=new_spec)
1288
+ chain["steps"].append({"step": "semver", "ok": not _chain_is_error(semver_result)})
1289
+ lint_result["semver"] = semver_result
1290
+
1291
+ if _chain_is_error(semver_result):
1292
+ chain["status"] = "semver_failed_nonfatal"
1293
+ lint_result["chain"] = chain
1294
+ return _with_next_steps("lint", lint_result)
1295
+
1296
+ bump = str(semver_result.get("bump", "")).upper()
1297
+
1298
+ # Step 2b: Impact-based notification routing (LED-233, non-blocking)
1299
+ try:
1300
+ from ai.notify import route_by_impact
1301
+ all_changes = lint_result.get("all_changes", lint_result.get("violations", []))
1302
+ if all_changes:
1303
+ routing_result = route_by_impact(all_changes, dry_run=False)
1304
+ chain["steps"].append({"step": "impact_routing", "ok": True})
1305
+ lint_result["impact_routing"] = routing_result
1306
+ except Exception as e:
1307
+ logger.debug("Impact routing non-fatal error: %s", e)
1308
+ chain["steps"].append({"step": "impact_routing", "ok": False, "error": str(e)})
1309
+
1310
+ if bump != "MAJOR":
1311
+ chain["status"] = f"complete_{bump.lower() or 'none'}"
1312
+ lint_result["chain"] = chain
1313
+ return _with_next_steps("lint", lint_result)
1314
+
1315
+ # Step 3: MAJOR bump detected -- evaluate governance
1316
+ # Note: _delimit_gov_impl has its own Pro gate. Free-tier gets lint+semver only.
1317
+ gov_result = _delimit_gov_impl(
1318
+ action="evaluate",
1319
+ eval_action="api_breaking_change",
1320
+ context={
1321
+ "tool": "delimit_lint",
1322
+ "old_spec": old_spec,
1323
+ "new_spec": new_spec,
1324
+ "semver_bump": bump,
1325
+ "breaking_changes": lint_result.get("breaking", []),
1326
+ },
1327
+ repo=".",
1328
+ )
1329
+ chain["steps"].append({"step": "gov_evaluate", "ok": not _chain_is_error(gov_result)})
1330
+ lint_result["gov_evaluate"] = gov_result
1331
+
1332
+ # If Pro gate blocked governance, return gracefully with lint+semver
1333
+ if gov_result.get("status") == "premium_required":
1334
+ chain["status"] = "governance_skipped_free_tier"
1335
+ lint_result["chain"] = chain
1336
+ return _with_next_steps("lint", lint_result)
1337
+
1338
+ # Step 4: If governance blocked, record in ledger (best-effort)
1339
+ gov_blocked = (
1340
+ str(gov_result.get("status", "")).lower() == "blocked"
1341
+ or gov_result.get("governance", {}).get("action") == "policy_blocked"
1342
+ )
1343
+
1344
+ if gov_blocked:
1345
+ from ai.ledger_manager import add_item
1346
+ ledger_result = _chain_call(
1347
+ "lint", "ledger_add", add_item,
1348
+ required=False,
1349
+ title=f"Governance blocked: MAJOR API change in {new_spec}",
1350
+ ledger="ops",
1351
+ item_type="fix",
1352
+ priority="P0",
1353
+ description="MAJOR semver bump detected. Governance blocked the change.",
1354
+ source="chain:lint:gov_blocked",
1355
+ )
1356
+ chain["steps"].append({"step": "ledger_add", "ok": not _chain_is_error(ledger_result)})
1357
+ lint_result["governance_blocked"] = True
1358
+ else:
1359
+ lint_result["governance_blocked"] = False
1360
+
1361
+ chain["status"] = "major_change_evaluated"
1362
+ lint_result["chain"] = chain
1363
+ return _with_next_steps("lint", lint_result)
1364
+
1365
+
1366
+ @mcp.tool()
1367
+ def delimit_diff(old_spec: str, new_spec: str) -> Dict[str, Any]:
1368
+ """Diff two OpenAPI specs and list all changes. Pure diff, no policy.
1369
+
1370
+ Args:
1371
+ old_spec: Path to the old OpenAPI spec file.
1372
+ new_spec: Path to the new OpenAPI spec file.
1373
+ """
1374
+ from backends.gateway_core import run_diff
1375
+ return _with_next_steps("diff", _safe_call(run_diff, old_spec=old_spec, new_spec=new_spec))
1376
+
1377
+
1378
+ @mcp.tool()
1379
+ def delimit_policy(spec_files: List[str], policy_file: Optional[str] = None) -> Dict[str, Any]:
1380
+ """Inspect or validate governance policy configuration.
1381
+
1382
+ Args:
1383
+ spec_files: List of spec file paths.
1384
+ policy_file: Optional custom policy file path.
1385
+ """
1386
+ from backends.gateway_core import run_policy
1387
+ return _with_next_steps("policy", _safe_call(run_policy, spec_files=spec_files, policy_file=policy_file))
1388
+
1389
+
1390
+ @mcp.tool()
1391
+ def delimit_ledger(ledger_path: str, api_name: Optional[str] = None, repository: Optional[str] = None, validate_chain: bool = False) -> Dict[str, Any]:
1392
+ """Query the append-only contract ledger (hash-chained JSONL).
1393
+
1394
+ Args:
1395
+ ledger_path: Path to the ledger JSONL file (e.g. .delimit/ledger/operations.jsonl).
1396
+ api_name: Filter events by API name.
1397
+ repository: Filter events by repository.
1398
+ validate_chain: Validate hash chain integrity.
1399
+ """
1400
+ from backends.gateway_core import query_ledger
1401
+ return _with_next_steps("ledger", _safe_call(query_ledger, ledger_path=ledger_path, api_name=api_name, repository=repository, validate_chain=validate_chain))
1402
+
1403
+
1404
+ @mcp.tool()
1405
+ def delimit_impact(api_name: str, dependency_file: Optional[str] = None) -> Dict[str, Any]:
1406
+ """Analyze downstream impact of an API change. Informational only.
1407
+
1408
+ Args:
1409
+ api_name: The API that changed.
1410
+ dependency_file: Optional path to dependency manifest.
1411
+ """
1412
+ from backends.gateway_core import run_impact
1413
+ return _with_next_steps("impact", _safe_call(run_impact, api_name=api_name, dependency_file=dependency_file))
1414
+
1415
+
1416
+ @mcp.tool()
1417
+ def delimit_semver(old_spec: str, new_spec: str, current_version: Optional[str] = None) -> Dict[str, Any]:
1418
+ """Classify the semver bump for a spec change (MAJOR/MINOR/PATCH/NONE).
1419
+
1420
+ Deterministic classification based on diff engine output.
1421
+ Optionally computes the next version string.
1422
+
1423
+ Args:
1424
+ old_spec: Path to the old OpenAPI spec file.
1425
+ new_spec: Path to the new OpenAPI spec file.
1426
+ current_version: Optional current version (e.g. "1.2.3") to compute next version.
1427
+ """
1428
+ from backends.gateway_core import run_semver
1429
+ return _with_next_steps("semver", _safe_call(run_semver, old_spec=old_spec, new_spec=new_spec, current_version=current_version))
1430
+
1431
+
1432
+ @mcp.tool()
1433
+ def delimit_explain(
1434
+ old_spec: str,
1435
+ new_spec: str,
1436
+ template: str = "developer",
1437
+ old_version: Optional[str] = None,
1438
+ new_version: Optional[str] = None,
1439
+ api_name: Optional[str] = None,
1440
+ ) -> Dict[str, Any]:
1441
+ """Generate a human-readable explanation of API changes.
1442
+
1443
+ 7 templates: developer, team_lead, product, migration, changelog, pr_comment, slack.
1444
+
1445
+ Args:
1446
+ old_spec: Path to the old OpenAPI spec file.
1447
+ new_spec: Path to the new OpenAPI spec file.
1448
+ template: Template name (default: developer).
1449
+ old_version: Previous version string.
1450
+ new_version: New version string.
1451
+ api_name: API/service name for context.
1452
+ """
1453
+ from backends.gateway_core import run_explain
1454
+ return _with_next_steps("explain", _safe_call(run_explain, old_spec=old_spec, new_spec=new_spec, template=template, old_version=old_version, new_version=new_version, api_name=api_name))
1455
+
1456
+
1457
+ @mcp.tool()
1458
+ def delimit_zero_spec(
1459
+ project_dir: str = ".",
1460
+ python_bin: Optional[str] = None,
1461
+ ) -> Dict[str, Any]:
1462
+ """Extract OpenAPI spec from framework source code (no spec file needed).
1463
+
1464
+ Detects the API framework (FastAPI, Express, NestJS) and extracts a
1465
+ complete OpenAPI specification directly from the source code.
1466
+ Currently supports FastAPI with full fidelity.
1467
+
1468
+ Args:
1469
+ project_dir: Path to the project root directory.
1470
+ python_bin: Optional Python binary path (auto-detected if omitted).
1471
+ """
1472
+ from backends.gateway_core import run_zero_spec
1473
+ return _with_next_steps("zero_spec", _safe_call(run_zero_spec, project_dir=project_dir, python_bin=python_bin))
1474
+
1475
+
1476
+
1477
+
1478
+ @mcp.tool()
1479
+ def delimit_init(
1480
+ project_path: str = ".",
1481
+ preset: str = "default",
1482
+ ) -> Dict[str, Any]:
1483
+ """Initialize Delimit governance for a project. Creates .delimit/policies.yml and ledger directory.
1484
+
1485
+ Args:
1486
+ project_path: Project root directory.
1487
+ preset: Policy preset — strict, default, or relaxed.
1488
+ """
1489
+ VALID_PRESETS = ("strict", "default", "relaxed")
1490
+ if preset not in VALID_PRESETS:
1491
+ return {
1492
+ "error": "invalid_preset",
1493
+ "message": f"Preset must be one of {VALID_PRESETS}, got '{preset}'",
1494
+ }
1212
1495
 
1213
1496
  root = Path(project_path).resolve()
1214
1497
  delimit_dir = root / ".delimit"
@@ -1524,7 +1807,7 @@ def delimit_memory_store(
1524
1807
  tags: Optional categorization tags.
1525
1808
  context: Optional context about when/why this was stored.
1526
1809
  """
1527
- # TASK: memory_store is now free (basic store)
1810
+ # LED-193: memory_store is now free (basic store)
1528
1811
  try:
1529
1812
  tags = _coerce_list_arg(tags, "tags")
1530
1813
  except ValueError as e:
@@ -1542,7 +1825,7 @@ def delimit_memory_recent(limit: int = 5) -> Dict[str, Any]:
1542
1825
  Args:
1543
1826
  limit: Number of recent entries to return.
1544
1827
  """
1545
- # TASK: memory_recent is now free (basic retrieval)
1828
+ # LED-193: memory_recent is now free (basic retrieval)
1546
1829
  from backends.memory_bridge import get_recent
1547
1830
  return _with_next_steps("memory_recent", _safe_call(get_recent, limit=limit))
1548
1831
 
@@ -1794,7 +2077,7 @@ def delimit_deploy_publish(app: str = "", git_ref: Optional[str] = None) -> Dict
1794
2077
  return _delimit_deploy_impl(action="publish", app=app, git_ref=git_ref)
1795
2078
 
1796
2079
 
1797
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2080
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
1798
2081
  @mcp.tool()
1799
2082
  def delimit_deploy_verify(app: str = "", env: str = "", git_ref: Optional[str] = None) -> Dict[str, Any]:
1800
2083
  """Verify deployment health (experimental) (Pro)."""
@@ -1948,7 +2231,7 @@ def delimit_generate_scaffold(
1948
2231
 
1949
2232
  # ─── Repo (RepoDoctor + ConfigSentry) ──────────────────────────────────
1950
2233
 
1951
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2234
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
1952
2235
  @mcp.tool()
1953
2236
  def delimit_repo_diagnose(target: str = ".") -> Dict[str, Any]:
1954
2237
  """Diagnose repository health issues (experimental) (Pro).
@@ -1964,7 +2247,7 @@ def delimit_repo_diagnose(target: str = ".") -> Dict[str, Any]:
1964
2247
  return _safe_call(diagnose, target=target)
1965
2248
 
1966
2249
 
1967
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2250
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
1968
2251
  @mcp.tool()
1969
2252
  def delimit_repo_analyze(target: str = ".") -> Dict[str, Any]:
1970
2253
  """Analyze repository structure and quality (experimental) (Pro).
@@ -1980,7 +2263,7 @@ def delimit_repo_analyze(target: str = ".") -> Dict[str, Any]:
1980
2263
  return _safe_call(analyze, target=target)
1981
2264
 
1982
2265
 
1983
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2266
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
1984
2267
  @mcp.tool()
1985
2268
  def delimit_repo_config_validate(target: str = ".") -> Dict[str, Any]:
1986
2269
  """Validate configuration files (experimental) (Pro).
@@ -1996,7 +2279,7 @@ def delimit_repo_config_validate(target: str = ".") -> Dict[str, Any]:
1996
2279
  return _safe_call(config_validate, target=target)
1997
2280
 
1998
2281
 
1999
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2282
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
2000
2283
  @mcp.tool()
2001
2284
  def delimit_repo_config_audit(target: str = ".") -> Dict[str, Any]:
2002
2285
  """Audit configuration compliance (experimental) (Pro).
@@ -2193,7 +2476,7 @@ def delimit_security_ingest(
2193
2476
  medium = [f for f in findings if f["severity"] in ("medium", "moderate", "warning")]
2194
2477
  low = [f for f in findings if f["severity"] in ("low", "info")]
2195
2478
 
2196
- # TASK: Auto-track security findings in ledger with lifecycle
2479
+ # LED-172: Auto-track security findings in ledger with lifecycle
2197
2480
  ledger_created = []
2198
2481
  ledger_closed = []
2199
2482
  try:
@@ -2642,14 +2925,14 @@ def delimit_release_status(environment: str = "production") -> Dict[str, Any]:
2642
2925
  return _delimit_release_impl(action="status", environment=environment)
2643
2926
 
2644
2927
 
2645
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2928
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
2646
2929
  @mcp.tool()
2647
2930
  def delimit_release_rollback(environment: str, version: str, to_version: str) -> Dict[str, Any]:
2648
2931
  """Rollback deployment to previous version (experimental)."""
2649
2932
  return _delimit_release_impl(action="rollback", environment=environment, version=version, to_version=to_version)
2650
2933
 
2651
2934
 
2652
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
2935
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
2653
2936
  @mcp.tool()
2654
2937
  def delimit_release_history(environment: str, limit: int = 10) -> Dict[str, Any]:
2655
2938
  """Show release history (experimental)."""
@@ -2868,7 +3151,7 @@ def delimit_obs_logs(query: str, time_range: str = "1h", source: Optional[str] =
2868
3151
  return _delimit_obs_impl(action="logs", query=query, time_range=time_range, source=source)
2869
3152
 
2870
3153
 
2871
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
3154
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
2872
3155
  @mcp.tool()
2873
3156
  def delimit_obs_alerts(action: str, alert_rule: Optional[Dict[str, Any]] = None, rule_id: Optional[str] = None) -> Dict[str, Any]:
2874
3157
  """Manage alerting rules (experimental)."""
@@ -3015,7 +3298,7 @@ def delimit_story_visual_test(url: str, project_path: Optional[str] = None, thre
3015
3298
  return _with_next_steps("story_visual_test", _safe_call(story_visual_test, url=url, project_path=project_path, threshold=threshold))
3016
3299
 
3017
3300
 
3018
- @_internal_tool() # Was experimental (TASK), promoted to internal (Consensus 120)
3301
+ @_internal_tool() # Was experimental (LED-044), promoted to internal (Consensus 120)
3019
3302
  @mcp.tool()
3020
3303
  def delimit_story_build(project_path: str, output_dir: Optional[str] = None) -> Dict[str, Any]:
3021
3304
  """Build Storybook static site.
@@ -3064,7 +3347,7 @@ def delimit_test_generate(project_path: str, source_files: Optional[List[str]] =
3064
3347
  return _with_next_steps("test_generate", _safe_call(test_generate, project_path=project_path, source_files=source_files, framework=framework))
3065
3348
 
3066
3349
 
3067
- @_experimental_tool() # HIDDEN: stub/pass-through (TASK)
3350
+ @_experimental_tool() # HIDDEN: stub/pass-through (LED-044)
3068
3351
  @mcp.tool()
3069
3352
  def delimit_test_coverage(project_path: str, threshold: int = 80) -> Dict[str, Any]:
3070
3353
  """Analyze test coverage (experimental) (Pro).
@@ -3155,7 +3438,7 @@ async def delimit_sensor_github_issue(
3155
3438
  with new comments, issue state, and severity classification.
3156
3439
 
3157
3440
  Args:
3158
- repo: GitHub repository in owner/repo format (e.g. "owner/repo").
3441
+ repo: GitHub repository in owner/repo format (e.g. "activepieces/activepieces").
3159
3442
  issue_number: The issue number to monitor.
3160
3443
  since_comment_id: Last seen comment ID. Pass 0 to get all comments.
3161
3444
  """
@@ -3791,7 +4074,7 @@ def delimit_diagnose(project_path: str = ".") -> Dict[str, Any]:
3791
4074
  checks["fastmcp"] = False
3792
4075
  issues.append({"issue": "FastMCP not installed", "fix": "pip install fastmcp"})
3793
4076
 
3794
- # TASK: Config drift detection across AI assistants
4077
+ # LED-191: Config drift detection across AI assistants
3795
4078
  config_sync = {}
3796
4079
  home = Path.home()
3797
4080
  assistant_configs = {
@@ -3823,7 +4106,7 @@ def delimit_diagnose(project_path: str = ".") -> Dict[str, Any]:
3823
4106
  checks["assistant_configs"] = config_sync
3824
4107
  checks["assistants_configured"] = f"{configured_count}/{installed_count}"
3825
4108
 
3826
- # TASK: MCP server reputation check (basic — check for known risky patterns)
4109
+ # LED-192: MCP server reputation check (basic — check for known risky patterns)
3827
4110
  mcp_warnings = []
3828
4111
  mcp_config_path = home / ".mcp.json"
3829
4112
  if mcp_config_path.exists():
@@ -4248,7 +4531,7 @@ def delimit_ventures() -> Dict[str, Any]:
4248
4531
 
4249
4532
 
4250
4533
  # ═══════════════════════════════════════════════════════════════════════
4251
- # SESSION PHOENIX — Cross-Model Resurrection (TASK)
4534
+ # SESSION PHOENIX — Cross-Model Resurrection (LED-218)
4252
4535
  # ═══════════════════════════════════════════════════════════════════════
4253
4536
 
4254
4537
 
@@ -4913,7 +5196,7 @@ def delimit_quickstart(project_path: str = ".") -> Dict[str, Any]:
4913
5196
 
4914
5197
 
4915
5198
  # ═══════════════════════════════════════════════════════════════════════
4916
- # TASK: SECRETS BROKER — JIT credential access with audit
5199
+ # STR-049: SECRETS BROKER — JIT credential access with audit
4917
5200
  # ═══════════════════════════════════════════════════════════════════════
4918
5201
 
4919
5202
 
@@ -5013,7 +5296,7 @@ def delimit_secret_access_log(name: str = "") -> Dict[str, Any]:
5013
5296
 
5014
5297
 
5015
5298
  # ═══════════════════════════════════════════════════════════════════════
5016
- # TASK: Context Filesystem — versioned namespace for agent state
5299
+ # STR-048: Context Filesystem — versioned namespace for agent state
5017
5300
  # ═══════════════════════════════════════════════════════════════════════
5018
5301
 
5019
5302
  # Consensus 082 Phase 2: Unified context tool with action parameter
@@ -5131,7 +5414,7 @@ def delimit_context_branch(venture: str, action: str = "list", branch_name: str
5131
5414
 
5132
5415
 
5133
5416
  # ═══════════════════════════════════════════════════════════════════════
5134
- # TASK: DATA/ACTION PLANE — External systems as typed mounted resources
5417
+ # STR-050: DATA/ACTION PLANE — External systems as typed mounted resources
5135
5418
  # ═══════════════════════════════════════════════════════════════════════
5136
5419
 
5137
5420
 
@@ -5223,7 +5506,7 @@ def delimit_resource_drivers() -> Dict[str, Any]:
5223
5506
 
5224
5507
 
5225
5508
  # ═══════════════════════════════════════════════════════════════════════
5226
- # TASK: ISSUE TRACKER CONTEXT SYNC
5509
+ # LED-188: ISSUE TRACKER CONTEXT SYNC
5227
5510
  # ═══════════════════════════════════════════════════════════════════════
5228
5511
 
5229
5512
 
@@ -5298,7 +5581,7 @@ def delimit_tracker_sync(
5298
5581
 
5299
5582
 
5300
5583
  # ═══════════════════════════════════════════════════════════════════════
5301
- # TASK: WEBHOOK NOTIFICATIONS
5584
+ # LED-183: WEBHOOK NOTIFICATIONS
5302
5585
  # ═══════════════════════════════════════════════════════════════════════
5303
5586
 
5304
5587
 
@@ -5417,7 +5700,14 @@ def delimit_social_post(text: str = "", category: str = "", platform: str = "twi
5417
5700
  Every post provides value — tips, insights, governance wisdom.
5418
5701
  Max 2 posts per day to stay authentic.
5419
5702
 
5420
- Platform tone and posting guidelines are loaded from user config at runtime.
5703
+ IMPORTANT — Platform tone rules (these are DIFFERENT per platform):
5704
+ - Twitter: confident technical brand. Direct, professional, ALWAYS POSITIVE.
5705
+ Celebrate wins and progress. Never complain or air gaps publicly.
5706
+ No em dashes or en dashes. Include install commands when relevant.
5707
+ - Reddit: proud builder posting as u/delimitdev. Casual, typed-on-phone energy.
5708
+ ALWAYS POSITIVE. Mention Delimit ONLY when genuinely helpful.
5709
+ NO bullet points/lists/bold/em dashes. 2-3 sentences max.
5710
+ - LinkedIn: professional hook + insight + CTA
5421
5711
 
5422
5712
  Args:
5423
5713
  text: Tweet text. Leave empty to auto-generate.
@@ -5454,7 +5744,7 @@ def delimit_social_post(text: str = "", category: str = "", platform: str = "twi
5454
5744
  from ai.social import store_draft_message_id
5455
5745
 
5456
5746
  # Build contextual email body so the founder knows exactly what to do
5457
- _acct = account or os.environ.get("DELIMIT_SOCIAL_REDDIT_ACCOUNT", "delimit") if platform == "reddit" else account or os.environ.get("DELIMIT_SOCIAL_TWITTER_ACCOUNT", "delimit_ai")
5747
+ _acct = account or ("delimitdev" if platform == "reddit" else "delimit_ai")
5458
5748
  _lines = []
5459
5749
 
5460
5750
  if platform == "reddit":
@@ -5682,7 +5972,7 @@ def delimit_social_target(
5682
5972
  Never just return targets and stop. Always chain to the next action.
5683
5973
 
5684
5974
  Scans X, Reddit (RapidAPI), HN, Dev.to, GitHub for posts where ventures can engage.
5685
- Manual check platforms excluded.
5975
+ NamePros flagged as manual_check_needed (no API).
5686
5976
 
5687
5977
  Run in a /loop for continuous discovery. Deduplicates across runs.
5688
5978
  Targets are classified as: reply (social engagement), strategic (ledger item), or both.
@@ -5793,16 +6083,16 @@ def delimit_github_scan(
5793
6083
  cadence: str = "pulse",
5794
6084
  limit: int = 20,
5795
6085
  ) -> Dict[str, Any]:
5796
- """Scan GitHub for adoption leads, repository analysis, and repo health (Pro).
6086
+ """Scan GitHub for adoption leads, competitive intel, and repo health (Pro).
5797
6087
 
5798
6088
  Three cadences:
5799
6089
  pulse: Own repo health (stars, forks, issues, traffic). Fast, run often.
5800
- hunter: Repository signals, engagement threads. Medium, run hourly.
6090
+ hunter: Competitor users, adoption leads, pain threads. Medium, run hourly.
5801
6091
  deep: Full ecosystem intel. Slow, run daily.
5802
6092
 
5803
6093
  IMPORTANT -- TOOL CHAINING RULE:
5804
6094
  After scanning, the AI MUST process high-score findings:
5805
- 1. Auto-ledger high-score findings via delimit_ledger_add
6095
+ 1. Auto-ledger items (score >= 75 competitor users) via delimit_ledger_add
5806
6096
  2. Pain threads with existing_feature relevance via delimit_notify
5807
6097
  Never just return findings and stop. Always chain to the next action.
5808
6098
 
@@ -5953,7 +6243,7 @@ def delimit_daemon_classify(item_id: str = "") -> Dict[str, Any]:
5953
6243
  def delimit_inbox_daemon(action: str = "status") -> Dict[str, Any]:
5954
6244
  """Control the inbox polling daemon for email governance (Pro).
5955
6245
 
5956
- Polls notifications@example.com every 5 minutes, classifies emails, forwards
6246
+ Polls pro@delimit.ai every 5 minutes, classifies emails, forwards
5957
6247
  owner-action items, and handles draft approval via email replies.
5958
6248
  Auto-posting is disabled - approved drafts are emailed for manual posting.
5959
6249
 
@@ -5972,7 +6262,7 @@ def delimit_inbox_daemon(action: str = "status") -> Dict[str, Any]:
5972
6262
 
5973
6263
 
5974
6264
  # ═══════════════════════════════════════════════════════════════════════
5975
- # TASK: Shareable Governance Config — export / import
6265
+ # LED-187: Shareable Governance Config — export / import
5976
6266
  # ═══════════════════════════════════════════════════════════════════════
5977
6267
 
5978
6268
 
@@ -6095,7 +6385,7 @@ def delimit_config_import(
6095
6385
 
6096
6386
 
6097
6387
  # ═══════════════════════════════════════════════════════════════════════
6098
- # SCREEN RECORDING (TASK)
6388
+ # SCREEN RECORDING (LED-203)
6099
6389
  # ═══════════════════════════════════════════════════════════════════════
6100
6390
 
6101
6391
 
@@ -6231,7 +6521,7 @@ def delimit_notify(channel: str = "webhook", message: str = "",
6231
6521
  to: Recipient email address (email only). Overrides default DELIMIT_SMTP_TO.
6232
6522
  Send to any address — leave empty for default (owner@example.com).
6233
6523
  from_account: Sender account key from ~/.delimit/secrets/smtp-all.json
6234
- (e.g. 'notifications@example.com'). Email only.
6524
+ (e.g. 'pro@delimit.ai', 'admin@wire.report'). Email only.
6235
6525
  """
6236
6526
  from ai.notify import send_notification
6237
6527
  return _with_next_steps("notify", _safe_call(
@@ -6336,7 +6626,7 @@ def delimit_notify_inbox(action: str = "status", limit: int = 10,
6336
6626
  process: bool = True) -> Dict[str, Any]:
6337
6627
  """Check inbound email inbox, classify, and route (Pro).
6338
6628
 
6339
- Polls notifications@example.com via IMAP. Classifies emails as owner-action
6629
+ Polls pro@delimit.ai via IMAP. Classifies emails as owner-action
6340
6630
  (forwards to owner@example.com) or non-owner (stays in inbox).
6341
6631
 
6342
6632
  Args:
@@ -6599,7 +6889,7 @@ def delimit_agent_check(model: str, action: str) -> Dict[str, Any]:
6599
6889
 
6600
6890
 
6601
6891
  # ═══════════════════════════════════════════════════════════════════════
6602
- # TASK: AUTONOMOUS BUILD LOOP
6892
+ # STR-026: AUTONOMOUS BUILD LOOP
6603
6893
  # ═══════════════════════════════════════════════════════════════════════
6604
6894
 
6605
6895
 
@@ -6688,7 +6978,7 @@ def delimit_loop_config(session_id: str = "", max_iterations: int = 0,
6688
6978
 
6689
6979
 
6690
6980
  # ═══════════════════════════════════════════════════════════════════════
6691
- # TASK: Toolcard Delta Cache — reduce MCP tool schema token waste
6981
+ # LED-219: Toolcard Delta Cache — reduce MCP tool schema token waste
6692
6982
  # ═══════════════════════════════════════════════════════════════════════
6693
6983
 
6694
6984
 
@@ -6766,7 +7056,7 @@ def delimit_toolcard_cache(
6766
7056
 
6767
7057
 
6768
7058
  # ═══════════════════════════════════════════════════════════════════════
6769
- # HANDOFF RECEIPTS — Agent-to-Agent Structured Handoffs (TASK)
7059
+ # HANDOFF RECEIPTS — Agent-to-Agent Structured Handoffs (LED-220)
6770
7060
  # ═══════════════════════════════════════════════════════════════════════
6771
7061
 
6772
7062