@0dai-dev/cli 4.3.5 → 4.3.7
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/README.md +12 -11
- package/bin/0dai.js +214 -40
- package/lib/ai/manifest/mcp-exposure-contract.json +121 -0
- package/lib/ai/meta/manifest/mcp-tool-tiers.json +435 -0
- package/lib/ai/registry/mcp-catalog.json +98 -0
- package/lib/commands/auth.js +55 -1
- package/lib/commands/compliance.js +1 -1
- package/lib/commands/detect.js +10 -4
- package/lib/commands/doctor.js +545 -26
- package/lib/commands/experience.js +40 -5
- package/lib/commands/export.js +73 -0
- package/lib/commands/feedback.js +157 -15
- package/lib/commands/gh.js +26 -0
- package/lib/commands/graph.js +9 -4
- package/lib/commands/heatmap.js +1 -1
- package/lib/commands/init.js +222 -30
- package/lib/commands/mcp.js +129 -21
- package/lib/commands/models.js +138 -41
- package/lib/commands/provider.js +30 -59
- package/lib/commands/quota.js +1 -1
- package/lib/commands/receipt.js +1 -1
- package/lib/commands/run.js +18 -7
- package/lib/commands/runner.js +31 -1
- package/lib/commands/status.js +44 -11
- package/lib/commands/swarm.js +130 -12
- package/lib/commands/trust.js +286 -0
- package/lib/commands/update.js +184 -38
- package/lib/commands/usage.js +1 -1
- package/lib/commands/validate.js +32 -3
- package/lib/commands/vault.js +46 -9
- package/lib/python/__init__.py +0 -0
- package/lib/python/agent_quotas.py +525 -0
- package/lib/python/anomaly_alert.py +397 -0
- package/lib/python/anti_pattern_detector.py +799 -0
- package/lib/python/auth.py +443 -0
- package/lib/python/capi_profile_guard.py +477 -0
- package/lib/python/compliance_report.py +581 -0
- package/lib/python/drift_detector.py +388 -0
- package/lib/python/experience_pipeline.py +1130 -0
- package/lib/python/graph.py +19 -0
- package/lib/python/graph_core.py +293 -0
- package/lib/python/graph_io.py +179 -0
- package/lib/python/graph_legacy.py +2052 -0
- package/lib/python/graph_legacy_helpers.py +221 -0
- package/lib/python/graph_outcomes_core.py +85 -0
- package/lib/python/graph_queries.py +171 -0
- package/lib/python/graph_slice.py +198 -0
- package/lib/python/graph_slicer.py +576 -0
- package/lib/python/graph_slicer_cli.py +60 -0
- package/lib/python/graph_validation.py +64 -0
- package/lib/python/heatmap.py +934 -0
- package/lib/python/json_utils.py +193 -0
- package/lib/python/mcp_exposure_check.py +247 -0
- package/lib/python/model_router.py +1434 -0
- package/lib/python/project_manager.py +621 -0
- package/lib/python/provider_profiles.py +1618 -0
- package/lib/python/provider_registry.py +1211 -0
- package/lib/python/provider_registry_cli.py +125 -0
- package/lib/python/receipt_png.py +727 -0
- package/lib/python/structural_memory.py +325 -0
- package/lib/python/swarm_cost.py +177 -0
- package/lib/python/usage_ledger.py +569 -0
- package/lib/scripts/mcp_tier_config.py +240 -0
- package/lib/shared.js +97 -14
- package/lib/tui/index.mjs +35174 -0
- package/lib/utils/activation_telemetry.js +230 -11
- package/lib/utils/constants.js +7 -1
- package/lib/utils/export-bundler.js +285 -0
- package/lib/utils/identity.js +198 -1
- package/lib/utils/mcp-auth.js +81 -15
- package/lib/utils/plan.js +1 -1
- package/lib/vault/index.js +19 -3
- package/lib/vault/storage.js +21 -2
- package/lib/wizard.js +5 -2
- package/package.json +9 -3
- package/scripts/build-python-bundle.js +106 -0
- package/scripts/build-tui.js +14 -1
- package/scripts/harvest_experience.py +523 -0
- package/scripts/postinstall.js +15 -9
package/README.md
CHANGED
|
@@ -43,39 +43,36 @@ Daily (regular work):
|
|
|
43
43
|
0dai run <goal> # split a backlog item into agent tasks [--dry-run] [--dry-cost] [--agent claude|codex|gemini]
|
|
44
44
|
0dai swarm status # show queued, active, and done tasks
|
|
45
45
|
0dai swarm add # queue one task [--task '...' --to agent]
|
|
46
|
-
0dai swarm
|
|
46
|
+
0dai swarm pick # pick one queued task [--agent codex]
|
|
47
|
+
0dai swarm-run # repo-checkout helper: add, dispatch, and wait for one swarm task as JSON
|
|
47
48
|
0dai harvest # convert experience events into candidate lessons
|
|
48
49
|
0dai watch # live task monitor [--interval N]
|
|
49
50
|
0dai reflect # session reflection: delivered, delegation, blockers
|
|
50
|
-
0dai standup # morning voice briefing about overnight agent work
|
|
51
|
-
0dai feedback push
|
|
51
|
+
0dai standup # repo-checkout helper: morning voice briefing about overnight agent work
|
|
52
|
+
0dai feedback push --target . # send feedback to 0dai
|
|
52
53
|
0dai feedback retry # retry queued feedback after a failed push
|
|
53
|
-
0dai persona-simulate "topic" # focus-group report and optional issue drafts
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
Pro / advanced:
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
0dai init-existing # existing-repo setup alias for init
|
|
60
|
-
0dai project bind # bind repository to your 0dai account [--json]
|
|
60
|
+
0dai project bind # bind repository to your 0dai account [--name NAME] [--json]
|
|
61
61
|
0dai project status # local project binding and health [--json]
|
|
62
62
|
0dai graph push # upload local graph (Pro: edges, Free: nodes)
|
|
63
63
|
0dai graph pull # download server graph and merge locally
|
|
64
64
|
0dai graph status # local graph stats and sync state
|
|
65
|
-
0dai ci # portable CI pipelines and AI-MQ [list|plan|mq-status]
|
|
66
65
|
0dai heatmap # repo treemap: LOC × agent-edit intensity
|
|
67
66
|
0dai session save # save session for roaming
|
|
68
|
-
0dai provider #
|
|
67
|
+
0dai provider # provider profiles and local BYOK registry [status|list|switch|clear]
|
|
69
68
|
0dai models # model ratings (--fast/--balanced/--deep/--available)
|
|
70
69
|
0dai quota # agent subscription usage [--refresh] [--json]
|
|
71
70
|
0dai usage # local token, task, and USD ledger [status|daily|monthly]
|
|
72
71
|
0dai workspace # tmux workspace sessions (init|up|status)
|
|
73
|
-
0dai
|
|
74
|
-
0dai report # privacy-safe project reports (preview|push|status)
|
|
72
|
+
0dai report # repo-checkout helper: privacy-safe project reports (preview|push|status)
|
|
75
73
|
0dai compliance # SOC2/ISO evidence and ADR audit-trail export
|
|
76
|
-
0dai experience # structured experience events (list|stats|sync|warnings|dismiss)
|
|
74
|
+
0dai experience # structured experience events (list|stats|record-json|sync|warnings|dismiss)
|
|
77
75
|
0dai receipt # session receipt PNG [--last|--active|--session ID]
|
|
78
|
-
0dai boneyard # weekly digest of worst agent moves [--week YYYY-WW|current]
|
|
79
76
|
0dai gh branch-protection # GitHub branch protection [print|apply|install]
|
|
80
77
|
0dai import claude-code-agents # import .claude/agents/*.md as personas [--dry-run]
|
|
81
78
|
0dai auth logout # remove credentials
|
|
@@ -100,6 +97,10 @@ Global flags: `--target PATH`, `--version`, `--help`, `--json`, `--quiet`. See `
|
|
|
100
97
|
|
|
101
98
|
Your source code is never sent. Only file names and package/build manifests.
|
|
102
99
|
|
|
100
|
+
## Privacy
|
|
101
|
+
|
|
102
|
+
Local activation and experience records stay on your machine: `ai/meta/telemetry/activation.jsonl`, `ai/experience/`, and the generated `ai/` layer/project agent configs are local files. Server-side activation sends one `free_tier_activated` event when you run `0dai activate free` unless telemetry is opted out. See https://0dai.dev/legal/privacy.
|
|
103
|
+
|
|
103
104
|
## Why 0dai, not just Cursor or Copilot?
|
|
104
105
|
|
|
105
106
|
Cursor and Copilot are editors. They help inside one coding session. 0dai writes a project layer that Claude Code, Codex, OpenCode, Gemini, and Aider can all read. The point is not another autocomplete box; it is one manifest, one set of agent roles, one task queue, and one health check for the repo.
|
package/bin/0dai.js
CHANGED
|
@@ -26,23 +26,63 @@ if ((earlyParsed.args[0] || "help") === "swarm-run") {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const shared = require("../lib/shared");
|
|
29
|
-
const { T, R, D, log, VERSION, fs, path, spawnSync, findRepoScript, checkVersion } = shared;
|
|
29
|
+
const { T, R, D, log, VERSION, fs, path, spawnSync, findRepoScript, repoScriptCandidates, checkVersion } = shared;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Hot-path Go binary fallback (issue #2424).
|
|
33
33
|
*
|
|
34
|
-
* For read-only hot-path commands
|
|
35
|
-
* to a Go binary when:
|
|
34
|
+
* For read-only hot-path commands we attempt to delegate to a Go binary when:
|
|
36
35
|
* 1. ODAI_GO_BIN is set and points at an executable file, OR a binary called
|
|
37
36
|
* `0dai-go` is found on PATH.
|
|
38
|
-
* 2. The binary reports `
|
|
39
|
-
* (
|
|
37
|
+
* 2. The binary reports `dispatcher_compat_version` matching the dispatcher
|
|
38
|
+
* VERSION (legacy binaries may still use `binary_version` for this).
|
|
40
39
|
* 3. ODAI_GO_DISABLE is NOT set to a truthy value.
|
|
40
|
+
* 4. The command's batch flag is not disabled. SPEC-035 rollback Level 1
|
|
41
|
+
* is `ODAI_GO_BATCH_<N>=0`, which transparently routes the whole batch
|
|
42
|
+
* back to the Node/Python implementation without reinstalling.
|
|
43
|
+
*
|
|
44
|
+
* Only commands listed in GO_HOT_PATH_COMMANDS are eligible for automatic
|
|
45
|
+
* delegation. `status` moved into batch 2 only after the #4098 Go↔Node
|
|
46
|
+
* payload parity proof landed. Base `doctor` moved into the same batch only
|
|
47
|
+
* after #4111 made the local `doctor_checks` shadow contract explicit;
|
|
48
|
+
* `doctor --drift` stays on the full Node implementation.
|
|
41
49
|
*
|
|
42
50
|
* If any of these checks fail we silently fall through to the existing
|
|
43
51
|
* Python/Node implementations. The goal is zero behaviour change when the Go
|
|
44
52
|
* binary is missing, broken, or version-skewed.
|
|
45
53
|
*/
|
|
54
|
+
const GO_HOT_PATH_COMMANDS = new Set(["version", "status", "doctor"]);
|
|
55
|
+
const GO_HOT_PATH_BATCHES = Object.freeze({
|
|
56
|
+
version: 1,
|
|
57
|
+
status: 2,
|
|
58
|
+
doctor: 2,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
function goFlagDisabled(raw) {
|
|
62
|
+
if (raw === undefined || raw === null || raw === "") return false;
|
|
63
|
+
const value = String(raw).trim().toLowerCase();
|
|
64
|
+
return ["0", "false", "no", "off"].includes(value);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function goCommandEnvName(cmdName) {
|
|
68
|
+
return `ODAI_GO_COMMAND_${String(cmdName).toUpperCase().replace(/[^A-Z0-9]+/g, "_")}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function goBatchEnvName(batch) {
|
|
72
|
+
return `ODAI_GO_BATCH_${batch}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function goBatchEnabled(cmdName) {
|
|
76
|
+
const batch = GO_HOT_PATH_BATCHES[cmdName];
|
|
77
|
+
if (!batch) return false;
|
|
78
|
+
if (goFlagDisabled(process.env[goBatchEnvName(batch)])) return false;
|
|
79
|
+
|
|
80
|
+
// Drill-only per-command rollback override. Batch flags remain the release
|
|
81
|
+
// contract; this override is intentionally narrower for staging drills.
|
|
82
|
+
if (goFlagDisabled(process.env[goCommandEnvName(cmdName)])) return false;
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
46
86
|
function locateGoBinary() {
|
|
47
87
|
if (process.env.ODAI_GO_DISABLE && process.env.ODAI_GO_DISABLE !== "0") return null;
|
|
48
88
|
const explicit = process.env.ODAI_GO_BIN;
|
|
@@ -68,7 +108,11 @@ function goBinaryCompatible(binPath) {
|
|
|
68
108
|
if (res.status !== 0 || !res.stdout) return false;
|
|
69
109
|
const info = JSON.parse(res.stdout.toString());
|
|
70
110
|
if (!info || typeof info.binary_version !== "string") return false;
|
|
71
|
-
|
|
111
|
+
const compatVersion =
|
|
112
|
+
typeof info.dispatcher_compat_version === "string"
|
|
113
|
+
? info.dispatcher_compat_version
|
|
114
|
+
: info.binary_version;
|
|
115
|
+
return compatVersion === VERSION;
|
|
72
116
|
} catch {
|
|
73
117
|
return false;
|
|
74
118
|
}
|
|
@@ -80,18 +124,32 @@ function goBinaryCompatible(binPath) {
|
|
|
80
124
|
* caller must fall back to the existing Python/Node path.
|
|
81
125
|
*/
|
|
82
126
|
function tryGoHotPath(cmdName, target, argv) {
|
|
127
|
+
if (!GO_HOT_PATH_COMMANDS.has(cmdName)) return false;
|
|
128
|
+
if (!goBatchEnabled(cmdName)) return false;
|
|
83
129
|
const bin = locateGoBinary();
|
|
84
130
|
if (!bin) return false;
|
|
85
131
|
if (!goBinaryCompatible(bin)) return false;
|
|
86
132
|
const forwarded = [cmdName, "--target", target, ...argv];
|
|
87
|
-
const res = spawnSync(bin, forwarded, {
|
|
133
|
+
const res = spawnSync(bin, forwarded, {
|
|
134
|
+
stdio: "inherit",
|
|
135
|
+
env: { ...process.env, ODAI_NODE_PTY_PROBE_DIR: path.join(__dirname, "..") },
|
|
136
|
+
});
|
|
88
137
|
if (res.error) return false;
|
|
89
138
|
if (typeof res.status === "number") process.exit(res.status);
|
|
90
139
|
process.exit(0);
|
|
91
140
|
}
|
|
92
141
|
|
|
93
142
|
// Export for tests; harmless at runtime.
|
|
94
|
-
module.exports = {
|
|
143
|
+
module.exports = {
|
|
144
|
+
locateGoBinary,
|
|
145
|
+
goBinaryCompatible,
|
|
146
|
+
goBatchEnabled,
|
|
147
|
+
goBatchEnvName,
|
|
148
|
+
goCommandEnvName,
|
|
149
|
+
tryGoHotPath,
|
|
150
|
+
GO_HOT_PATH_BATCHES,
|
|
151
|
+
GO_HOT_PATH_COMMANDS,
|
|
152
|
+
};
|
|
95
153
|
const { loadCanonicalCounts, mcpToolsLabel } = require("../lib/utils/canonical-counts");
|
|
96
154
|
|
|
97
155
|
// --- Command imports ---
|
|
@@ -99,7 +157,10 @@ const { cmdAuthLogin, cmdAuthLogout, cmdRedeem, cmdAuthStatus, cmdAuthMcp, cmdAc
|
|
|
99
157
|
const { cmdInit, cmdSync, cmdProjectBind } = require("../lib/commands/init");
|
|
100
158
|
const { cmdDetect } = require("../lib/commands/detect");
|
|
101
159
|
const { cmdAudit } = require("../lib/commands/audit");
|
|
102
|
-
const {
|
|
160
|
+
const { cmdExport } = require("../lib/commands/export");
|
|
161
|
+
const { cmdMcp } = require("../lib/commands/mcp");
|
|
162
|
+
const { cmdVault } = require("../lib/commands/vault");
|
|
163
|
+
const { cmdDoctor, collectLayerVersionFreshness } = require("../lib/commands/doctor");
|
|
103
164
|
const { cmdValidate } = require("../lib/commands/validate");
|
|
104
165
|
const { cmdUpdate } = require("../lib/commands/update");
|
|
105
166
|
const { cmdUpgrade } = require("../lib/commands/upgrade");
|
|
@@ -110,7 +171,7 @@ const { cmdStatus } = require("../lib/commands/status");
|
|
|
110
171
|
const { cmdPortfolio } = require("../lib/commands/portfolio");
|
|
111
172
|
const { cmdRun } = require("../lib/commands/run");
|
|
112
173
|
const { cmdWatch } = require("../lib/commands/watch");
|
|
113
|
-
const { cmdModels } = require("../lib/commands/models");
|
|
174
|
+
const { cmdModels, cmdModelsRecommend } = require("../lib/commands/models");
|
|
114
175
|
const { cmdSession } = require("../lib/commands/session");
|
|
115
176
|
const { cmdSwarm, cmdSwarmRun } = require("../lib/commands/swarm");
|
|
116
177
|
const { cmdStandup } = require("../lib/commands/standup");
|
|
@@ -135,10 +196,18 @@ const { cmdLoop } = require("../lib/commands/loop");
|
|
|
135
196
|
const { cmdImportClaudeCodeAgents } = require("../lib/commands/import_claude_code_agents");
|
|
136
197
|
const { cmdRunner } = require("../lib/commands/runner");
|
|
137
198
|
const { cmdCi } = require("../lib/commands/ci");
|
|
199
|
+
const { cmdTrust } = require("../lib/commands/trust");
|
|
138
200
|
|
|
139
201
|
function printHelp() {
|
|
140
202
|
const counts = loadCanonicalCounts();
|
|
141
203
|
console.log(`\n ${T}0dai${R} v${VERSION} — One config for ${counts.agent_clis_total} AI agent CLIs · ${mcpToolsLabel(counts)}\n`);
|
|
204
|
+
console.log("First-run sequence (canonical):");
|
|
205
|
+
console.log(" npm install -g @0dai-dev/cli # install once, globally");
|
|
206
|
+
console.log(" 0dai auth login # sign in (OAuth / device code)");
|
|
207
|
+
console.log(" 0dai activate free # claim free-tier license");
|
|
208
|
+
console.log(" 0dai init # generate ai/ layer in cwd");
|
|
209
|
+
console.log(" 0dai doctor # verify health and drift");
|
|
210
|
+
console.log("");
|
|
142
211
|
console.log("Start (first 5 minutes):");
|
|
143
212
|
console.log(" init Create ai/ layer + MCP [--local] [--dry-run] [--minimal]");
|
|
144
213
|
console.log(" doctor Check health, credentials, and drift [--drift]");
|
|
@@ -155,36 +224,38 @@ function printHelp() {
|
|
|
155
224
|
console.log(" run <goal> Split a backlog item into agent tasks [--dry-run] [--dry-cost] [--max-cost N] [--agent claude|codex|gemini] [--provider X]");
|
|
156
225
|
console.log(" swarm status Show queued, active, and done tasks");
|
|
157
226
|
console.log(" swarm add Queue one task for an agent [--task '...' --to agent]");
|
|
158
|
-
console.log(" swarm
|
|
227
|
+
console.log(" swarm pick Pick one queued task for this agent [--agent codex]");
|
|
228
|
+
console.log(" swarm-run Repo-checkout helper: add, dispatch, and wait for one swarm task as JSON");
|
|
159
229
|
console.log(" harvest Convert experience events into candidate lessons");
|
|
160
230
|
console.log(" watch Live task monitor: queue, active, recently done [--interval N]");
|
|
161
231
|
console.log(" reflect Session reflection: delivered, delegation rate, blockers");
|
|
162
|
-
console.log(" standup
|
|
232
|
+
console.log(" standup Repo-checkout helper: morning voice briefing about overnight agent work");
|
|
163
233
|
console.log(" feedback push Send feedback to 0dai");
|
|
234
|
+
console.log(" feedback submit Send one feedback report file [--file ai/feedback/codex-report.json]");
|
|
164
235
|
console.log(" feedback retry Retry queued feedback after a failed push");
|
|
165
236
|
console.log("");
|
|
166
237
|
console.log("Pro / advanced:");
|
|
167
|
-
console.log(" init-existing
|
|
168
|
-
console.log(" project bind Bind current repository to your 0dai account [--json]");
|
|
238
|
+
console.log(" init-existing Legacy alias for init (older docs / scripted bootstraps); use 'init' [--minimal] [--dry-run]");
|
|
239
|
+
console.log(" project bind Bind current repository to your 0dai account [--name NAME] [--json]");
|
|
169
240
|
console.log(" project status Show local project binding and health state [--json]");
|
|
170
241
|
console.log(" graph push Upload local graph to server (Pro: edges, Free: nodes)");
|
|
171
242
|
console.log(" graph pull Download server graph and merge locally");
|
|
172
243
|
console.log(" graph status Show local graph stats and sync state");
|
|
173
|
-
console.log("
|
|
244
|
+
console.log(" mcp MCP server, tools, and health [list|catalog|doctor|call] [--json]");
|
|
245
|
+
console.log(" vault Local age-encrypted secrets vault [init|add|get] [--json]");
|
|
174
246
|
console.log(" heatmap Repo treemap: LOC x agent-edit intensity");
|
|
175
247
|
console.log(" session save Save session for roaming");
|
|
176
|
-
console.log(" provider Local provider profiles
|
|
248
|
+
console.log(" provider Local provider profiles and BYOK registry [status|list|switch|clear]");
|
|
177
249
|
console.log(" models Show model ratings (--fast/--balanced/--deep/--available)");
|
|
250
|
+
console.log(" models recommend Ledger-ranked model pick for a task type [--task TYPE] [--goal '...'] [--json]");
|
|
178
251
|
console.log(" quota Agent subscription usage table [--refresh] [--json]");
|
|
179
252
|
console.log(" usage Local token, task, and USD usage ledger [status|daily|monthly]");
|
|
180
253
|
console.log(" workspace Manage tmux workspace sessions (init|up|status)");
|
|
181
|
-
console.log("
|
|
182
|
-
console.log("
|
|
254
|
+
console.log(" report Repo-checkout helper: privacy-safe project reports (preview|push|status)");
|
|
255
|
+
console.log(" trust Pre-run blast-radius: protected paths, authority matrix, egress [--json]");
|
|
183
256
|
console.log(" compliance SOC2/ISO evidence and ADR audit-trail export");
|
|
184
|
-
console.log(" experience Structured experience events (list|stats|sync|warnings|dismiss)");
|
|
185
|
-
console.log(" persona-simulate Produce a focus-group report and optional issue drafts");
|
|
257
|
+
console.log(" experience Structured experience events (list|stats|record-json|sync|warnings|dismiss)");
|
|
186
258
|
console.log(" receipt Render a 1200×630 session receipt PNG [--last|--active|--session ID]");
|
|
187
|
-
console.log(" boneyard Weekly digest of worst agent moves [--week YYYY-WW|current]");
|
|
188
259
|
console.log(" gh branch-protection [print|apply|install] Manage generated GitHub branch protection");
|
|
189
260
|
console.log(" import claude-code-agents Import .claude/agents/*.md as 0dai personas [--source DIR] [--target DIR] [--dry-run]");
|
|
190
261
|
console.log(" auth logout Remove credentials");
|
|
@@ -200,29 +271,105 @@ function printHelp() {
|
|
|
200
271
|
console.log("https://0dai.dev");
|
|
201
272
|
}
|
|
202
273
|
|
|
274
|
+
function explainMissingHarvestHelper(target, args) {
|
|
275
|
+
const candidates = repoScriptCandidates(target, "harvest_experience.py");
|
|
276
|
+
if (args.includes("--json")) {
|
|
277
|
+
console.log(JSON.stringify({
|
|
278
|
+
error: "harvest_helper_unavailable",
|
|
279
|
+
helper: "harvest_experience.py",
|
|
280
|
+
target,
|
|
281
|
+
looked_in: candidates,
|
|
282
|
+
fallback_inputs: [
|
|
283
|
+
"ai/experience/events/*.json",
|
|
284
|
+
"ai/experience/events/*.jsonl",
|
|
285
|
+
"ai/experience/outbox/*.json",
|
|
286
|
+
"ai/experience/outbox/*.jsonl",
|
|
287
|
+
],
|
|
288
|
+
required_fallback_fields: [
|
|
289
|
+
"schema",
|
|
290
|
+
"event_id",
|
|
291
|
+
"timestamp",
|
|
292
|
+
"event_type",
|
|
293
|
+
"tool",
|
|
294
|
+
"task_type",
|
|
295
|
+
"summary",
|
|
296
|
+
"paths",
|
|
297
|
+
"ci_passed",
|
|
298
|
+
"source",
|
|
299
|
+
],
|
|
300
|
+
}, null, 2));
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
log("harvest helper unavailable");
|
|
305
|
+
if (!args.includes("--explain")) return;
|
|
306
|
+
|
|
307
|
+
console.log(` ${D}looked for scripts/harvest_experience.py at:${R}`);
|
|
308
|
+
for (const candidate of candidates) {
|
|
309
|
+
console.log(` - ${candidate}`);
|
|
310
|
+
}
|
|
311
|
+
console.log(` ${D}fallback event inputs:${R}`);
|
|
312
|
+
console.log(" - ai/experience/events/*.json");
|
|
313
|
+
console.log(" - ai/experience/events/*.jsonl");
|
|
314
|
+
console.log(" - ai/experience/outbox/*.json");
|
|
315
|
+
console.log(" - ai/experience/outbox/*.jsonl");
|
|
316
|
+
console.log(` ${D}fallback schema fields:${R}`);
|
|
317
|
+
console.log(" schema, event_id, timestamp, event_type, tool, task_type, summary, paths, ci_passed, source");
|
|
318
|
+
}
|
|
319
|
+
|
|
203
320
|
const SYNC_ALLOWED_FLAGS = new Set([
|
|
321
|
+
"--check",
|
|
204
322
|
"--dry-run",
|
|
205
323
|
"--yes",
|
|
206
324
|
"-y",
|
|
207
325
|
"--quiet",
|
|
208
326
|
"-q",
|
|
209
327
|
"--force",
|
|
328
|
+
"--force-template-reset",
|
|
210
329
|
"--no-diff",
|
|
211
330
|
"--no-mcp-auth",
|
|
212
331
|
"--skip-link-check",
|
|
213
332
|
"--strict-links",
|
|
214
333
|
]);
|
|
215
334
|
|
|
335
|
+
function printInitHelp(commandName = "init") {
|
|
336
|
+
console.log(`\n ${T}0dai ${commandName}${R} — Create or refresh the managed ai/ layer\n`);
|
|
337
|
+
console.log("Usage:");
|
|
338
|
+
console.log(` 0dai ${commandName} [--local] [--minimal] [--dry-run] [--no-wizard] [--auth-code CODE] [--code LICENSE] [--no-mcp-auth] [--mcp-host HOST] [--reset] [--target PATH]`);
|
|
339
|
+
console.log("");
|
|
340
|
+
console.log("Options:");
|
|
341
|
+
console.log(" --local Generate the ai/ layer offline without signing in");
|
|
342
|
+
console.log(" --minimal Generate the smallest starter layer");
|
|
343
|
+
console.log(" --dry-run Preview init changes without writing them");
|
|
344
|
+
console.log(" --no-wizard Skip the interactive first-run wizard");
|
|
345
|
+
console.log(" --auth-code Exchange a browser/device auth code before init");
|
|
346
|
+
console.log(" --code Redeem an activation/license code before init");
|
|
347
|
+
console.log(" --no-mcp-auth Skip MCP cloud-token bootstrap during init");
|
|
348
|
+
console.log(" --mcp-host Use a custom MCP cloud host");
|
|
349
|
+
console.log(" --reset Overwrite managed MCP entries during bootstrap");
|
|
350
|
+
console.log(" --target PATH Run init against another project path");
|
|
351
|
+
console.log("");
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
function handleInitHelp(args) {
|
|
355
|
+
const initArgs = args.slice(1);
|
|
356
|
+
if (!initArgs.includes("--help") && !initArgs.includes("-h")) return false;
|
|
357
|
+
printInitHelp(args[0] || "init");
|
|
358
|
+
return true;
|
|
359
|
+
}
|
|
360
|
+
|
|
216
361
|
function printSyncHelp() {
|
|
217
362
|
console.log(`\n ${T}0dai sync${R} — Update the managed ai/ layer\n`);
|
|
218
363
|
console.log("Usage:");
|
|
219
|
-
console.log(" 0dai sync [--dry-run] [--yes|-y] [--quiet|-q] [--force] [--no-diff] [--no-mcp-auth] [--target PATH]");
|
|
364
|
+
console.log(" 0dai sync [--check] [--dry-run] [--yes|-y] [--quiet|-q] [--force] [--force-template-reset] [--no-diff] [--no-mcp-auth] [--target PATH]");
|
|
220
365
|
console.log("");
|
|
221
366
|
console.log("Options:");
|
|
367
|
+
console.log(" --check CI-friendly alias for --dry-run --quiet");
|
|
222
368
|
console.log(" --dry-run Preview managed file changes without writing them");
|
|
223
369
|
console.log(" --yes, -y Apply changes without an interactive confirmation prompt");
|
|
224
370
|
console.log(" --quiet, -q Reduce non-essential output");
|
|
225
371
|
console.log(" --force Also overwrite native config files from managed ai/ sources");
|
|
372
|
+
console.log(" --force-template-reset Allow replacing project-owned roadmap/manifest state");
|
|
226
373
|
console.log(" --no-diff Hide unified diff output in previews/prompts");
|
|
227
374
|
console.log(" --no-mcp-auth Skip MCP cloud-token bootstrap during sync");
|
|
228
375
|
console.log(" --skip-link-check Skip the SPEC-028 doc cross-link scan after sync");
|
|
@@ -248,6 +395,24 @@ function handleSyncHelpOrInvalidArgs(args) {
|
|
|
248
395
|
return false;
|
|
249
396
|
}
|
|
250
397
|
|
|
398
|
+
function printActivateHelp() {
|
|
399
|
+
console.log(`\n ${T}0dai activate${R} — Claim or inspect a 0dai activation\n`);
|
|
400
|
+
console.log("Usage:");
|
|
401
|
+
console.log(" 0dai activate [free|status|code <CODE>] [--target PATH]");
|
|
402
|
+
console.log("");
|
|
403
|
+
console.log("Privacy:");
|
|
404
|
+
console.log(" Local files stay local; 'activate free' sends one free_tier_activated event unless telemetry is opted out.");
|
|
405
|
+
console.log(" https://0dai.dev/legal/privacy");
|
|
406
|
+
console.log("");
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function handleActivateHelp(args) {
|
|
410
|
+
const activateArgs = args.slice(1);
|
|
411
|
+
if (!activateArgs.includes("--help") && !activateArgs.includes("-h")) return false;
|
|
412
|
+
printActivateHelp();
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
|
|
251
416
|
async function main() {
|
|
252
417
|
const { args, target } = parseTargetAndArgs(process.argv.slice(2), process.cwd());
|
|
253
418
|
|
|
@@ -263,8 +428,18 @@ async function main() {
|
|
|
263
428
|
return;
|
|
264
429
|
}
|
|
265
430
|
|
|
266
|
-
|
|
267
|
-
|
|
431
|
+
if ((cmd === "init" || cmd === "init-existing") && handleInitHelp(args)) {
|
|
432
|
+
return;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (cmd === "activate" && handleActivateHelp(args)) {
|
|
436
|
+
return;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Non-blocking version check (runs in background, once per day). Skip
|
|
440
|
+
// machine-readable commands so update network I/O never holds JSON output
|
|
441
|
+
// open or pollutes stdout.
|
|
442
|
+
if (!args.includes("--json") && cmd !== "init" && cmd !== "init-existing") checkVersion();
|
|
268
443
|
|
|
269
444
|
// Track first run for time-to-init telemetry
|
|
270
445
|
try { require("../lib/onboarding").trackFirstRun(target); } catch {}
|
|
@@ -288,6 +463,8 @@ async function main() {
|
|
|
288
463
|
case "run": await cmdRun(args[1] || "", target, args.slice(2)); break;
|
|
289
464
|
case "watch": cmdWatch(target, args.slice(1)); break;
|
|
290
465
|
case "audit": cmdAudit(target); break;
|
|
466
|
+
case "export": await cmdExport(target, args); break;
|
|
467
|
+
case "vault": cmdVault(target, args[1], args.slice(2)); break;
|
|
291
468
|
case "security": {
|
|
292
469
|
const subSec = args[1] || "";
|
|
293
470
|
if (subSec === "install-hook") {
|
|
@@ -328,24 +505,15 @@ async function main() {
|
|
|
328
505
|
const driftMode = args.includes("--drift");
|
|
329
506
|
// Go fast-path covers only the base read-only doctor (no --drift, no
|
|
330
507
|
// network). Anything else falls through to the full Node implementation.
|
|
331
|
-
|
|
508
|
+
const layerFreshness = collectLayerVersionFreshness(target);
|
|
509
|
+
if (!driftMode && layerFreshness.status !== "stale") {
|
|
332
510
|
tryGoHotPath("doctor", target, args.slice(1));
|
|
333
511
|
}
|
|
334
|
-
cmdDoctor(target, { drift: driftMode });
|
|
335
|
-
if (args.includes("--drift")) {
|
|
336
|
-
const ds = findRepoScript(target, "drift_detector.py");
|
|
337
|
-
console.log("\n drift report:");
|
|
338
|
-
if (ds) {
|
|
339
|
-
const result = spawnSync("python3", [ds, "report", "--target", target], { stdio: "inherit" });
|
|
340
|
-
if (typeof result.status === "number" && result.status !== 0) process.exit(result.status);
|
|
341
|
-
} else {
|
|
342
|
-
console.log(` ${D}drift detector unavailable in this environment${R}`);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
512
|
+
cmdDoctor(target, { drift: driftMode, json: args.includes("--json") });
|
|
345
513
|
break;
|
|
346
514
|
}
|
|
347
515
|
case "drift": {
|
|
348
|
-
const ds =
|
|
516
|
+
const ds = shared.resolvePythonScript(target, "drift_detector.py");
|
|
349
517
|
if (!ds) { log("drift detector unavailable"); break; }
|
|
350
518
|
if (sub === "accept" && args[2]) {
|
|
351
519
|
spawnSync("python3", [ds, "accept", args[2], "--target", target], { stdio: "inherit" });
|
|
@@ -370,7 +538,7 @@ async function main() {
|
|
|
370
538
|
case "project":
|
|
371
539
|
if (sub === "status") cmdStatus(target, { json: args.includes("--json") });
|
|
372
540
|
else if (sub === "bind") await cmdProjectBind(target, args.slice(2));
|
|
373
|
-
else console.log("Usage: 0dai project [bind [--json]|status [--json]] [--target PATH]");
|
|
541
|
+
else console.log("Usage: 0dai project [bind [--name NAME] [--json]|status [--json]] [--target PATH]");
|
|
374
542
|
break;
|
|
375
543
|
case "auth":
|
|
376
544
|
if (sub === "login") await cmdAuthLogin(args.slice(2));
|
|
@@ -385,6 +553,8 @@ async function main() {
|
|
|
385
553
|
else if (sub === "code" || sub === "redeem") await cmdRedeem(args[2]);
|
|
386
554
|
else console.log("Usage: 0dai activate [free|status|code <CODE>]");
|
|
387
555
|
break;
|
|
556
|
+
case "mcp": cmdMcp(target, sub, args); break;
|
|
557
|
+
case "vault": cmdVault(target, args[1], args.slice(2)); break;
|
|
388
558
|
case "session": cmdSession(target, sub, args); break;
|
|
389
559
|
case "swarm": cmdSwarm(target, sub, args); break;
|
|
390
560
|
case "workspace": cmdWorkspace(target, sub, args.slice(2)); break;
|
|
@@ -406,7 +576,7 @@ async function main() {
|
|
|
406
576
|
case "feedback": await cmdFeedback(target, sub, args); break;
|
|
407
577
|
case "harvest": {
|
|
408
578
|
const harvestScript = findRepoScript(target, "harvest_experience.py");
|
|
409
|
-
if (!harvestScript) {
|
|
579
|
+
if (!harvestScript) { explainMissingHarvestHelper(target, args); break; }
|
|
410
580
|
const result = spawnSync("python3", [harvestScript, "--target", target, ...args.slice(1)], { stdio: "inherit", timeout: 15000 });
|
|
411
581
|
if (typeof result.status === "number" && result.status !== 0) process.exit(result.status);
|
|
412
582
|
break;
|
|
@@ -438,11 +608,15 @@ async function main() {
|
|
|
438
608
|
break;
|
|
439
609
|
}
|
|
440
610
|
case "report": cmdReport(target, sub, args); break;
|
|
611
|
+
case "trust": cmdTrust(target, args.slice(1)); break;
|
|
441
612
|
case "compliance": cmdCompliance(target, args.slice(1)); break;
|
|
442
613
|
case "experience": cmdExperience(target, sub, args); break;
|
|
443
614
|
case "persona-simulate": cmdPersonaSimulate(target, args.slice(1)); break;
|
|
444
615
|
case "graph": await cmdGraph(target, sub, args); break;
|
|
445
|
-
case "models":
|
|
616
|
+
case "models":
|
|
617
|
+
if (sub === "recommend") await cmdModelsRecommend(target, args.slice(2));
|
|
618
|
+
else cmdModels(sub || args[1]);
|
|
619
|
+
break;
|
|
446
620
|
case "delegate": case "delegation": {
|
|
447
621
|
const deScript = findRepoScript(target, "delegation_engine.py");
|
|
448
622
|
if (!deScript) { log("delegation engine unavailable"); break; }
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema_version": 1,
|
|
3
|
+
"updated_at": "2026-05-28",
|
|
4
|
+
"purpose": "Expected MCP exposure contract for agent runtimes. Health checks compare observed tool names against this manifest and scripts/mcp_tier_config.py.",
|
|
5
|
+
"project_server": {
|
|
6
|
+
"name": "0dai",
|
|
7
|
+
"config_path": ".mcp.json",
|
|
8
|
+
"tool_count_source": "scripts/mcp_tier_config.py",
|
|
9
|
+
"expected_tool_count": 113
|
|
10
|
+
},
|
|
11
|
+
"normalization": {
|
|
12
|
+
"accepted_examples": [
|
|
13
|
+
"memory_search",
|
|
14
|
+
"mcp__0dai__memory_search",
|
|
15
|
+
"mcp__0dai_internal__.memory_search",
|
|
16
|
+
"mcp__0dai_internal__memory_search"
|
|
17
|
+
],
|
|
18
|
+
"rule": "Compare by canonical tool basename after MCP namespace prefixes."
|
|
19
|
+
},
|
|
20
|
+
"agents": {
|
|
21
|
+
"default": {
|
|
22
|
+
"required": [
|
|
23
|
+
"memory_search",
|
|
24
|
+
"get_project_health",
|
|
25
|
+
"get_codebase_map",
|
|
26
|
+
"record_experience"
|
|
27
|
+
],
|
|
28
|
+
"recommended": [
|
|
29
|
+
"get_anomaly_taxonomy",
|
|
30
|
+
"get_anomaly_events",
|
|
31
|
+
"get_anomaly_summary",
|
|
32
|
+
"memory_add",
|
|
33
|
+
"memory_inject",
|
|
34
|
+
"search_experience",
|
|
35
|
+
"get_project_graph",
|
|
36
|
+
"get_specs",
|
|
37
|
+
"get_session",
|
|
38
|
+
"save_session",
|
|
39
|
+
"get_wal"
|
|
40
|
+
]
|
|
41
|
+
},
|
|
42
|
+
"codex": {
|
|
43
|
+
"inherits": "default",
|
|
44
|
+
"required": [
|
|
45
|
+
"memory_search",
|
|
46
|
+
"get_project_health",
|
|
47
|
+
"get_codebase_map",
|
|
48
|
+
"record_experience"
|
|
49
|
+
],
|
|
50
|
+
"recommended": [
|
|
51
|
+
"get_anomaly_taxonomy",
|
|
52
|
+
"get_anomaly_events",
|
|
53
|
+
"get_anomaly_summary",
|
|
54
|
+
"record_anomaly",
|
|
55
|
+
"memory_add",
|
|
56
|
+
"memory_inject",
|
|
57
|
+
"search_experience",
|
|
58
|
+
"get_project_graph",
|
|
59
|
+
"get_specs",
|
|
60
|
+
"get_session",
|
|
61
|
+
"save_session",
|
|
62
|
+
"swarm_delegate",
|
|
63
|
+
"watch_tasks",
|
|
64
|
+
"get_portfolio",
|
|
65
|
+
"get_wal"
|
|
66
|
+
]
|
|
67
|
+
},
|
|
68
|
+
"claude": {
|
|
69
|
+
"inherits": "default",
|
|
70
|
+
"required": [
|
|
71
|
+
"memory_search",
|
|
72
|
+
"get_project_health",
|
|
73
|
+
"get_codebase_map",
|
|
74
|
+
"record_experience",
|
|
75
|
+
"get_session",
|
|
76
|
+
"save_session"
|
|
77
|
+
],
|
|
78
|
+
"recommended": [
|
|
79
|
+
"get_anomaly_taxonomy",
|
|
80
|
+
"get_anomaly_events",
|
|
81
|
+
"get_anomaly_summary",
|
|
82
|
+
"record_anomaly",
|
|
83
|
+
"memory_add",
|
|
84
|
+
"memory_inject",
|
|
85
|
+
"search_experience",
|
|
86
|
+
"swarm_delegate",
|
|
87
|
+
"watch_tasks",
|
|
88
|
+
"get_portfolio",
|
|
89
|
+
"gh_label_create",
|
|
90
|
+
"gh_issue_edit_body"
|
|
91
|
+
]
|
|
92
|
+
},
|
|
93
|
+
"pool": {
|
|
94
|
+
"inherits": "default",
|
|
95
|
+
"required": [
|
|
96
|
+
"memory_search",
|
|
97
|
+
"get_project_health",
|
|
98
|
+
"get_codebase_map",
|
|
99
|
+
"record_experience",
|
|
100
|
+
"watch_tasks"
|
|
101
|
+
],
|
|
102
|
+
"recommended": [
|
|
103
|
+
"get_anomaly_taxonomy",
|
|
104
|
+
"get_anomaly_events",
|
|
105
|
+
"get_anomaly_summary",
|
|
106
|
+
"memory_add",
|
|
107
|
+
"search_experience",
|
|
108
|
+
"swarm_delegate",
|
|
109
|
+
"run_task",
|
|
110
|
+
"get_swarm_status",
|
|
111
|
+
"save_session"
|
|
112
|
+
]
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"statuses": {
|
|
116
|
+
"green": "All required and recommended tools for the agent were observed.",
|
|
117
|
+
"yellow": "Required tools were observed, but recommended tools or full server count evidence is missing.",
|
|
118
|
+
"red": "One or more required tools are missing from the observed runtime.",
|
|
119
|
+
"unknown": "No observed tool evidence was supplied by the runtime."
|
|
120
|
+
}
|
|
121
|
+
}
|