@worca/app 0.0.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/README.md +403 -0
- package/agents/clarify.meta.json +19 -0
- package/agents/decomposer.meta.json +21 -0
- package/agents/implementer.meta.json +20 -0
- package/agents/manualTestsChecklist.meta.json +18 -0
- package/agents/manualWebUiTesting.meta.json +18 -0
- package/agents/planReviewer.meta.json +19 -0
- package/agents/planner.meta.json +20 -0
- package/agents/refiner.meta.json +19 -0
- package/agents/reviewer.meta.json +19 -0
- package/agents/worca-cc-clarify.md +67 -0
- package/agents/worca-cc-code-reviewer.md +66 -0
- package/agents/worca-cc-decomposer.md +84 -0
- package/agents/worca-cc-implementer.md +69 -0
- package/agents/worca-cc-manual-tests-checklist.md +63 -0
- package/agents/worca-cc-manual-web-ui-testing.md +64 -0
- package/agents/worca-cc-plan-refiner.md +69 -0
- package/agents/worca-cc-plan-reviewer.md +70 -0
- package/agents/worca-cc-planner.md +70 -0
- package/agents/worca-cc-workspace-reviewer.md +56 -0
- package/agents/worca-cc-workspace-scanner.md +55 -0
- package/agents/workspaceReviewer.meta.json +20 -0
- package/agents/workspaceScanner.meta.json +18 -0
- package/package.json +61 -0
- package/scripts/install.mjs +209 -0
- package/skills/worca/SKILL.md +66 -0
- package/src/cli/worca-cc.mjs +1520 -0
- package/src/core/agent-gen.mjs +206 -0
- package/src/core/agent-registry.mjs +417 -0
- package/src/core/agent-store.mjs +143 -0
- package/src/core/artifacts.mjs +2019 -0
- package/src/core/channels.mjs +302 -0
- package/src/core/chat/allowlist.mjs +27 -0
- package/src/core/chat/channel-host.mjs +562 -0
- package/src/core/chat/channel-protocol.mjs +117 -0
- package/src/core/chat/channel-worker-child.mjs +211 -0
- package/src/core/chat/chat-context.mjs +66 -0
- package/src/core/chat/command-router.mjs +343 -0
- package/src/core/chat/notifier.mjs +120 -0
- package/src/core/chat/parser.mjs +30 -0
- package/src/core/chat/rate-limiter.mjs +133 -0
- package/src/core/chat/redact.mjs +27 -0
- package/src/core/chat/renderers.mjs +136 -0
- package/src/core/claude-runner.mjs +1356 -0
- package/src/core/config.mjs +882 -0
- package/src/core/cost-budget.mjs +103 -0
- package/src/core/db.mjs +864 -0
- package/src/core/fanout.mjs +48 -0
- package/src/core/folder-dialog.mjs +138 -0
- package/src/core/fs-browse.mjs +49 -0
- package/src/core/git-info.mjs +200 -0
- package/src/core/guardrail-store.mjs +204 -0
- package/src/core/guardrails.mjs +302 -0
- package/src/core/marketplaces.mjs +267 -0
- package/src/core/migrate-fs-to-db.mjs +612 -0
- package/src/core/model-env.mjs +74 -0
- package/src/core/orchestrator.mjs +4279 -0
- package/src/core/overview-agent.mjs +124 -0
- package/src/core/phases.mjs +1279 -0
- package/src/core/pipeline-delete.mjs +428 -0
- package/src/core/plugin-api.mjs +13 -0
- package/src/core/plugin-config.mjs +100 -0
- package/src/core/plugin-inventory.mjs +50 -0
- package/src/core/plugin-manifest.mjs +447 -0
- package/src/core/plugin-models.mjs +130 -0
- package/src/core/plugin-repo.mjs +303 -0
- package/src/core/plugin-shim-child.mjs +76 -0
- package/src/core/plugin-shim.mjs +197 -0
- package/src/core/plugin-store.mjs +485 -0
- package/src/core/plugin-workflows.mjs +179 -0
- package/src/core/plugins-lock.mjs +49 -0
- package/src/core/preflight-node.mjs +122 -0
- package/src/core/preflight.mjs +341 -0
- package/src/core/projects.mjs +157 -0
- package/src/core/protocol.mjs +257 -0
- package/src/core/recoverable-error.mjs +51 -0
- package/src/core/results.mjs +188 -0
- package/src/core/run-context.mjs +1375 -0
- package/src/core/run-log.mjs +64 -0
- package/src/core/run-manifest.mjs +317 -0
- package/src/core/runners.mjs +167 -0
- package/src/core/settings.mjs +682 -0
- package/src/core/skills.mjs +210 -0
- package/src/core/sources.mjs +232 -0
- package/src/core/stats.mjs +182 -0
- package/src/core/store.mjs +67 -0
- package/src/core/title.mjs +64 -0
- package/src/core/workflow-validator.mjs +185 -0
- package/src/core/workflows.mjs +568 -0
- package/src/core/workspace-scan.mjs +420 -0
- package/src/core/workspaces.mjs +353 -0
- package/src/core/worktree.mjs +708 -0
- package/src/feature.mjs +9 -0
- package/ui/public/app.js +10647 -0
- package/ui/public/assets/worca-favicon.png +0 -0
- package/ui/public/assets/worca-logo.png +0 -0
- package/ui/public/chat-settings-view.mjs +89 -0
- package/ui/public/composer-core.mjs +211 -0
- package/ui/public/fonts/jetbrains-mono-latin-400-normal.woff2 +0 -0
- package/ui/public/fonts/poppins-latin-400-normal.woff2 +0 -0
- package/ui/public/fonts/poppins-latin-500-normal.woff2 +0 -0
- package/ui/public/fonts/poppins-latin-600-normal.woff2 +0 -0
- package/ui/public/fonts/poppins-latin-700-normal.woff2 +0 -0
- package/ui/public/guardrails-view.mjs +244 -0
- package/ui/public/index.html +1145 -0
- package/ui/public/log-filter.mjs +81 -0
- package/ui/public/log-line.mjs +86 -0
- package/ui/public/models-view.mjs +433 -0
- package/ui/public/plugins-view.mjs +430 -0
- package/ui/public/results-view.mjs +121 -0
- package/ui/public/source-pane.mjs +156 -0
- package/ui/public/stats-view.mjs +523 -0
- package/ui/public/style.css +1557 -0
- package/ui/server.mjs +3573 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// src/core/agent-store.mjs
|
|
2
|
+
// CRUD for USER agents: <worcaHome()>/agents/<key>.meta.json + <key>.md pairs,
|
|
3
|
+
// layered under the read-only built-in repo agents/ dir by the Phase 1 registry.
|
|
4
|
+
// Validation + persistence live here (thin-core pattern, mirrors workspaces.mjs);
|
|
5
|
+
// HTTP mapping is the route's.
|
|
6
|
+
|
|
7
|
+
import { mkdir, readFile, writeFile, rm } from 'node:fs/promises';
|
|
8
|
+
import { join } from 'node:path';
|
|
9
|
+
import { loadAgentRegistry, normalizeMeta, userAgentsDir } from './agent-registry.mjs';
|
|
10
|
+
import { listWorkflows } from './workflows.mjs';
|
|
11
|
+
|
|
12
|
+
export { userAgentsDir }; // single source: the Phase 1 layer resolver
|
|
13
|
+
|
|
14
|
+
/** A key is a bare alphanumeric stem — can never contain "/" or "..". */
|
|
15
|
+
export const AGENT_KEY_RE = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
|
|
16
|
+
|
|
17
|
+
function err(message, code) { return Object.assign(new Error(message), { code }); }
|
|
18
|
+
|
|
19
|
+
/** The writable user layer dir. userAgentsDir() returns null only when the home
|
|
20
|
+
* cannot be resolved (no WORCA_HOME under node:test) — surface that as a 400. */
|
|
21
|
+
function requireUserDir() {
|
|
22
|
+
const dir = userAgentsDir();
|
|
23
|
+
if (!dir) throw err('cannot resolve the user agents directory (WORCA_HOME unset?)', 'BAD_REQUEST');
|
|
24
|
+
return dir;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** lower-camel key from a display name: "API Docs Writer" -> "apiDocsWriter". */
|
|
28
|
+
export function keyFromName(name) {
|
|
29
|
+
const words = String(name || '').split(/[^A-Za-z0-9]+/).filter(Boolean).map((w) => w.toLowerCase());
|
|
30
|
+
if (!words.length) return '';
|
|
31
|
+
return words[0] + words.slice(1).map((w) => w[0].toUpperCase() + w.slice(1)).join('');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Merged layered registry as an ordered array. origin ('builtin'|'user') and the
|
|
35
|
+
* layer-correct absolute agentPath are stamped by the Phase 1 registry; built-ins
|
|
36
|
+
* win key collisions there; the result is already sorted by .order. */
|
|
37
|
+
export async function listAgents() {
|
|
38
|
+
return Object.values(loadAgentRegistry());
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Full read: { meta (with origin), markdown } or null. */
|
|
42
|
+
export async function readAgent(key) {
|
|
43
|
+
if (!AGENT_KEY_RE.test(String(key || ''))) return null;
|
|
44
|
+
const meta = loadAgentRegistry()[key];
|
|
45
|
+
if (!meta) return null;
|
|
46
|
+
let markdown = '';
|
|
47
|
+
if (meta.agentPath) {
|
|
48
|
+
try { markdown = await readFile(meta.agentPath, 'utf8'); } catch { markdown = ''; }
|
|
49
|
+
}
|
|
50
|
+
return { meta, markdown };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Create a user agent. key = meta.key || keyFromName(displayName). */
|
|
54
|
+
export async function createAgent({ meta: rawMeta, markdown } = {}) {
|
|
55
|
+
const raw = rawMeta && typeof rawMeta === 'object' ? { ...rawMeta } : {};
|
|
56
|
+
const key = (typeof raw.key === 'string' && raw.key.trim()) || keyFromName(raw.displayName);
|
|
57
|
+
if (!AGENT_KEY_RE.test(key)) throw err('agent key must be alphanumeric (letters, digits, - or _)', 'BAD_REQUEST');
|
|
58
|
+
if (typeof markdown !== 'string' || !markdown.trim()) throw err('markdown body is required', 'BAD_REQUEST');
|
|
59
|
+
raw.key = key;
|
|
60
|
+
raw.agentFile = `${key}.md`; // store-owned sibling file
|
|
61
|
+
if (!Number.isFinite(Number(raw.order))) raw.order = 99; // sort after built-ins by default
|
|
62
|
+
const meta = normalizeMeta(raw);
|
|
63
|
+
if (!meta) throw err('invalid agent metadata', 'BAD_REQUEST');
|
|
64
|
+
const existing = loadAgentRegistry()[key];
|
|
65
|
+
if (existing && existing.origin === 'builtin') {
|
|
66
|
+
throw err(`"${key}" is a built-in agent — duplicate it under a new name instead`, 'BUILTIN');
|
|
67
|
+
}
|
|
68
|
+
if (existing) throw err(`a user agent "${key}" already exists`, 'DUPLICATE');
|
|
69
|
+
const dir = requireUserDir();
|
|
70
|
+
await mkdir(dir, { recursive: true });
|
|
71
|
+
await writeFile(join(dir, `${key}.md`), markdown, 'utf8');
|
|
72
|
+
await writeFile(join(dir, `${key}.meta.json`), JSON.stringify(meta, null, 2) + '\n', 'utf8');
|
|
73
|
+
return { meta: { ...meta, origin: 'user' }, markdown };
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Update a USER agent (meta and/or markdown). Built-ins -> BUILTIN (409). */
|
|
77
|
+
export async function updateAgent(key, { meta: rawMeta, markdown } = {}) {
|
|
78
|
+
if (!AGENT_KEY_RE.test(String(key || ''))) throw err(`agent not found: ${key}`, 'NOT_FOUND');
|
|
79
|
+
const existing = loadAgentRegistry()[key];
|
|
80
|
+
if (existing && existing.origin === 'builtin') {
|
|
81
|
+
throw err(`"${key}" is a built-in agent — duplicate it instead of editing`, 'BUILTIN');
|
|
82
|
+
}
|
|
83
|
+
const origin = String(existing?.origin || '');
|
|
84
|
+
if (origin.startsWith('plugin:')) {
|
|
85
|
+
throw Object.assign(
|
|
86
|
+
new Error(`agent "${key}" is managed by plugin "${origin.slice('plugin:'.length)}" — disable or uninstall the plugin instead`),
|
|
87
|
+
{ code: 'PLUGIN' },
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
if (!existing) throw err(`agent not found: ${key}`, 'NOT_FOUND');
|
|
91
|
+
// `existing` carries the COMPUTED origin/agentPath/descriptionDerived fields;
|
|
92
|
+
// normalizeMeta's fixed return set drops them, so the spread below never
|
|
93
|
+
// persists them to the sidecar. `description` is the exception: it DOES have a
|
|
94
|
+
// slot, so a description resolved from the .md frontmatter has to be dropped
|
|
95
|
+
// explicitly — otherwise any later save (even one that never touches the
|
|
96
|
+
// field) freezes the fallback into the sidecar and it stops tracking the .md.
|
|
97
|
+
const base = { ...existing };
|
|
98
|
+
if (base.descriptionDerived) base.description = '';
|
|
99
|
+
const raw = { ...base, ...(rawMeta && typeof rawMeta === 'object' ? rawMeta : {}) };
|
|
100
|
+
raw.key = key; // key immutable on update
|
|
101
|
+
raw.agentFile = `${key}.md`;
|
|
102
|
+
if (!Number.isFinite(Number(raw.order))) raw.order = existing.order;
|
|
103
|
+
const meta = normalizeMeta(raw);
|
|
104
|
+
if (!meta) throw err('invalid agent metadata', 'BAD_REQUEST');
|
|
105
|
+
const dir = requireUserDir();
|
|
106
|
+
if (typeof markdown === 'string') {
|
|
107
|
+
if (!markdown.trim()) throw err('markdown body cannot be empty', 'BAD_REQUEST');
|
|
108
|
+
await writeFile(join(dir, `${key}.md`), markdown, 'utf8');
|
|
109
|
+
}
|
|
110
|
+
await writeFile(join(dir, `${key}.meta.json`), JSON.stringify(meta, null, 2) + '\n', 'utf8');
|
|
111
|
+
const body = typeof markdown === 'string'
|
|
112
|
+
? markdown
|
|
113
|
+
: await readFile(join(dir, `${key}.md`), 'utf8').catch(() => '');
|
|
114
|
+
return { meta: { ...meta, origin: 'user' }, markdown: body };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** Delete a USER agent; REFERENCED (409) while a saved workflow uses the key. */
|
|
118
|
+
export async function deleteAgent(key) {
|
|
119
|
+
if (!AGENT_KEY_RE.test(String(key || ''))) throw err(`agent not found: ${key}`, 'NOT_FOUND');
|
|
120
|
+
const existing = loadAgentRegistry()[key];
|
|
121
|
+
if (existing && existing.origin === 'builtin') {
|
|
122
|
+
// "duplicate it" must appear here: the API test pins /duplicate it/i on DELETE.
|
|
123
|
+
throw err(`"${key}" is a built-in agent and cannot be deleted — duplicate it under a new name instead`, 'BUILTIN');
|
|
124
|
+
}
|
|
125
|
+
const origin = String(existing?.origin || '');
|
|
126
|
+
if (origin.startsWith('plugin:')) {
|
|
127
|
+
throw Object.assign(
|
|
128
|
+
new Error(`agent "${key}" is managed by plugin "${origin.slice('plugin:'.length)}" — disable or uninstall the plugin instead`),
|
|
129
|
+
{ code: 'PLUGIN' },
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
if (!existing) throw err(`agent not found: ${key}`, 'NOT_FOUND');
|
|
133
|
+
const refs = (await listWorkflows())
|
|
134
|
+
.filter((wf) => (wf.steps || []).some((col) => (col || []).some((n) => n && n.key === key)))
|
|
135
|
+
.map((wf) => wf.name || wf.id);
|
|
136
|
+
if (refs.length) {
|
|
137
|
+
throw err(`agent "${key}" is used by saved workflow(s): ${refs.join(', ')} — delete or edit those first`, 'REFERENCED');
|
|
138
|
+
}
|
|
139
|
+
const dir = requireUserDir();
|
|
140
|
+
await rm(join(dir, `${key}.meta.json`), { force: true });
|
|
141
|
+
await rm(join(dir, `${key}.md`), { force: true });
|
|
142
|
+
return { ok: true };
|
|
143
|
+
}
|