@0dai-dev/cli 2.8.0 → 2.9.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/bin/0dai.js +159 -6
- package/package.json +1 -1
package/bin/0dai.js
CHANGED
|
@@ -7,7 +7,7 @@ const fs = require("fs");
|
|
|
7
7
|
const path = require("path");
|
|
8
8
|
const os = require("os");
|
|
9
9
|
|
|
10
|
-
const VERSION = "2.
|
|
10
|
+
const VERSION = "2.9.0";
|
|
11
11
|
const API_URL = process.env.ODAI_API_URL || "https://api.0dai.dev";
|
|
12
12
|
const T = process.stdout.isTTY ? "\x1b[38;2;45;212;168m" : ""; // teal
|
|
13
13
|
const R = process.stdout.isTTY ? "\x1b[0m" : ""; // reset
|
|
@@ -147,6 +147,7 @@ function writeFiles(target, files) {
|
|
|
147
147
|
|
|
148
148
|
async function cmdInit(target, args = []) {
|
|
149
149
|
const dryRun = args.includes("--dry-run");
|
|
150
|
+
const minimal = args.includes("--minimal");
|
|
150
151
|
|
|
151
152
|
if (fs.existsSync(path.join(target, "ai", "VERSION"))) {
|
|
152
153
|
const v = fs.readFileSync(path.join(target, "ai", "VERSION"), "utf8").trim();
|
|
@@ -169,6 +170,7 @@ async function cmdInit(target, args = []) {
|
|
|
169
170
|
file_contents: fileContents,
|
|
170
171
|
available_clis: clis,
|
|
171
172
|
dry_run: dryRun,
|
|
173
|
+
minimal: minimal,
|
|
172
174
|
});
|
|
173
175
|
|
|
174
176
|
if (result.error) {
|
|
@@ -216,6 +218,7 @@ async function cmdInit(target, args = []) {
|
|
|
216
218
|
|
|
217
219
|
async function cmdSync(target, args = []) {
|
|
218
220
|
const dryRun = args.includes("--dry-run");
|
|
221
|
+
const quiet = args.includes("--quiet") || args.includes("-q");
|
|
219
222
|
|
|
220
223
|
// Quick local check: skip API if already at current version (unless dry-run)
|
|
221
224
|
let version = "unknown";
|
|
@@ -257,7 +260,7 @@ async function cmdSync(target, args = []) {
|
|
|
257
260
|
const result = await apiCall("/v1/sync", {
|
|
258
261
|
ai_version: version, stack, agents: agents.length ? agents : clis,
|
|
259
262
|
current_files: currentFiles, file_contents: fileContents,
|
|
260
|
-
dry_run: dryRun,
|
|
263
|
+
dry_run: dryRun, quiet,
|
|
261
264
|
});
|
|
262
265
|
|
|
263
266
|
if (result.error) { log(`error: ${result.error}`); process.exit(1); }
|
|
@@ -273,8 +276,16 @@ async function cmdSync(target, args = []) {
|
|
|
273
276
|
}
|
|
274
277
|
return;
|
|
275
278
|
}
|
|
276
|
-
|
|
277
|
-
|
|
279
|
+
const changedCount = Object.keys(updated).length;
|
|
280
|
+
if (changedCount) {
|
|
281
|
+
writeFiles(target, updated);
|
|
282
|
+
if (!quiet) {
|
|
283
|
+
for (const f of Object.keys(updated)) console.log(` ~ ${f}`);
|
|
284
|
+
}
|
|
285
|
+
log(`sync: ${changedCount} file(s) updated`);
|
|
286
|
+
} else {
|
|
287
|
+
log("already up to date");
|
|
288
|
+
}
|
|
278
289
|
}
|
|
279
290
|
|
|
280
291
|
async function cmdDetect(target) {
|
|
@@ -712,6 +723,146 @@ function cmdReflect(target, args) {
|
|
|
712
723
|
console.log();
|
|
713
724
|
}
|
|
714
725
|
|
|
726
|
+
function cmdMetrics(target) {
|
|
727
|
+
const ai = path.join(target, "ai");
|
|
728
|
+
const G = "\x1b[32m", W = "\x1b[33m", R2 = "\x1b[0m", D = "\x1b[2m",
|
|
729
|
+
B = "\x1b[34m", T = "\x1b[36m", M = "\x1b[35m";
|
|
730
|
+
|
|
731
|
+
// --- Data sources ---
|
|
732
|
+
let stats = {}, budget = {}, discovery = {};
|
|
733
|
+
try { stats = JSON.parse(fs.readFileSync(path.join(ai, "feedback", ".usage_stats.json"), "utf8")); } catch {}
|
|
734
|
+
try { budget = JSON.parse(fs.readFileSync(path.join(ai, "swarm", "budget.json"), "utf8")); } catch {}
|
|
735
|
+
try { discovery = JSON.parse(fs.readFileSync(path.join(ai, "manifest", "discovery.json"), "utf8")); } catch {}
|
|
736
|
+
|
|
737
|
+
const projectName = discovery.project_name || path.basename(target);
|
|
738
|
+
const stack = discovery.stack || "?";
|
|
739
|
+
const totalSessions = stats.total_sessions || 0;
|
|
740
|
+
const agentBreakdown = stats.agents || {};
|
|
741
|
+
const layerInfo = stats.layer || {};
|
|
742
|
+
const lastSession = stats.last_session ? new Date(stats.last_session) : null;
|
|
743
|
+
const layerVersion = stats.version || "?";
|
|
744
|
+
|
|
745
|
+
// Swarm tasks: count done files
|
|
746
|
+
let tasksDone = 0, tasksQueue = 0;
|
|
747
|
+
const doneDir = path.join(ai, "swarm", "done");
|
|
748
|
+
const queueDir = path.join(ai, "swarm", "queue");
|
|
749
|
+
try { tasksDone = fs.readdirSync(doneDir).filter(f => f.endsWith(".json")).length; } catch {}
|
|
750
|
+
try { tasksQueue = fs.readdirSync(queueDir).filter(f => f.endsWith(".json")).length; } catch {}
|
|
751
|
+
|
|
752
|
+
// Activity: count events from activity.jsonl
|
|
753
|
+
let activityEvents = 0;
|
|
754
|
+
try {
|
|
755
|
+
const lines = fs.readFileSync(path.join(ai, "swarm", "activity.jsonl"), "utf8").trim().split("\n").filter(Boolean);
|
|
756
|
+
activityEvents = lines.length;
|
|
757
|
+
} catch {}
|
|
758
|
+
|
|
759
|
+
// Feedback submissions
|
|
760
|
+
let feedbackCount = layerInfo.feedback_reports || 0;
|
|
761
|
+
|
|
762
|
+
// Budget totals
|
|
763
|
+
const totalSpent = budget.total_spent || 0;
|
|
764
|
+
const sessionsWithBudget = Object.keys(budget.sessions || {}).length;
|
|
765
|
+
|
|
766
|
+
// --- Effectiveness score (0-100) ---
|
|
767
|
+
let score = 0, scoreNotes = [];
|
|
768
|
+
|
|
769
|
+
// Sessions depth: 1 = tried, 3 = habit forming, 7 = regular use
|
|
770
|
+
const sessionScore = Math.min(Math.floor((totalSessions / 7) * 35), 35);
|
|
771
|
+
score += sessionScore;
|
|
772
|
+
if (totalSessions === 0) scoreNotes.push("not started");
|
|
773
|
+
else if (totalSessions === 1) scoreNotes.push("first session");
|
|
774
|
+
else if (totalSessions < 3) scoreNotes.push("early");
|
|
775
|
+
else if (totalSessions < 7) scoreNotes.push("habit forming");
|
|
776
|
+
else scoreNotes.push("regular use");
|
|
777
|
+
|
|
778
|
+
// Delegation: did they delegate to swarm?
|
|
779
|
+
const delegationScore = tasksDone > 0 ? Math.min(Math.floor((tasksDone / 5) * 30), 30) : 0;
|
|
780
|
+
score += delegationScore;
|
|
781
|
+
if (tasksDone > 0) scoreNotes.push(`${tasksDone} tasks delegated`);
|
|
782
|
+
|
|
783
|
+
// Feedback: submitted = trust signal
|
|
784
|
+
const feedbackScore = feedbackCount > 0 ? 20 : 0;
|
|
785
|
+
score += feedbackScore;
|
|
786
|
+
if (feedbackCount > 0) scoreNotes.push("feedback submitted");
|
|
787
|
+
|
|
788
|
+
// Layer completeness: has playbooks and commands?
|
|
789
|
+
const layerScore = (layerInfo.playbooks && layerInfo.commands) ? 15 : (layerInfo.commands ? 8 : 0);
|
|
790
|
+
score += layerScore;
|
|
791
|
+
|
|
792
|
+
const scoreColor = score >= 70 ? G : score >= 40 ? W : "\x1b[31m";
|
|
793
|
+
const bar = "█".repeat(Math.round(score / 5)).padEnd(20, "░");
|
|
794
|
+
|
|
795
|
+
// --- Output ---
|
|
796
|
+
console.log(`\n ${T}Metrics${R2} ${D}${projectName} · ${stack} · ai v${layerVersion}${R2}\n`);
|
|
797
|
+
|
|
798
|
+
// Effectiveness score
|
|
799
|
+
console.log(` ${B}Effectiveness${R2}`);
|
|
800
|
+
console.log(` ${scoreColor}${score}/100${R2} ${D}${bar}${R2}`);
|
|
801
|
+
if (scoreNotes.length) console.log(` ${D}${scoreNotes.join(" · ")}${R2}`);
|
|
802
|
+
|
|
803
|
+
// Adoption funnel
|
|
804
|
+
console.log(`\n ${B}Adoption funnel${R2}`);
|
|
805
|
+
const funnelStep = (label, value, done, hint) => {
|
|
806
|
+
const icon = done ? `${G}✓${R2}` : `${D}○${R2}`;
|
|
807
|
+
const val = value !== null ? ` ${D}${value}${R2}` : "";
|
|
808
|
+
const h = !done && hint ? ` ${D}← ${hint}${R2}` : "";
|
|
809
|
+
console.log(` ${icon} ${label}${val}${h}`);
|
|
810
|
+
};
|
|
811
|
+
funnelStep("Initialized", `ai/ v${layerVersion}`, true);
|
|
812
|
+
funnelStep("Returned (>1 session)", `${totalSessions} total`, totalSessions > 1, "run 0dai reflect after each session");
|
|
813
|
+
funnelStep("Used swarm delegation", tasksDone > 0 ? `${tasksDone} tasks done` : null, tasksDone > 0, "try: 0dai swarm add --task '...' --to codex");
|
|
814
|
+
funnelStep("Submitted feedback", feedbackCount > 0 ? `${feedbackCount} reports` : null, feedbackCount > 0, "0dai feedback log + push");
|
|
815
|
+
|
|
816
|
+
// Session stats
|
|
817
|
+
if (totalSessions > 0) {
|
|
818
|
+
console.log(`\n ${B}Sessions${R2}`);
|
|
819
|
+
console.log(` Total ${totalSessions}`);
|
|
820
|
+
if (lastSession) {
|
|
821
|
+
const daysAgo = Math.floor((Date.now() - lastSession.getTime()) / 86400000);
|
|
822
|
+
const when = daysAgo === 0 ? "today" : daysAgo === 1 ? "yesterday" : `${daysAgo}d ago`;
|
|
823
|
+
console.log(` Last ${when}`);
|
|
824
|
+
}
|
|
825
|
+
const agentEntries = Object.entries(agentBreakdown).sort((a, b) => b[1] - a[1]);
|
|
826
|
+
if (agentEntries.length) {
|
|
827
|
+
console.log(` Agents ${agentEntries.map(([a, n]) => `${a}: ${n}`).join(" ")}`);
|
|
828
|
+
}
|
|
829
|
+
if (sessionsWithBudget > 0 && totalSpent > 0) {
|
|
830
|
+
console.log(` Cost $${totalSpent.toFixed(4)} total ${D}(${sessionsWithBudget} sessions tracked)${R2}`);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Delegation stats
|
|
835
|
+
if (tasksDone > 0 || tasksQueue > 0) {
|
|
836
|
+
console.log(`\n ${B}Delegation${R2}`);
|
|
837
|
+
if (tasksDone > 0) console.log(` Done ${G}${tasksDone}${R2}`);
|
|
838
|
+
if (tasksQueue > 0) console.log(` Queue ${W}${tasksQueue}${R2}`);
|
|
839
|
+
if (activityEvents > 0) console.log(` Events ${activityEvents}`);
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// Layer health
|
|
843
|
+
console.log(`\n ${B}ai/ layer${R2}`);
|
|
844
|
+
const checks = [
|
|
845
|
+
["commands.yaml", layerInfo.commands],
|
|
846
|
+
["playbooks", layerInfo.playbooks],
|
|
847
|
+
["personas", layerInfo.personas],
|
|
848
|
+
["session roaming", layerInfo.session_active],
|
|
849
|
+
["swarm queue", (layerInfo.swarm_queue || 0) > 0],
|
|
850
|
+
];
|
|
851
|
+
for (const [label, ok] of checks) {
|
|
852
|
+
const icon = ok ? `${G}✓${R2}` : `${D}—${R2}`;
|
|
853
|
+
console.log(` ${icon} ${label}`);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Next suggested action
|
|
857
|
+
console.log(`\n ${B}Next${R2}`);
|
|
858
|
+
if (totalSessions === 0) console.log(` ${D}Start a Claude Code session — session_start hook will print project context${R2}`);
|
|
859
|
+
else if (tasksDone === 0) console.log(` ${D}Try delegating a task: 0dai swarm add --task "write tests for auth module" --to codex${R2}`);
|
|
860
|
+
else if (feedbackCount === 0) console.log(` ${D}Submit feedback: 0dai feedback log --type positive --detail "what worked"${R2}`);
|
|
861
|
+
else console.log(` ${D}Score ${score}/100 — keep delegating and submitting feedback${R2}`);
|
|
862
|
+
|
|
863
|
+
console.log();
|
|
864
|
+
}
|
|
865
|
+
|
|
715
866
|
function cmdStatus(target) {
|
|
716
867
|
const ai = path.join(target, "ai");
|
|
717
868
|
let v = "?", stack = "?";
|
|
@@ -1245,6 +1396,7 @@ async function main() {
|
|
|
1245
1396
|
case "doctor": cmdDoctor(target); break;
|
|
1246
1397
|
case "validate": cmdValidate(target); break;
|
|
1247
1398
|
case "reflect": cmdReflect(target, args); break;
|
|
1399
|
+
case "metrics": cmdMetrics(target); break;
|
|
1248
1400
|
case "status": cmdStatus(target); break;
|
|
1249
1401
|
case "auth":
|
|
1250
1402
|
if (sub === "login") await cmdAuthLogin();
|
|
@@ -1285,12 +1437,13 @@ async function main() {
|
|
|
1285
1437
|
console.log(`\n ${T}0dai${R} v${VERSION} — One config for 5 AI agent CLIs\n`);
|
|
1286
1438
|
console.log("Commands:");
|
|
1287
1439
|
console.log(" audit Scan ai/ and agent configs for leaked secrets");
|
|
1288
|
-
console.log(" init Initialize ai/ layer (via API) [--dry-run]");
|
|
1289
|
-
console.log(" sync Update ai/ layer (via API) [--dry-run]");
|
|
1440
|
+
console.log(" init Initialize ai/ layer (via API) [--dry-run] [--minimal]");
|
|
1441
|
+
console.log(" sync Update ai/ layer (via API) [--dry-run] [--quiet]");
|
|
1290
1442
|
console.log(" detect Show detected stack");
|
|
1291
1443
|
console.log(" doctor Check health + credentials checklist");
|
|
1292
1444
|
console.log(" validate Validate ai/ layer completeness");
|
|
1293
1445
|
console.log(" reflect Session reflection: delivered, delegation rate, blockers");
|
|
1446
|
+
console.log(" metrics Effectiveness score: adoption funnel, sessions, delegation");
|
|
1294
1447
|
console.log(" status Show maturity, swarm, session");
|
|
1295
1448
|
console.log(" session save Save session for roaming");
|
|
1296
1449
|
console.log(" swarm status Task queue & delegation");
|