speckl/reference-agent/src/cli.ts
Scott Scoble 7297b5ec58 feat: whitepaper sections 2-6, README overhaul, SpeckDL spec v0.1, reference agent scaffold
- Dark Provenance whitepaper: complete sections 2-6 (What Is Dark Provenance,
  The Speckl Approach, Semantic Preservation & SA-11, Implications, Call to Action)
- README: public launch-ready with architecture, roadmap, getting started
- SpeckDL SPEC.md: v0.1 language specification with EBNF grammar,
  type system, compilation pipeline, 5 example Specks
- Reference agent: TypeScript scaffold with Orchestrator, TeamLead, SMEs
  (VerificationSME, CodegenSME), TillDone condition tracking,
  ProvenanceGraph, and CLI with example Specks
2026-04-28 09:33:48 -07:00

180 lines
No EOL
5.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Speckl Reference Agent — CLI entry point.
*
* Usage:
* npx tsx src/cli.ts examples/ --speck RetryHandler
* npx tsx src/cli.ts --help
*/
import { Orchestrator } from "./orchestrator.js";
import type { SpeckDefinition, Constraint, VerifyCondition } from "./types.js";
// ─── Example Specks ─────────────────────────────────────────
const exampleSpecks: Record<string, SpeckDefinition> = {
RetryHandler: {
name: "RetryHandler",
input: {
operation: { kind: "named", name: "Operation" },
maxRetries: "Nat",
},
output: { kind: "named", name: "Result" },
constraints: [
{ id: "c1", expression: "maxRetries <= 5" },
{ id: "c2", expression: "backoff(delay(n)) == delay(n-1) * 2" },
{ id: "c3", expression: "totalDelay < 30s" },
] as Constraint[],
verify: [
{ id: "v1", kind: "always", expression: "implies(attempt > 1, delay > 0)" },
] as VerifyCondition[],
},
RateLimiter: {
name: "RateLimiter",
input: {
requests: { kind: "list", element: { kind: "named", name: "Request" } },
window: "Nat",
maxRequests: "Nat",
},
output: {
kind: "record",
fields: {
allowed: { kind: "list", element: "Bool" },
remaining: "Nat",
},
},
constraints: [
{ id: "c1", expression: "maxRequests > 0" },
{ id: "c2", expression: "window > 0" },
{ id: "c3", expression: "count(allowed, true) <= maxRequests" },
{ id: "c4", expression: "remaining == maxRequests - count(allowed, true)" },
] as Constraint[],
verify: [
{ id: "v1", kind: "always", expression: "implies(remaining == 0, not(allowed[current]))" },
] as VerifyCondition[],
},
DrugDosageCalculator: {
name: "DrugDosageCalculator",
input: {
patientWeight: "Real",
drugConcentration: "Real",
prescribedDose: "Real",
minDose: "Real",
maxDose: "Real",
frequency: "Nat",
},
output: {
kind: "record",
fields: {
volume: "Real",
safe: "Bool",
},
},
constraints: [
{ id: "c1", expression: "patientWeight > 0" },
{ id: "c2", expression: "drugConcentration > 0" },
{ id: "c3", expression: "minDose > 0" },
{ id: "c4", expression: "maxDose > minDose" },
{ id: "c5", expression: "frequency >= 1" },
{ id: "c6", expression: "frequency <= 6" },
{ id: "c7", expression: "implies(safe, volume >= minDose / drugConcentration)" },
{ id: "c8", expression: "implies(safe, volume <= maxDose / drugConcentration)" },
{ id: "c9", expression: "implies(not safe, volume == 0)" },
] as Constraint[],
verify: [
{ id: "v1", kind: "always", expression: "implies(prescribedDose >= minDose and prescribedDose <= maxDose, safe)" },
{ id: "v2", kind: "always", expression: "implies(not safe, volume == 0)" },
] as VerifyCondition[],
events: [
{
name: "DoseCalculated",
fields: { volume: "Real", safe: "Bool" },
},
{
name: "DoseRejected",
fields: { reason: "String" },
},
],
},
};
// ─── Main ────────────────────────────────────────────────────
async function main(): Promise<void> {
const args = process.argv.slice(2);
if (args.includes("--help") || args.includes("-h")) {
console.log(`
Speckl Reference Agent v0.1
Usage:
npx tsx src/cli.ts [options]
Options:
--speck <name> Process a specific example Speck
--list List available example Specks
--provenance Show provenance graph after processing
--help Show this help message
Example Specks:
${Object.keys(exampleSpecks).join("\n ")}
`);
return;
}
if (args.includes("--list")) {
console.log("Available example Specks:");
for (const [name, speck] of Object.entries(exampleSpecks)) {
console.log(` ${name}: ${speck.constraints.length} constraints, ${speck.verify.length} verify conditions`);
}
return;
}
const speckName = args[args.indexOf("--speck") + 1] || "RetryHandler";
const speck = exampleSpecks[speckName];
if (!speck) {
console.error(`Unknown Speck: ${speckName}`);
console.error(`Available: ${Object.keys(exampleSpecks).join(", ")}`);
process.exit(1);
}
console.log(`\n🧬 Processing Speck: ${speck.name}`);
console.log(` Constraints: ${speck.constraints.length}`);
console.log(` Verify conditions: ${speck.verify.length}`);
console.log(` Events: ${speck.events?.length ?? 0}\n`);
const orchestrator = new Orchestrator();
const result = await orchestrator.processSpeck(speck);
console.log(`\n✅ Result: ${result.status}`);
console.log(` Duration: ${result.durationMs}ms`);
for (const [taskId, taskResult] of Object.entries(result.results)) {
const r = taskResult as any;
console.log(`\n Task ${taskId}: ${r.status}`);
if (r.output) {
if (r.output.z3Result) {
console.log(` Z3 result: ${r.output.z3Result}`);
console.log(` All verified: ${r.output.allVerified ?? "N/A"}`);
}
if (r.output.code) {
console.log(` Generated ${r.output.language} code (${r.output.code.split("\n").length} lines)`);
}
}
}
if (args.includes("--provenance")) {
console.log(`\n📊 Provenance Graph:`);
console.log(JSON.stringify(result.provenance, null, 2));
}
console.log();
}
main().catch((err) => {
console.error("Fatal error:", err);
process.exit(1);
});