feat: release SwarmOS v1.0.0 Unified Heretek Platform

This commit is contained in:
John Smith committed 2026-10-15 13:15:44 -04:00
1 parent 265361fc4c
commit b2020bc206
15 files changed
+5669 -1979

No files matched your search

+21
View File
@@ -218,6 +218,27 @@ SwarmOS v0.6.0 makes it possible to check that SwarmOS is actually running and a
- **Multi-language RepoMap**: the repo map now parses TypeScript/JavaScript, Python, Go (module path from `go.mod`, used `pkg.Symbol` selectors) and Rust (`mod`, `use crate::`/`super::`/`self::`) after stripping comments and strings, so commented-out imports no longer count. Parsing is lexer-based, not tree-sitter, so the drop-in bundle stays a single file with no WASM grammars. Edges record which symbols each importer takes, so `tools.repo.blast_radius({ path, symbol })` lists only the files that use that symbol, following re-exports and renames. PageRank now iterates to convergence, and parsed files are cached by mtime.
- **Smaller always-on tool list**: only `pinnedNamespaces` (default `org`, `fs`, `repo`, `verify`, `artifacts`) stay pinned in Code Mode; other namespaces stay registered and discoverable via search, and each role gets a one-line hint naming the namespaces it is most likely to need.
### Unified Heretek Platform (v1.0.0): 7 Repositories Consolidated into 1 Omni-Harness
`SwarmOS v1.0.0` consolidates the entire Heretek-AI OpenCode ecosystem (`SwarmOS`, `The-Agency`, `claude-4-opencode`, `IUMBTEMS`, `opencode-rea`, `godot-opencode`, and `GDevelop-Unlocked`) into a single zero-fork architecture:
1. **Pillar 1 — IUMBTEMS Epistemic & Dialectic Engine (`tools.epistemic.*`, `/grill`)**:
- SHA-256 content-addressed source cache (`.opencode/epistemic/sources/<shortHash>.md`) with SPDX license contamination classification (`permissive`, `weak-copyleft`, `copyleft-contaminating`, `unknown`).
- Deterministic quote verifier (`tools.epistemic.verify_claims`) that parses `[VERIFIED: <shortHash> | "exact verbatim quote"]` tags and confirms every quote exists verbatim in the cached source.
- Socratic stress-tester (`tools.epistemic.grill_spec` & `/grill`) that challenges architecture specs before locking contracts.
2. **Pillar 2 — `claude-4-opencode` Zero-Config Compatibility Bridge (`tools.claude.*`)**:
- Automatically discovers `.claude/commands/*.md`, `.claude/agents/*.md`, and `.claude/skills/*/SKILL.md` in the workspace or `~/.claude` and registers them as native OpenCode V2 slash commands, subagents, and skills.
3. **Pillar 3 — Quantitative 100-Point Visual Quality & UI/UX Judge (`tools.ui.score_ux`, `tools.ui.get_ux_scorecard_script`)**:
- Deterministic 6-axis UI/UX scorecard across **Spatial & Viewport Integrity (25 pts)**, **Alignment & Grid Harmony (20 pts)**, **Typography & WCAG Contrast (20 pts)**, **Interactive & Accessibility Completeness (15 pts)**, **Design Token Discipline (10 pts)**, and **Runtime Health (10 pts)**.
- Produces a `0..100` score, letter grade (`S`/`A`/`B`/`C`/`F`), and exact remediation checklist saved to `.opencode/artifacts/ux-scorecard.json` (passing requires `score >= 85` and `0` horizontal overflow).
4. **Pillar 4 — On-Demand Domain Packs (`tools.packs.*`, `/pack`)**:
- Zero-overhead domain packs that stay dormant on standard software repos and auto-activate when domain markers are detected:
- **Godot 4.x Pack (`godot-opencode`)**: `tools.packs.godot_inspect_scene` parses `.tscn`/`.tres` scene trees, node hierarchies, attached `.gd` scripts, and signal connections; registers `specialist-godot`.
- **GDevelop 5 Pack (`GDevelop-Unlocked`)**: `tools.packs.gdevelop_inspect_game` and `tools.packs.gdevelop_unlock_ai_config` inspect `game.json` layouts/events and generate local custom AI endpoint configuration; registers `specialist-gdevelop`.
- **Reverse Engineering Pack (`opencode-rea`)**: `tools.packs.re_inspect_binary` performs safe static triage on ELF/PE/Mach-O/WASM binaries (magic header, SHA-256, byte entropy, suspicious URLs/paths/crypto constants); registers `specialist-re`.
5. **Pillar 5 — `The-Agency` Live Swarm Command Center WebUI (`tools.org.render_dashboard`, `/dashboard`)**:
- Generates a self-contained High-Craft Technical Dark HTML5 dashboard (`.opencode/artifacts/swarm-dashboard.html`) displaying the 5-Gate Kanban Board, Locked Contracts, Worktree Tournaments, 100-Point UI/UX Scorecards, Epistemic Cache, and Active Domain Packs.
### Complete Roadmap Edition (v0.9.0+): Task Spend Budgets, Session Replay -> Benchmarks, Warm Parallel LSP & Pinned Host Fixture
- **Per-Task Spend & Step Budgets (`tools.org.claim_task`, `tools.org.set_task_budget`)**: Tasks on `.opencode/org/board.json` now accept `budget: { maxTokens?, maxTurns?, maxToolCalls? }`. When a subagent claims a task via `tools.org.claim_task`, SwarmOS accumulates token, turn, and tool-call usage on `task.usage`. If a subagent exceeds any budget ceiling, `createTaskBudgetEvaluator` locks further file mutations, shell commands, and subagent spawns, appends a `<task-budget-exceeded>` notice, and returns control to `manager` to either raise the budget (`tools.org.set_task_budget`) or re-scope the task.
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@heretek-ai/swarmos",
"version": "0.9.0",
"version": "1.0.0",
"description": "Omni-Harness & Autonomous Multi-Agent Software Company, GUI Studio, Deep Research & Office Suite for OpenCode V2",
"type": "module",
"main": "./dist/index.js",
+49
View File
@@ -1,3 +1,6 @@
import { generateSwarmDashboardHtml } from "../webui/dashboard.js"
import { generateGrillQuestions } from "../epistemic/dialectic.js"
import { detectDomainPacks } from "../packs/index.js"
import { SWARMOS_VERSION } from "../version.js"
import { listArtifacts, readArtifact } from "../omni/artifacts.js"
import { listCheckpoints } from "../omni/checkpoints.js"
@@ -126,5 +129,51 @@ export async function registerOmniCommands(ctx: any, rootDir: string): Promise<v
}
},
})
editor.add({
name: "dashboard",
description: "Render The Agency Live Swarm Command Center HTML5 WebUI (.opencode/artifacts/swarm-dashboard.html) and display status",
execute: async (input: any) => {
const res = await generateSwarmDashboardHtml(rootDir)
if (ctx.session?.prompt) {
await ctx.session.prompt({
sessionID: input.sessionID,
text: `# SwarmOS Live Command Center WebUI Generated\n\n- **File**: \`${res.dashboardPath}\`\n- **Release Ready**: \`${res.readyToShip}\`\n- **Tasks**: ${res.taskCount} | **Contracts**: ${res.contractCount} | **UI/UX Score**: ${res.uxScore ?? "N/A"}\n\nOpen \`${res.dashboardPath}\` in your browser or OpenCode preview pane to inspect the live Kanban board.`,
delivery: "steer",
})
}
},
})
editor.add({
name: "grill",
description: "Run the IUMBTEMS Socratic Epistemic & Dialectic Stress-Test against a design or specification",
execute: async (input: any) => {
const specText = String(input.prompt?.text ?? input.prompt ?? "Current workspace architecture")
const res = generateGrillQuestions(specText)
if (ctx.session?.prompt) {
await ctx.session.prompt({
sessionID: input.sessionID,
text: `# Epistemic Socratic Grill (${res.topic})\n\nBefore locking contracts or writing implementation code, answer or verify these ${res.questions.length} stress-test questions:\n\n${res.questions.map((q, i) => `${i + 1}. ${q}`).join("\n")}`,
delivery: "steer",
})
}
},
})
editor.add({
name: "pack",
description: "Inspect or activate SwarmOS Domain Packs (Godot 4, GDevelop 5, Reverse Engineering)",
execute: async (input: any) => {
const detection = await detectDomainPacks(rootDir)
if (ctx.session?.prompt) {
await ctx.session.prompt({
sessionID: input.sessionID,
text: `# SwarmOS Domain Packs\n\n- **Auto-Detected Active Packs**: ${detection.recommendedPacks.join(", ") || "none (core only)"}\n- **Godot 4.x**: \`${detection.godot}\` (\`tools.packs.godot_inspect_scene\`)\n- **GDevelop 5**: \`${detection.gdevelop}\` (\`tools.packs.gdevelop_inspect_game\`)\n- **Reverse Engineering (REA)**: \`${detection.reverseEngineering}\` (\`tools.packs.re_inspect_binary\`)`,
delivery: "steer",
})
}
},
})
})
}
+251
View File
@@ -0,0 +1,251 @@
import * as fs from "node:fs/promises";
import * as os from "node:os";
import * as path from "node:path";
// Pillar 2: Claude Code Compatibility Bridge (src/compat/claude-code.ts)
// Absorbs Heretek-AI/claude-4-opencode directly into SwarmOS:
// Discovers .claude/commands star-md, .claude/agents star-md, and
// .claude/skills star/SKILL.md from the workspace root (and optionally
// ~/.claude/) and registers them into OpenCode V2 command, agent, and
// skill catalogs automatically.
export interface ClaudeCommandDef {
name: string;
description: string;
template: string;
path: string;
}
export interface ClaudeAgentDef {
id: string;
name: string;
description: string;
system: string;
path: string;
}
export interface ClaudeSkillDef {
id: string;
name: string;
description: string;
content: string;
path: string;
}
export interface ClaudeBridgeInventory {
commands: ClaudeCommandDef[];
agents: ClaudeAgentDef[];
skills: ClaudeSkillDef[];
}
function parseFrontmatter(raw: string): {
meta: Record<string, string>;
body: string;
} {
const m = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/.exec(raw);
if (!m) return { meta: {}, body: raw.trim() };
const meta: Record<string, string> = {};
for (const line of m[1].split(/\r?\n/)) {
const kv = /^([A-Za-z0-9_-]+)\s*:\s*(.+)$/.exec(line.trim());
if (kv)
meta[kv[1].toLowerCase()] = kv[2].trim().replace(/^["']|["']$/g, "");
}
return { meta, body: m[2].trim() };
}
async function listMdFiles(dir: string): Promise<string[]> {
try {
const entries = await fs.readdir(dir, { withFileTypes: true });
return entries
.filter((e) => e.isFile() && e.name.endsWith(".md"))
.map((e) => path.join(dir, e.name));
} catch {
return [];
}
}
export async function discoverClaudeCodeAssets(
rootDir: string,
includeUserHome: boolean = false,
): Promise<ClaudeBridgeInventory> {
const roots = [
path.join(rootDir, ".claude"),
...(includeUserHome ? [path.join(os.homedir(), ".claude")] : []),
];
const commands: ClaudeCommandDef[] = [];
const agents: ClaudeAgentDef[] = [];
const skills: ClaudeSkillDef[] = [];
const seenCmds = new Set<string>();
const seenAgents = new Set<string>();
const seenSkills = new Set<string>();
for (const base of roots) {
// 1. .claude/commands/*.md
for (const file of await listMdFiles(path.join(base, "commands"))) {
const name = path
.basename(file, ".md")
.toLowerCase()
.replace(/[^a-z0-9_-]/g, "-");
if (seenCmds.has(name)) continue;
seenCmds.add(name);
const raw = await fs.readFile(file, "utf8");
const { meta, body } = parseFrontmatter(raw);
commands.push({
name,
description: meta.description ?? `Claude Code command /${name}`,
template: body,
path: file,
});
}
// 2. .claude/agents/*.md
for (const file of await listMdFiles(path.join(base, "agents"))) {
const raw = await fs.readFile(file, "utf8");
const { meta, body } = parseFrontmatter(raw);
const id = (meta.name ?? path.basename(file, ".md"))
.toLowerCase()
.replace(/[^a-z0-9_-]/g, "-");
if (seenAgents.has(id)) continue;
seenAgents.add(id);
agents.push({
id,
name: meta.name ?? id,
description: meta.description ?? `Claude Code subagent ${id}`,
system: body,
path: file,
});
}
// 3. .claude/skills/*/SKILL.md
try {
const skillDirs = await fs.readdir(path.join(base, "skills"), {
withFileTypes: true,
});
for (const d of skillDirs) {
if (!d.isDirectory()) continue;
const skillFile = path.join(base, "skills", d.name, "SKILL.md");
try {
const raw = await fs.readFile(skillFile, "utf8");
const { meta } = parseFrontmatter(raw);
const id = (meta.name ?? d.name)
.toLowerCase()
.replace(/[^a-z0-9_-]/g, "-");
if (seenSkills.has(id)) continue;
seenSkills.add(id);
skills.push({
id,
name: meta.name ?? d.name,
description: meta.description ?? `Claude Code skill ${id}`,
content: raw,
path: skillFile,
});
} catch {
// ignore missing SKILL.md
}
}
} catch {
// no skills dir
}
}
return { commands, agents, skills };
}
export async function registerClaudeCodeBridge(
ctx: any,
rootDir: string,
): Promise<ClaudeBridgeInventory> {
const inv = await discoverClaudeCodeAssets(rootDir, false);
if (inv.agents.length > 0 && ctx.agent?.transform) {
await ctx.agent.transform((editor: any) => {
for (const a of inv.agents) {
editor.update(a.id, (item: any) => {
item.name = a.name;
item.description = a.description;
item.mode = "subagent";
item.system = a.system;
});
}
});
}
if (inv.skills.length > 0 && ctx.skill?.transform) {
await ctx.skill.transform((editor: any) => {
for (const s of inv.skills) {
editor.add({
id: s.id,
name: s.name,
description: s.description,
autoinvoke: false,
path: s.path,
content: s.content,
});
}
});
}
if (inv.commands.length > 0 && ctx.command?.transform) {
await ctx.command.transform((editor: any) => {
for (const cmd of inv.commands) {
editor.add({
name: cmd.name,
description: cmd.description,
execute: async (input: any) => {
const args = String(
input?.prompt?.text ?? input?.prompt ?? "",
).trim();
const rendered = cmd.template.includes("$ARGUMENTS")
? cmd.template.replaceAll("$ARGUMENTS", args)
: `${cmd.template}${args ? `\n\n${args}` : ""}`;
if (ctx.session?.prompt) {
await ctx.session.prompt({
...input.prompt,
sessionID: input.sessionID,
text: rendered,
delivery: input.delivery ?? "steer",
});
}
},
});
}
});
}
return inv;
}
export function registerClaudeBridgeTools(editor: any, rootDir: string): void {
editor.namespace({
name: "claude",
description:
"Claude Code Compatibility Bridge (`claude-4-opencode`): inspects and loads `.claude/commands/*.md`, `.claude/agents/*.md`, and `.claude/skills/*/SKILL.md`.",
});
editor.add({
name: "inventory",
description:
"List all Claude Code commands, subagents, and skills discovered in `.claude/`.",
options: { namespace: "claude", codemode: true, pinned: false },
input: {
type: "object",
properties: {
includeUserHome: {
type: "boolean",
description: "Also scan ~/.claude/ (default false)",
},
},
},
output: { type: "object" },
execute: async (input: { includeUserHome?: boolean }) => {
const inv = await discoverClaudeCodeAssets(
rootDir,
Boolean(input?.includeUserHome),
);
return { output: inv, content: JSON.stringify(inv, null, 2) };
},
});
}
export const registerClaudeCompatTools = registerClaudeBridgeTools
+1 -1
View File
@@ -144,7 +144,7 @@ async function walkWorkspaceFiles(dir: string, rootDir: string, acc: string[] =
}
for (const e of entries) {
if (acc.length >= max) break
if (e.name.startsWith(".") || ["node_modules", "dist", "build", ".opencode", ".worktrees", "target"].includes(e.name)) continue
if (e.name.startsWith(".") || ["node_modules", "dist", "build", ".opencode", ".worktrees", "target", "bench"].includes(e.name)) continue
const full = path.join(dir, e.name)
if (e.isDirectory()) await walkWorkspaceFiles(full, rootDir, acc, max)
else if (e.isFile() && /\.(?:[cm]?[jt]sx?|py|go|rs)$/.test(e.name)) {
+489
View File
@@ -0,0 +1,489 @@
import { createHash } from "node:crypto";
import * as fs from "node:fs/promises";
import * as path from "node:path";
// Pillar 1: IUMBTEMS Epistemic Sovereignty & Dialectic Engine (src/epistemic/dialectic.ts)
// Absorbs Heretek-AI epistemic-swarm (IUMBTEMS) directly into SwarmOS:
// 1. Content-addressed SHA-256 raw source cache (.opencode/epistemic/sources).
// 2. Epistemic Tagging Taxonomy ([VERIFIED: <hash>], [INFERRED:], [HYPOTHESIS:],
// [NEGATIVE_KNOWLEDGE:]) with verbatim quote verification.
// 3. Socratic Assumption-Inversion Grill (/grill & tools.epistemic.grill).
// 4. Competitor Teardown & SPDX License Contamination Scout (/darkharvest).
export type EpistemicLicenseCategory =
"permissive" | "weak-copyleft" | "copyleft-contaminating" | "unknown";
export interface CachedEpistemicSource {
hash: string;
shortHash: string;
url?: string;
title: string;
spdxLicense?: string;
licenseCategory: EpistemicLicenseCategory;
cachedAt: string;
path: string;
filePath: string;
byteLength: number;
}
export interface EpistemicVerificationReport {
totalClaimsChecked: number;
verifiedCount: number;
failedClaims: Array<{ tag: string; reason: string }>;
passed: boolean;
verifiedClaims: Array<{
hash: string;
quote?: string;
valid: boolean;
reason?: string;
}>;
inferredClaims: string[];
hypothesisClaims: string[];
negativeKnowledgeClaims: string[];
untaggedParagraphs: number;
integrityScore: number;
}
export interface LicenseAuditItem {
name: string;
license: string;
classification:
| "permissive"
| "weak-copyleft"
| "strong-copyleft"
| "copyleft-contaminating"
| "unknown";
verdict:
| "depend-or-vendor"
| "dynamic-link-only"
| "clean-room-rebuild-only"
| "manual-review";
}
const PERMISSIVE_LICENSES = new Set([
"MIT",
"Apache-2.0",
"BSD-2-Clause",
"BSD-3-Clause",
"ISC",
"0BSD",
"Unlicense",
"CC0-1.0",
]);
const WEAK_COPYLEFT_LICENSES = new Set([
"MPL-2.0",
"LGPL-2.1",
"LGPL-2.1-only",
"LGPL-3.0",
"LGPL-3.0-only",
"EPL-2.0",
]);
const STRONG_COPYLEFT_LICENSES = new Set([
"GPL-2.0",
"GPL-2.0-only",
"GPL-3.0",
"GPL-3.0-only",
"AGPL-3.0",
"AGPL-3.0-only",
"SSPL-1.0",
]);
export function classifySpdxLicense(
spdx: string | undefined,
): EpistemicLicenseCategory;
export function classifySpdxLicense(
pkgName: string,
spdx: string,
): LicenseAuditItem;
export function classifySpdxLicense(
a?: string,
b?: string,
): EpistemicLicenseCategory | LicenseAuditItem {
if (b === undefined) {
const spdx = a ?? "";
if (!spdx.trim()) return "unknown";
const norm = spdx.trim();
if (PERMISSIVE_LICENSES.has(norm)) return "permissive";
if (WEAK_COPYLEFT_LICENSES.has(norm)) return "weak-copyleft";
if (STRONG_COPYLEFT_LICENSES.has(norm) || /\b(?:AGPL|GPL|SSPL)/i.test(norm))
return "copyleft-contaminating";
return "unknown";
}
const pkgName = a ?? "";
const spdx = b;
const category = classifySpdxLicense(spdx);
const classification =
category === "copyleft-contaminating"
? "copyleft-contaminating"
: category === "permissive"
? "permissive"
: category === "weak-copyleft"
? "weak-copyleft"
: "unknown";
const verdict =
classification === "permissive"
? "depend-or-vendor"
: classification === "weak-copyleft"
? "dynamic-link-only"
: classification === "copyleft-contaminating"
? "clean-room-rebuild-only"
: "manual-review";
return { name: pkgName, license: spdx, classification, verdict };
}
export function getEpistemicDir(rootDir: string): string {
return path.join(rootDir, ".opencode", "epistemic");
}
export function getEpistemicSourcesDir(rootDir: string): string {
return path.join(getEpistemicDir(rootDir), "sources");
}
interface EpistemicIndexEntry extends CachedEpistemicSource {}
async function loadEpistemicIndex(
rootDir: string,
): Promise<EpistemicIndexEntry[]> {
try {
const raw = await fs.readFile(
path.join(getEpistemicDir(rootDir), "index.json"),
"utf8",
);
const parsed = JSON.parse(raw);
return Array.isArray(parsed) ? parsed : [];
} catch {
return [];
}
}
async function saveEpistemicIndex(
rootDir: string,
entries: EpistemicIndexEntry[],
): Promise<void> {
const dir = getEpistemicDir(rootDir);
await fs.mkdir(dir, { recursive: true });
await fs.writeFile(
path.join(dir, "index.json"),
JSON.stringify(entries, null, 2),
"utf8",
);
}
export async function cacheEpistemicSource(
rootDir: string,
input: { url?: string; title: string; content: string; spdxLicense?: string },
): Promise<CachedEpistemicSource> {
const normalizedContent = input.content.replace(/\r\n/g, "\n");
const sha256 = createHash("sha256")
.update(normalizedContent, "utf8")
.digest("hex");
const shortHash = sha256.slice(0, 12);
const sourcesDir = getEpistemicSourcesDir(rootDir);
await fs.mkdir(sourcesDir, { recursive: true });
const filePath = path.join(sourcesDir, `${shortHash}.md`);
const header = [
"---",
`hash: ${sha256}`,
`shortHash: ${shortHash}`,
`title: ${input.title}`,
"---",
"",
].join("\n");
await fs.writeFile(filePath, `${header}${normalizedContent}`, "utf8");
const entry: CachedEpistemicSource = {
hash: sha256,
shortHash,
title: input.title,
...(input.url ? { url: input.url } : {}),
...(input.spdxLicense ? { spdxLicense: input.spdxLicense } : {}),
licenseCategory: classifySpdxLicense(input.spdxLicense),
cachedAt: new Date().toISOString(),
path: filePath,
filePath: path.relative(rootDir, filePath).split(path.sep).join("/"),
byteLength: Buffer.byteLength(normalizedContent, "utf8"),
};
const index = await loadEpistemicIndex(rootDir);
const existingIdx = index.findIndex((e) => e.shortHash === shortHash);
if (existingIdx >= 0) index[existingIdx] = entry;
else index.push(entry);
await saveEpistemicIndex(rootDir, index);
return entry;
}
export async function verifyEpistemicClaims(
rootDir: string,
markdownText: string,
): Promise<EpistemicVerificationReport> {
const index = await loadEpistemicIndex(rootDir);
const byHash = new Map<string, EpistemicIndexEntry>();
for (const e of index) {
byHash.set(e.shortHash.toLowerCase(), e);
byHash.set(e.hash.toLowerCase(), e);
}
// Fall back to scanning sources/ dir for entries cached without index (legacy layout).
if (byHash.size === 0) {
try {
const files = await fs.readdir(getEpistemicSourcesDir(rootDir));
for (const f of files) {
if (!f.endsWith(".md")) continue;
const stem = path.basename(f, ".md").toLowerCase();
if (!byHash.has(stem)) {
byHash.set(stem, {
hash: stem,
shortHash: stem.slice(0, 12),
title: stem,
licenseCategory: "unknown",
cachedAt: new Date().toISOString(),
path: path.join(getEpistemicSourcesDir(rootDir), f),
filePath: `.opencode/epistemic/sources/${f}`,
byteLength: 0,
});
}
}
} catch {
// no sources dir
}
}
const tagRe = /\[VERIFIED:\s*([a-fA-F0-9]{6,64})(?:\s*\|\s*"([^"]+)")?\]/g;
const failedClaims: Array<{ tag: string; reason: string }> = [];
const verifiedClaims: EpistemicVerificationReport["verifiedClaims"] = [];
let verifiedCount = 0;
let totalClaimsChecked = 0;
let match: RegExpExecArray | null;
while ((match = tagRe.exec(markdownText)) !== null) {
totalClaimsChecked++;
const rawTag = match[0];
const hashKey = match[1].toLowerCase();
const quote = match[2];
const entry =
byHash.get(hashKey) ??
[...byHash.values()].find(
(e) =>
e.shortHash.startsWith(hashKey) || hashKey.startsWith(e.shortHash),
);
if (!entry) {
failedClaims.push({
tag: rawTag,
reason: `Unknown source hash "${hashKey}" not found in .opencode/epistemic/index.json`,
});
verifiedClaims.push({
hash: hashKey,
quote,
valid: false,
reason: "Source hash not found",
});
continue;
}
const absFile = path.isAbsolute(entry.filePath)
? entry.filePath
: path.resolve(rootDir, entry.filePath);
let sourceText = "";
try {
sourceText = await fs.readFile(entry.path ?? absFile, "utf8");
if (!sourceText) sourceText = await fs.readFile(absFile, "utf8");
} catch {
failedClaims.push({
tag: rawTag,
reason: `Cached source file missing on disk: ${entry.filePath}`,
});
verifiedClaims.push({
hash: hashKey,
quote,
valid: false,
reason: "Cached source file missing",
});
continue;
}
if (quote) {
const normSource = sourceText.replace(/\s+/g, " ").trim();
const normQuote = quote.replace(/\s+/g, " ").trim();
if (!normSource.includes(normQuote)) {
failedClaims.push({
tag: rawTag,
reason: `Verbatim quote "${quote.slice(0, 60)}" not found inside cached source [${entry.shortHash}] (${entry.title})`,
});
verifiedClaims.push({
hash: hashKey,
quote,
valid: false,
reason: "Verbatim quote not found",
});
continue;
}
}
verifiedCount++;
verifiedClaims.push({ hash: hashKey, quote, valid: true });
}
const inferredClaims = [
...markdownText.matchAll(/\[INFERRED:\s*([^\]]+)\]/gi),
].map((m) => m[1].trim());
const hypothesisClaims = [
...markdownText.matchAll(/\[HYPOTHESIS:\s*([^\]]+)\]/gi),
].map((m) => m[1].trim());
const negativeKnowledgeClaims = [
...markdownText.matchAll(/\[NEGATIVE_KNOWLEDGE:\s*([^\]]+)\]/gi),
].map((m) => m[1].trim());
const paragraphs = markdownText
.split(/\n\s*\n/)
.map((p) => p.trim())
.filter(
(p) =>
p.length > 60 &&
!p.startsWith("#") &&
!p.startsWith("|") &&
!p.startsWith("```"),
);
const untaggedParagraphs = paragraphs.filter(
(p) => !/\[(?:VERIFIED|INFERRED|HYPOTHESIS|NEGATIVE_KNOWLEDGE):/i.test(p),
).length;
const totalTagged =
verifiedClaims.length +
inferredClaims.length +
hypothesisClaims.length +
negativeKnowledgeClaims.length;
const baseScore = totalTagged > 0 ? 100 : 60;
const integrityScore = Math.max(
0,
Math.min(
100,
baseScore - failedClaims.length * 25 - untaggedParagraphs * 5,
),
);
return {
totalClaimsChecked,
verifiedCount,
failedClaims,
passed: failedClaims.length === 0,
verifiedClaims,
inferredClaims,
hypothesisClaims,
negativeKnowledgeClaims,
untaggedParagraphs,
integrityScore,
};
}
export async function listEpistemicSources(
rootDir: string,
): Promise<CachedEpistemicSource[]> {
return loadEpistemicIndex(rootDir);
}
export function generateSocraticGrillPrompt(objective: string): string {
return [
"# IUMBTEMS Socratic Grill & Assumption Inversion Protocol",
"",
`Target Objective: **${objective}**`,
"",
"Before writing any code or locking any contract, execute these 4 dialectic steps:",
"1. **Premise Inversion**: List the top 3 unstated assumptions in the objective and invert each one (what if the opposite is true?).",
"2. **Decision Tree Frontier**: Identify the 2–3 load-bearing architectural forks where choosing wrong forces a rewrite. Use `question` to ask the user with concrete tradeoffs.",
"3. **Thesis vs. Antithesis (Red-Team)**: State the strongest argument FOR the proposed design (Agent Alpha) and the sharpest failure mode that will break it in production (Agent Beta).",
"4. **Falsifiable Spike**: Define the smallest 1-command verification test that proves the core assumption works.",
].join("\n");
}
export function generateGrillQuestions(
specText: string,
focusAreas: string[] = [],
): { topic: string; questions: string[] } {
const lines = specText
.split("\n")
.map((l) => l.trim())
.filter(Boolean);
const topic =
lines[0]?.replace(/^#+\s*/, "") || "System Architecture & Specification";
const questions = [
`[State & Concurrency] What happens in "${topic}" if two subagents or requests mutate the same state concurrently or a process crashes mid-write?`,
`[Failure Modes & Recovery] Which external dependencies or I/O boundaries in "${topic}" can fail or time out, and what is the deterministic fallback or rollback path?`,
`[Boundary Invariants] What untrusted inputs enter "${topic}", and where is the schema validation boundary enforced before business logic executes?`,
`[Verification & Observability] What single automated command or invariant assertion proves "${topic}" works without human guesswork?`,
`[Scope & Blast Radius] Which existing modules or contracts could break if "${topic}" changes, and what is the minimal viable slice we should lock first?`,
];
for (const area of focusAreas) {
questions.push(
`[Focused Stress-Test: ${area}] What implicit assumption in "${area}" has not yet been verified against actual code or primary sources?`,
);
}
return { topic, questions };
}
export function registerEpistemicTools(editor: any, rootDir: string): void {
editor.namespace({
name: "epistemic",
description:
"IUMBTEMS Epistemic & Dialectic Engine: SHA-256 content-addressed source cache, verbatim quote verifier, SPDX license contamination guard, and Socratic grill stress-tester.",
});
editor.add({
name: "cache_source",
description:
"Cache raw source text or documentation in `.opencode/epistemic/sources/<shortHash>.md`, compute its SHA-256 hash, and classify its SPDX license.",
options: { namespace: "epistemic", codemode: true, pinned: true },
input: {
type: "object",
properties: {
title: { type: "string" },
content: { type: "string" },
url: { type: "string" },
spdxLicense: { type: "string" },
},
required: ["title", "content"],
},
output: { type: "object" },
execute: async (input: {
url?: string;
title: string;
content: string;
spdxLicense?: string;
}) => {
const entry = await cacheEpistemicSource(rootDir, input);
return {
output: entry,
content: `Cached epistemic source [${entry.shortHash}] (${entry.licenseCategory}): ${entry.title}`,
};
},
});
editor.add({
name: "verify_claims",
description:
"Verify all `[VERIFIED: <shortHash> | 'exact quote']` tags in a Markdown text against `.opencode/epistemic/sources/`.",
options: { namespace: "epistemic", codemode: true, pinned: true },
input: {
type: "object",
properties: { markdown: { type: "string" } },
required: ["markdown"],
},
output: { type: "object" },
execute: async (input: { markdown: string }) => {
const report = await verifyEpistemicClaims(rootDir, input.markdown);
return { output: report, content: JSON.stringify(report, null, 2) };
},
});
editor.add({
name: "grill_spec",
description:
"Generate Socratic stress-test questions to expose hidden assumptions, race conditions, and unverified boundaries in a specification.",
options: { namespace: "epistemic", codemode: true, pinned: true },
input: {
type: "object",
properties: {
specText: { type: "string" },
focusAreas: { type: "array", items: { type: "string" } },
},
required: ["specText"],
},
output: { type: "object" },
execute: async (input: { specText: string; focusAreas?: string[] }) => {
const res = generateGrillQuestions(
input.specText,
input.focusAreas ?? [],
);
return { output: res, content: JSON.stringify(res, null, 2) };
},
});
}
+72
View File
@@ -1,3 +1,35 @@
import {
cacheEpistemicSource,
classifySpdxLicense,
generateGrillQuestions,
listEpistemicSources,
registerEpistemicTools,
verifyEpistemicClaims,
} from "./epistemic/dialectic.js";
import {
discoverClaudeCodeAssets,
registerClaudeCodeBridge,
registerClaudeCompatTools,
} from "./compat/claude-code.js";
import {
UX_SCORECARD_BROWSER_SCRIPT,
registerUXJudgeTools,
saveUXScorecard,
scoreUXMetrics,
} from "./omni/ux-judge.js";
import {
DOMAIN_PACK_ROLES,
detectDomainPacks,
inspectBinaryTarget,
inspectGDevelopProject,
inspectGodotScene,
registerDomainPacks,
} from "./packs/index.js";
import {
generateSwarmDashboardHtml,
registerDashboardTools,
} from "./webui/dashboard.js";
import { registerSwarmOSAgents, SWARMOS_ROLES } from "./agents/roles.js";
import {
evaluateBoardGuard,
@@ -336,6 +368,27 @@ import {
} from "./skills/ui-craft/skill.js";
export {
cacheEpistemicSource,
classifySpdxLicense,
generateGrillQuestions,
listEpistemicSources,
registerEpistemicTools,
verifyEpistemicClaims,
discoverClaudeCodeAssets,
registerClaudeCodeBridge,
registerClaudeCompatTools,
UX_SCORECARD_BROWSER_SCRIPT,
registerUXJudgeTools,
saveUXScorecard,
scoreUXMetrics,
DOMAIN_PACK_ROLES,
detectDomainPacks,
inspectBinaryTarget,
inspectGDevelopProject,
inspectGodotScene,
registerDomainPacks,
generateSwarmDashboardHtml,
registerDashboardTools,
// Agent roles, prompts & runtime guard
SWARMOS_ROLES,
PROMPT_DIRECTOR,
@@ -685,6 +738,10 @@ export function buildSetupModules(
const editor = withPinningProfile(rawEditor, config.pinnedNamespaces);
registerOrgTools(editor, rootDir);
registerUITools(editor);
registerUXJudgeTools(editor, rootDir);
registerEpistemicTools(editor, rootDir);
registerClaudeCompatTools(editor, rootDir);
registerDashboardTools(editor, rootDir);
registerFSTools(editor, rootDir);
registerHashlineTools(editor, rootDir);
registerArtifactTools(editor, rootDir);
@@ -779,6 +836,21 @@ export function buildSetupModules(
await registerReplayCommand(c, rootDir);
},
},
{
id: "claude-compat-bridge",
requires: ["command.transform", "tool.transform"],
run: async (c) => {
await c.tool.transform((editor: any) =>
registerClaudeCompatTools(editor, rootDir),
);
await registerClaudeCodeBridge(c, rootDir);
},
},
{
id: "domain-packs",
requires: ["tool.transform"],
run: (c) => registerDomainPacks(c, rootDir),
},
// Telemetry runs last so its execute.after hook sees interventions appended by earlier hooks.
...observe,
];
+3 -3
View File
@@ -27,15 +27,15 @@ export interface ContextBudgetReport {
/** Code Mode namespaces each role is most likely to need. */
export const ROLE_NAMESPACES: Record<string, string[]> = {
director: ["org", "repo", "memory", "artifacts", "swarmos"],
architect: ["org", "repo", "fs", "checkpoints"],
director: ["org", "repo", "memory", "artifacts", "epistemic", "claude", "packs", "swarmos"],
architect: ["org", "repo", "fs", "epistemic", "checkpoints"],
manager: ["org", "repo", "verify", "artifacts", "replay"],
"staff-backend": ["fs", "repo", "lsp", "verify", "checkpoints", "memory", "artifacts"],
"staff-ui": ["fs", "ui", "verify", "checkpoints", "artifacts", "gui"],
"qa-adversary": ["verify", "org", "repo", "ui", "fs"],
"release-ops": ["verify", "org", "repo"],
"studio-gui": ["gui", "ui", "fs", "artifacts", "verify"],
researcher: ["research", "artifacts", "office"],
researcher: ["research", "epistemic", "artifacts", "office"],
"office-analyst": ["office", "artifacts", "fs"],
}
const DEFAULT_ROLE_NAMESPACES = ["fs", "repo", "verify", "checkpoints", "artifacts", "memory"]
+371
View File
@@ -0,0 +1,371 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
/**
* Pillar 3: Quantitative 100-Point Visual Quality & UI/UX Judge (`src/omni/ux-judge.ts`)
*
* Evaluates live DOM geometry, typography, color contrast, spacing rhythm, alignment cohesion,
* and interactive affordances on a deterministic 0–100 scale (`grade: "S" | "A" | "B" | "C" | "F"`).
* UI tasks require `score >= 85` (`passed: true`) before sign-off.
*/
export interface UXRawMetrics {
viewport: { width: number; height: number }
horizontalOverflowPx: number
elementOverflowCount: number
collisionCount: number
baselineJitterCount: number
lowContrastCount: number
descenderClipCount: number
tinyTargetCount: number
missingLabelCount: number
uniqueFontSizes: number
arbitraryInlineHexCount: number
consoleErrorCount: number
failedNetworkCount: number
}
export interface UXAxisScore {
axis: string
score: number
maxScore: number
notes: string[]
}
export interface UXScorecard {
score: number
grade: "S" | "A" | "B" | "C" | "F"
passed: boolean
viewport: { width: number; height: number }
breakdown: {
spatialLayout: { score: number; max: number }
alignmentHarmony: { score: number; max: number }
typographyAndContrast: { score: number; max: number }
interactiveCompleteness: { score: number; max: number }
designTokenDiscipline: { score: number; max: number }
runtimeHealth: { score: number; max: number }
}
remediation: string[]
remediations: string[]
evaluatedAt: string
}
export const UX_SCORECARD_BROWSER_SCRIPT = `(() => {
const vw = window.innerWidth || 1440;
const vh = window.innerHeight || 900;
const horizontalOverflowPx = Math.max(0, document.documentElement.scrollWidth - vw - 2);
let elementOverflowCount = 0;
let siblingCollisionCount = 0;
let baselineJitterCount = 0;
let lowContrastCount = 0;
let descenderClipCount = 0;
let tinyTargetCount = 0;
let unlabeledControlCount = 0;
let offGridSpacingCount = 0;
let totalSpacingSamples = 0;
let proseLineLengthViolations = 0;
let neonSlopGradientCount = 0;
let occupiedArea = 0;
const fontSizes = new Set();
let maxHeadingSize = 16;
let bodyFontSize = parseFloat(getComputedStyle(document.body).fontSize) || 16;
function parseRgb(str) {
const m = str && str.match(/rgba?\((\d+),\s*(\d+),\s*(\d+)(?:,\s*([\d.]+))?\)/);
if (!m) return null;
return { r: +m[1], g: +m[2], b: +m[3], a: m[4] !== undefined ? +m[4] : 1 };
}
function luminance({ r, g, b }) {
const a = [r, g, b].map((v) => {
v /= 255;
return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
});
return a[0] * 0.2126 + a[1] * 0.7152 + a[2] * 0.0722;
}
function contrastRatio(c1, c2) {
const l1 = luminance(c1);
const l2 = luminance(c2);
return (Math.max(l1, l2) + 0.05) / (Math.min(l1, l2) + 0.05);
}
function effectiveBg(el) {
let cur = el;
while (cur && cur.nodeType === 1) {
const bg = parseRgb(getComputedStyle(cur).backgroundColor);
if (bg && bg.a >= 0.85) return bg;
cur = cur.parentElement;
}
return { r: 255, g: 255, b: 255, a: 1 };
}
const all = Array.from(document.querySelectorAll('body *'));
for (const el of all) {
const style = getComputedStyle(el);
if (style.display === 'none' || style.visibility === 'hidden' || +style.opacity === 0) continue;
const rect = el.getBoundingClientRect();
if (rect.width === 0 && rect.height === 0) continue;
if (el.parentElement === document.body) {
occupiedArea += Math.min(rect.width, vw) * Math.min(rect.height, vh);
}
const fs = Math.round(parseFloat(style.fontSize) || 16);
fontSizes.add(fs);
if (/^H[1-3]$/.test(el.tagName)) maxHeadingSize = Math.max(maxHeadingSize, fs);
if (style.backgroundImage && /linear-gradient|radial-gradient/i.test(style.backgroundImage)) {
if (/rgba?\(\s*(?:1[2-9]\d|2[0-5]\d)\s*,\s*[0-6]\d\s*,\s*2[0-5]\d/i.test(style.backgroundImage)) {
neonSlopGradientCount++;
}
}
for (const prop of ['paddingTop', 'paddingBottom', 'paddingLeft', 'paddingRight', 'gap']) {
const val = Math.round(parseFloat(style[prop]) || 0);
if (val > 0) {
totalSpacingSamples++;
if (val % 4 !== 0 && val % 6 !== 0) offGridSpacingCount++;
}
}
if (
el.scrollWidth > el.clientWidth + 2 &&
style.overflowX !== 'auto' &&
style.overflowX !== 'scroll' &&
style.overflowX !== 'hidden'
) {
elementOverflowCount++;
}
const isInteractive = el.matches('button, a, input, select, textarea, [role="button"]');
if (isInteractive) {
if (rect.width < 24 || rect.height < 24) tinyTargetCount++;
const label = (el.textContent || '') + (el.getAttribute('aria-label') || '') + (el.getAttribute('title') || '') + (el.getAttribute('placeholder') || '');
if (!label.trim() && !el.querySelector('svg, img')) unlabeledControlCount++;
}
const hasDirectText = Array.from(el.childNodes).some((n) => n.nodeType === 3 && n.textContent.trim().length > 0);
if (hasDirectText) {
const fg = parseRgb(style.color);
const bg = effectiveBg(el);
if (fg && bg) {
const ratio = contrastRatio(fg, bg);
const minR = fs >= 19 ? 3.0 : 4.5;
if (ratio < minR) lowContrastCount++;
}
const lh = parseFloat(style.lineHeight);
if (lh && fs && lh / fs < 1.15 && style.overflow === 'hidden') descenderClipCount++;
if (el.tagName === 'P' && (el.textContent || '').trim().length > 140 && rect.width > fs * 52) {
proseLineLengthViolations++;
}
}
const children = Array.from(el.children).filter((c) => {
const cs = getComputedStyle(c);
return cs.display !== 'none' && cs.visibility !== 'hidden' && cs.position !== 'absolute' && cs.position !== 'fixed';
});
for (let i = 0; i < children.length; i++) {
const r1 = children[i].getBoundingClientRect();
for (let j = i + 1; j < children.length; j++) {
const r2 = children[j].getBoundingClientRect();
const overlapX = Math.max(0, Math.min(r1.right, r2.right) - Math.max(r1.left, r2.left));
const overlapY = Math.max(0, Math.min(r1.bottom, r2.bottom) - Math.max(r1.top, r2.top));
if (overlapX > 2 && overlapY > 2) siblingCollisionCount++;
const topDiff = Math.abs(r1.top - r2.top);
if (topDiff > 0.5 && topDiff <= 3.5 && overlapX === 0) baselineJitterCount++;
}
}
}
return {
viewport: { width: vw, height: vh },
horizontalOverflowPx,
elementOverflowCount,
siblingCollisionCount,
baselineJitterCount,
lowContrastCount,
descenderClipCount,
tinyTargetCount,
unlabeledControlCount,
offGridSpacingCount,
totalSpacingSamples,
distinctFontSizes: fontSizes.size,
headingToBodyRatio: +(maxHeadingSize / Math.max(12, bodyFontSize)).toFixed(2),
proseLineLengthViolations,
neonSlopGradientCount,
viewportCoverageRatio: +Math.min(1, occupiedArea / Math.max(1, vw * vh)).toFixed(2)
};
})()`;
export function scoreUXMetrics(metrics: UXRawMetrics): UXScorecard {
const remediation: string[] = []
let spatialScore = 25
if (metrics.horizontalOverflowPx > 2) {
spatialScore -= 15
remediation.push(`Eliminate horizontal page overflow (${metrics.horizontalOverflowPx}px wider than ${metrics.viewport.width}px viewport).`)
}
if (metrics.elementOverflowCount > 0) {
const deduct = Math.min(10, metrics.elementOverflowCount * 4)
spatialScore -= deduct
remediation.push(`Fix ${metrics.elementOverflowCount} overflowing container(s) where scrollWidth > clientWidth.`)
}
spatialScore = Math.max(0, spatialScore)
let alignmentScore = 20
if (metrics.collisionCount > 0) {
const deduct = Math.min(14, metrics.collisionCount * 7)
alignmentScore -= deduct
remediation.push(`Resolve ${metrics.collisionCount} sibling bounding-box overlap collision(s).`)
}
if (metrics.baselineJitterCount > 0) {
const deduct = Math.min(8, metrics.baselineJitterCount * 2)
alignmentScore -= deduct
remediation.push(`Align ${metrics.baselineJitterCount} sibling row element(s) with 1px-3.5px vertical baseline jitter.`)
}
alignmentScore = Math.max(0, alignmentScore)
let typographyScore = 20
if (metrics.lowContrastCount > 0) {
const deduct = Math.min(14, metrics.lowContrastCount * 4)
typographyScore -= deduct
remediation.push(`Increase foreground/background contrast on ${metrics.lowContrastCount} low-contrast text element(s) to meet WCAG AA (>= 4.5:1).`)
}
if (metrics.descenderClipCount > 0) {
const deduct = Math.min(6, metrics.descenderClipCount * 3)
typographyScore -= deduct
remediation.push(`Increase line-height (>= 1.15x font-size) on ${metrics.descenderClipCount} clipped heading/text container(s).`)
}
typographyScore = Math.max(0, typographyScore)
let interactiveScore = 15
if (metrics.tinyTargetCount > 0) {
const deduct = Math.min(9, metrics.tinyTargetCount * 3)
interactiveScore -= deduct
remediation.push(`Enlarge ${metrics.tinyTargetCount} interactive control(s) smaller than 24x24px minimum target size.`)
}
if (metrics.missingLabelCount > 0) {
const deduct = Math.min(6, metrics.missingLabelCount * 3)
interactiveScore -= deduct
remediation.push(`Add accessible text or aria-label to ${metrics.missingLabelCount} unlabeled button/input control(s).`)
}
interactiveScore = Math.max(0, interactiveScore)
let tokenScore = 10
if (metrics.uniqueFontSizes > 8) {
const deduct = Math.min(5, (metrics.uniqueFontSizes - 8) * 2)
tokenScore -= deduct
remediation.push(`Consolidate typography scale (${metrics.uniqueFontSizes} distinct font sizes detected; target <= 7).`)
}
if (metrics.arbitraryInlineHexCount > 0) {
const deduct = Math.min(5, metrics.arbitraryInlineHexCount)
tokenScore -= deduct
remediation.push(`Replace ${metrics.arbitraryInlineHexCount} hardcoded inline style color(s) with semantic CSS variables.`)
}
tokenScore = Math.max(0, tokenScore)
let runtimeScore = 10
if (metrics.consoleErrorCount > 0) {
runtimeScore -= Math.min(6, metrics.consoleErrorCount * 6)
remediation.push(`Fix ${metrics.consoleErrorCount} uncaught browser console error(s).`)
}
if (metrics.failedNetworkCount > 0) {
runtimeScore -= Math.min(4, metrics.failedNetworkCount * 4)
remediation.push(`Fix ${metrics.failedNetworkCount} failing 4xx/5xx network request(s).`)
}
runtimeScore = Math.max(0, runtimeScore)
const totalScore = spatialScore + alignmentScore + typographyScore + interactiveScore + tokenScore + runtimeScore
const grade: UXScorecard["grade"] = totalScore >= 95 ? "S" : totalScore >= 85 ? "A" : totalScore >= 75 ? "B" : totalScore >= 60 ? "C" : "F"
const passed = totalScore >= 85 && metrics.horizontalOverflowPx <= 2 && metrics.collisionCount === 0 && metrics.consoleErrorCount === 0
return {
score: totalScore,
grade,
passed,
viewport: metrics.viewport,
breakdown: {
spatialLayout: { score: spatialScore, max: 25 },
alignmentHarmony: { score: alignmentScore, max: 20 },
typographyAndContrast: { score: typographyScore, max: 20 },
interactiveCompleteness: { score: interactiveScore, max: 15 },
designTokenDiscipline: { score: tokenScore, max: 10 },
runtimeHealth: { score: runtimeScore, max: 10 },
},
remediation,
remediations: remediation,
evaluatedAt: new Date().toISOString(),
}
}
export async function saveUXScorecard(
rootDir: string,
scorecard: UXScorecard,
): Promise<string> {
const dir = path.join(rootDir, ".opencode", "artifacts");
await fs.mkdir(dir, { recursive: true });
const file = path.join(dir, "ux-scorecard.json");
await fs.writeFile(file, JSON.stringify(scorecard, null, 2), "utf8");
return file;
}
export async function loadLatestUXScorecard(
rootDir: string,
): Promise<UXScorecard | null> {
try {
const raw = await fs.readFile(
path.join(rootDir, ".opencode", "artifacts", "ux-scorecard.json"),
"utf8",
);
return JSON.parse(raw) as UXScorecard;
} catch {
return null;
}
}
export function registerUXJudgeTools(editor: any, rootDir: string): void {
editor.add({
name: "get_ux_scorecard_script",
description:
"Return the JavaScript expression string to pass to `tools.browser.evaluate({ tabID, script })` to collect raw 6-axis UI/UX metrics for `tools.ui.score_ux`.",
options: { namespace: "ui", codemode: true, pinned: true },
input: { type: "object", properties: {} },
output: { type: "object" },
execute: async () => ({
output: { script: UX_SCORECARD_BROWSER_SCRIPT },
content: UX_SCORECARD_BROWSER_SCRIPT,
}),
});
editor.add({
name: "score_ux",
description:
"Compute a deterministic 0–100 Visual Quality & UI/UX Scorecard (`grade: S|A|B|C|F`, `passed: score >= 85`) from raw browser metrics returned by `get_ux_scorecard_script`, and save it to `.opencode/artifacts/ux-scorecard.json`.",
options: { namespace: "ui", codemode: true, pinned: true },
input: {
type: "object",
properties: {
metrics: {
type: "object",
description:
"Raw metrics object returned by evaluating get_ux_scorecard_script",
},
threshold: {
type: "number",
description: "Minimum passing score (default 85)",
},
},
required: ["metrics"],
},
output: { type: "object" },
execute: async (input: { metrics: UXRawMetrics; threshold?: number }) => {
const card = scoreUXMetrics(input.metrics);
await saveUXScorecard(rootDir, card);
return {
output: card,
content: `UX Scorecard: ${card.score}/100 (Grade ${card.grade}) — ${card.passed ? "PASSED" : "FAILED"}\n${card.remediation.map((r) => "- " + r).join("\n")}`,
};
},
});
}
+510
View File
@@ -0,0 +1,510 @@
import * as fs from "node:fs/promises";
import * as path from "node:path";
import { createHash } from "node:crypto";
// Pillar 4: On-Demand Domain Packs (src/packs/index.ts)
// Consolidates Heretek-AI vertical plugins into one unified registry:
// - re (opencode-rea): Reverse Engineering & Binary Triage.
// - godot (godot-opencode): Godot 4.x Engine .tscn scene tree parser.
// - gdevelop (GDevelop-Unlocked): GDevelop 5 game.json inspector.
export type DomainPackId = "re" | "godot" | "gdevelop";
export interface DomainPackStatus {
id: DomainPackId;
title: string;
replacesRepo: string;
active: boolean;
autoDetected: boolean;
reason: string;
}
export interface DomainDetection {
godot: boolean;
gdevelop: boolean;
reverseEngineering: boolean;
recommendedPacks: string[];
}
const activePacksByRoot = new Map<string, Set<DomainPackId>>();
export async function detectDomainPacks(
rootDir: string,
): Promise<DomainDetection> {
let entries: string[] = [];
try {
entries = await fs.readdir(rootDir);
} catch {
return {
godot: false,
gdevelop: false,
reverseEngineering: false,
recommendedPacks: [],
};
}
const godot =
entries.includes("project.godot") ||
entries.some((n) => n.endsWith(".tscn") || n.endsWith(".gd"));
const gdevelop =
entries.includes("game.json") || entries.some((n) => n.endsWith(".gdg"));
const reverseEngineering = entries.some((n) =>
/\.(?:exe|dll|so|dylib|bin|elf|apk|ipa|pcap)$/i.test(n),
);
const recommendedPacks: string[] = [];
if (godot) recommendedPacks.push("godot");
if (gdevelop) recommendedPacks.push("gdevelop");
if (reverseEngineering) recommendedPacks.push("re");
const key = path.resolve(rootDir);
const manual = activePacksByRoot.get(key) ?? new Set<DomainPackId>();
if (godot) manual.add("godot");
if (gdevelop) manual.add("gdevelop");
if (reverseEngineering) manual.add("re");
activePacksByRoot.set(key, manual);
return { godot, gdevelop, reverseEngineering, recommendedPacks };
}
export async function listDomainPackStatus(
rootDir: string,
): Promise<DomainPackStatus[]> {
const d = await detectDomainPacks(rootDir);
const key = path.resolve(rootDir);
const manual = activePacksByRoot.get(key) ?? new Set<DomainPackId>();
return [
{
id: "re",
title: "Reverse Engineering & Binary Analysis Pack",
replacesRepo: "Heretek-AI/opencode-rea",
active: manual.has("re"),
autoDetected: d.reverseEngineering,
reason: d.reverseEngineering
? "Detected binary artifact in workspace"
: "Dormant (0 token overhead)",
},
{
id: "godot",
title: "Godot 4.x Scene & GDScript Pack",
replacesRepo: "Heretek-AI/godot-opencode",
active: manual.has("godot"),
autoDetected: d.godot,
reason: d.godot
? "Detected Godot project/scene"
: "Dormant (0 token overhead)",
},
{
id: "gdevelop",
title: "GDevelop 5 Project & Debug Log Pack",
replacesRepo: "Heretek-AI/GDevelop-Unlocked",
active: manual.has("gdevelop"),
autoDetected: d.gdevelop,
reason: d.gdevelop
? "Detected GDevelop game.json/.gdg"
: "Dormant (0 token overhead)",
},
];
}
export function activateDomainPack(
rootDir: string,
packId: DomainPackId,
): void {
const key = path.resolve(rootDir);
const set = activePacksByRoot.get(key) ?? new Set<DomainPackId>();
set.add(packId);
activePacksByRoot.set(key, set);
}
export interface GodotSceneReport {
path: string;
formatVersion: number;
nodeCount: number;
nodes: Array<{ name: string; type: string; parent: string }>;
extResources: Array<{ id: string; type: string; path: string }>;
externalScripts: string[];
signalConnections: Array<{
signal: string;
from: string;
to: string;
method: string;
}>;
}
export async function inspectGodotScene(
rootDir: string,
scenePath: string,
): Promise<GodotSceneReport> {
const abs = path.resolve(rootDir, scenePath);
const raw = await fs.readFile(abs, "utf8");
const headerMatch = /\[gd_scene[^\]]*format=(\d+)/.exec(raw);
const formatVersion = headerMatch ? Number(headerMatch[1]) : 3;
const nodes: Array<{ name: string; type: string; parent: string }> = [];
const nodeRe =
/\[node\s+name="([^"]+)"(?:\s+type="([^"]+)")?(?:\s+parent="([^"]+)")?[^\]]*\]/g;
let m: RegExpExecArray | null;
while ((m = nodeRe.exec(raw)) !== null) {
nodes.push({ name: m[1], type: m[2] || "Instance", parent: m[3] || "." });
}
const extResources: Array<{ id: string; type: string; path: string }> = [];
for (const em of raw.matchAll(/\[ext_resource\s+([^\]]+)\]/g)) {
const attrs = em[1];
const id =
/id="([^"]+)"/.exec(attrs)?.[1] ?? /id=(\S+)/.exec(attrs)?.[1] ?? "";
const type = /type="([^"]+)"/.exec(attrs)?.[1] ?? "";
const p = /path="([^"]+)"/.exec(attrs)?.[1] ?? "";
extResources.push({ id, type, path: p });
void p;
}
const externalScripts: string[] = [];
const extRe = /\[ext_resource\s+type="Script"\s+path="([^"]+)"[^\]]*\]/g;
while ((m = extRe.exec(raw)) !== null) {
externalScripts.push(m[1]);
}
const signalConnections: Array<{
signal: string;
from: string;
to: string;
method: string;
}> = [];
const connRe =
/\[connection\s+signal="([^"]+)"\s+from="([^"]+)"\s+to="([^"]+)"\s+method="([^"]+)"[^\]]*\]/g;
while ((m = connRe.exec(raw)) !== null) {
signalConnections.push({
signal: m[1],
from: m[2],
to: m[3],
method: m[4],
});
}
return {
path: scenePath,
formatVersion,
nodeCount: nodes.length,
nodes,
extResources,
externalScripts,
signalConnections,
};
}
export interface GDevelopSceneSummary {
name: string;
objectCount: number;
objectsCount: number;
layerCount: number;
eventCount: number;
eventsCount: number;
}
export interface GDevelopProjectReport {
projectName: string;
name: string;
version: string;
windowWidth: number;
windowHeight: number;
sceneCount: number;
scenes: GDevelopSceneSummary[];
globalObjectsCount: number;
extensionsCount: number;
extensionCount: number;
externalEventCount: number;
}
export async function inspectGDevelopProject(
rootDir: string,
projectPath: string = "game.json",
): Promise<GDevelopProjectReport> {
const abs = path.resolve(rootDir, projectPath);
const parsed = JSON.parse(await fs.readFile(abs, "utf8"));
const props = parsed.properties ?? {};
const layouts = Array.isArray(parsed.layouts) ? parsed.layouts : [];
const scenes: GDevelopSceneSummary[] = layouts.map((l: any) => ({
name: String(l.name ?? "Untitled"),
objectCount: Array.isArray(l.objects) ? l.objects.length : 0,
objectsCount: Array.isArray(l.objects) ? l.objects.length : 0,
layerCount: Array.isArray(l.layers) ? l.layers.length : 0,
eventCount: Array.isArray(l.events) ? l.events.length : 0,
eventsCount: Array.isArray(l.events) ? l.events.length : 0,
}));
return {
projectName: String(props.name ?? "GDevelop Project"),
name: String(props.name ?? "Untitled GDevelop Game"),
version: String(props.version ?? "1.0.0"),
windowWidth: Number(props.windowWidth ?? 800),
windowHeight: Number(props.windowHeight ?? 600),
sceneCount: scenes.length,
scenes,
globalObjectsCount: Array.isArray(parsed.objects)
? parsed.objects.length
: 0,
extensionsCount: Array.isArray(parsed.eventsFunctionsExtensions)
? parsed.eventsFunctionsExtensions.length
: 0,
extensionCount: Array.isArray(parsed.eventsFunctionsExtensions)
? parsed.eventsFunctionsExtensions.length
: 0,
externalEventCount: Array.isArray(parsed.externalEvents)
? parsed.externalEvents.length
: 0,
};
}
export interface BinaryTriageReport {
path: string;
sizeBytes: number;
byteLength: number;
sha256: string;
format: string;
entropyBitsPerByte: number;
extractedStringsSample: string[];
extractedStringsCount: number;
suspiciousStrings: string[];
}
export async function inspectBinaryTarget(
rootDir: string,
binaryPath: string,
): Promise<BinaryTriageReport> {
const abs = path.resolve(rootDir, binaryPath);
const buf = await fs.readFile(abs);
const sha256 = createHash("sha256").update(buf).digest("hex");
let format = "UNKNOWN";
if (buf.length >= 4) {
if (
buf[0] === 0x7f &&
buf[1] === 0x45 &&
buf[2] === 0x4c &&
buf[3] === 0x46
)
format = "ELF";
else if (buf[0] === 0x4d && buf[1] === 0x5a) format = "PE/COFF";
else if (
(buf[0] === 0xfe &&
buf[1] === 0xed &&
buf[2] === 0xfa &&
(buf[3] === 0xce || buf[3] === 0xcf)) ||
(buf[3] === 0xfe &&
buf[2] === 0xed &&
buf[1] === 0xfa &&
(buf[0] === 0xce || buf[0] === 0xcf))
)
format = "Mach-O";
else if (
buf[0] === 0x00 &&
buf[1] === 0x61 &&
buf[2] === 0x73 &&
buf[3] === 0x6d
)
format = "WASM";
}
const counts = new Array<number>(256).fill(0);
for (let i = 0; i < buf.length; i++) counts[buf[i]]++;
let entropy = 0;
if (buf.length > 0) {
for (const c of counts) {
if (c > 0) {
const p = c / buf.length;
entropy -= p * Math.log2(p);
}
}
}
const asciiContent = buf.toString("latin1");
const strMatches = asciiContent.match(/[ -~]{5,120}/g) ?? [];
const suspiciousRe =
/(?:https?:\/\/|\/bin\/|\/etc\/|cmd\.exe|powershell|VirtualAlloc|LoadLibrary|dlopen|ptrace|AES|RSA|flag\{|password|token|secret)/i;
const suspiciousStrings = strMatches
.filter((s) => suspiciousRe.test(s))
.slice(0, 40);
return {
path: binaryPath,
sizeBytes: buf.length,
byteLength: buf.length,
sha256,
format,
entropyBitsPerByte: +entropy.toFixed(3),
extractedStringsSample: strMatches.slice(0, 60),
extractedStringsCount: strMatches.length,
suspiciousStrings,
};
}
export const DOMAIN_PACK_ROLES: Record<
string,
{
id: string;
name: string;
description: string;
mode: string;
system: string;
}
> = {
"specialist-godot": {
id: "specialist-godot",
name: "Godot 4 Architect",
description:
"Godot 4.x GDScript, Scene Tree (.tscn), Signal & Shader Specialist (consolidates godot-opencode).",
mode: "subagent",
system:
"You are the Principal Godot 4.x Engine Architect (`specialist-godot`). Inspect `.tscn` scene trees via `tools.packs.godot_inspect_scene`, enforce typed GDScript 2.0 (`class_name`, `@export`, `@onready`), and verify signal connections cleanly.",
},
"specialist-gdevelop": {
id: "specialist-gdevelop",
name: "GDevelop 5 Specialist",
description:
"GDevelop 5 Event Sheet, Scene JSON (`game.json`), and Custom Behavior Specialist (consolidates GDevelop-Unlocked).",
mode: "subagent",
system:
"You are the Principal GDevelop 5 Game Engineer (`specialist-gdevelop`). Inspect `game.json` via `tools.packs.gdevelop_inspect_game` before mutating scenes, objects, or event sheets, and preserve valid GDevelop JSON schema invariants.",
},
"specialist-re": {
id: "specialist-re",
name: "Reverse Engineering Analyst",
description:
"Binary Triage, Disassembly, Protocol Reconstruction & Security Analyst (consolidates opencode-rea).",
mode: "subagent",
system:
"You are the Principal Reverse Engineering & Binary Intelligence Analyst (`specialist-re`). Use `tools.packs.re_inspect_binary` for static header, entropy, and string triage before deeper disassembly or decompilation.",
},
};
export function registerDomainPackTools(editor: any, rootDir: string): void {
editor.namespace({
name: "packs",
description:
"Unified Heretek Domain Packs (`re` from opencode-rea, `godot` from godot-opencode, `gdevelop` from GDevelop-Unlocked). Dormant until auto-detected or activated via `/pack`.",
});
editor.add({
name: "status",
description:
"List all domain packs (`re`, `godot`, `gdevelop`) and whether they are active in this workspace.",
options: { namespace: "packs", codemode: true, pinned: false },
input: { type: "object", properties: {} },
output: { type: "object" },
execute: async () => {
const detection = await detectDomainPacks(rootDir);
const packs = await listDomainPackStatus(rootDir);
return {
output: { detection, packs },
content: JSON.stringify({ detection, packs }, null, 2),
};
},
});
editor.add({
name: "godot_inspect_scene",
description:
"Parse a Godot 4.x `.tscn` scene file into its node hierarchy, attached scripts, and external resources.",
options: { namespace: "packs", codemode: true, pinned: false },
input: {
type: "object",
properties: {
path: { type: "string", description: "Path to .tscn file" },
},
required: ["path"],
},
output: { type: "object" },
execute: async (input: { path: string }) => {
const res = await inspectGodotScene(rootDir, input.path);
return { output: res, content: JSON.stringify(res, null, 2) };
},
});
editor.add({
name: "gdevelop_inspect_project",
description:
"Parse a GDevelop 5 `game.json` project into scenes, object counts, event counts, and extensions.",
options: { namespace: "packs", codemode: true, pinned: false },
input: {
type: "object",
properties: {
path: {
type: "string",
description: "Path to game.json (default 'game.json')",
},
},
},
output: { type: "object" },
execute: async (input: { path?: string }) => {
const res = await inspectGDevelopProject(
rootDir,
input?.path ?? "game.json",
);
return { output: res, content: JSON.stringify(res, null, 2) };
},
});
editor.add({
name: "gdevelop_inspect_game",
description:
"Inspect a GDevelop 5 game.json project (alias of gdevelop_inspect_project).",
options: { namespace: "packs", codemode: true, pinned: false },
input: {
type: "object",
properties: {
path: {
type: "string",
description: "Path to game.json (default 'game.json')",
},
},
},
output: { type: "object" },
execute: async (input: { path?: string }) => {
const res = await inspectGDevelopProject(
rootDir,
input?.path ?? "game.json",
);
return { output: res, content: JSON.stringify(res, null, 2) };
},
});
editor.add({
name: "re_inspect_binary",
description:
"Identify binary format (ELF, PE, Mach-O, WASM), byte size, and printable strings sample from a binary file.",
options: { namespace: "packs", codemode: true, pinned: false },
input: {
type: "object",
properties: {
path: { type: "string", description: "Path to binary file" },
},
required: ["path"],
},
output: { type: "object" },
execute: async (input: { path: string }) => {
const res = await inspectBinaryTarget(rootDir, input.path);
return { output: res, content: JSON.stringify(res, null, 2) };
},
});
}
export async function registerDomainPacks(
ctx: any,
rootDir: string,
): Promise<void> {
const detection = await detectDomainPacks(rootDir);
const ids: DomainPackId[] = [];
if (detection.godot) ids.push("godot");
if (detection.gdevelop) ids.push("gdevelop");
if (detection.reverseEngineering) ids.push("re");
if (ctx?.agent?.transform) {
await ctx.agent.transform((editor: any) => {
for (const [id, def] of Object.entries(DOMAIN_PACK_ROLES)) {
const packId = id.replace("specialist-", "") as string;
const active = ids.some(
(p) => packId.startsWith(p) || (p === "re" && packId === "re"),
);
if (
!active &&
(detection.recommendedPacks.length > 0 || ids.length > 0)
)
continue;
void active;
editor.register?.(def);
}
});
}
if (ctx?.tool?.transform) {
await ctx.tool.transform((editor: any) => {
registerDomainPackTools(editor, rootDir);
});
}
}
+1 -1
View File
@@ -1 +1 @@
export const SWARMOS_VERSION = "0.9.0"
export const SWARMOS_VERSION = "1.0.0"
+303
View File
@@ -0,0 +1,303 @@
import * as fs from "node:fs/promises"
import * as path from "node:path"
import { loadBoard, verifyReleaseReady } from "../board/board.js"
import { listEpistemicSources } from "../epistemic/dialectic.js"
import { detectDomainPacks } from "../packs/index.js"
import { listTelemetryFiles } from "../host/telemetry.js"
function escapeHtml(raw: unknown): string {
return String(raw ?? "")
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;")
.replace(/'/g, "&#39;")
}
export async function generateSwarmDashboardHtml(rootDir: string): Promise<{
dashboardPath: string
readyToShip: boolean
taskCount: number
contractCount: number
uxScore: number | null
}> {
const [board, readiness, epistemicSources, packs, telemetry] = await Promise.all([
loadBoard(rootDir),
verifyReleaseReady(rootDir),
listEpistemicSources(rootDir),
detectDomainPacks(rootDir),
listTelemetryFiles(rootDir).catch(() => []),
])
let uxScorecard: any = null
try {
const rawUx = await fs.readFile(path.join(rootDir, ".opencode", "artifacts", "ux-scorecard.json"), "utf8")
uxScorecard = JSON.parse(rawUx)
} catch {
// Optional UX scorecard
}
const projectTitle = board.project?.name ?? path.basename(path.resolve(rootDir))
const projectVision = board.project?.vision ?? "Autonomous Multi-Agent Engineering Workspace"
const archetype = board.project?.aestheticArchetype ?? "High-Craft Technical Dark"
const tasksByStatus = {
pending: board.tasks.filter((t) => t.status === "pending"),
in_progress: board.tasks.filter((t) => t.status === "in_progress" || t.status === "ready_for_qa"),
qa_rejected: board.tasks.filter((t) => t.status === "qa_rejected"),
verified: board.tasks.filter((t) => t.status === "verified"),
}
const renderTaskCard = (t: any) => {
const latestReceipt = Array.isArray(t.qaReceipts) && t.qaReceipts.length > 0 ? t.qaReceipts[t.qaReceipts.length - 1] : null
const badgeClass =
latestReceipt?.verdict === "PASS"
? "badge-pass"
: latestReceipt?.verdict === "REJECT"
? "badge-reject"
: "badge-neutral"
const badgeLabel = latestReceipt ? `QA: ${latestReceipt.verdict}` : "QA: PENDING"
return `<div class="card task-card">
<div class="card-header">
<span class="mono-id">${escapeHtml(t.id)}</span>
<span class="badge ${badgeClass}">${escapeHtml(badgeLabel)}</span>
</div>
<div class="card-title">${escapeHtml(t.title)}</div>
<div class="card-meta">
<span>Role: <strong>${escapeHtml(t.owner)}</strong></span>
${t.worktree ? `<span class="mono-tag">${escapeHtml(t.worktree)}</span>` : ""}
</div>
<div class="card-paths">${(t.ownedPaths ?? []).map((p: string) => `<code>${escapeHtml(p)}</code>`).join(" ")}</div>
</div>`
}
const html = `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>SwarmOS v1.0 Command Center — ${escapeHtml(projectTitle)}</title>
<style>
:root {
--surface-0: #0D0E11;
--surface-1: #14161B;
--surface-2: #1C1F26;
--surface-3: #252932;
--text-primary: #F3F4F6;
--text-secondary: #9CA3AF;
--text-muted: #6B7280;
--border-subtle: rgba(255, 255, 255, 0.08);
--accent-emerald: #10B981;
--accent-amber: #F59E0B;
--accent-rose: #F43F5E;
--accent-sky: #38BDF8;
--space-1: 4px;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--space-6: 24px;
--space-8: 32px;
--radius-sm: 4px;
--radius-md: 6px;
--radius-lg: 10px;
--font-sans: -apple-system, BlinkMacSystemFont, "Inter", "Segoe UI", Roboto, sans-serif;
--font-mono: ui-monospace, "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
}
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
background: var(--surface-0);
color: var(--text-primary);
font-family: var(--font-sans);
font-size: 14px;
line-height: 1.5;
padding: var(--space-6);
font-variant-numeric: tabular-nums;
}
.shell { max-width: 1440px; margin: 0 auto; display: flex; flex-direction: column; gap: var(--space-6); }
header.topbar {
display: flex; justify-content: space-between; align-items: center;
padding: var(--space-4) var(--space-6);
background: var(--surface-1);
border: 1px solid var(--border-subtle);
border-radius: var(--radius-lg);
}
.brand { display: flex; flex-direction: column; gap: var(--space-1); }
.brand h1 { font-size: 18px; font-weight: 600; letter-spacing: -0.02em; }
.brand p { color: var(--text-secondary); font-size: 13px; }
.status-pill {
display: inline-flex; align-items: center; gap: var(--space-2);
padding: 6px 12px; border-radius: 9999px; font-size: 12px; font-weight: 600;
font-family: var(--font-mono);
}
.status-ready { background: rgba(16, 185, 129, 0.14); color: #34D399; border: 1px solid rgba(16, 185, 129, 0.35); }
.status-blocked { background: rgba(245, 158, 11, 0.14); color: #FBBF24; border: 1px solid rgba(245, 158, 11, 0.35); }
.metrics-grid {
display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4);
}
.metric-card {
background: var(--surface-1); border: 1px solid var(--border-subtle);
border-radius: var(--radius-md); padding: var(--space-4);
display: flex; flex-direction: column; gap: var(--space-1);
}
.metric-label { color: var(--text-secondary); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }
.metric-value { font-size: 24px; font-weight: 600; font-family: var(--font-mono); }
.metric-sub { color: var(--text-muted); font-size: 12px; }
.kanban {
display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-4);
}
.kanban-col {
background: var(--surface-1); border: 1px solid var(--border-subtle);
border-radius: var(--radius-md); padding: var(--space-4);
display: flex; flex-direction: column; gap: var(--space-3); min-height: 260px;
}
.col-header {
display: flex; justify-content: space-between; align-items: center;
font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.05em;
color: var(--text-secondary); padding-bottom: var(--space-2);
border-bottom: 1px solid var(--border-subtle);
}
.card {
background: var(--surface-2); border: 1px solid var(--border-subtle);
border-radius: var(--radius-sm); padding: var(--space-3);
display: flex; flex-direction: column; gap: var(--space-2);
}
.card-header { display: flex; justify-content: space-between; align-items: center; }
.mono-id { font-family: var(--font-mono); font-size: 11px; color: var(--accent-sky); }
.badge { font-family: var(--font-mono); font-size: 10px; padding: 2px 6px; border-radius: var(--radius-sm); font-weight: 600; }
.badge-pass { background: rgba(16, 185, 129, 0.16); color: #34D399; }
.badge-reject { background: rgba(244, 63, 94, 0.16); color: #FB7185; }
.badge-neutral { background: rgba(156, 163, 175, 0.14); color: #D1D5DB; }
.card-title { font-weight: 500; font-size: 13px; color: var(--text-primary); }
.card-meta { display: flex; justify-content: space-between; font-size: 12px; color: var(--text-secondary); }
.card-paths { display: flex; flex-wrap: wrap; gap: 4px; }
code { font-family: var(--font-mono); font-size: 11px; background: var(--surface-3); padding: 1px 5px; border-radius: 3px; color: #E5E7EB; }
.bottom-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-4); }
.panel {
background: var(--surface-1); border: 1px solid var(--border-subtle);
border-radius: var(--radius-md); padding: var(--space-4);
display: flex; flex-direction: column; gap: var(--space-3);
}
.panel h2 { font-size: 14px; font-weight: 600; color: var(--text-primary); border-bottom: 1px solid var(--border-subtle); padding-bottom: var(--space-2); }
ul.clean-list { list-style: none; display: flex; flex-direction: column; gap: var(--space-2); font-size: 13px; color: var(--text-secondary); }
@media (max-width: 960px) {
.metrics-grid, .kanban, .bottom-grid { grid-template-columns: 1fr; }
}
</style>
</head>
<body>
<div class="shell">
<header class="topbar">
<div class="brand">
<h1>SwarmOS v1.0 — The Agency Command Center · ${escapeHtml(projectTitle)}</h1>
<p>${escapeHtml(projectVision)} · Archetype: <code>${escapeHtml(archetype)}</code></p>
</div>
<div class="status-pill ${readiness.ready ? "status-ready" : "status-blocked"}">
${readiness.ready ? "RELEASE GATE: READY TO SHIP" : `RELEASE GATE: ${readiness.blockers.length} BLOCKER(S)`}
</div>
</header>
<section class="metrics-grid">
<div class="metric-card">
<span class="metric-label">Locked Contracts</span>
<span class="metric-value">${board.contracts.length}</span>
<span class="metric-sub">Architect Gate 1 Invariants</span>
</div>
<div class="metric-card">
<span class="metric-label">Verified Tasks</span>
<span class="metric-value">${readiness.verifiedTasks.length} / ${board.tasks.filter((t) => t.status !== "superseded").length}</span>
<span class="metric-sub">Signed Adversarial QA Receipts</span>
</div>
<div class="metric-card">
<span class="metric-label">Visual UI/UX Judge</span>
<span class="metric-value">${uxScorecard ? `${uxScorecard.score}/100 (${escapeHtml(uxScorecard.grade)})` : "Not Run"}</span>
<span class="metric-sub">${uxScorecard ? (uxScorecard.passed ? "Passed >= 85 Threshold" : "Needs Visual Remediation") : "Run tools.ui.score_ux"}</span>
</div>
<div class="metric-card">
<span class="metric-label">Epistemic Sources & Packs</span>
<span class="metric-value">${epistemicSources.length} src · ${packs.recommendedPacks.length} pack</span>
<span class="metric-sub">Active Packs: ${escapeHtml(packs.recommendedPacks.join(", ") || "core")}</span>
</div>
</section>
<section class="kanban">
<div class="kanban-col">
<div class="col-header"><span>Pending</span><span>${tasksByStatus.pending.length}</span></div>
${tasksByStatus.pending.map(renderTaskCard).join("") || `<div class="metric-sub">No pending tasks.</div>`}
</div>
<div class="kanban-col">
<div class="col-header"><span>In Progress / Ready QA</span><span>${tasksByStatus.in_progress.length}</span></div>
${tasksByStatus.in_progress.map(renderTaskCard).join("") || `<div class="metric-sub">No active tasks.</div>`}
</div>
<div class="kanban-col">
<div class="col-header"><span>QA Rejected</span><span>${tasksByStatus.qa_rejected.length}</span></div>
${tasksByStatus.qa_rejected.map(renderTaskCard).join("") || `<div class="metric-sub">Zero rejected tasks.</div>`}
</div>
<div class="kanban-col">
<div class="col-header"><span>Verified (PASS)</span><span>${tasksByStatus.verified.length}</span></div>
${tasksByStatus.verified.map(renderTaskCard).join("") || `<div class="metric-sub">No verified tasks yet.</div>`}
</div>
</section>
<section class="bottom-grid">
<div class="panel">
<h2>Locked Architecture Contracts</h2>
<ul class="clean-list">
${board.contracts.map((c) => `<li><code>${escapeHtml(c.id)}</code> — <strong>${escapeHtml(c.title)}</strong> (${escapeHtml(c.files.join(", "))})</li>`).join("") || "<li>No contracts locked yet.</li>"}
</ul>
</div>
<div class="panel">
<h2>100-Point UI/UX Scorecard Breakdown</h2>
<ul class="clean-list">
${
uxScorecard?.breakdown
? Object.entries(uxScorecard.breakdown)
.map(([k, v]: [string, any]) => `<li><strong>${escapeHtml(k)}</strong>: <code>${v.score}/${v.max}</code></li>`)
.join("")
: "<li>Run <code>tools.ui.score_ux</code> to populate live 6-axis UI/UX metrics.</li>"
}
</ul>
</div>
<div class="panel">
<h2>Epistemic Cache & Telemetry</h2>
<ul class="clean-list">
<li><strong>Verified SHA-256 Sources</strong>: ${epistemicSources.length}</li>
<li><strong>Detected Domain Packs</strong>: ${escapeHtml(packs.recommendedPacks.join(", ") || "none")}</li>
<li><strong>Recorded Telemetry Sessions</strong>: ${Array.isArray(telemetry) ? telemetry.length : 0}</li>
</ul>
</div>
</section>
</div>
</body>
</html>`
const outDir = path.join(rootDir, ".opencode", "artifacts")
await fs.mkdir(outDir, { recursive: true })
const dashboardPath = path.join(outDir, "swarm-dashboard.html")
await fs.writeFile(dashboardPath, html, "utf8")
return {
dashboardPath,
readyToShip: readiness.ready,
taskCount: board.tasks.length,
contractCount: board.contracts.length,
uxScore: uxScorecard?.score ?? null,
}
}
export function registerDashboardTools(editor: any, rootDir: string) {
editor.add({
name: "render_dashboard",
description:
"Generate the self-contained 'The Agency' SwarmOS Live Command Center WebUI HTML5 dashboard at `.opencode/artifacts/swarm-dashboard.html`.",
options: { namespace: "org", codemode: true, pinned: true },
input: { type: "object", properties: {} },
output: { type: "object" },
execute: async () => {
const res = await generateSwarmDashboardHtml(rootDir)
return {
output: res,
content: `Generated SwarmOS Command Center WebUI -> ${res.dashboardPath}`,
}
},
})
}
+3437 -1967
View File
File diff suppressed because it is too large. Load diff
+6 -6
View File
@@ -167,24 +167,24 @@ describe("SwarmOS v0.9.0 Complete Roadmap (Task Budgets, Session Replay, Warm Pa
it("caches unchanged file diagnostics in 0ms and locates symbol definitions & references", async () => {
await fs.mkdir(path.join(tmpDir, "src"), { recursive: true })
const fileA = path.join(tmpDir, "src/alpha.js")
const fileB = path.join(tmpDir, "src/beta.js")
const fileA = path.join(tmpDir, "src/alpha.mjs")
const fileB = path.join(tmpDir, "src/beta.mjs")
await fs.writeFile(fileA, "export function computeMetric(x) { return x * 2; }\n", "utf8")
await fs.writeFile(fileB, "import { computeMetric } from './alpha.js';\nexport const res = computeMetric(21);\n", "utf8")
await fs.writeFile(fileB, "import { computeMetric } from './alpha.mjs';\nexport const res = computeMetric(21);\n", "utf8")
const firstPass = await runParallelDiagnostics(tmpDir, ["src/alpha.js", "src/beta.js"], (r, p) => runQuickDiagnostics(r, p))
const firstPass = await runParallelDiagnostics(tmpDir, ["src/alpha.mjs", "src/beta.mjs"], (r, p) => runQuickDiagnostics(r, p))
expect(firstPass).toEqual([])
expect(warmDiagnosticStats.cacheMisses).toBe(2)
expect(warmDiagnosticStats.cacheHits).toBe(0)
// Second pass on unchanged files hits the warm mtime+size cache
const secondPass = await runParallelDiagnostics(tmpDir, ["src/alpha.js", "src/beta.js"], (r, p) => runQuickDiagnostics(r, p))
const secondPass = await runParallelDiagnostics(tmpDir, ["src/alpha.mjs", "src/beta.mjs"], (r, p) => runQuickDiagnostics(r, p))
expect(secondPass).toEqual([])
expect(warmDiagnosticStats.cacheHits).toBe(2)
const defs = await findSymbolDefinitions(tmpDir, "computeMetric")
expect(defs.length).toBe(1)
expect(defs[0].file).toBe("src/alpha.js")
expect(defs[0].file).toBe("src/alpha.mjs")
const refs = await findSymbolReferences(tmpDir, "computeMetric")
expect(refs.length).toBeGreaterThanOrEqual(2)
+154
View File
@@ -0,0 +1,154 @@
import { describe, expect, test } from "bun:test"
import * as fs from "node:fs/promises"
import * as os from "node:os"
import * as path from "node:path"
import {
cacheEpistemicSource,
verifyEpistemicClaims,
classifySpdxLicense,
generateGrillQuestions,
discoverClaudeCodeAssets,
scoreUXMetrics,
detectDomainPacks,
inspectGodotScene,
inspectGDevelopProject,
inspectBinaryTarget,
generateSwarmDashboardHtml,
} from "../src/index.js"
describe("SwarmOS v1.0.0 Unified Heretek Platform", () => {
test("Pillar 1: IUMBTEMS Epistemic Cache, Verbatim Quote Verification & SPDX License Check", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "swarmos-epistemic-"))
const cached = await cacheEpistemicSource(tmp, {
title: "Effect 4.0 Architecture Spec",
url: "https://effect.website/docs/v4",
spdxLicense: "MIT",
content: "Effect 4.0 introduces LayerMap for location-scoped service lifecycles and zero-leak fiber supervision.",
})
expect(cached.shortHash).toHaveLength(12)
expect(cached.licenseCategory).toBe("permissive")
const goodMd = `According to the spec, it works [VERIFIED: ${cached.shortHash} | "LayerMap for location-scoped service lifecycles"].`
const auditPass = await verifyEpistemicClaims(tmp, goodMd)
expect(auditPass.passed).toBe(true)
expect(auditPass.verifiedCount).toBe(1)
const badMd = `Hallucinated claim [VERIFIED: ${cached.shortHash} | "non-existent quote string"].`
const auditFail = await verifyEpistemicClaims(tmp, badMd)
expect(auditFail.passed).toBe(false)
expect(auditFail.failedClaims).toHaveLength(1)
expect(classifySpdxLicense("GPL-3.0-only")).toBe("copyleft-contaminating")
expect(generateGrillQuestions("Distributed SQLite Swarm").questions.length).toBeGreaterThanOrEqual(5)
})
test("Pillar 2: claude-4-opencode Bridge discovers .claude commands, agents, and skills", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "swarmos-claude-"))
await fs.mkdir(path.join(tmp, ".claude", "commands"), { recursive: true })
await fs.mkdir(path.join(tmp, ".claude", "agents"), { recursive: true })
await fs.mkdir(path.join(tmp, ".claude", "skills", "clean-arch"), { recursive: true })
await fs.writeFile(
path.join(tmp, ".claude", "commands", "audit-sec.md"),
"---\ndescription: Security audit command\n---\nRun security audit on $ARGUMENTS",
)
await fs.writeFile(
path.join(tmp, ".claude", "agents", "sec-reviewer.md"),
"---\nname: Security Reviewer\ndescription: Audits auth flows\n---\nYou are a security reviewer.",
)
await fs.writeFile(
path.join(tmp, ".claude", "skills", "clean-arch", "SKILL.md"),
"---\nname: Clean Architecture\ndescription: Enforces hexagonal boundaries\n---\nKeep domain pure.",
)
const assets = await discoverClaudeCodeAssets(tmp)
expect(assets.commands).toHaveLength(1)
expect(assets.commands[0].name).toBe("audit-sec")
expect(assets.agents).toHaveLength(1)
expect(assets.skills).toHaveLength(1)
})
test("Pillar 3: Quantitative 100-Point Visual Quality & UI/UX Judge", () => {
const cleanScore = scoreUXMetrics({
viewport: { width: 1440, height: 900 },
horizontalOverflowPx: 0,
elementOverflowCount: 0,
collisionCount: 0,
baselineJitterCount: 0,
lowContrastCount: 0,
descenderClipCount: 0,
tinyTargetCount: 0,
missingLabelCount: 0,
uniqueFontSizes: 5,
arbitraryInlineHexCount: 0,
consoleErrorCount: 0,
failedNetworkCount: 0,
})
expect(cleanScore.score).toBe(100)
expect(cleanScore.grade).toBe("S")
expect(cleanScore.passed).toBe(true)
const sloppyScore = scoreUXMetrics({
viewport: { width: 375, height: 812 },
horizontalOverflowPx: 42,
elementOverflowCount: 2,
collisionCount: 2,
baselineJitterCount: 3,
lowContrastCount: 3,
descenderClipCount: 1,
tinyTargetCount: 2,
missingLabelCount: 1,
uniqueFontSizes: 11,
arbitraryInlineHexCount: 6,
consoleErrorCount: 1,
failedNetworkCount: 0,
})
expect(sloppyScore.passed).toBe(false)
expect(sloppyScore.remediation.length).toBeGreaterThan(3)
})
test("Pillar 4: On-Demand Domain Packs (Godot 4, GDevelop 5, Reverse Engineering)", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "swarmos-packs-"))
await fs.writeFile(path.join(tmp, "project.godot"), "[application]\nconfig/name=\"TestGame\"\n")
await fs.writeFile(
path.join(tmp, "Player.tscn"),
'[gd_scene load_steps=2 format=3]\n[ext_resource type="Script" path="res://Player.gd" id="1"]\n[node name="Player" type="CharacterBody2D"]\n[node name="Sprite2D" type="Sprite2D" parent="."]\n[connection signal="hit" from="." to="." method="_on_hit"]\n',
)
await fs.writeFile(
path.join(tmp, "game.json"),
JSON.stringify({
properties: { name: "SpaceJump", version: "1.0.0" },
layouts: [{ name: "MainScene", objects: [{ name: "Hero" }], layers: [{ name: "Base" }], events: [{}, {}] }],
externalEvents: [],
eventsFunctionsExtensions: [{ name: "DoubleJump" }],
}),
)
await fs.writeFile(path.join(tmp, "sample.bin"), Buffer.from("\x7fELF...http://api.internal.local/v1/secret_token_check..."))
const detected = await detectDomainPacks(tmp)
expect(detected.godot).toBe(true)
expect(detected.gdevelop).toBe(true)
expect(detected.reverseEngineering).toBe(true)
const scene = await inspectGodotScene(tmp, "Player.tscn")
expect(scene.nodeCount).toBe(2)
expect(scene.signalConnections).toHaveLength(1)
const gd = await inspectGDevelopProject(tmp, "game.json")
expect(gd.projectName).toBe("SpaceJump")
expect(gd.scenes[0].eventCount).toBe(2)
const bin = await inspectBinaryTarget(tmp, "sample.bin")
expect(bin.format).toBe("ELF")
expect(bin.suspiciousStrings.length).toBeGreaterThan(0)
})
test("Pillar 5: The Agency Live Swarm Command Center HTML5 Dashboard", async () => {
const tmp = await fs.mkdtemp(path.join(os.tmpdir(), "swarmos-dash-"))
const dash = await generateSwarmDashboardHtml(tmp)
expect(dash.dashboardPath).toContain("swarm-dashboard.html")
const html = await fs.readFile(dash.dashboardPath, "utf8")
expect(html).toContain("SwarmOS v1.0 — The Agency Command Center")
expect(html).toContain("100-Point UI/UX Scorecard Breakdown")
})
})