delimit-cli 4.1.25 → 4.1.26
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/bin/delimit-cli.js +36 -3
- package/gateway/ai/server.py +140 -10
- package/package.json +1 -1
package/bin/delimit-cli.js
CHANGED
|
@@ -3133,23 +3133,56 @@ program
|
|
|
3133
3133
|
process.exit(1);
|
|
3134
3134
|
}
|
|
3135
3135
|
|
|
3136
|
+
console.log(chalk.gray(' Validating license key...'));
|
|
3137
|
+
|
|
3138
|
+
// Validate against Lemon Squeezy API
|
|
3139
|
+
let validated = false;
|
|
3140
|
+
let licenseId = null;
|
|
3141
|
+
let customerEmail = '';
|
|
3142
|
+
try {
|
|
3143
|
+
const resp = await axios.post('https://api.lemonsqueezy.com/v1/licenses/validate', {
|
|
3144
|
+
license_key: key,
|
|
3145
|
+
}, {
|
|
3146
|
+
headers: { 'Accept': 'application/json' },
|
|
3147
|
+
timeout: 10000,
|
|
3148
|
+
});
|
|
3149
|
+
if (resp.data && resp.data.valid) {
|
|
3150
|
+
validated = true;
|
|
3151
|
+
licenseId = resp.data.license_key?.id;
|
|
3152
|
+
customerEmail = resp.data.meta?.customer_email || '';
|
|
3153
|
+
console.log(chalk.green(' License valid.'));
|
|
3154
|
+
} else {
|
|
3155
|
+
console.log(chalk.red(` License invalid: ${resp.data?.error || 'unknown error'}`));
|
|
3156
|
+
process.exit(1);
|
|
3157
|
+
}
|
|
3158
|
+
} catch (err) {
|
|
3159
|
+
// If API unreachable, accept locally (grace period)
|
|
3160
|
+
console.log(chalk.yellow(' Could not reach license server. Activating locally (7-day grace).'));
|
|
3161
|
+
validated = true;
|
|
3162
|
+
}
|
|
3163
|
+
|
|
3136
3164
|
// Write license file
|
|
3137
3165
|
const crypto = require('crypto');
|
|
3138
3166
|
const machineHash = crypto.createHash('sha256').update(os.homedir()).digest('hex').slice(0, 16);
|
|
3139
3167
|
const licenseData = {
|
|
3140
3168
|
key: key,
|
|
3141
3169
|
tier: 'pro',
|
|
3142
|
-
valid:
|
|
3170
|
+
valid: validated,
|
|
3171
|
+
license_id: licenseId,
|
|
3172
|
+
customer_email: customerEmail,
|
|
3143
3173
|
activated_at: Date.now() / 1000,
|
|
3144
3174
|
machine_hash: machineHash,
|
|
3175
|
+
validated_at: Date.now() / 1000,
|
|
3145
3176
|
};
|
|
3146
3177
|
|
|
3147
3178
|
if (!fs.existsSync(licenseDir)) {
|
|
3148
3179
|
fs.mkdirSync(licenseDir, { recursive: true });
|
|
3149
3180
|
}
|
|
3150
3181
|
fs.writeFileSync(licensePath, JSON.stringify(licenseData, null, 2));
|
|
3151
|
-
console.log(chalk.green('License activated successfully.'));
|
|
3152
|
-
console.log(chalk.dim(
|
|
3182
|
+
console.log(chalk.green('\n License activated successfully.'));
|
|
3183
|
+
console.log(chalk.dim(` Tier: pro`));
|
|
3184
|
+
if (customerEmail) console.log(chalk.dim(` Email: ${customerEmail}`));
|
|
3185
|
+
console.log('');
|
|
3153
3186
|
});
|
|
3154
3187
|
|
|
3155
3188
|
// ---------------------------------------------------------------------------
|
package/gateway/ai/server.py
CHANGED
|
@@ -5756,16 +5756,108 @@ def delimit_quickstart(project_path: str = ".") -> Dict[str, Any]:
|
|
|
5756
5756
|
"models": enabled_models,
|
|
5757
5757
|
})
|
|
5758
5758
|
|
|
5759
|
+
# Step 6: First Governance Run -- show value with bundled example specs
|
|
5760
|
+
demo_result: Dict[str, Any] = {"skipped": False}
|
|
5761
|
+
examples_dir = Path(__file__).resolve().parent.parent / "examples"
|
|
5762
|
+
petstore_v1 = examples_dir / "petstore-v1.yaml"
|
|
5763
|
+
petstore_v2 = examples_dir / "petstore-v2.yaml"
|
|
5764
|
+
if petstore_v1.is_file() and petstore_v2.is_file():
|
|
5765
|
+
from backends.gateway_core import run_lint as _qs_run_lint, run_spec_health as _qs_run_spec_health
|
|
5766
|
+
|
|
5767
|
+
# 6a: Lint petstore v1 vs v2 to show breaking change detection
|
|
5768
|
+
try:
|
|
5769
|
+
lint_demo = _qs_run_lint(
|
|
5770
|
+
old_spec=str(petstore_v1),
|
|
5771
|
+
new_spec=str(petstore_v2),
|
|
5772
|
+
)
|
|
5773
|
+
breaking_count = len(lint_demo.get("breaking", lint_demo.get("violations", [])))
|
|
5774
|
+
total_changes = lint_demo.get("total_changes", 0)
|
|
5775
|
+
demo_result["lint"] = {
|
|
5776
|
+
"breaking_changes": breaking_count,
|
|
5777
|
+
"total_changes": total_changes,
|
|
5778
|
+
"status": lint_demo.get("status", "unknown"),
|
|
5779
|
+
"sample_violations": [
|
|
5780
|
+
v.get("message", v.get("type", "unknown"))
|
|
5781
|
+
for v in lint_demo.get("breaking", lint_demo.get("violations", []))[:3]
|
|
5782
|
+
],
|
|
5783
|
+
}
|
|
5784
|
+
except Exception as e:
|
|
5785
|
+
demo_result["lint"] = {"error": str(e)}
|
|
5786
|
+
|
|
5787
|
+
# 6b: Spec health score on petstore v1
|
|
5788
|
+
try:
|
|
5789
|
+
health_demo = _qs_run_spec_health(spec_path=str(petstore_v1))
|
|
5790
|
+
demo_result["spec_health"] = {
|
|
5791
|
+
"score": health_demo.get("score", health_demo.get("overall_score")),
|
|
5792
|
+
"grade": health_demo.get("grade", health_demo.get("letter_grade")),
|
|
5793
|
+
"dimensions": {
|
|
5794
|
+
k: v for k, v in health_demo.get("dimensions", {}).items()
|
|
5795
|
+
} if health_demo.get("dimensions") else {},
|
|
5796
|
+
"recommendations_count": len(health_demo.get("recommendations", [])),
|
|
5797
|
+
}
|
|
5798
|
+
except Exception as e:
|
|
5799
|
+
demo_result["spec_health"] = {"error": str(e)}
|
|
5800
|
+
else:
|
|
5801
|
+
demo_result["skipped"] = True
|
|
5802
|
+
demo_result["reason"] = "Example specs not found"
|
|
5803
|
+
|
|
5804
|
+
steps_completed.append({
|
|
5805
|
+
"step": 6,
|
|
5806
|
+
"name": "First Governance Run (Demo)",
|
|
5807
|
+
"result": demo_result,
|
|
5808
|
+
})
|
|
5809
|
+
|
|
5810
|
+
# Step 7: Project Spec Discovery -- check if this project has OpenAPI specs
|
|
5811
|
+
project_specs: List[str] = []
|
|
5812
|
+
project_lint_result: Optional[Dict[str, Any]] = None
|
|
5813
|
+
spec_patterns = [
|
|
5814
|
+
"**/openapi.yaml", "**/openapi.yml", "**/openapi.json",
|
|
5815
|
+
"**/swagger.yaml", "**/swagger.yml", "**/swagger.json",
|
|
5816
|
+
]
|
|
5817
|
+
for pattern in spec_patterns:
|
|
5818
|
+
for match in p.glob(pattern):
|
|
5819
|
+
rel = str(match.relative_to(p))
|
|
5820
|
+
if "node_modules" not in rel and ".next" not in rel and "venv" not in rel:
|
|
5821
|
+
project_specs.append(str(match))
|
|
5822
|
+
project_specs = list(set(project_specs))[:5]
|
|
5823
|
+
|
|
5824
|
+
if project_specs:
|
|
5825
|
+
# Run spec_health on the first discovered spec
|
|
5826
|
+
try:
|
|
5827
|
+
from backends.gateway_core import run_spec_health as _qs_health
|
|
5828
|
+
proj_health = _qs_health(spec_path=project_specs[0])
|
|
5829
|
+
project_lint_result = {
|
|
5830
|
+
"spec": project_specs[0],
|
|
5831
|
+
"score": proj_health.get("score", proj_health.get("overall_score")),
|
|
5832
|
+
"grade": proj_health.get("grade", proj_health.get("letter_grade")),
|
|
5833
|
+
}
|
|
5834
|
+
except Exception as e:
|
|
5835
|
+
project_lint_result = {"spec": project_specs[0], "error": str(e)}
|
|
5836
|
+
|
|
5837
|
+
steps_completed.append({
|
|
5838
|
+
"step": 7,
|
|
5839
|
+
"name": "Project Spec Discovery",
|
|
5840
|
+
"specs_found": len(project_specs),
|
|
5841
|
+
"spec_files": project_specs,
|
|
5842
|
+
"health_result": project_lint_result,
|
|
5843
|
+
})
|
|
5844
|
+
|
|
5759
5845
|
# Build suggested next actions based on findings
|
|
5760
5846
|
next_actions = []
|
|
5761
|
-
if
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
|
|
5847
|
+
if project_specs:
|
|
5848
|
+
next_actions.append(f"Run `delimit_spec_health` on {project_specs[0]} to see your full quality report")
|
|
5849
|
+
if len(project_specs) > 1:
|
|
5850
|
+
next_actions.append(f"You have {len(project_specs)} OpenAPI specs -- run `delimit_lint` to compare versions")
|
|
5851
|
+
next_actions.append("Add the Delimit GitHub Action to catch breaking changes on every PR")
|
|
5852
|
+
else:
|
|
5853
|
+
if scan_result.get("findings"):
|
|
5854
|
+
for f in scan_result["findings"]:
|
|
5855
|
+
if f.get("type") == "openapi_specs":
|
|
5856
|
+
next_actions.append("Run `delimit_lint` on your OpenAPI spec to check for breaking changes")
|
|
5857
|
+
if f.get("type") == "security_concerns":
|
|
5858
|
+
next_actions.append("Run `delimit_security_scan` to audit for vulnerabilities")
|
|
5859
|
+
if f.get("type") == "tests_found":
|
|
5860
|
+
next_actions.append("Run `delimit_test_smoke` to verify tests pass")
|
|
5769
5861
|
|
|
5770
5862
|
if not deliberation_ready:
|
|
5771
5863
|
next_actions.append("Add more AI models for multi-model deliberation: say 'configure delimit models'")
|
|
@@ -5773,16 +5865,54 @@ def delimit_quickstart(project_path: str = ".") -> Dict[str, Any]:
|
|
|
5773
5865
|
next_actions.append("Say 'add to ledger: [task]' to start tracking work across sessions")
|
|
5774
5866
|
next_actions.append("Say 'deliberate [question]' to get AI consensus on a decision")
|
|
5775
5867
|
|
|
5868
|
+
# Build the "wow moment" summary
|
|
5869
|
+
wow_moment: Dict[str, Any] = {}
|
|
5870
|
+
lint_data = demo_result.get("lint", {})
|
|
5871
|
+
health_data = demo_result.get("spec_health", {})
|
|
5872
|
+
if lint_data and not lint_data.get("error"):
|
|
5873
|
+
wow_moment["breaking_changes_caught"] = lint_data.get("breaking_changes", 0)
|
|
5874
|
+
wow_moment["total_api_changes"] = lint_data.get("total_changes", 0)
|
|
5875
|
+
wow_moment["sample_catches"] = lint_data.get("sample_violations", [])
|
|
5876
|
+
if health_data and not health_data.get("error"):
|
|
5877
|
+
wow_moment["spec_health_grade"] = health_data.get("grade")
|
|
5878
|
+
wow_moment["spec_health_score"] = health_data.get("score")
|
|
5879
|
+
wow_moment["governance_gates"] = [
|
|
5880
|
+
"Breaking change detection (CI/CD)",
|
|
5881
|
+
"Spec health scoring (quality)",
|
|
5882
|
+
"Policy enforcement (custom rules)",
|
|
5883
|
+
"Semver classification (automated)",
|
|
5884
|
+
"Contract ledger (audit trail)",
|
|
5885
|
+
]
|
|
5886
|
+
if project_specs:
|
|
5887
|
+
wow_moment["your_project"] = {
|
|
5888
|
+
"specs_found": len(project_specs),
|
|
5889
|
+
"ready_to_govern": True,
|
|
5890
|
+
}
|
|
5891
|
+
if project_lint_result and not project_lint_result.get("error"):
|
|
5892
|
+
wow_moment["your_project"]["health_grade"] = project_lint_result.get("grade")
|
|
5893
|
+
wow_moment["your_project"]["health_score"] = project_lint_result.get("score")
|
|
5894
|
+
|
|
5895
|
+
bc = wow_moment.get("breaking_changes_caught", 0)
|
|
5896
|
+
grade = wow_moment.get("spec_health_grade", "N/A")
|
|
5897
|
+
msg_parts = [
|
|
5898
|
+
f"Quickstart complete! {len(steps_completed)} steps run.",
|
|
5899
|
+
f"Demo: {bc} breaking changes caught, spec health grade: {grade}.",
|
|
5900
|
+
f"5 governance gates ready.",
|
|
5901
|
+
]
|
|
5902
|
+
if project_specs:
|
|
5903
|
+
msg_parts.append(f"Found {len(project_specs)} OpenAPI spec(s) in your project -- ready to govern.")
|
|
5904
|
+
|
|
5776
5905
|
return _with_next_steps("quickstart", {
|
|
5777
5906
|
"tool": "quickstart",
|
|
5778
5907
|
"status": "complete",
|
|
5779
5908
|
"project": str(p),
|
|
5780
5909
|
"steps": steps_completed,
|
|
5910
|
+
"wow_moment": wow_moment,
|
|
5781
5911
|
"environment": environment,
|
|
5782
5912
|
"scan_findings": scan_result.get("findings", []),
|
|
5783
5913
|
"scan_suggestions": scan_result.get("suggestions", []),
|
|
5784
5914
|
"next_actions": next_actions,
|
|
5785
|
-
"message":
|
|
5915
|
+
"message": " ".join(msg_parts),
|
|
5786
5916
|
})
|
|
5787
5917
|
|
|
5788
5918
|
|
|
@@ -6817,7 +6947,7 @@ def delimit_build_loop(action: str = "run", session_id: str = "") -> Dict[str, A
|
|
|
6817
6947
|
"""Execute the governed continuous build loop (LED-239).
|
|
6818
6948
|
|
|
6819
6949
|
Requirements:
|
|
6820
|
-
- root ledger in
|
|
6950
|
+
- root ledger in /root/.delimit is authoritative
|
|
6821
6951
|
- select only build-safe open items
|
|
6822
6952
|
- resolve venture + repo before dispatch
|
|
6823
6953
|
- use Delimit swarm/governance as control plane
|
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.1.
|
|
4
|
+
"version": "4.1.26",
|
|
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": [
|