delimit-cli 3.15.13 → 4.0.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/gateway/ai/license_core.py +2 -1
- package/gateway/ai/notify.py +8 -8
- package/gateway/ai/server.py +7 -15
- package/gateway/ai/swarm.py +2 -2
- package/gateway/core/contract_ledger.py +1 -1
- package/gateway/core/dependency_graph.py +1 -1
- package/gateway/core/dependency_manifest.py +1 -1
- package/gateway/core/event_backbone.py +2 -2
- package/gateway/core/event_schema.py +1 -1
- package/gateway/core/impact_analyzer.py +1 -1
- package/package.json +7 -1
- package/scripts/security-check.sh +50 -6
- package/gateway/ai/cross_model_audit.py +0 -600
- package/gateway/ai/github_scanner.py +0 -622
- package/gateway/ai/handoff_receipts.py +0 -409
- package/gateway/ai/reddit_scanner.py +0 -562
- package/gateway/ai/session_phoenix.py +0 -371
- package/gateway/ai/toolcard_cache.py +0 -327
|
@@ -5,6 +5,7 @@ This module is distributed as a native binary (.so/.pyd), not readable Python.
|
|
|
5
5
|
"""
|
|
6
6
|
import hashlib
|
|
7
7
|
import json
|
|
8
|
+
import os
|
|
8
9
|
import time
|
|
9
10
|
from pathlib import Path
|
|
10
11
|
|
|
@@ -166,7 +167,7 @@ def activate(key: str) -> dict:
|
|
|
166
167
|
def _revalidate(data: dict) -> dict:
|
|
167
168
|
"""Re-validate against Lemon Squeezy."""
|
|
168
169
|
key = data.get("key", "")
|
|
169
|
-
if not key or key.startswith("
|
|
170
|
+
if not key or key.startswith(os.environ.get("DELIMIT_INTERNAL_KEY_PREFIX", "")):
|
|
170
171
|
return {"valid": True}
|
|
171
172
|
try:
|
|
172
173
|
import urllib.request
|
package/gateway/ai/notify.py
CHANGED
|
@@ -34,10 +34,10 @@ HISTORY_FILE = Path.home() / ".delimit" / "notifications.jsonl"
|
|
|
34
34
|
INBOX_ROUTING_FILE = Path.home() / ".delimit" / "inbox_routing.jsonl"
|
|
35
35
|
|
|
36
36
|
# ── Inbound email configuration ──────────────────────────────────────
|
|
37
|
-
IMAP_HOST =
|
|
38
|
-
IMAP_PORT = 993
|
|
39
|
-
IMAP_USER =
|
|
40
|
-
FORWARD_TO =
|
|
37
|
+
IMAP_HOST = os.environ.get("DELIMIT_IMAP_HOST", "")
|
|
38
|
+
IMAP_PORT = int(os.environ.get("DELIMIT_IMAP_PORT", "993"))
|
|
39
|
+
IMAP_USER = os.environ.get("DELIMIT_IMAP_USER", "")
|
|
40
|
+
FORWARD_TO = os.environ.get("DELIMIT_FORWARD_TO", "")
|
|
41
41
|
|
|
42
42
|
# Domains/senders whose emails require owner action
|
|
43
43
|
OWNER_ACTION_DOMAINS = {
|
|
@@ -60,9 +60,9 @@ OWNER_ACTION_DOMAINS = {
|
|
|
60
60
|
"digitalocean.com",
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
OWNER_ACTION_SENDERS =
|
|
64
|
-
|
|
65
|
-
|
|
63
|
+
OWNER_ACTION_SENDERS = set(
|
|
64
|
+
filter(None, [os.environ.get("DELIMIT_OWNER_EMAIL", "")])
|
|
65
|
+
)
|
|
66
66
|
|
|
67
67
|
# Subject patterns that indicate owner-action (compiled once)
|
|
68
68
|
import re as _re
|
|
@@ -258,7 +258,7 @@ def send_email(
|
|
|
258
258
|
smtp_pass = os.environ.get("DELIMIT_SMTP_PASS", "")
|
|
259
259
|
smtp_from = os.environ.get("DELIMIT_SMTP_FROM", "")
|
|
260
260
|
|
|
261
|
-
smtp_to = to or os.environ.get("DELIMIT_SMTP_TO", "
|
|
261
|
+
smtp_to = to or os.environ.get("DELIMIT_SMTP_TO", "")
|
|
262
262
|
|
|
263
263
|
if not all([smtp_host, smtp_from, smtp_to]):
|
|
264
264
|
record = {
|
package/gateway/ai/server.py
CHANGED
|
@@ -19,9 +19,8 @@ All tools follow the Adapter Boundary Contract v1.0:
|
|
|
19
19
|
- Stateless between calls
|
|
20
20
|
"""
|
|
21
21
|
|
|
22
|
-
# ──
|
|
22
|
+
# ── Output Quality Rules ──────────────────────────────────────────────
|
|
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
|
|
25
24
|
#
|
|
26
25
|
# Core: serious builder/operator, not a marketer. Credibility over persuasion.
|
|
27
26
|
# Truth over excitement. Concrete mechanisms, not vague benefits.
|
|
@@ -3438,7 +3437,7 @@ async def delimit_sensor_github_issue(
|
|
|
3438
3437
|
with new comments, issue state, and severity classification.
|
|
3439
3438
|
|
|
3440
3439
|
Args:
|
|
3441
|
-
repo: GitHub repository in owner/repo format (e.g. "
|
|
3440
|
+
repo: GitHub repository in owner/repo format (e.g. "owner/repo").
|
|
3442
3441
|
issue_number: The issue number to monitor.
|
|
3443
3442
|
since_comment_id: Last seen comment ID. Pass 0 to get all comments.
|
|
3444
3443
|
"""
|
|
@@ -5700,14 +5699,7 @@ def delimit_social_post(text: str = "", category: str = "", platform: str = "twi
|
|
|
5700
5699
|
Every post provides value — tips, insights, governance wisdom.
|
|
5701
5700
|
Max 2 posts per day to stay authentic.
|
|
5702
5701
|
|
|
5703
|
-
|
|
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
|
|
5702
|
+
Platform tone and posting guidelines are loaded from user config at runtime.
|
|
5711
5703
|
|
|
5712
5704
|
Args:
|
|
5713
5705
|
text: Tweet text. Leave empty to auto-generate.
|
|
@@ -5744,7 +5736,7 @@ def delimit_social_post(text: str = "", category: str = "", platform: str = "twi
|
|
|
5744
5736
|
from ai.social import store_draft_message_id
|
|
5745
5737
|
|
|
5746
5738
|
# Build contextual email body so the founder knows exactly what to do
|
|
5747
|
-
_acct = account or ("
|
|
5739
|
+
_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")
|
|
5748
5740
|
_lines = []
|
|
5749
5741
|
|
|
5750
5742
|
if platform == "reddit":
|
|
@@ -6087,12 +6079,12 @@ def delimit_github_scan(
|
|
|
6087
6079
|
|
|
6088
6080
|
Three cadences:
|
|
6089
6081
|
pulse: Own repo health (stars, forks, issues, traffic). Fast, run often.
|
|
6090
|
-
hunter:
|
|
6082
|
+
hunter: Repository signals, engagement threads. Medium, run hourly.
|
|
6091
6083
|
deep: Full ecosystem intel. Slow, run daily.
|
|
6092
6084
|
|
|
6093
6085
|
IMPORTANT -- TOOL CHAINING RULE:
|
|
6094
6086
|
After scanning, the AI MUST process high-score findings:
|
|
6095
|
-
1. Auto-ledger
|
|
6087
|
+
1. Auto-ledger high-score findings via delimit_ledger_add
|
|
6096
6088
|
2. Pain threads with existing_feature relevance via delimit_notify
|
|
6097
6089
|
Never just return findings and stop. Always chain to the next action.
|
|
6098
6090
|
|
|
@@ -6521,7 +6513,7 @@ def delimit_notify(channel: str = "webhook", message: str = "",
|
|
|
6521
6513
|
to: Recipient email address (email only). Overrides default DELIMIT_SMTP_TO.
|
|
6522
6514
|
Send to any address — leave empty for default (owner@example.com).
|
|
6523
6515
|
from_account: Sender account key from ~/.delimit/secrets/smtp-all.json
|
|
6524
|
-
(e.g. '
|
|
6516
|
+
(e.g. 'notifications@example.com'). Email only.
|
|
6525
6517
|
"""
|
|
6526
6518
|
from ai.notify import send_notification
|
|
6527
6519
|
return _with_next_steps("notify", _safe_call(
|
package/gateway/ai/swarm.py
CHANGED
|
@@ -39,7 +39,7 @@ DEFAULT_ROSTER = {
|
|
|
39
39
|
"fallback_model": "codex-gpt-5.4",
|
|
40
40
|
},
|
|
41
41
|
"ops": {
|
|
42
|
-
"role": "Strategy, Deliberation,
|
|
42
|
+
"role": "Strategy, Deliberation, Community, Analysis",
|
|
43
43
|
"default_model": "grok-4",
|
|
44
44
|
"fallback_model": "gemini-3.1-pro-preview",
|
|
45
45
|
},
|
|
@@ -274,7 +274,7 @@ APPROVAL_TIERS = {
|
|
|
274
274
|
"deploy_staging": "auto_approved",
|
|
275
275
|
"social_post": "founder_email",
|
|
276
276
|
"social_low_risk": "auto_after_consensus",
|
|
277
|
-
"
|
|
277
|
+
"community_issue": "founder_email",
|
|
278
278
|
"ledger_update": "auto_approved",
|
|
279
279
|
"code_commit": "auto_approved",
|
|
280
280
|
"security_audit": "auto_approved",
|
|
@@ -3,7 +3,7 @@ Delimit Contract Ledger
|
|
|
3
3
|
Reads, validates, and queries the append-only JSONL event ledger.
|
|
4
4
|
Optional SQLite index for fast lookups (never required for CI).
|
|
5
5
|
|
|
6
|
-
Per
|
|
6
|
+
Per Delimit Stability Contract:
|
|
7
7
|
- Deterministic outputs
|
|
8
8
|
- Append-only artifacts
|
|
9
9
|
- SQLite index is optional, not required for CI
|
|
@@ -5,7 +5,7 @@ Constructs a deterministic service dependency graph from manifests.
|
|
|
5
5
|
The graph maps each API/service to its downstream consumers,
|
|
6
6
|
enabling impact analysis when an API contract changes.
|
|
7
7
|
|
|
8
|
-
Per
|
|
8
|
+
Per Delimit Stability Contract:
|
|
9
9
|
- Deterministic outputs (sorted, reproducible)
|
|
10
10
|
- No telemetry
|
|
11
11
|
- Graceful degradation when manifests are missing
|
|
@@ -3,7 +3,7 @@ Delimit Event Backbone
|
|
|
3
3
|
Constructs ledger events, generates SHA-256 hashes, links hash chains,
|
|
4
4
|
and appends to the append-only JSONL ledger.
|
|
5
5
|
|
|
6
|
-
Per
|
|
6
|
+
Per Delimit Stability Contract:
|
|
7
7
|
- Deterministic outputs
|
|
8
8
|
- Append-only artifacts
|
|
9
9
|
- Fail-closed CI behavior (ledger failures never affect CI)
|
|
@@ -199,7 +199,7 @@ class EventBackbone:
|
|
|
199
199
|
This is the primary API for event generation. It is best-effort:
|
|
200
200
|
if the ledger write fails, the event is still returned but not persisted.
|
|
201
201
|
|
|
202
|
-
CRITICAL: This method NEVER raises exceptions. Per
|
|
202
|
+
CRITICAL: This method NEVER raises exceptions. Per Delimit Stability Contract,
|
|
203
203
|
ledger failures must not affect CI pass/fail outcome.
|
|
204
204
|
|
|
205
205
|
Returns:
|
|
@@ -3,7 +3,7 @@ Delimit Impact Analyzer
|
|
|
3
3
|
Determines downstream consumers affected by an API change
|
|
4
4
|
and produces informational impact summaries for CI output.
|
|
5
5
|
|
|
6
|
-
Per
|
|
6
|
+
Per Delimit Stability Contract:
|
|
7
7
|
- Impact analysis is INFORMATIONAL ONLY
|
|
8
8
|
- NEVER affects CI pass/fail outcome
|
|
9
9
|
- Deterministic outputs
|
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
|
+
"version": "4.0.0",
|
|
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": [
|
|
@@ -14,6 +14,12 @@
|
|
|
14
14
|
"!gateway/ai/founding_users.py",
|
|
15
15
|
"!gateway/ai/inbox_daemon.py",
|
|
16
16
|
"!gateway/ai/deliberation.py",
|
|
17
|
+
"!gateway/ai/reddit_scanner.py",
|
|
18
|
+
"!gateway/ai/github_scanner.py",
|
|
19
|
+
"!gateway/ai/cross_model_audit.py",
|
|
20
|
+
"!gateway/ai/session_phoenix.py",
|
|
21
|
+
"!gateway/ai/handoff_receipts.py",
|
|
22
|
+
"!gateway/ai/toolcard_cache.py",
|
|
17
23
|
"scripts/",
|
|
18
24
|
"server.json",
|
|
19
25
|
"README.md",
|
|
@@ -25,7 +25,7 @@ fi
|
|
|
25
25
|
|
|
26
26
|
# 2. Blocklist terms
|
|
27
27
|
echo -n " Blocklist... "
|
|
28
|
-
BLOCKLIST="jamsonsholdings|Bladabah|Domainvested26|Delimit26|home/jamsons|infracore|crypttrx|\.wr_env"
|
|
28
|
+
BLOCKLIST="jamsonsholdings|Bladabah|Domainvested26|Delimit26|home/jamsons|infracore|crypttrx|\.wr_env|delimitdev|typed-on-phone|em dash.*ai tell|PAIN_CATEGORIES|VENTURE_CONFIG|VENTURE_SUBREDDITS|karma_building"
|
|
29
29
|
if grep -rEi "$BLOCKLIST" "$TMPDIR/package/" --include="*.py" --include="*.js" --include="*.json" 2>/dev/null; then
|
|
30
30
|
echo "❌ BLOCKED TERMS FOUND"
|
|
31
31
|
FAIL=1
|
|
@@ -42,9 +42,17 @@ else
|
|
|
42
42
|
echo "✅ clean"
|
|
43
43
|
fi
|
|
44
44
|
|
|
45
|
-
# 4.
|
|
45
|
+
# 4. Internal ticket IDs
|
|
46
|
+
echo -n " Internal ticket IDs... "
|
|
47
|
+
if grep -rE "LED-[0-9]{3}|STR-[0-9]{3}" "$TMPDIR/package/" --include="*.py" --include="*.js" 2>/dev/null | grep -v "node_modules" | head -1; then
|
|
48
|
+
echo " WARNING: Internal ticket IDs found (cosmetic, not blocking)"
|
|
49
|
+
else
|
|
50
|
+
echo "clean"
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# 5. Proprietary files that shouldn't ship
|
|
46
54
|
echo -n " Proprietary files... "
|
|
47
|
-
PROPRIETARY="social_target\.py|social\.py|founding_users\.py|inbox_daemon\.py|deliberation\.py"
|
|
55
|
+
PROPRIETARY="social_target\.py|social\.py|founding_users\.py|inbox_daemon\.py|deliberation\.py|reddit_scanner\.py|github_scanner\.py|cross_model_audit\.py|session_phoenix\.py|handoff_receipts\.py|toolcard_cache\.py"
|
|
48
56
|
if find "$TMPDIR/package/" -name "*.py" | grep -Ei "$PROPRIETARY" 2>/dev/null; then
|
|
49
57
|
echo "❌ PROPRIETARY FILES IN PACKAGE"
|
|
50
58
|
FAIL=1
|
|
@@ -52,15 +60,51 @@ else
|
|
|
52
60
|
echo "✅ clean"
|
|
53
61
|
fi
|
|
54
62
|
|
|
55
|
-
# Cleanup
|
|
63
|
+
# Cleanup npm tarball
|
|
56
64
|
rm -rf "$TMPDIR"
|
|
57
65
|
|
|
66
|
+
# ── PyPI dist scan (if dist/ exists) ─────────────────────────────────
|
|
67
|
+
PYPI_DIST="/home/delimit/delimit-gateway/dist"
|
|
68
|
+
if [ -d "$PYPI_DIST" ] && ls "$PYPI_DIST"/*.tar.gz 1>/dev/null 2>&1; then
|
|
69
|
+
echo ""
|
|
70
|
+
echo "PyPI dist scan..."
|
|
71
|
+
PYPI_TMPDIR=$(mktemp -d)
|
|
72
|
+
PYPI_TARBALL=$(ls -t "$PYPI_DIST"/*.tar.gz | head -1)
|
|
73
|
+
tar -xzf "$PYPI_TARBALL" -C "$PYPI_TMPDIR" 2>/dev/null
|
|
74
|
+
|
|
75
|
+
echo -n " Credentials... "
|
|
76
|
+
if grep -rEi '(password|passwd|secret|api_key|apikey)\s*[:=]\s*["\x27][^"\x27]{4,}' "$PYPI_TMPDIR/" --include="*.py" 2>/dev/null | grep -v 'environ\|getenv\|os\.environ\|<configured\|example\|placeholder\|REDACTED'; then
|
|
77
|
+
echo "FOUND CREDENTIALS IN PYPI DIST"
|
|
78
|
+
FAIL=1
|
|
79
|
+
else
|
|
80
|
+
echo "clean"
|
|
81
|
+
fi
|
|
82
|
+
|
|
83
|
+
echo -n " Blocklist... "
|
|
84
|
+
if grep -rEi "$BLOCKLIST" "$PYPI_TMPDIR/" --include="*.py" 2>/dev/null; then
|
|
85
|
+
echo "BLOCKED TERMS IN PYPI DIST"
|
|
86
|
+
FAIL=1
|
|
87
|
+
else
|
|
88
|
+
echo "clean"
|
|
89
|
+
fi
|
|
90
|
+
|
|
91
|
+
echo -n " PII... "
|
|
92
|
+
if grep -rEi '[a-z0-9._%+-]+@(gmail|yahoo|hotmail|outlook|proton|jamsons|wire\.report|domainvested)' "$PYPI_TMPDIR/" --include="*.py" 2>/dev/null | grep -v "example\|placeholder\|<configured\|noreply\|e\.g\.\|docstring"; then
|
|
93
|
+
echo "PII IN PYPI DIST"
|
|
94
|
+
FAIL=1
|
|
95
|
+
else
|
|
96
|
+
echo "clean"
|
|
97
|
+
fi
|
|
98
|
+
|
|
99
|
+
rm -rf "$PYPI_TMPDIR"
|
|
100
|
+
fi
|
|
101
|
+
|
|
58
102
|
if [ $FAIL -ne 0 ]; then
|
|
59
103
|
echo ""
|
|
60
|
-
echo "
|
|
104
|
+
echo "SECURITY CHECK FAILED -- do not publish"
|
|
61
105
|
exit 1
|
|
62
106
|
fi
|
|
63
107
|
|
|
64
108
|
echo ""
|
|
65
|
-
echo "
|
|
109
|
+
echo "All security checks passed"
|
|
66
110
|
exit 0
|