@pat-lewczuk/cezar 0.1.0 → 0.1.1
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/dist/config.d.ts +55 -0
- package/dist/config.js +44 -0
- package/dist/config.js.map +1 -0
- package/dist/core/agent-runner.d.ts +13 -0
- package/dist/core/claude-cli-runner.js +23 -2
- package/dist/core/claude-cli-runner.js.map +1 -1
- package/dist/git-worktree.d.ts +49 -0
- package/dist/git-worktree.js +129 -0
- package/dist/git-worktree.js.map +1 -0
- package/dist/handoff.d.ts +42 -0
- package/dist/handoff.js +120 -0
- package/dist/handoff.js.map +1 -0
- package/dist/index.js +27 -6
- package/dist/index.js.map +1 -1
- package/dist/planner.d.ts +17 -0
- package/dist/planner.js +244 -0
- package/dist/planner.js.map +1 -0
- package/dist/runs/store.d.ts +40 -13
- package/dist/runs/store.js +33 -3
- package/dist/runs/store.js.map +1 -1
- package/dist/server/github.d.ts +28 -0
- package/dist/server/github.js +133 -0
- package/dist/server/github.js.map +1 -0
- package/dist/server/launch-key.d.ts +7 -0
- package/dist/server/launch-key.js +33 -0
- package/dist/server/launch-key.js.map +1 -0
- package/dist/server/pr.d.ts +22 -0
- package/dist/server/pr.js +92 -0
- package/dist/server/pr.js.map +1 -0
- package/dist/server/server.js +370 -15
- package/dist/server/server.js.map +1 -1
- package/dist/skills-remote.d.ts +35 -0
- package/dist/skills-remote.js +266 -0
- package/dist/skills-remote.js.map +1 -0
- package/dist/skills.d.ts +18 -6
- package/dist/skills.js +8 -4
- package/dist/skills.js.map +1 -1
- package/dist/todos.d.ts +60 -0
- package/dist/todos.js +166 -0
- package/dist/todos.js.map +1 -0
- package/dist/workflows/load.js +5 -9
- package/dist/workflows/load.js.map +1 -1
- package/dist/workflows/run.d.ts +62 -3
- package/dist/workflows/run.js +332 -43
- package/dist/workflows/run.js.map +1 -1
- package/dist/workflows/types.d.ts +27 -20
- package/dist/workflows/types.js +21 -0
- package/dist/workflows/types.js.map +1 -1
- package/package.json +1 -1
- package/scripts/mock-claude.mjs +118 -0
- package/web/app.js +1851 -149
- package/web/index.html +73 -22
- package/web/style.css +1271 -223
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import { existsSync } from 'node:fs';
|
|
3
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { basename, dirname, join } from 'node:path';
|
|
6
|
+
import { loadConfig } from './config.js';
|
|
7
|
+
import { parseFrontmatter } from './skills.js';
|
|
8
|
+
/**
|
|
9
|
+
* Team skills from remote git repos (spec 005), janitor-style: a bare clone
|
|
10
|
+
* without a checkout, listed with `git ls-tree` and read with `git show`.
|
|
11
|
+
* The cache lives in `~/.cache/cez/skills/<owner>__<name>/` — global, so one
|
|
12
|
+
* fetch serves every project. Everything degrades: no network / no access to
|
|
13
|
+
* the skills repo means the team skills quietly disappear from the list while
|
|
14
|
+
* local skills keep working. Nothing here ever blocks startup.
|
|
15
|
+
*/
|
|
16
|
+
const LIST_TIMEOUT_MS = 10_000; // ls-tree / show / rev-parse
|
|
17
|
+
const CLONE_TIMEOUT_MS = 60_000; // clone / fetch
|
|
18
|
+
function git(args, timeoutMs, cwd) {
|
|
19
|
+
return new Promise((resolve) => {
|
|
20
|
+
execFile('git', args, { cwd, timeout: timeoutMs, killSignal: 'SIGKILL', maxBuffer: 16 * 1024 * 1024, encoding: 'utf8' }, (err, stdout, stderr) => resolve({ ok: !err, stdout: stdout ?? '', stderr: stderr ?? '' }));
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
/** `owner/name` shorthand → GitHub HTTPS; anything else (URL, file://, local path) as-is. */
|
|
24
|
+
function remoteFor(repo) {
|
|
25
|
+
return /^[\w.-]+\/[\w.-]+$/.test(repo) ? `https://github.com/${repo}.git` : repo;
|
|
26
|
+
}
|
|
27
|
+
/** Stable cache directory name: the last two path segments, `owner__name`. */
|
|
28
|
+
export function bareDirFor(repo) {
|
|
29
|
+
const trimmed = repo
|
|
30
|
+
.replace(/\/+$/, '')
|
|
31
|
+
.replace(/\.git$/, '')
|
|
32
|
+
.replace(/^[a-z+]+:\/\//i, '')
|
|
33
|
+
.replace(/^~\//, '');
|
|
34
|
+
const segments = trimmed.split(/[/:]/).filter(Boolean).map(sanitizeSegment);
|
|
35
|
+
const key = segments.slice(-2).join('__') || 'skills';
|
|
36
|
+
return join(homedir(), '.cache', 'cez', 'skills', key);
|
|
37
|
+
}
|
|
38
|
+
function sanitizeSegment(s) {
|
|
39
|
+
return s.replace(/[^\w.-]/g, '-');
|
|
40
|
+
}
|
|
41
|
+
/** Clone the skills repo bare (no checkout) into the global cache, once. */
|
|
42
|
+
export async function ensureBareClone(repo) {
|
|
43
|
+
const bareDir = bareDirFor(repo);
|
|
44
|
+
if (existsSync(join(bareDir, 'HEAD')))
|
|
45
|
+
return { bareDir, created: false };
|
|
46
|
+
await mkdir(dirname(bareDir), { recursive: true });
|
|
47
|
+
const remote = remoteFor(repo);
|
|
48
|
+
const res = await git(['clone', '--bare', remote, bareDir], CLONE_TIMEOUT_MS);
|
|
49
|
+
if (!res.ok)
|
|
50
|
+
throw new Error(`git clone --bare ${remote} failed: ${res.stderr.trim()}`);
|
|
51
|
+
return { bareDir, created: true };
|
|
52
|
+
}
|
|
53
|
+
/** "Refresh" — update every branch head in the bare clone from origin. */
|
|
54
|
+
export async function fetchAll(bareDir) {
|
|
55
|
+
const res = await git(['fetch', 'origin', '--prune', '+refs/heads/*:refs/heads/*'], CLONE_TIMEOUT_MS, bareDir);
|
|
56
|
+
if (!res.ok)
|
|
57
|
+
throw new Error(`git fetch failed: ${res.stderr.trim() || res.stdout.trim()}`);
|
|
58
|
+
}
|
|
59
|
+
async function resolveRef(bareDir, ref) {
|
|
60
|
+
for (const candidate of [ref, `refs/heads/${ref}`, 'HEAD']) {
|
|
61
|
+
const probe = await git(['rev-parse', '--verify', '--quiet', candidate], LIST_TIMEOUT_MS, bareDir);
|
|
62
|
+
if (probe.ok)
|
|
63
|
+
return candidate;
|
|
64
|
+
}
|
|
65
|
+
return ref;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Match the janitor conventions plus our own:
|
|
69
|
+
* - `**\/SKILL.md` → skill named after the parent directory (with references/)
|
|
70
|
+
* - `**\/commands/<n>.md` → skill `<n>`
|
|
71
|
+
* - `.ai/skills/**\/*.md` or `.ai/cezar/skills/**\/*.md` → frontmatter/basename name
|
|
72
|
+
*/
|
|
73
|
+
function matchSkillPath(line) {
|
|
74
|
+
if (line === 'SKILL.md' || line.endsWith('/SKILL.md')) {
|
|
75
|
+
const parts = line.split('/');
|
|
76
|
+
if (parts.length < 2)
|
|
77
|
+
return null;
|
|
78
|
+
const parent = parts[parts.length - 2];
|
|
79
|
+
return parent && parent !== '.' ? { name: parent, kind: 'skill' } : null;
|
|
80
|
+
}
|
|
81
|
+
const cmd = /(?:^|\/)commands\/([^/]+)\.md$/.exec(line);
|
|
82
|
+
if (cmd)
|
|
83
|
+
return { name: cmd[1], kind: 'command' };
|
|
84
|
+
if (/(?:^|\/)\.ai\/(?:cezar\/)?skills\/.+\.md$/.test(line))
|
|
85
|
+
return { name: null, kind: 'markdown' };
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
/** Read one file from the bare clone at the source's ref. Null on any failure. */
|
|
89
|
+
export async function readRemoteSkill(src, path) {
|
|
90
|
+
const bareDir = bareDirFor(src.repo);
|
|
91
|
+
if (!existsSync(join(bareDir, 'HEAD')))
|
|
92
|
+
return null;
|
|
93
|
+
const ref = await resolveRef(bareDir, src.ref);
|
|
94
|
+
const res = await git(['show', `${ref}:${path}`], LIST_TIMEOUT_MS, bareDir);
|
|
95
|
+
return res.ok ? res.stdout : null;
|
|
96
|
+
}
|
|
97
|
+
/**
|
|
98
|
+
* List every skill the repo defines at `src.ref`. Reads from the local bare
|
|
99
|
+
* clone only — no network. Empty list when the clone doesn't exist yet or
|
|
100
|
+
* the ref can't be resolved.
|
|
101
|
+
*/
|
|
102
|
+
export async function listRemoteSkills(src) {
|
|
103
|
+
const bareDir = bareDirFor(src.repo);
|
|
104
|
+
if (!existsSync(join(bareDir, 'HEAD')))
|
|
105
|
+
return [];
|
|
106
|
+
const ref = await resolveRef(bareDir, src.ref);
|
|
107
|
+
const ls = await git(['ls-tree', '-r', '--name-only', ref], LIST_TIMEOUT_MS, bareDir);
|
|
108
|
+
if (!ls.ok)
|
|
109
|
+
return [];
|
|
110
|
+
const skills = [];
|
|
111
|
+
const seen = new Set();
|
|
112
|
+
for (const line of ls.stdout.split('\n')) {
|
|
113
|
+
if (!line)
|
|
114
|
+
continue;
|
|
115
|
+
const hit = matchSkillPath(line);
|
|
116
|
+
if (!hit)
|
|
117
|
+
continue;
|
|
118
|
+
const raw = await readRemoteSkill(src, line);
|
|
119
|
+
if (raw === null)
|
|
120
|
+
continue;
|
|
121
|
+
const { frontmatter, body } = parseFrontmatter(raw);
|
|
122
|
+
const name = hit.name ??
|
|
123
|
+
(typeof frontmatter.name === 'string' && frontmatter.name.trim()
|
|
124
|
+
? frontmatter.name.trim()
|
|
125
|
+
: basename(line, '.md'));
|
|
126
|
+
if (seen.has(name))
|
|
127
|
+
continue;
|
|
128
|
+
seen.add(name);
|
|
129
|
+
const description = typeof frontmatter.description === 'string' && frontmatter.description.trim()
|
|
130
|
+
? frontmatter.description.trim()
|
|
131
|
+
: undefined;
|
|
132
|
+
skills.push({
|
|
133
|
+
name,
|
|
134
|
+
description,
|
|
135
|
+
body,
|
|
136
|
+
path: `${src.repo}@${src.ref}:${line}`,
|
|
137
|
+
source: 'team',
|
|
138
|
+
team: { repo: src.repo, ref: src.ref, path: line, dir: hit.kind === 'skill' },
|
|
139
|
+
});
|
|
140
|
+
}
|
|
141
|
+
return skills;
|
|
142
|
+
}
|
|
143
|
+
// ---- materialization (directory skills) ---------------------------------------
|
|
144
|
+
/**
|
|
145
|
+
* Copy a directory skill (SKILL.md + references/…) out of the bare clone into
|
|
146
|
+
* `<repoRoot>/.claude/skills/<name>/` so claude sees the references on disk,
|
|
147
|
+
* and keep it out of the user's git via `.git/info/exclude`. Returns false
|
|
148
|
+
* when there is nothing to materialize (not a directory skill, no clone…).
|
|
149
|
+
*/
|
|
150
|
+
export async function materializeSkillDir(repoRoot, skill) {
|
|
151
|
+
if (!skill.team?.dir || !skill.team.path.endsWith('SKILL.md'))
|
|
152
|
+
return false;
|
|
153
|
+
const bareDir = bareDirFor(skill.team.repo);
|
|
154
|
+
if (!existsSync(join(bareDir, 'HEAD')))
|
|
155
|
+
return false;
|
|
156
|
+
const ref = await resolveRef(bareDir, skill.team.ref);
|
|
157
|
+
const srcDir = skill.team.path.slice(0, -'/SKILL.md'.length);
|
|
158
|
+
const ls = await git(['ls-tree', '-r', '--name-only', ref, '--', srcDir], LIST_TIMEOUT_MS, bareDir);
|
|
159
|
+
if (!ls.ok)
|
|
160
|
+
return false;
|
|
161
|
+
const destDir = join(repoRoot, '.claude', 'skills', skill.name);
|
|
162
|
+
let wrote = 0;
|
|
163
|
+
for (const file of ls.stdout.split('\n').filter(Boolean)) {
|
|
164
|
+
const rel = file.slice(srcDir.length + 1);
|
|
165
|
+
// git paths are repo-relative and normalized, but never trust them blindly.
|
|
166
|
+
if (!rel || rel.split('/').includes('..'))
|
|
167
|
+
continue;
|
|
168
|
+
const show = await git(['show', `${ref}:${file}`], LIST_TIMEOUT_MS, bareDir);
|
|
169
|
+
if (!show.ok)
|
|
170
|
+
continue;
|
|
171
|
+
const target = join(destDir, rel);
|
|
172
|
+
await mkdir(dirname(target), { recursive: true });
|
|
173
|
+
await writeFile(target, show.stdout, 'utf8');
|
|
174
|
+
wrote++;
|
|
175
|
+
}
|
|
176
|
+
if (wrote === 0)
|
|
177
|
+
return false;
|
|
178
|
+
await excludeFromGit(repoRoot, `.claude/skills/${skill.name}/`);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
/** Append a pattern to git's `info/exclude` (idempotent, non-fatal). */
|
|
182
|
+
async function excludeFromGit(repoRoot, pattern) {
|
|
183
|
+
try {
|
|
184
|
+
// Resolve the real exclude file: in a linked worktree (spec 006) `.git`
|
|
185
|
+
// is a file and `info/exclude` lives in the shared common dir — which
|
|
186
|
+
// also means one exclude entry covers every task worktree.
|
|
187
|
+
const probe = await git(['rev-parse', '--path-format=absolute', '--git-common-dir'], LIST_TIMEOUT_MS, repoRoot);
|
|
188
|
+
const gitDir = probe.ok && probe.stdout.trim() ? probe.stdout.trim() : join(repoRoot, '.git');
|
|
189
|
+
const excludePath = join(gitDir, 'info', 'exclude');
|
|
190
|
+
let prev = '';
|
|
191
|
+
try {
|
|
192
|
+
prev = await readFile(excludePath, 'utf8');
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// no exclude file yet
|
|
196
|
+
}
|
|
197
|
+
if (prev.split('\n').includes(pattern))
|
|
198
|
+
return;
|
|
199
|
+
await mkdir(dirname(excludePath), { recursive: true });
|
|
200
|
+
await writeFile(excludePath, prev + (prev && !prev.endsWith('\n') ? '\n' : '') + pattern + '\n', 'utf8');
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// non-fatal — `.git` might be a linked file (worktree) or absent entirely
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// ---- in-process cache ----------------------------------------------------------
|
|
207
|
+
// Clone attempts are expensive when the network is down (git can hang on
|
|
208
|
+
// DNS/TCP), so each source gets one implicit attempt per process. "Refresh"
|
|
209
|
+
// always retries.
|
|
210
|
+
const cloneAttempted = new Set();
|
|
211
|
+
let teamSkills = [];
|
|
212
|
+
let firstLoadStarted = false;
|
|
213
|
+
/**
|
|
214
|
+
* The current team-skill list, straight from memory. The first call kicks off
|
|
215
|
+
* an async background load (clone + list) and returns immediately — the GUI
|
|
216
|
+
* refetches, so remote skills appear moments later instead of blocking the
|
|
217
|
+
* first `GET /api/skills`.
|
|
218
|
+
*/
|
|
219
|
+
export function getTeamSkillsCached(repoRoot) {
|
|
220
|
+
if (!firstLoadStarted) {
|
|
221
|
+
firstLoadStarted = true;
|
|
222
|
+
void loadTeamSkills(repoRoot, false).catch(() => undefined);
|
|
223
|
+
}
|
|
224
|
+
return teamSkills;
|
|
225
|
+
}
|
|
226
|
+
/** Refresh: clone missing sources, `git fetch` existing ones, reload the list. */
|
|
227
|
+
export async function refreshTeamSkills(repoRoot) {
|
|
228
|
+
firstLoadStarted = true;
|
|
229
|
+
return loadTeamSkills(repoRoot, true);
|
|
230
|
+
}
|
|
231
|
+
async function loadTeamSkills(repoRoot, refresh) {
|
|
232
|
+
const config = await loadConfig(repoRoot);
|
|
233
|
+
const out = [];
|
|
234
|
+
const seen = new Set();
|
|
235
|
+
for (const src of config.skillsRepos) {
|
|
236
|
+
try {
|
|
237
|
+
if (refresh) {
|
|
238
|
+
const { bareDir, created } = await ensureBareClone(src.repo);
|
|
239
|
+
if (!created)
|
|
240
|
+
await fetchAll(bareDir);
|
|
241
|
+
cloneAttempted.add(src.repo);
|
|
242
|
+
}
|
|
243
|
+
else if (!cloneAttempted.has(src.repo)) {
|
|
244
|
+
cloneAttempted.add(src.repo);
|
|
245
|
+
await ensureBareClone(src.repo);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
// offline / no access — list whatever an older clone has (or nothing)
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
252
|
+
for (const skill of await listRemoteSkills(src)) {
|
|
253
|
+
if (seen.has(skill.name))
|
|
254
|
+
continue;
|
|
255
|
+
seen.add(skill.name);
|
|
256
|
+
out.push(skill);
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
catch {
|
|
260
|
+
// degrade: this source contributes nothing
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
teamSkills = out;
|
|
264
|
+
return out;
|
|
265
|
+
}
|
|
266
|
+
//# sourceMappingURL=skills-remote.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"skills-remote.js","sourceRoot":"","sources":["../src/skills-remote.ts"],"names":[],"mappings":"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"}
|
package/dist/skills.d.ts
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* A skill is a Markdown file with optional YAML-ish frontmatter (`name`,
|
|
3
3
|
* `description`). Discovered from the repo's `.ai/skills/` (shared with other
|
|
4
|
-
* agent tooling)
|
|
5
|
-
*
|
|
4
|
+
* agent tooling), `.ai/cezar/skills/` (cez-local), and the configured team
|
|
5
|
+
* skills repos (spec 005 — bare clones, no checkout). Adapted from
|
|
6
|
+
* @cezar/core's skill-catalog.
|
|
6
7
|
*/
|
|
7
8
|
export interface Skill {
|
|
8
9
|
name: string;
|
|
9
10
|
description?: string;
|
|
10
11
|
body: string;
|
|
11
12
|
path: string;
|
|
12
|
-
source: 'ai' | 'cezar';
|
|
13
|
+
source: 'ai' | 'cezar' | 'team';
|
|
14
|
+
/** Team skills only: where the definition lives in its skills repo. */
|
|
15
|
+
team?: {
|
|
16
|
+
repo: string;
|
|
17
|
+
ref: string;
|
|
18
|
+
path: string;
|
|
19
|
+
/** True for the `SKILL.md` convention — a whole directory (references/…). */
|
|
20
|
+
dir: boolean;
|
|
21
|
+
};
|
|
13
22
|
}
|
|
14
23
|
/**
|
|
15
|
-
* Discover the merged skill catalog for a repo.
|
|
16
|
-
*
|
|
17
|
-
*
|
|
24
|
+
* Discover the merged skill catalog for a repo. Name collisions resolve
|
|
25
|
+
* local-first: `.ai/cezar/skills` → `.ai/skills` → team repo ("the user's
|
|
26
|
+
* repo is the source of truth"). Missing directories are fine — an empty
|
|
27
|
+
* catalog is fully supported (steps fall back to their plain prompt). Team
|
|
28
|
+
* skills come from the in-process cache; the first call starts a background
|
|
29
|
+
* load so nothing here ever waits on the network.
|
|
18
30
|
*/
|
|
19
31
|
export declare function discoverSkills(repoRoot: string): Promise<Skill[]>;
|
|
20
32
|
type FrontmatterValue = string | string[];
|
package/dist/skills.js
CHANGED
|
@@ -1,19 +1,23 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { join, resolve, basename, extname } from 'node:path';
|
|
3
|
+
import { getTeamSkillsCached } from './skills-remote.js';
|
|
3
4
|
const SKILL_DIRS = [
|
|
4
5
|
{ dir: '.ai/cezar/skills', source: 'cezar' },
|
|
5
6
|
{ dir: '.ai/skills', source: 'ai' },
|
|
6
7
|
];
|
|
7
8
|
/**
|
|
8
|
-
* Discover the merged skill catalog for a repo.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* Discover the merged skill catalog for a repo. Name collisions resolve
|
|
10
|
+
* local-first: `.ai/cezar/skills` → `.ai/skills` → team repo ("the user's
|
|
11
|
+
* repo is the source of truth"). Missing directories are fine — an empty
|
|
12
|
+
* catalog is fully supported (steps fall back to their plain prompt). Team
|
|
13
|
+
* skills come from the in-process cache; the first call starts a background
|
|
14
|
+
* load so nothing here ever waits on the network.
|
|
11
15
|
*/
|
|
12
16
|
export async function discoverSkills(repoRoot) {
|
|
13
17
|
const lists = await Promise.all(SKILL_DIRS.map(({ dir, source }) => readMarkdownSkills(resolve(repoRoot, dir), source)));
|
|
14
18
|
const merged = [];
|
|
15
19
|
const seen = new Set();
|
|
16
|
-
for (const skills of lists) {
|
|
20
|
+
for (const skills of [...lists, getTeamSkillsCached(repoRoot)]) {
|
|
17
21
|
for (const skill of skills) {
|
|
18
22
|
if (seen.has(skill.name))
|
|
19
23
|
continue;
|
package/dist/skills.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AACrD,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"skills.js","sourceRoot":"","sources":["../src/skills.ts"],"names":[],"mappings":"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"}
|
package/dist/todos.d.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* The global follow-up inbox (spec 007): `.ai/cezar/todos.json`, a flat JSON
|
|
4
|
+
* array agents append to (via CEZ_TODOS_FILE). Agent entries are external
|
|
5
|
+
* data — each one is zod-validated on read and malformed ones are skipped
|
|
6
|
+
* with a warning, never fatal. Server writes are serialized with an
|
|
7
|
+
* in-process lock (the janitor `withLock` pattern) and land atomically
|
|
8
|
+
* (tmp + rename, the runs.json pattern).
|
|
9
|
+
*/
|
|
10
|
+
declare const todoSchema: z.ZodObject<{
|
|
11
|
+
id: z.ZodOptional<z.ZodString>;
|
|
12
|
+
ts: z.ZodOptional<z.ZodString>;
|
|
13
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
14
|
+
summary: z.ZodString;
|
|
15
|
+
action: z.ZodOptional<z.ZodString>;
|
|
16
|
+
prUrl: z.ZodOptional<z.ZodString>;
|
|
17
|
+
suggestedSkill: z.ZodOptional<z.ZodString>;
|
|
18
|
+
suggestedArgs: z.ZodOptional<z.ZodString>;
|
|
19
|
+
suggestedPrompt: z.ZodOptional<z.ZodString>;
|
|
20
|
+
/** Set by the server when "▶ Run" turned this entry into a task. */
|
|
21
|
+
startedTaskId: z.ZodOptional<z.ZodString>;
|
|
22
|
+
}, "strip", z.ZodTypeAny, {
|
|
23
|
+
summary: string;
|
|
24
|
+
id?: string | undefined;
|
|
25
|
+
ts?: string | undefined;
|
|
26
|
+
taskId?: string | undefined;
|
|
27
|
+
action?: string | undefined;
|
|
28
|
+
prUrl?: string | undefined;
|
|
29
|
+
suggestedSkill?: string | undefined;
|
|
30
|
+
suggestedArgs?: string | undefined;
|
|
31
|
+
suggestedPrompt?: string | undefined;
|
|
32
|
+
startedTaskId?: string | undefined;
|
|
33
|
+
}, {
|
|
34
|
+
summary: string;
|
|
35
|
+
id?: string | undefined;
|
|
36
|
+
ts?: string | undefined;
|
|
37
|
+
taskId?: string | undefined;
|
|
38
|
+
action?: string | undefined;
|
|
39
|
+
prUrl?: string | undefined;
|
|
40
|
+
suggestedSkill?: string | undefined;
|
|
41
|
+
suggestedArgs?: string | undefined;
|
|
42
|
+
suggestedPrompt?: string | undefined;
|
|
43
|
+
startedTaskId?: string | undefined;
|
|
44
|
+
}>;
|
|
45
|
+
export type TodoItem = z.infer<typeof todoSchema> & {
|
|
46
|
+
id: string;
|
|
47
|
+
};
|
|
48
|
+
export declare function todosPath(dataDir: string): string;
|
|
49
|
+
export declare function readTodos(dataDir: string): Promise<TodoItem[]>;
|
|
50
|
+
/** Check off (delete) an entry. False when the id isn't there. */
|
|
51
|
+
export declare function removeTodo(dataDir: string, id: string): Promise<boolean>;
|
|
52
|
+
/** Record that "▶ Run" turned the entry into task `taskId`. The entry stays
|
|
53
|
+
* in the file as an audit trail; the GUI hides started entries. */
|
|
54
|
+
export declare function markStarted(dataDir: string, id: string, taskId: string): Promise<boolean>;
|
|
55
|
+
/** Watch `.ai/cezar/` for todos.json changes (agents write it from another
|
|
56
|
+
* process) and emit debounced change events. Degrades to no watch on error. */
|
|
57
|
+
export declare function startTodosWatch(dataDir: string): void;
|
|
58
|
+
/** Subscribe to inbox changes; returns the unsubscribe function. */
|
|
59
|
+
export declare function onTodosChanged(cb: () => void): () => void;
|
|
60
|
+
export {};
|
package/dist/todos.js
ADDED
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import { EventEmitter } from 'node:events';
|
|
2
|
+
import { randomUUID } from 'node:crypto';
|
|
3
|
+
import { promises as fs } from 'node:fs';
|
|
4
|
+
import { mkdirSync, watch } from 'node:fs';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
/**
|
|
8
|
+
* The global follow-up inbox (spec 007): `.ai/cezar/todos.json`, a flat JSON
|
|
9
|
+
* array agents append to (via CEZ_TODOS_FILE). Agent entries are external
|
|
10
|
+
* data — each one is zod-validated on read and malformed ones are skipped
|
|
11
|
+
* with a warning, never fatal. Server writes are serialized with an
|
|
12
|
+
* in-process lock (the janitor `withLock` pattern) and land atomically
|
|
13
|
+
* (tmp + rename, the runs.json pattern).
|
|
14
|
+
*/
|
|
15
|
+
const todoSchema = z.object({
|
|
16
|
+
id: z.string().min(1).optional(),
|
|
17
|
+
ts: z.string().optional(),
|
|
18
|
+
taskId: z.string().optional(),
|
|
19
|
+
summary: z.string().min(1),
|
|
20
|
+
action: z.string().optional(),
|
|
21
|
+
prUrl: z.string().optional(),
|
|
22
|
+
suggestedSkill: z.string().optional(),
|
|
23
|
+
suggestedArgs: z.string().optional(),
|
|
24
|
+
suggestedPrompt: z.string().optional(),
|
|
25
|
+
/** Set by the server when "▶ Run" turned this entry into a task. */
|
|
26
|
+
startedTaskId: z.string().optional(),
|
|
27
|
+
});
|
|
28
|
+
export function todosPath(dataDir) {
|
|
29
|
+
return join(dataDir, 'todos.json');
|
|
30
|
+
}
|
|
31
|
+
// ---- in-process lock (15-line port of janitor's storage.withLock) ----------
|
|
32
|
+
const locks = new Map();
|
|
33
|
+
async function withLock(key, fn) {
|
|
34
|
+
const prev = locks.get(key) ?? Promise.resolve();
|
|
35
|
+
let release = () => undefined;
|
|
36
|
+
const next = new Promise((r) => {
|
|
37
|
+
release = r;
|
|
38
|
+
});
|
|
39
|
+
locks.set(key, prev.then(() => next));
|
|
40
|
+
try {
|
|
41
|
+
await prev;
|
|
42
|
+
return await fn();
|
|
43
|
+
}
|
|
44
|
+
finally {
|
|
45
|
+
release();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
// ---- read / write -----------------------------------------------------------
|
|
49
|
+
/** Parse + validate the file. Broken JSON / non-array → []; bad entries are
|
|
50
|
+
* skipped with a warning; entries without an id get one assigned. */
|
|
51
|
+
async function readRaw(dataDir) {
|
|
52
|
+
let raw;
|
|
53
|
+
try {
|
|
54
|
+
raw = await fs.readFile(todosPath(dataDir), 'utf8');
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
return { items: [], needsRewrite: false }; // no file yet — empty inbox
|
|
58
|
+
}
|
|
59
|
+
let parsed;
|
|
60
|
+
try {
|
|
61
|
+
parsed = JSON.parse(raw);
|
|
62
|
+
}
|
|
63
|
+
catch (err) {
|
|
64
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
65
|
+
console.warn(`[cez] todos.json is not valid JSON — showing an empty inbox (${message})`);
|
|
66
|
+
return { items: [], needsRewrite: false };
|
|
67
|
+
}
|
|
68
|
+
if (!Array.isArray(parsed)) {
|
|
69
|
+
console.warn('[cez] todos.json is not a JSON array — showing an empty inbox');
|
|
70
|
+
return { items: [], needsRewrite: false };
|
|
71
|
+
}
|
|
72
|
+
const items = [];
|
|
73
|
+
let needsRewrite = false;
|
|
74
|
+
for (const entry of parsed) {
|
|
75
|
+
const result = todoSchema.safeParse(entry);
|
|
76
|
+
if (!result.success) {
|
|
77
|
+
console.warn(`[cez] skipped a malformed todos.json entry: ${result.error.issues.map((i) => i.message).join('; ')}`);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!result.data.id) {
|
|
81
|
+
// Agent entries arrive without ids — assign one so the GUI can address
|
|
82
|
+
// the entry; the file is rewritten (under the lock) on this read.
|
|
83
|
+
needsRewrite = true;
|
|
84
|
+
items.push({ ...result.data, id: randomUUID() });
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
items.push({ ...result.data, id: result.data.id });
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { items, needsRewrite };
|
|
91
|
+
}
|
|
92
|
+
async function writeAtomic(dataDir, items) {
|
|
93
|
+
const file = todosPath(dataDir);
|
|
94
|
+
const tmp = `${file}.tmp`;
|
|
95
|
+
await fs.mkdir(dataDir, { recursive: true });
|
|
96
|
+
await fs.writeFile(tmp, JSON.stringify(items, null, 2), 'utf8');
|
|
97
|
+
await fs.rename(tmp, file);
|
|
98
|
+
}
|
|
99
|
+
export async function readTodos(dataDir) {
|
|
100
|
+
return withLock(dataDir, async () => {
|
|
101
|
+
const { items, needsRewrite } = await readRaw(dataDir);
|
|
102
|
+
if (needsRewrite) {
|
|
103
|
+
await writeAtomic(dataDir, items).catch(() => undefined); // best effort
|
|
104
|
+
}
|
|
105
|
+
return items;
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
/** Check off (delete) an entry. False when the id isn't there. */
|
|
109
|
+
export async function removeTodo(dataDir, id) {
|
|
110
|
+
return withLock(dataDir, async () => {
|
|
111
|
+
const { items } = await readRaw(dataDir);
|
|
112
|
+
const next = items.filter((t) => t.id !== id);
|
|
113
|
+
if (next.length === items.length)
|
|
114
|
+
return false;
|
|
115
|
+
await writeAtomic(dataDir, next);
|
|
116
|
+
return true;
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
/** Record that "▶ Run" turned the entry into task `taskId`. The entry stays
|
|
120
|
+
* in the file as an audit trail; the GUI hides started entries. */
|
|
121
|
+
export async function markStarted(dataDir, id, taskId) {
|
|
122
|
+
return withLock(dataDir, async () => {
|
|
123
|
+
const { items } = await readRaw(dataDir);
|
|
124
|
+
const item = items.find((t) => t.id === id);
|
|
125
|
+
if (!item)
|
|
126
|
+
return false;
|
|
127
|
+
item.startedTaskId = taskId;
|
|
128
|
+
await writeAtomic(dataDir, items);
|
|
129
|
+
return true;
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
// ---- change notifications ----------------------------------------------------
|
|
133
|
+
const emitter = new EventEmitter();
|
|
134
|
+
emitter.setMaxListeners(100);
|
|
135
|
+
let watching = false;
|
|
136
|
+
/** Watch `.ai/cezar/` for todos.json changes (agents write it from another
|
|
137
|
+
* process) and emit debounced change events. Degrades to no watch on error. */
|
|
138
|
+
export function startTodosWatch(dataDir) {
|
|
139
|
+
if (watching)
|
|
140
|
+
return;
|
|
141
|
+
watching = true;
|
|
142
|
+
try {
|
|
143
|
+
mkdirSync(dataDir, { recursive: true });
|
|
144
|
+
let timer;
|
|
145
|
+
const watcher = watch(dataDir, (_event, filename) => {
|
|
146
|
+
if (filename && filename !== 'todos.json')
|
|
147
|
+
return;
|
|
148
|
+
if (timer)
|
|
149
|
+
clearTimeout(timer);
|
|
150
|
+
timer = setTimeout(() => emitter.emit('changed'), 300);
|
|
151
|
+
timer.unref?.();
|
|
152
|
+
});
|
|
153
|
+
watcher.on('error', () => undefined); // a dying watcher must not kill the server
|
|
154
|
+
watcher.unref?.();
|
|
155
|
+
}
|
|
156
|
+
catch (err) {
|
|
157
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
158
|
+
console.warn(`[cez] todos watch unavailable — the Inbox updates on refresh only (${message})`);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/** Subscribe to inbox changes; returns the unsubscribe function. */
|
|
162
|
+
export function onTodosChanged(cb) {
|
|
163
|
+
emitter.on('changed', cb);
|
|
164
|
+
return () => emitter.off('changed', cb);
|
|
165
|
+
}
|
|
166
|
+
//# sourceMappingURL=todos.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"todos.js","sourceRoot":"","sources":["../src/todos.ts"],"names":[],"mappings":"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"}
|
package/dist/workflows/load.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { readdir, readFile } from 'node:fs/promises';
|
|
2
2
|
import { extname, join, resolve } from 'node:path';
|
|
3
3
|
import { parse as parseYaml } from 'yaml';
|
|
4
|
-
import { QUICK_TASK_WORKFLOW, workflowFileSchema } from './types.js';
|
|
4
|
+
import { QUICK_TASK_WORKFLOW, stepsIssue, workflowFileSchema } from './types.js';
|
|
5
5
|
export const WORKFLOWS_DIR = '.ai/cezar/workflows';
|
|
6
6
|
/**
|
|
7
7
|
* Load the workflow catalog: the built-in `quick-task` plus every
|
|
@@ -31,14 +31,10 @@ export async function loadWorkflows(repoRoot) {
|
|
|
31
31
|
issues.push({ path, message: parsed.error.issues.map((i) => i.message).join('; ') });
|
|
32
32
|
continue;
|
|
33
33
|
}
|
|
34
|
-
// Steps referenced by onFail.retry must exist and come earlier.
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
issues.push({
|
|
39
|
-
path,
|
|
40
|
-
message: `step "${badRetry.id}": onFail.retry must reference an earlier step (got "${badRetry.onFail.retry}")`,
|
|
41
|
-
});
|
|
34
|
+
// Steps referenced by onFail.retry must exist and come earlier; ids unique.
|
|
35
|
+
const issue = stepsIssue(parsed.data.steps);
|
|
36
|
+
if (issue) {
|
|
37
|
+
issues.push({ path, message: issue });
|
|
42
38
|
continue;
|
|
43
39
|
}
|
|
44
40
|
fromFiles.push({ ...parsed.data, source: 'file', path });
|