z0gcode 0.2.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/.env.example +41 -0
- package/LICENSE +21 -0
- package/README.md +152 -0
- package/bin/z0g.mjs +1052 -0
- package/contracts/Z0gSession.json +424 -0
- package/contracts/Z0gSession.sol +106 -0
- package/package.json +60 -0
- package/skills/0g/CHAIN.md +229 -0
- package/skills/0g/COMPUTE.md +296 -0
- package/skills/0g/NETWORK_CONFIG.md +163 -0
- package/skills/0g/SECURITY.md +174 -0
- package/skills/0g/STORAGE.md +167 -0
- package/skills/0g/TESTING.md +263 -0
- package/src/agent.mjs +434 -0
- package/src/anchor.mjs +65 -0
- package/src/checkpoints.mjs +64 -0
- package/src/client.mjs +134 -0
- package/src/commands.mjs +93 -0
- package/src/config.mjs +74 -0
- package/src/context.mjs +55 -0
- package/src/crypto.mjs +47 -0
- package/src/env.mjs +47 -0
- package/src/goal.mjs +45 -0
- package/src/inft.mjs +79 -0
- package/src/mcp-server.mjs +38 -0
- package/src/mcp.mjs +91 -0
- package/src/media.mjs +52 -0
- package/src/models-info.mjs +97 -0
- package/src/plan.mjs +21 -0
- package/src/prompt.mjs +149 -0
- package/src/provenance.mjs +46 -0
- package/src/sessions.mjs +168 -0
- package/src/settings.mjs +37 -0
- package/src/skills.mjs +43 -0
- package/src/tools.mjs +481 -0
- package/src/ui.mjs +798 -0
- package/src/user-skills.mjs +111 -0
- package/src/worktree.mjs +61 -0
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
// User skills: Claude-Code-style extensibility. Drop a markdown file with
|
|
2
|
+
// frontmatter (name, description) into ~/.z0gcode/skills (global) or
|
|
3
|
+
// <cwd>/.z0g/skills (project) and it is auto-discovered: the description is
|
|
4
|
+
// injected into the system prompt so the model knows when to use it, and the
|
|
5
|
+
// body is loaded on demand via the read_skill tool (progressive disclosure).
|
|
6
|
+
// A skill can be a single <name>.md or a <name>/SKILL.md directory.
|
|
7
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
import path from "node:path";
|
|
10
|
+
import { loadSettings, saveSetting } from "./settings.mjs";
|
|
11
|
+
|
|
12
|
+
const skillDirs = (cwd) => [
|
|
13
|
+
["global", path.join(os.homedir(), ".z0gcode", "skills")],
|
|
14
|
+
["project", path.join(cwd || process.cwd(), ".z0g", "skills")],
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
// Parse a leading `---\n...\n---` YAML-ish frontmatter block (name/description).
|
|
18
|
+
function parseFrontmatter(text) {
|
|
19
|
+
const meta = {};
|
|
20
|
+
let body = text;
|
|
21
|
+
const m = /^?---\r?\n([\s\S]*?)\r?\n---\r?\n?/.exec(text);
|
|
22
|
+
if (m) {
|
|
23
|
+
body = text.slice(m[0].length);
|
|
24
|
+
for (const line of m[1].split("\n")) {
|
|
25
|
+
const i = line.indexOf(":");
|
|
26
|
+
if (i === -1) continue;
|
|
27
|
+
const k = line.slice(0, i).trim();
|
|
28
|
+
let v = line.slice(i + 1).trim();
|
|
29
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
30
|
+
meta[k] = v;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return { meta, body };
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function resolveSkillFile(dir, entry) {
|
|
37
|
+
const p = path.join(dir, entry);
|
|
38
|
+
try {
|
|
39
|
+
const st = statSync(p);
|
|
40
|
+
if (st.isDirectory()) {
|
|
41
|
+
const sk = path.join(p, "SKILL.md");
|
|
42
|
+
return existsSync(sk) ? { file: sk, defaultName: entry } : null;
|
|
43
|
+
}
|
|
44
|
+
if (entry.toLowerCase().endsWith(".md")) {
|
|
45
|
+
return { file: p, defaultName: entry.replace(/\.md$/i, "") };
|
|
46
|
+
}
|
|
47
|
+
} catch {}
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Discover all user skills. Project scope overrides global by name.
|
|
52
|
+
// Each: { name, description, file, scope, enabled }.
|
|
53
|
+
export function discoverSkills(cwd) {
|
|
54
|
+
const disabled = new Set(loadSettings(cwd).disabledSkills || []);
|
|
55
|
+
const found = new Map();
|
|
56
|
+
for (const [scope, dir] of skillDirs(cwd)) {
|
|
57
|
+
if (!existsSync(dir)) continue;
|
|
58
|
+
let entries;
|
|
59
|
+
try {
|
|
60
|
+
entries = readdirSync(dir).sort();
|
|
61
|
+
} catch {
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
for (const entry of entries) {
|
|
65
|
+
const r = resolveSkillFile(dir, entry);
|
|
66
|
+
if (!r) continue;
|
|
67
|
+
let text;
|
|
68
|
+
try {
|
|
69
|
+
text = readFileSync(r.file, "utf8");
|
|
70
|
+
} catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const { meta } = parseFrontmatter(text);
|
|
74
|
+
const name = String(meta.name || r.defaultName).trim();
|
|
75
|
+
if (!name) continue;
|
|
76
|
+
found.set(name, { name, description: String(meta.description || "").trim(), file: r.file, scope });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return [...found.values()].map((s) => ({ ...s, enabled: !disabled.has(s.name) }));
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function readUserSkill(cwd, name) {
|
|
83
|
+
const hit = discoverSkills(cwd).find((s) => s.name === name);
|
|
84
|
+
if (!hit) return null;
|
|
85
|
+
try {
|
|
86
|
+
return parseFrontmatter(readFileSync(hit.file, "utf8")).body.trim();
|
|
87
|
+
} catch {
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// System-prompt block listing the ENABLED user skills so the model can decide
|
|
93
|
+
// to load them. Empty string when there are none.
|
|
94
|
+
export function skillsPromptBlock(cwd) {
|
|
95
|
+
const enabled = discoverSkills(cwd).filter((s) => s.enabled);
|
|
96
|
+
if (!enabled.length) return "";
|
|
97
|
+
const lines = enabled.map((s) => `- ${s.name}: ${s.description || "(no description)"}`);
|
|
98
|
+
return [
|
|
99
|
+
"",
|
|
100
|
+
"User skills available (call read_skill with the exact name to load the full instructions before you act on it):",
|
|
101
|
+
...lines,
|
|
102
|
+
].join("\n");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Enable/disable a skill by name (persisted in global settings.disabledSkills).
|
|
106
|
+
export function setSkillEnabled(cwd, name, enabled) {
|
|
107
|
+
const cur = new Set(loadSettings(cwd).disabledSkills || []);
|
|
108
|
+
if (enabled) cur.delete(name);
|
|
109
|
+
else cur.add(name);
|
|
110
|
+
saveSetting("disabledSkills", [...cur]);
|
|
111
|
+
}
|
package/src/worktree.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// Git worktree isolation for parallel WRITE subagents. Each write subagent gets
|
|
2
|
+
// its own worktree branched from HEAD, edits there, and its diff is applied back
|
|
3
|
+
// to the main working tree. Non-overlapping edits merge cleanly; overlapping
|
|
4
|
+
// files are reported and skipped (never half-applied).
|
|
5
|
+
import { exec } from "node:child_process";
|
|
6
|
+
import { promises as fs } from "node:fs";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import os from "node:os";
|
|
9
|
+
|
|
10
|
+
const run = (cmd, cwd) => new Promise((resolve) => {
|
|
11
|
+
exec(cmd, { cwd, timeout: 180000, maxBuffer: 1 << 26 }, (err, stdout, stderr) =>
|
|
12
|
+
resolve({ code: err?.code ?? 0, out: stdout || "", err: stderr || "" }));
|
|
13
|
+
});
|
|
14
|
+
const q = (s) => JSON.stringify(s); // shell-quote a path
|
|
15
|
+
|
|
16
|
+
export async function isGitRepo(cwd) {
|
|
17
|
+
const r = await run("git rev-parse --is-inside-work-tree", cwd);
|
|
18
|
+
return r.code === 0 && /true/.test(r.out);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
// Create a worktree (outside the repo, in a temp dir) on a fresh branch at HEAD.
|
|
22
|
+
export async function addWorktree(cwd, name) {
|
|
23
|
+
const wtPath = path.join(os.tmpdir(), "z0g-wt", name);
|
|
24
|
+
const branch = "z0g/" + name;
|
|
25
|
+
await fs.mkdir(path.dirname(wtPath), { recursive: true });
|
|
26
|
+
await run(`git worktree remove --force ${q(wtPath)}`, cwd);
|
|
27
|
+
await run(`git branch -D ${q(branch)}`, cwd);
|
|
28
|
+
const r = await run(`git worktree add --quiet -b ${q(branch)} ${q(wtPath)} HEAD`, cwd);
|
|
29
|
+
if (r.code !== 0) throw new Error("git worktree add failed: " + (r.err || r.out).trim());
|
|
30
|
+
return { wtPath, branch };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Stage everything in the worktree and return its diff vs HEAD (may be empty).
|
|
34
|
+
export async function collectPatch(wtPath) {
|
|
35
|
+
await run("git add -A", wtPath);
|
|
36
|
+
const patch = (await run("git diff --cached --binary HEAD", wtPath)).out;
|
|
37
|
+
const files = (await run("git diff --cached --name-only HEAD", wtPath)).out.split("\n").filter(Boolean);
|
|
38
|
+
return { patch, files };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export async function removeWorktree(cwd, wtPath, branch) {
|
|
42
|
+
await run(`git worktree remove --force ${q(wtPath)}`, cwd);
|
|
43
|
+
if (branch) await run(`git branch -D ${q(branch)}`, cwd);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Apply a patch to the main tree, all-or-nothing (no conflict markers left).
|
|
47
|
+
// Returns { ok, reason }. ok:false with reason when it would conflict.
|
|
48
|
+
export async function applyPatch(cwd, patch) {
|
|
49
|
+
if (!patch || !patch.trim()) return { ok: true, reason: "no changes" };
|
|
50
|
+
const tmp = path.join(os.tmpdir(), "z0g-wt", "apply-" + Math.floor(Math.random() * 1e9) + ".patch");
|
|
51
|
+
await fs.mkdir(path.dirname(tmp), { recursive: true });
|
|
52
|
+
await fs.writeFile(tmp, patch, "utf8");
|
|
53
|
+
try {
|
|
54
|
+
const check = await run(`git apply --binary --check ${q(tmp)}`, cwd);
|
|
55
|
+
if (check.code !== 0) return { ok: false, reason: "conflicts with current tree, skipped" };
|
|
56
|
+
const r = await run(`git apply --binary --whitespace=nowarn ${q(tmp)}`, cwd);
|
|
57
|
+
return r.code === 0 ? { ok: true, reason: "applied" } : { ok: false, reason: (r.err || r.out).trim() };
|
|
58
|
+
} finally {
|
|
59
|
+
await fs.rm(tmp, { force: true });
|
|
60
|
+
}
|
|
61
|
+
}
|