@hybridlabor-api/aos 4.7.0 → 4.7.2
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/.agents/agents.md +8 -3
- package/.agents/nodes.json +2 -1
- package/.agents/state.schema.json +24 -0
- package/.agents/vendor-manifest.json +6 -0
- package/.agents/workflows/startcycle-graph.md +2 -2
- package/.claude/agents/architect.md +5 -4
- package/.claude/agents/techlead.md +2 -0
- package/.claude/workflows/startcycle-dispatch.mjs +175 -17
- package/.opencode/agents/architect.md +5 -4
- package/.opencode/agents/techlead.md +2 -0
- package/THIRD_PARTY_NOTICES.md +9 -1
- package/bin/aos-doctor.mjs +386 -0
- package/bin/aos-store.mjs +171 -0
- package/docs/AUDIT_BRIEF_ECC_STORE.md +122 -0
- package/docs/PLAN_AOS_STORE_PHASE1.md +190 -0
- package/installer.js +68 -15
- package/lib/aos-archify-contract.mjs +277 -0
- package/lib/ecc-store-index.json +1 -0
- package/mcp_config.json +4 -0
- package/mcps/mcsc/packages/core/src/adapters/codex.js +160 -37
- package/package.json +7 -2
- package/scripts/build-ecc-store-index.mjs +147 -0
- package/skills/basic/startcycle-graph/SKILL.md +13 -0
- package/skills/global_config/agenttrail/bin/agenttrail.mjs +153 -42
- package/skills/global_config/agenttrail/public/index.html +5 -4
- package/skills/global_config/aos-setup/scripts/aos-doctor.mjs +8 -2
- package/skills/global_config/archify/examples/dataflow-product-analytics.html +15048 -0
- package/skills/global_config/archify/examples/lifecycle-agent-run.html +14983 -0
- package/skills/global_config/archify/examples/sequence-cache-miss-request.html +15063 -0
- package/skills/global_config/archify/examples/web-app-rendered.html +15012 -0
- package/skills/global_config/archify/examples/workflow-agent-tool-call-rendered.html +15054 -0
- package/skills/global_config/ask-tim/SKILL.md +10 -0
- package/skills/global_config/mcsc/SKILL.md +60 -0
- package/skills/global_config/plan-canvas/scripts/lib/plan-canvas/server.js +31 -6
- package/skills/global_config/plan-canvas/scripts/plan-canvas.js +4 -2
- package/docs/handover/agenttrail-for-ao.md +0 -58
- package/docs/handover/deja-for-ao.md +0 -51
|
@@ -1,56 +1,179 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { promisify } from 'node:util';
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
3
2
|
import { emitTrail } from '../trail.js';
|
|
4
3
|
|
|
5
|
-
const
|
|
4
|
+
const MAX_OUTPUT = 1024 * 1024 * 50; // 50MB, mirrors the old execFile maxBuffer
|
|
6
5
|
|
|
7
6
|
/**
|
|
8
7
|
* Adapter für die Codex CLI.
|
|
9
8
|
* Implementiert die DelegationRequest Schnittstelle.
|
|
10
|
-
*
|
|
9
|
+
*
|
|
10
|
+
* Uses `codex exec --json` (confirmed present: `codex exec --help` lists
|
|
11
|
+
* `--json Print events to stdout as JSONL`) instead of a blind exec, so a
|
|
12
|
+
* delegated Codex run gets the same mid-run tool-call visibility on
|
|
13
|
+
* agenttrail's board as the opencode adapter already has. The envelope shape
|
|
14
|
+
* below (`thread.started` / `turn.started` / `item.completed` / `turn.failed`,
|
|
15
|
+
* `item.completed` items carrying `{id, type, ...}`) was confirmed live
|
|
16
|
+
* against the installed CLI (codex-cli 0.154.0). The exact `item.type` string
|
|
17
|
+
* for a real tool call (e.g. shell exec, patch apply, the plan/todo tool)
|
|
18
|
+
* was NOT confirmed live — every probe run in this environment hit a 401
|
|
19
|
+
* auth error before Codex executed anything. `buildToolInput` below is
|
|
20
|
+
* therefore deliberately generic/defensive rather than keyed to guessed
|
|
21
|
+
* field names, and Codex's plan/todo tool is not mapped into a
|
|
22
|
+
* `PostToolUse` with `tool_name: 'update_plan'` here — doing that from
|
|
23
|
+
* unverified field names would silently ship a wrong mapping. Re-verify
|
|
24
|
+
* with authenticated Codex and tighten this once the real item shape for a
|
|
25
|
+
* tool call (and for its plan/todo tool specifically) is known.
|
|
26
|
+
*
|
|
27
|
+
* @param {import('./types.js').DelegationRequest} req
|
|
11
28
|
* @returns {Promise<{ exit: number; output: string }>}
|
|
12
29
|
*/
|
|
13
30
|
export async function delegate(req) {
|
|
31
|
+
const cwd = req.cwd || process.cwd();
|
|
14
32
|
const trailEvent = {
|
|
15
33
|
session_id: `mcsc-codex-${process.pid}-${Date.now()}`,
|
|
16
|
-
cwd
|
|
34
|
+
cwd,
|
|
17
35
|
agent:
|
|
18
36
|
typeof req.label === 'string' && req.label.length > 0
|
|
19
37
|
? `codex:${req.label}`
|
|
20
38
|
: 'codex',
|
|
21
39
|
};
|
|
22
40
|
emitTrail({ ...trailEvent, hook_event_name: 'SessionStart' });
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
41
|
+
|
|
42
|
+
const env = { ...process.env, MCSC_CALLER: 'codex' };
|
|
43
|
+
const args = ['exec', '--json', '--skip-git-repo-check'];
|
|
44
|
+
if (req.model) args.push('-m', req.model);
|
|
45
|
+
args.push(req.prompt);
|
|
46
|
+
|
|
47
|
+
const child = spawn('codex', args, {
|
|
48
|
+
cwd,
|
|
49
|
+
env,
|
|
50
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
let lastAgentMessage = '';
|
|
55
|
+
let stdoutBuf = '';
|
|
56
|
+
let stderrBuf = '';
|
|
57
|
+
let stdoutBytes = 0;
|
|
58
|
+
let stderrBytes = 0;
|
|
59
|
+
let overflowed = false;
|
|
60
|
+
let aborted = false;
|
|
61
|
+
let settled = false;
|
|
62
|
+
let turnFailed = false;
|
|
63
|
+
|
|
64
|
+
const onAbort = () => {
|
|
65
|
+
aborted = true;
|
|
66
|
+
child.kill('SIGKILL');
|
|
67
|
+
};
|
|
68
|
+
if (req.signal) {
|
|
69
|
+
if (req.signal.aborted) onAbort();
|
|
70
|
+
else req.signal.addEventListener('abort', onAbort, { once: true });
|
|
52
71
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
72
|
+
|
|
73
|
+
const finalize = (result) => {
|
|
74
|
+
if (settled) return;
|
|
75
|
+
settled = true;
|
|
76
|
+
if (req.signal) req.signal.removeEventListener('abort', onAbort);
|
|
77
|
+
emitTrail({ ...trailEvent, hook_event_name: 'SessionEnd' });
|
|
78
|
+
resolve(result);
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
// Defensive/generic: no confirmed field names for a real tool-call item
|
|
82
|
+
// (see the doc comment above), so this only forwards what's plausibly
|
|
83
|
+
// present rather than asserting a specific Codex item shape.
|
|
84
|
+
const buildToolInput = (item) => {
|
|
85
|
+
const toolInput = {};
|
|
86
|
+
if (!item || typeof item !== 'object') return toolInput;
|
|
87
|
+
const rawCommand = item.command || item.cmd;
|
|
88
|
+
if (typeof rawCommand === 'string' && rawCommand.length > 0) toolInput.command = rawCommand;
|
|
89
|
+
else if (Array.isArray(rawCommand)) toolInput.command = rawCommand.join(' ');
|
|
90
|
+
const rawPath = item.file_path || item.path;
|
|
91
|
+
if (typeof rawPath === 'string' && rawPath.length > 0) toolInput.file_path = rawPath;
|
|
92
|
+
return toolInput;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const handleLine = (line) => {
|
|
96
|
+
if (!line) return;
|
|
97
|
+
let evt;
|
|
98
|
+
try {
|
|
99
|
+
evt = JSON.parse(line);
|
|
100
|
+
} catch {
|
|
101
|
+
return; // Not valid JSON — ignore.
|
|
102
|
+
}
|
|
103
|
+
if (!evt || typeof evt !== 'object') return;
|
|
104
|
+
|
|
105
|
+
if (evt.type === 'turn.completed' && typeof evt.last_agent_message === 'string') {
|
|
106
|
+
lastAgentMessage = evt.last_agent_message;
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (evt.type === 'turn.failed') {
|
|
110
|
+
turnFailed = true;
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (evt.type === 'item.completed' && evt.item && typeof evt.item === 'object') {
|
|
114
|
+
const itemType = evt.item.type;
|
|
115
|
+
// Informational item types carry no tool call — skip them so the
|
|
116
|
+
// board doesn't show a fake "tool" for plain error/text output.
|
|
117
|
+
if (itemType === 'error' || itemType === 'reasoning' || itemType === 'agent_message') return;
|
|
118
|
+
emitTrail({
|
|
119
|
+
...trailEvent,
|
|
120
|
+
hook_event_name: 'PostToolUse',
|
|
121
|
+
tool_name: String(itemType || 'codex_item')
|
|
122
|
+
.replace(/(^|_)([a-z])/g, (_, sep, c) => c.toUpperCase()),
|
|
123
|
+
tool_input: buildToolInput(evt.item),
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
child.stdout.setEncoding('utf8');
|
|
129
|
+
child.stdout.on('data', (chunk) => {
|
|
130
|
+
stdoutBytes += Buffer.byteLength(chunk);
|
|
131
|
+
if (stdoutBytes > MAX_OUTPUT) {
|
|
132
|
+
if (!overflowed) {
|
|
133
|
+
overflowed = true;
|
|
134
|
+
child.kill('SIGKILL');
|
|
135
|
+
}
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
stdoutBuf += chunk;
|
|
139
|
+
let idx;
|
|
140
|
+
while ((idx = stdoutBuf.indexOf('\n')) !== -1) {
|
|
141
|
+
const line = stdoutBuf.slice(0, idx).trim();
|
|
142
|
+
stdoutBuf = stdoutBuf.slice(idx + 1);
|
|
143
|
+
handleLine(line);
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
child.stderr.setEncoding('utf8');
|
|
148
|
+
child.stderr.on('data', (chunk) => {
|
|
149
|
+
stderrBytes += Buffer.byteLength(chunk);
|
|
150
|
+
if (stderrBytes > MAX_OUTPUT) return; // cap stderr, never fail on it
|
|
151
|
+
stderrBuf += chunk;
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
child.on('error', (err) => {
|
|
155
|
+
finalize({ exit: 1, output: (stderrBuf || err.message || '').trim() });
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
child.on('close', (code) => {
|
|
159
|
+
const rest = stdoutBuf.trim();
|
|
160
|
+
if (rest) handleLine(rest); // flush a trailing line without newline
|
|
161
|
+
if (aborted) {
|
|
162
|
+
finalize({ exit: 1, output: 'Cancelled by orchestrator' });
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (overflowed) {
|
|
166
|
+
finalize({ exit: 1, output: 'stdout maxBuffer length exceeded' });
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (code === 0 && !turnFailed) {
|
|
170
|
+
finalize({ exit: 0, output: lastAgentMessage || stderrBuf });
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
finalize({ exit: 1, output: (lastAgentMessage || stderrBuf || `codex exited with code ${code}`).trim() });
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
// An open stdin pipe makes the CLI wait for input forever.
|
|
177
|
+
child.stdin.end();
|
|
178
|
+
});
|
|
56
179
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hybridlabor-api/aos",
|
|
3
|
-
"version": "4.7.
|
|
3
|
+
"version": "4.7.2",
|
|
4
4
|
"description": "AOS — A Curated AI AGENT OS. Optimized agent skills and add-ons like memB, OpenWiki, Heimdall Token Saver, and Godmode architectures.",
|
|
5
5
|
"main": "installer.js",
|
|
6
6
|
"engines": {
|
|
@@ -16,12 +16,16 @@
|
|
|
16
16
|
"aos-archify": "skills/global_config/archify/bin/archify.mjs",
|
|
17
17
|
"aos-dashboard": "bin/aos-dashboard.mjs",
|
|
18
18
|
"aos-config": "bin/aos-config.mjs",
|
|
19
|
+
"aos-store": "bin/aos-store.mjs",
|
|
20
|
+
"aos-doctor": "bin/aos-doctor.mjs",
|
|
19
21
|
"aos-uninstall": "bin/aos-uninstall.mjs"
|
|
20
22
|
},
|
|
21
23
|
"scripts": {
|
|
22
24
|
"validate": "node scripts/validate-skills.mjs",
|
|
23
25
|
"validate:strict": "node scripts/validate-skills.mjs --strict",
|
|
24
|
-
"
|
|
26
|
+
"store:index": "node scripts/build-ecc-store-index.mjs",
|
|
27
|
+
"doctor": "node bin/aos-doctor.mjs",
|
|
28
|
+
"test": "node scripts/validate-skills.mjs --selftest && node scripts/validate-skills.mjs && node tests/aos-store.test.mjs && node tests/aos-doctor.test.mjs"
|
|
25
29
|
},
|
|
26
30
|
"publishConfig": {
|
|
27
31
|
"access": "public"
|
|
@@ -48,6 +52,7 @@
|
|
|
48
52
|
".codex-plugin/",
|
|
49
53
|
".opencode/",
|
|
50
54
|
"docs/",
|
|
55
|
+
"!docs/handover/**",
|
|
51
56
|
"assets/",
|
|
52
57
|
"skills/",
|
|
53
58
|
"mcps/",
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { execFileSync } from 'node:child_process';
|
|
4
|
+
import { createHash } from 'node:crypto';
|
|
5
|
+
import { existsSync, mkdtempSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { basename, dirname, join, relative, resolve, sep } from 'node:path';
|
|
7
|
+
import { tmpdir } from 'node:os';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
const ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
|
|
11
|
+
const MANIFEST_PATH = join(ROOT, '.agents', 'vendor-manifest.json');
|
|
12
|
+
const OUTPUT_PATH = join(ROOT, 'lib', 'ecc-store-index.json');
|
|
13
|
+
const manifest = JSON.parse(readFileSync(MANIFEST_PATH, 'utf8'));
|
|
14
|
+
const entry = manifest.vendors?.['ecc-store'];
|
|
15
|
+
if (!entry?.pinned_commit || !/^[a-f0-9]{40}$/.test(entry.pinned_commit)) {
|
|
16
|
+
throw new Error('Missing or invalid .agents/vendor-manifest.json ECC store pin');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const checkout = mkdtempSync(join(tmpdir(), 'aos-ecc-store-'));
|
|
20
|
+
|
|
21
|
+
function runGit(args) {
|
|
22
|
+
return execFileSync('git', args, { cwd: checkout, encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] }).trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function walk(dir, output = []) {
|
|
26
|
+
if (!existsSync(dir)) return output;
|
|
27
|
+
for (const item of readdirSync(dir, { withFileTypes: true })) {
|
|
28
|
+
const fullPath = join(dir, item.name);
|
|
29
|
+
if (item.isDirectory()) walk(fullPath, output);
|
|
30
|
+
else if (item.isFile()) output.push(fullPath);
|
|
31
|
+
}
|
|
32
|
+
return output;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function frontmatterValue(text, key) {
|
|
36
|
+
const match = text.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
37
|
+
if (!match) return '';
|
|
38
|
+
const lines = match[1].split(/\r?\n/);
|
|
39
|
+
const start = lines.findIndex((line) => new RegExp(`^${key}:`, 'i').test(line));
|
|
40
|
+
if (start < 0) return '';
|
|
41
|
+
const first = lines[start].slice(lines[start].indexOf(':') + 1).trim();
|
|
42
|
+
if (first && !/^[>|][-+]?$/.test(first)) return first.replace(/^['"]|['"]$/g, '');
|
|
43
|
+
const values = [];
|
|
44
|
+
for (let index = start + 1; index < lines.length; index += 1) {
|
|
45
|
+
const line = lines[index];
|
|
46
|
+
if (line && !/^\s/.test(line)) break;
|
|
47
|
+
values.push(line.trim());
|
|
48
|
+
}
|
|
49
|
+
return values.join(' ').trim();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function categoryFor(name) {
|
|
53
|
+
const value = name.toLowerCase();
|
|
54
|
+
if (/(media|video|motion|3d|blender|manim|creative|remotion|comfy)/.test(value)) return 'media-eventtech';
|
|
55
|
+
if (/(design|frontend|react|vue|angular|next|tailwind|ui-|uiux|brand)/.test(value) || /(^|-)ui($|-)/.test(value)) return 'design-ui-ux';
|
|
56
|
+
if (/(agent|pipeline|orchestr|autonom|benchmark|eval|security|skill|workflow)/.test(value)) return 'bdb-core';
|
|
57
|
+
if (/(hardware|pcb|circuit|electronic)/.test(value)) return 'engineering-hardware';
|
|
58
|
+
if (/(engineering|architecture|database|api|devops|docker|kubernetes|python|pytorch|typescript|rust|golang|java|spring|django|fastapi|react|test|build|resolver|reviewer|performance)/.test(value)) return 'engineering-method';
|
|
59
|
+
return 'library';
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function compatibilityWarnings(text) {
|
|
63
|
+
const warnings = [];
|
|
64
|
+
if (/\bBash\b/.test(text)) warnings.push('mentions Bash');
|
|
65
|
+
if (/\bView\b/.test(text)) warnings.push('mentions View');
|
|
66
|
+
return warnings;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function localNames() {
|
|
70
|
+
const names = new Set();
|
|
71
|
+
for (const file of walk(join(ROOT, 'skills'))) {
|
|
72
|
+
if (basename(file) === 'SKILL.md') {
|
|
73
|
+
const text = readFileSync(file, 'utf8');
|
|
74
|
+
names.add((frontmatterValue(text, 'name') || basename(dirname(file))).trim().toLowerCase());
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
for (const root of [join(ROOT, '.claude', 'agents'), join(ROOT, '.agents', 'agents'), join(ROOT, 'agents')]) {
|
|
78
|
+
if (!existsSync(root)) continue;
|
|
79
|
+
for (const name of readdirSync(root)) names.add(name.replace(/\.md$/i, '').toLowerCase());
|
|
80
|
+
}
|
|
81
|
+
return names;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function hash(content) {
|
|
85
|
+
return createHash('sha256').update(content).digest('hex');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function itemFromFile(file, type) {
|
|
89
|
+
const content = readFileSync(file, 'utf8');
|
|
90
|
+
const name = type === 'skills'
|
|
91
|
+
? (frontmatterValue(content, 'name') || basename(dirname(file))).trim()
|
|
92
|
+
: basename(file, '.md');
|
|
93
|
+
if (!/^[A-Za-z0-9_-]+$/.test(name)) throw new Error(`Invalid store name: ${name}`);
|
|
94
|
+
const description = (frontmatterValue(content, 'description') || type).replace(/\s+/g, ' ').trim();
|
|
95
|
+
const shortDescription = description.length > 32 ? `${description.slice(0, 31)}…` : description;
|
|
96
|
+
const base = {
|
|
97
|
+
description: shortDescription,
|
|
98
|
+
category: categoryFor(name),
|
|
99
|
+
tier: 'free',
|
|
100
|
+
origin: 'ecc',
|
|
101
|
+
requires_auth: false,
|
|
102
|
+
sha256: hash(content),
|
|
103
|
+
upstream_path: relative(checkout, file).split(sep).join('/'),
|
|
104
|
+
};
|
|
105
|
+
return { name, item: base, warnings: compatibilityWarnings(content) };
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
try {
|
|
109
|
+
runGit(['init', '--quiet']);
|
|
110
|
+
runGit(['remote', 'add', 'origin', entry.upstream]);
|
|
111
|
+
runGit(['fetch', '--quiet', '--depth=1', 'origin', entry.pinned_commit]);
|
|
112
|
+
runGit(['checkout', '--quiet', '--detach', 'FETCH_HEAD']);
|
|
113
|
+
const generatedAt = runGit(['show', '-s', '--format=%cI', 'FETCH_HEAD']);
|
|
114
|
+
const coreNames = localNames();
|
|
115
|
+
const skills = {};
|
|
116
|
+
const subagents = {};
|
|
117
|
+
let compatibilityWarningCount = 0;
|
|
118
|
+
const skillFiles = walk(join(checkout, 'skills')).filter((file) => basename(file) === 'SKILL.md');
|
|
119
|
+
const agentFiles = walk(join(checkout, 'agents')).filter((file) => file.endsWith('.md'));
|
|
120
|
+
for (const file of skillFiles) {
|
|
121
|
+
const { name, item, warnings } = itemFromFile(file, 'skills');
|
|
122
|
+
compatibilityWarningCount += warnings.length;
|
|
123
|
+
if (coreNames.has(name.toLowerCase())) continue;
|
|
124
|
+
skills[name] = item;
|
|
125
|
+
}
|
|
126
|
+
for (const file of agentFiles) {
|
|
127
|
+
const { name, item, warnings } = itemFromFile(file, 'subagents');
|
|
128
|
+
compatibilityWarningCount += warnings.length;
|
|
129
|
+
if (coreNames.has(name.toLowerCase())) continue;
|
|
130
|
+
subagents[name] = item;
|
|
131
|
+
}
|
|
132
|
+
const index = {
|
|
133
|
+
version: '1.0.0',
|
|
134
|
+
generated_at: generatedAt,
|
|
135
|
+
pinned_commit: entry.pinned_commit,
|
|
136
|
+
harness_support: ['claude', 'antigravity', 'codex', 'cursor', 'roo'],
|
|
137
|
+
compatibility_warning_count: compatibilityWarningCount,
|
|
138
|
+
skills: Object.fromEntries(Object.entries(skills).sort(([a], [b]) => a.localeCompare(b))),
|
|
139
|
+
subagents: Object.fromEntries(Object.entries(subagents).sort(([a], [b]) => a.localeCompare(b))),
|
|
140
|
+
};
|
|
141
|
+
const output = JSON.stringify(index) + '\n';
|
|
142
|
+
if (Buffer.byteLength(output) >= 100 * 1024) throw new Error(`Store index exceeds 100 KB: ${Buffer.byteLength(output)} bytes`);
|
|
143
|
+
writeFileSync(OUTPUT_PATH, output);
|
|
144
|
+
console.log(`Wrote ${OUTPUT_PATH} with ${Object.keys(skills).length} skills and ${Object.keys(subagents).length} subagents (${Buffer.byteLength(output)} bytes).`);
|
|
145
|
+
} finally {
|
|
146
|
+
rmSync(checkout, { recursive: true, force: true });
|
|
147
|
+
}
|
|
@@ -118,6 +118,19 @@ the status-mark rules from the `agenttrail` skill: `[~]` before starting,
|
|
|
118
118
|
`[x]` when done, `[!]` when stuck, with `by: <agent>`, saving the plan file
|
|
119
119
|
immediately.
|
|
120
120
|
|
|
121
|
+
## Architecture diagram (Archify, native)
|
|
122
|
+
|
|
123
|
+
Architect authors `production_artifacts/00_architecture.json` (editable truth)
|
|
124
|
+
and delivers `production_artifacts/00_architecture.html` (read-only) via
|
|
125
|
+
`aos-archify validate` / `aos-archify deliver` with `--quality showcase --json`
|
|
126
|
+
at the Architect→TechLead boundary; only a passing showcase receipt (9/9
|
|
127
|
+
checks, 0 errors) sets `state.artifacts.architecture`, and a failed
|
|
128
|
+
validate/deliver preserves the previous HTML and escalates instead of
|
|
129
|
+
proceeding silently. The plan links the diagram with a
|
|
130
|
+
`url: production_artifacts/00_architecture.html` line so the live map opens it
|
|
131
|
+
from the card. Review the HTML in plan-canvas alongside the plan:
|
|
132
|
+
`aos-plan-canvas open production_artifacts/00_architecture.html`.
|
|
133
|
+
|
|
121
134
|
## Why this file is a thin router, not a spec
|
|
122
135
|
|
|
123
136
|
The full contract — state schema, node/edge table, the Stop-hook
|