Sign In

@decantr/cli

Package Overview
Dependencies
Maintainers
1
Versions
116
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@decantr/cli - npm Package Compare versions

Comparing version
3.5.4
to
3.5.5
dist/chunk-24JR4ZNG.js

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

+380
import {
createProjectHealthReport,
listWorkspaceAppCandidates
} from "./chunk-U62GGKUO.js";
// src/commands/workspace.ts
import { execFileSync } from "child_process";
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join, relative, resolve } from "path";
import { WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL } from "@decantr/verifier";
var BOLD = "\x1B[1m";
var DIM = "\x1B[2m";
var GREEN = "\x1B[32m";
var RED = "\x1B[31m";
var YELLOW = "\x1B[33m";
var RESET = "\x1B[0m";
var WORKSPACE_HEALTH_SCHEMA_URL = WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL;
var DEFAULT_IGNORES = /* @__PURE__ */ new Set([
".git",
".next",
".turbo",
".vercel",
"coverage",
"dist",
"node_modules",
"playwright-report"
]);
function workspaceConfigPath(root) {
return join(root, ".decantr", "workspace.json");
}
function readWorkspaceConfig(root) {
const path = workspaceConfigPath(root);
if (!existsSync(path)) return null;
return JSON.parse(readFileSync(path, "utf-8"));
}
function normalizeProjectPath(raw) {
const normalized = raw.replace(/^\.\/+/, "").replace(/\/+$/, "");
if (!normalized || normalized.startsWith("/") || normalized.includes("..") || normalized.includes("\\") || /\s/.test(normalized)) {
throw new Error(`Invalid workspace project path: ${raw}`);
}
return normalized;
}
function projectIdFromPath(path) {
return path.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
}
function discoverProjectPaths(root, config) {
const ignored = /* @__PURE__ */ new Set([...config?.ignore ?? [], ...DEFAULT_IGNORES]);
const results = /* @__PURE__ */ new Set();
function walk(dir, depth) {
if (depth > 6) return;
const rel = relative(root, dir).replace(/\\/g, "/");
if (rel && [...ignored].some((entry) => rel === entry || rel.startsWith(`${entry}/`))) return;
if (existsSync(join(dir, "decantr.essence.json"))) {
results.add(rel || ".");
return;
}
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
if (ignored.has(entry.name)) continue;
walk(join(dir, entry.name), depth + 1);
}
}
walk(root, 0);
return [...results].sort();
}
function listWorkspaceProjects(root = process.cwd()) {
const workspaceRoot = resolve(root);
const config = readWorkspaceConfig(workspaceRoot);
const byPath = /* @__PURE__ */ new Map();
for (const project of config?.projects ?? []) {
const path = normalizeProjectPath(project.path);
byPath.set(path, {
id: project.id ?? projectIdFromPath(path),
path,
absolutePath: resolve(workspaceRoot, path),
owner: project.owner ?? null,
tags: project.tags ?? [],
criticality: project.criticality ?? "normal",
browser: project.browser ?? config?.browser ?? false,
source: "manifest"
});
}
for (const path of discoverProjectPaths(workspaceRoot, config)) {
if (byPath.has(path)) continue;
byPath.set(path, {
id: projectIdFromPath(path),
path,
absolutePath: resolve(workspaceRoot, path),
owner: null,
tags: [],
criticality: "normal",
browser: config?.browser ?? false,
source: "auto"
});
}
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
}
function listWorkspaceCandidates(root = process.cwd(), projects = listWorkspaceProjects(root)) {
const attached = new Set(projects.map((project) => project.path));
return listWorkspaceAppCandidates(root).map((path) => ({
path,
attached: attached.has(path),
suggestedAdoptCommand: `decantr adopt --project ${path} --yes`
}));
}
function changedPaths(root, since) {
try {
const output = execFileSync("git", ["diff", "--name-only", since, "--"], {
cwd: root,
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
});
return new Set(
output.split("\n").map((line) => line.trim()).filter(Boolean)
);
} catch {
return /* @__PURE__ */ new Set();
}
}
function projectChanged(project, changed) {
if (changed.size === 0) return false;
const prefix = project.path === "." ? "" : `${project.path}/`;
for (const path of changed) {
if (project.path === "." || path === project.path || path.startsWith(prefix)) return true;
}
return false;
}
async function withTimeout(promise, timeoutMs, label) {
let timeout;
const timer = new Promise((_, reject) => {
timeout = setTimeout(
() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),
timeoutMs
);
});
try {
return await Promise.race([promise, timer]);
} finally {
if (timeout) clearTimeout(timeout);
}
}
async function mapLimited(items, concurrency, fn) {
const results = new Array(items.length);
let next = 0;
async function worker() {
while (next < items.length) {
const index = next++;
results[index] = await fn(items[index]);
}
}
await Promise.all(Array.from({ length: Math.max(1, concurrency) }, () => worker()));
return results;
}
async function createWorkspaceHealthReport(root = process.cwd(), options = {}) {
const workspaceRoot = resolve(root);
const config = readWorkspaceConfig(workspaceRoot);
const since = options.since ?? "origin/main";
const changed = options.changedOnly ? changedPaths(workspaceRoot, since) : /* @__PURE__ */ new Set();
const allProjects = listWorkspaceProjects(workspaceRoot);
const projects = options.changedOnly ? allProjects.filter((project) => projectChanged(project, changed)) : allProjects;
const concurrency = options.concurrency ?? config?.concurrency ?? 4;
const timeoutMs = options.timeoutMs ?? config?.timeoutMs ?? 12e4;
const checked = await mapLimited(projects, concurrency, async (project) => {
const startedAt = Date.now();
try {
const report = await withTimeout(
createProjectHealthReport(project.absolutePath, {
browser: options.browser ?? project.browser
}),
timeoutMs,
project.path
);
return {
id: project.id,
path: project.path,
status: report.status,
score: report.score,
errorCount: report.summary.errorCount,
warnCount: report.summary.warnCount,
infoCount: report.summary.infoCount,
findingCount: report.summary.findingCount,
durationMs: Date.now() - startedAt,
changed: options.changedOnly ? projectChanged(project, changed) : false,
source: project.source,
error: null,
loopState: report.loop.state,
loopNextAction: report.loop.nextActions[0] ?? null
};
} catch (error) {
return {
id: project.id,
path: project.path,
status: "failed",
score: 0,
errorCount: 1,
warnCount: 0,
infoCount: 0,
findingCount: 1,
durationMs: Date.now() - startedAt,
changed: options.changedOnly ? projectChanged(project, changed) : false,
source: project.source,
error: error.message,
loopState: "blocked_missing_context",
loopNextAction: "Fix the project health failure, then rerun workspace health."
};
}
});
const summary = {
projectCount: allProjects.length,
checkedCount: checked.length,
healthyCount: checked.filter((project) => project.status === "healthy").length,
warningCount: checked.filter((project) => project.status === "warning").length,
errorCount: checked.filter((project) => project.status === "error").length,
failedCount: checked.filter((project) => project.status === "failed").length
};
const blockedCount = checked.filter(
(project) => project.loopState?.startsWith("blocked") || project.loopState === "human_resolution_required"
).length;
const repairRequiredCount = checked.filter(
(project) => project.loopState === "repair_required"
).length;
const workspaceLoopState = checked.length === 0 ? "needs_context" : blockedCount > 0 ? "human_resolution_required" : repairRequiredCount > 0 || summary.errorCount > 0 || summary.warningCount > 0 ? "repair_required" : "verified";
const workspaceLoopStatus = workspaceLoopState === "human_resolution_required" || workspaceLoopState.startsWith("blocked") ? "blocked" : summary.errorCount > 0 || summary.failedCount > 0 ? "error" : summary.warningCount > 0 ? "warning" : "healthy";
return {
$schema: WORKSPACE_HEALTH_SCHEMA_URL,
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
workspaceRoot,
changedOnly: options.changedOnly ?? false,
since: options.changedOnly ? since : null,
summary,
loop: {
state: workspaceLoopState,
status: workspaceLoopStatus,
projectCount: checked.length,
blockedCount,
repairRequiredCount,
nextActions: [
workspaceLoopState === "verified" ? "Workspace loop verified." : 'Open the highest-risk project, run `decantr task <route> "<intent>"`, repair, then rerun `decantr verify`.'
]
},
projects: checked
};
}
function formatWorkspaceHealthText(report) {
const lines = [
`${BOLD}Decantr Workspace Health${RESET}`,
"",
`Projects: ${report.summary.checkedCount}/${report.summary.projectCount}`,
`Healthy: ${report.summary.healthyCount} | Warnings: ${report.summary.warningCount} | Errors: ${report.summary.errorCount} | Failed: ${report.summary.failedCount}`,
`Loop: ${report.loop.state} | blocked ${report.loop.blockedCount} | repair ${report.loop.repairRequiredCount}`,
""
];
for (const project of report.projects) {
const color = project.status === "healthy" ? GREEN : project.status === "warning" ? YELLOW : RED;
lines.push(
`${color}${String(project.status).toUpperCase()}${RESET} ${project.path} score ${project.score}/100 findings ${project.findingCount}`
);
if (project.error) lines.push(` ${DIM}${project.error}${RESET}`);
}
return `${lines.join("\n")}
`;
}
function formatWorkspaceHealthMarkdown(report) {
const lines = [
"# Decantr Workspace Health",
"",
`- Projects checked: **${report.summary.checkedCount}/${report.summary.projectCount}**`,
`- Healthy: ${report.summary.healthyCount}`,
`- Warnings: ${report.summary.warningCount}`,
`- Errors: ${report.summary.errorCount}`,
`- Failed: ${report.summary.failedCount}`,
"",
"| Project | Status | Score | Findings | Source |",
"| --- | --- | ---: | ---: | --- |"
];
for (const project of report.projects) {
lines.push(
`| \`${project.path}\` | ${project.status} | ${project.score} | ${project.findingCount} | ${project.source} |`
);
}
return `${lines.join("\n")}
`;
}
function shouldFailWorkspaceHealth(report, failOn = "error") {
if (failOn === "none") return false;
if (report.summary.failedCount > 0 || report.summary.errorCount > 0) return true;
return failOn === "warn" && report.summary.warningCount > 0;
}
function parseHealthFailOn(value) {
if (value === "warn" || value === "none") return value;
return "error";
}
function parseWorkspaceArgs(args) {
const subcommand = args[1] === "health" ? "health" : "list";
const options = { subcommand };
for (let index = 2; index < args.length; index += 1) {
const arg = args[index];
if (arg === "--json") options.json = true;
else if (arg === "--markdown") options.markdown = true;
else if (arg === "--ci") options.ci = true;
else if (arg === "--browser") options.browser = true;
else if (arg === "--changed") options.changedOnly = true;
else if (arg === "--since" && args[index + 1]) options.since = args[++index];
else if (arg.startsWith("--since=")) options.since = arg.split("=")[1];
else if (arg === "--output" && args[index + 1]) options.output = args[++index];
else if (arg.startsWith("--output=")) options.output = arg.split("=")[1];
else if (arg === "--fail-on" && args[index + 1])
options.failOn = parseHealthFailOn(args[++index]);
else if (arg.startsWith("--fail-on=")) options.failOn = parseHealthFailOn(arg.split("=")[1]);
else if (arg === "--concurrency" && args[index + 1])
options.concurrency = Number(args[++index]);
else if (arg.startsWith("--concurrency=")) options.concurrency = Number(arg.split("=")[1]);
else if (arg === "--timeout-ms" && args[index + 1]) options.timeoutMs = Number(args[++index]);
else if (arg.startsWith("--timeout-ms=")) options.timeoutMs = Number(arg.split("=")[1]);
}
return options;
}
async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"]) {
const options = parseWorkspaceArgs(args);
if (options.subcommand === "list") {
const projects = listWorkspaceProjects(workspaceRoot);
const candidates = listWorkspaceCandidates(workspaceRoot, projects);
const unattachedCandidates = candidates.filter((candidate) => !candidate.attached);
const payload2 = `${JSON.stringify({ projects, candidates }, null, 2)}
`;
if (options.json) {
process.stdout.write(payload2);
return;
}
console.log(`${BOLD}Decantr workspace projects${RESET}`);
console.log("");
console.log("Attached Decantr projects:");
if (projects.length === 0) {
console.log(` ${DIM}(none yet)${RESET}`);
} else {
for (const project of projects) {
console.log(` ${project.path} ${DIM}${project.source}${RESET}`);
}
}
if (candidates.length > 0) {
console.log("");
console.log("App candidates:");
for (const candidate of candidates) {
const status = candidate.attached ? `${GREEN}attached${RESET}` : `${YELLOW}unattached${RESET}`;
console.log(` ${candidate.path} ${DIM}${status}${RESET}`);
}
}
if (unattachedCandidates.length > 0) {
console.log("");
console.log(projects.length > 0 ? "Attach another app:" : "Start by attaching one app:");
console.log(` ${unattachedCandidates[0].suggestedAdoptCommand}`);
}
return;
}
const report = await createWorkspaceHealthReport(workspaceRoot, options);
const payload = options.json ? `${JSON.stringify(report, null, 2)}
` : options.markdown ? formatWorkspaceHealthMarkdown(report) : formatWorkspaceHealthText(report);
if (options.output) {
mkdirSync(dirname(resolve(workspaceRoot, options.output)), { recursive: true });
writeFileSync(resolve(workspaceRoot, options.output), payload, "utf-8");
if (!options.ci)
console.log(`${GREEN}Wrote Decantr workspace health:${RESET} ${options.output}`);
} else {
process.stdout.write(payload);
}
if (options.ci && shouldFailWorkspaceHealth(report, options.failOn ?? "error")) {
process.exitCode = 1;
}
}
export {
listWorkspaceProjects,
listWorkspaceCandidates,
createWorkspaceHealthReport,
formatWorkspaceHealthText,
formatWorkspaceHealthMarkdown,
shouldFailWorkspaceHealth,
parseWorkspaceArgs,
cmdWorkspace
};

Sorry, the diff of this file is too big to display

import {
cmdHeal,
collectCheckIssues
} from "./chunk-24JR4ZNG.js";
export {
cmdHeal,
collectCheckIssues
};
import {
cmdHealth,
collectDesignTokenEvidence,
createProjectEvidenceBundle,
createProjectHealthReport,
formatDiagnosticCatalogJson,
formatDiagnosticCatalogMarkdown,
formatDiagnosticCatalogText,
formatProjectHealthJson,
formatProjectHealthMarkdown,
formatProjectHealthText,
parseHealthArgs,
renderProjectHealthCiWorkflow,
shouldFailHealth,
writeProjectHealthCiWorkflow
} from "./chunk-U62GGKUO.js";
import "./chunk-24JR4ZNG.js";
export {
cmdHealth,
collectDesignTokenEvidence,
createProjectEvidenceBundle,
createProjectHealthReport,
formatDiagnosticCatalogJson,
formatDiagnosticCatalogMarkdown,
formatDiagnosticCatalogText,
formatProjectHealthJson,
formatProjectHealthMarkdown,
formatProjectHealthText,
parseHealthArgs,
renderProjectHealthCiWorkflow,
shouldFailHealth,
writeProjectHealthCiWorkflow
};

Sorry, the diff of this file is too big to display

import {
cmdWorkspace,
createWorkspaceHealthReport,
formatWorkspaceHealthMarkdown,
formatWorkspaceHealthText,
listWorkspaceCandidates,
listWorkspaceProjects,
parseWorkspaceArgs,
shouldFailWorkspaceHealth
} from "./chunk-FIWVRMZN.js";
import "./chunk-U62GGKUO.js";
import "./chunk-24JR4ZNG.js";
export {
cmdWorkspace,
createWorkspaceHealthReport,
formatWorkspaceHealthMarkdown,
formatWorkspaceHealthText,
listWorkspaceCandidates,
listWorkspaceProjects,
parseWorkspaceArgs,
shouldFailWorkspaceHealth
};
+4
-4
#!/usr/bin/env node
import "./chunk-GPGBJFMF.js";
import "./chunk-4O74E2PS.js";
import "./chunk-SIDKK73N.js";
import "./chunk-UDBPH25I.js";
import "./chunk-EWUWVQYM.js";
import "./chunk-4WVKE2AQ.js";
import "./chunk-FIWVRMZN.js";
import "./chunk-U62GGKUO.js";
import "./chunk-24JR4ZNG.js";

@@ -1,5 +0,5 @@

import "./chunk-GPGBJFMF.js";
import "./chunk-4O74E2PS.js";
import "./chunk-SIDKK73N.js";
import "./chunk-UDBPH25I.js";
import "./chunk-EWUWVQYM.js";
import "./chunk-4WVKE2AQ.js";
import "./chunk-FIWVRMZN.js";
import "./chunk-U62GGKUO.js";
import "./chunk-24JR4ZNG.js";
{
"name": "@decantr/cli",
"version": "3.5.4",
"version": "3.5.5",
"description": "Decantr CLI - adopt, verify, graph, and govern frontend codebases touched by AI agents",

@@ -52,7 +52,7 @@ "keywords": [

"ajv": "^8.20.0",
"@decantr/core": "3.5.0",
"@decantr/essence-spec": "3.4.0",
"@decantr/core": "3.5.0",
"@decantr/registry": "3.4.0",
"@decantr/telemetry": "3.4.0",
"@decantr/registry": "3.4.0",
"@decantr/verifier": "3.5.4"
"@decantr/verifier": "3.5.5"
},

@@ -59,0 +59,0 @@ "scripts": {

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is too big to display

import {
createProjectHealthReport,
listWorkspaceAppCandidates
} from "./chunk-EWUWVQYM.js";
// src/commands/workspace.ts
import { execFileSync } from "child_process";
import { existsSync, mkdirSync, readdirSync, readFileSync, writeFileSync } from "fs";
import { dirname, join, relative, resolve } from "path";
import { WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL } from "@decantr/verifier";
var BOLD = "\x1B[1m";
var DIM = "\x1B[2m";
var GREEN = "\x1B[32m";
var RED = "\x1B[31m";
var YELLOW = "\x1B[33m";
var RESET = "\x1B[0m";
var WORKSPACE_HEALTH_SCHEMA_URL = WORKSPACE_HEALTH_REPORT_V2_SCHEMA_URL;
var DEFAULT_IGNORES = /* @__PURE__ */ new Set([
".git",
".next",
".turbo",
".vercel",
"coverage",
"dist",
"node_modules",
"playwright-report"
]);
function workspaceConfigPath(root) {
return join(root, ".decantr", "workspace.json");
}
function readWorkspaceConfig(root) {
const path = workspaceConfigPath(root);
if (!existsSync(path)) return null;
return JSON.parse(readFileSync(path, "utf-8"));
}
function normalizeProjectPath(raw) {
const normalized = raw.replace(/^\.\/+/, "").replace(/\/+$/, "");
if (!normalized || normalized.startsWith("/") || normalized.includes("..") || normalized.includes("\\") || /\s/.test(normalized)) {
throw new Error(`Invalid workspace project path: ${raw}`);
}
return normalized;
}
function projectIdFromPath(path) {
return path.replace(/[^a-zA-Z0-9._-]+/g, "-").replace(/^-+|-+$/g, "") || "project";
}
function discoverProjectPaths(root, config) {
const ignored = /* @__PURE__ */ new Set([...config?.ignore ?? [], ...DEFAULT_IGNORES]);
const results = /* @__PURE__ */ new Set();
function walk(dir, depth) {
if (depth > 6) return;
const rel = relative(root, dir).replace(/\\/g, "/");
if (rel && [...ignored].some((entry) => rel === entry || rel.startsWith(`${entry}/`))) return;
if (existsSync(join(dir, "decantr.essence.json"))) {
results.add(rel || ".");
return;
}
for (const entry of readdirSync(dir, { withFileTypes: true })) {
if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
if (ignored.has(entry.name)) continue;
walk(join(dir, entry.name), depth + 1);
}
}
walk(root, 0);
return [...results].sort();
}
function listWorkspaceProjects(root = process.cwd()) {
const workspaceRoot = resolve(root);
const config = readWorkspaceConfig(workspaceRoot);
const byPath = /* @__PURE__ */ new Map();
for (const project of config?.projects ?? []) {
const path = normalizeProjectPath(project.path);
byPath.set(path, {
id: project.id ?? projectIdFromPath(path),
path,
absolutePath: resolve(workspaceRoot, path),
owner: project.owner ?? null,
tags: project.tags ?? [],
criticality: project.criticality ?? "normal",
browser: project.browser ?? config?.browser ?? false,
source: "manifest"
});
}
for (const path of discoverProjectPaths(workspaceRoot, config)) {
if (byPath.has(path)) continue;
byPath.set(path, {
id: projectIdFromPath(path),
path,
absolutePath: resolve(workspaceRoot, path),
owner: null,
tags: [],
criticality: "normal",
browser: config?.browser ?? false,
source: "auto"
});
}
return [...byPath.values()].sort((a, b) => a.path.localeCompare(b.path));
}
function listWorkspaceCandidates(root = process.cwd(), projects = listWorkspaceProjects(root)) {
const attached = new Set(projects.map((project) => project.path));
return listWorkspaceAppCandidates(root).map((path) => ({
path,
attached: attached.has(path),
suggestedAdoptCommand: `decantr adopt --project ${path} --yes`
}));
}
function changedPaths(root, since) {
try {
const output = execFileSync("git", ["diff", "--name-only", since, "--"], {
cwd: root,
encoding: "utf-8",
stdio: ["ignore", "pipe", "ignore"]
});
return new Set(
output.split("\n").map((line) => line.trim()).filter(Boolean)
);
} catch {
return /* @__PURE__ */ new Set();
}
}
function projectChanged(project, changed) {
if (changed.size === 0) return false;
const prefix = project.path === "." ? "" : `${project.path}/`;
for (const path of changed) {
if (project.path === "." || path === project.path || path.startsWith(prefix)) return true;
}
return false;
}
async function withTimeout(promise, timeoutMs, label) {
let timeout;
const timer = new Promise((_, reject) => {
timeout = setTimeout(
() => reject(new Error(`${label} timed out after ${timeoutMs}ms`)),
timeoutMs
);
});
try {
return await Promise.race([promise, timer]);
} finally {
if (timeout) clearTimeout(timeout);
}
}
async function mapLimited(items, concurrency, fn) {
const results = new Array(items.length);
let next = 0;
async function worker() {
while (next < items.length) {
const index = next++;
results[index] = await fn(items[index]);
}
}
await Promise.all(Array.from({ length: Math.max(1, concurrency) }, () => worker()));
return results;
}
async function createWorkspaceHealthReport(root = process.cwd(), options = {}) {
const workspaceRoot = resolve(root);
const config = readWorkspaceConfig(workspaceRoot);
const since = options.since ?? "origin/main";
const changed = options.changedOnly ? changedPaths(workspaceRoot, since) : /* @__PURE__ */ new Set();
const allProjects = listWorkspaceProjects(workspaceRoot);
const projects = options.changedOnly ? allProjects.filter((project) => projectChanged(project, changed)) : allProjects;
const concurrency = options.concurrency ?? config?.concurrency ?? 4;
const timeoutMs = options.timeoutMs ?? config?.timeoutMs ?? 12e4;
const checked = await mapLimited(projects, concurrency, async (project) => {
const startedAt = Date.now();
try {
const report = await withTimeout(
createProjectHealthReport(project.absolutePath, {
browser: options.browser ?? project.browser
}),
timeoutMs,
project.path
);
return {
id: project.id,
path: project.path,
status: report.status,
score: report.score,
errorCount: report.summary.errorCount,
warnCount: report.summary.warnCount,
infoCount: report.summary.infoCount,
findingCount: report.summary.findingCount,
durationMs: Date.now() - startedAt,
changed: options.changedOnly ? projectChanged(project, changed) : false,
source: project.source,
error: null,
loopState: report.loop.state,
loopNextAction: report.loop.nextActions[0] ?? null
};
} catch (error) {
return {
id: project.id,
path: project.path,
status: "failed",
score: 0,
errorCount: 1,
warnCount: 0,
infoCount: 0,
findingCount: 1,
durationMs: Date.now() - startedAt,
changed: options.changedOnly ? projectChanged(project, changed) : false,
source: project.source,
error: error.message,
loopState: "blocked_missing_context",
loopNextAction: "Fix the project health failure, then rerun workspace health."
};
}
});
const summary = {
projectCount: allProjects.length,
checkedCount: checked.length,
healthyCount: checked.filter((project) => project.status === "healthy").length,
warningCount: checked.filter((project) => project.status === "warning").length,
errorCount: checked.filter((project) => project.status === "error").length,
failedCount: checked.filter((project) => project.status === "failed").length
};
const blockedCount = checked.filter(
(project) => project.loopState?.startsWith("blocked") || project.loopState === "human_resolution_required"
).length;
const repairRequiredCount = checked.filter(
(project) => project.loopState === "repair_required"
).length;
const workspaceLoopState = checked.length === 0 ? "needs_context" : blockedCount > 0 ? "human_resolution_required" : repairRequiredCount > 0 || summary.errorCount > 0 || summary.warningCount > 0 ? "repair_required" : "verified";
const workspaceLoopStatus = workspaceLoopState === "human_resolution_required" || workspaceLoopState.startsWith("blocked") ? "blocked" : summary.errorCount > 0 || summary.failedCount > 0 ? "error" : summary.warningCount > 0 ? "warning" : "healthy";
return {
$schema: WORKSPACE_HEALTH_SCHEMA_URL,
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
workspaceRoot,
changedOnly: options.changedOnly ?? false,
since: options.changedOnly ? since : null,
summary,
loop: {
state: workspaceLoopState,
status: workspaceLoopStatus,
projectCount: checked.length,
blockedCount,
repairRequiredCount,
nextActions: [
workspaceLoopState === "verified" ? "Workspace loop verified." : 'Open the highest-risk project, run `decantr task <route> "<intent>"`, repair, then rerun `decantr verify`.'
]
},
projects: checked
};
}
function formatWorkspaceHealthText(report) {
const lines = [
`${BOLD}Decantr Workspace Health${RESET}`,
"",
`Projects: ${report.summary.checkedCount}/${report.summary.projectCount}`,
`Healthy: ${report.summary.healthyCount} | Warnings: ${report.summary.warningCount} | Errors: ${report.summary.errorCount} | Failed: ${report.summary.failedCount}`,
`Loop: ${report.loop.state} | blocked ${report.loop.blockedCount} | repair ${report.loop.repairRequiredCount}`,
""
];
for (const project of report.projects) {
const color = project.status === "healthy" ? GREEN : project.status === "warning" ? YELLOW : RED;
lines.push(
`${color}${String(project.status).toUpperCase()}${RESET} ${project.path} score ${project.score}/100 findings ${project.findingCount}`
);
if (project.error) lines.push(` ${DIM}${project.error}${RESET}`);
}
return `${lines.join("\n")}
`;
}
function formatWorkspaceHealthMarkdown(report) {
const lines = [
"# Decantr Workspace Health",
"",
`- Projects checked: **${report.summary.checkedCount}/${report.summary.projectCount}**`,
`- Healthy: ${report.summary.healthyCount}`,
`- Warnings: ${report.summary.warningCount}`,
`- Errors: ${report.summary.errorCount}`,
`- Failed: ${report.summary.failedCount}`,
"",
"| Project | Status | Score | Findings | Source |",
"| --- | --- | ---: | ---: | --- |"
];
for (const project of report.projects) {
lines.push(
`| \`${project.path}\` | ${project.status} | ${project.score} | ${project.findingCount} | ${project.source} |`
);
}
return `${lines.join("\n")}
`;
}
function shouldFailWorkspaceHealth(report, failOn = "error") {
if (failOn === "none") return false;
if (report.summary.failedCount > 0 || report.summary.errorCount > 0) return true;
return failOn === "warn" && report.summary.warningCount > 0;
}
function parseHealthFailOn(value) {
if (value === "warn" || value === "none") return value;
return "error";
}
function parseWorkspaceArgs(args) {
const subcommand = args[1] === "health" ? "health" : "list";
const options = { subcommand };
for (let index = 2; index < args.length; index += 1) {
const arg = args[index];
if (arg === "--json") options.json = true;
else if (arg === "--markdown") options.markdown = true;
else if (arg === "--ci") options.ci = true;
else if (arg === "--browser") options.browser = true;
else if (arg === "--changed") options.changedOnly = true;
else if (arg === "--since" && args[index + 1]) options.since = args[++index];
else if (arg.startsWith("--since=")) options.since = arg.split("=")[1];
else if (arg === "--output" && args[index + 1]) options.output = args[++index];
else if (arg.startsWith("--output=")) options.output = arg.split("=")[1];
else if (arg === "--fail-on" && args[index + 1])
options.failOn = parseHealthFailOn(args[++index]);
else if (arg.startsWith("--fail-on=")) options.failOn = parseHealthFailOn(arg.split("=")[1]);
else if (arg === "--concurrency" && args[index + 1])
options.concurrency = Number(args[++index]);
else if (arg.startsWith("--concurrency=")) options.concurrency = Number(arg.split("=")[1]);
else if (arg === "--timeout-ms" && args[index + 1]) options.timeoutMs = Number(args[++index]);
else if (arg.startsWith("--timeout-ms=")) options.timeoutMs = Number(arg.split("=")[1]);
}
return options;
}
async function cmdWorkspace(workspaceRoot = process.cwd(), args = ["workspace"]) {
const options = parseWorkspaceArgs(args);
if (options.subcommand === "list") {
const projects = listWorkspaceProjects(workspaceRoot);
const candidates = listWorkspaceCandidates(workspaceRoot, projects);
const unattachedCandidates = candidates.filter((candidate) => !candidate.attached);
const payload2 = `${JSON.stringify({ projects, candidates }, null, 2)}
`;
if (options.json) {
process.stdout.write(payload2);
return;
}
console.log(`${BOLD}Decantr workspace projects${RESET}`);
console.log("");
console.log("Attached Decantr projects:");
if (projects.length === 0) {
console.log(` ${DIM}(none yet)${RESET}`);
} else {
for (const project of projects) {
console.log(` ${project.path} ${DIM}${project.source}${RESET}`);
}
}
if (candidates.length > 0) {
console.log("");
console.log("App candidates:");
for (const candidate of candidates) {
const status = candidate.attached ? `${GREEN}attached${RESET}` : `${YELLOW}unattached${RESET}`;
console.log(` ${candidate.path} ${DIM}${status}${RESET}`);
}
}
if (unattachedCandidates.length > 0) {
console.log("");
console.log(projects.length > 0 ? "Attach another app:" : "Start by attaching one app:");
console.log(` ${unattachedCandidates[0].suggestedAdoptCommand}`);
}
return;
}
const report = await createWorkspaceHealthReport(workspaceRoot, options);
const payload = options.json ? `${JSON.stringify(report, null, 2)}
` : options.markdown ? formatWorkspaceHealthMarkdown(report) : formatWorkspaceHealthText(report);
if (options.output) {
mkdirSync(dirname(resolve(workspaceRoot, options.output)), { recursive: true });
writeFileSync(resolve(workspaceRoot, options.output), payload, "utf-8");
if (!options.ci)
console.log(`${GREEN}Wrote Decantr workspace health:${RESET} ${options.output}`);
} else {
process.stdout.write(payload);
}
if (options.ci && shouldFailWorkspaceHealth(report, options.failOn ?? "error")) {
process.exitCode = 1;
}
}
export {
listWorkspaceProjects,
listWorkspaceCandidates,
createWorkspaceHealthReport,
formatWorkspaceHealthText,
formatWorkspaceHealthMarkdown,
shouldFailWorkspaceHealth,
parseWorkspaceArgs,
cmdWorkspace
};
import {
cmdHeal,
collectCheckIssues
} from "./chunk-4WVKE2AQ.js";
export {
cmdHeal,
collectCheckIssues
};
import {
cmdHealth,
collectDesignTokenEvidence,
createProjectEvidenceBundle,
createProjectHealthReport,
formatDiagnosticCatalogJson,
formatDiagnosticCatalogMarkdown,
formatDiagnosticCatalogText,
formatProjectHealthJson,
formatProjectHealthMarkdown,
formatProjectHealthText,
parseHealthArgs,
renderProjectHealthCiWorkflow,
shouldFailHealth,
writeProjectHealthCiWorkflow
} from "./chunk-EWUWVQYM.js";
import "./chunk-4WVKE2AQ.js";
export {
cmdHealth,
collectDesignTokenEvidence,
createProjectEvidenceBundle,
createProjectHealthReport,
formatDiagnosticCatalogJson,
formatDiagnosticCatalogMarkdown,
formatDiagnosticCatalogText,
formatProjectHealthJson,
formatProjectHealthMarkdown,
formatProjectHealthText,
parseHealthArgs,
renderProjectHealthCiWorkflow,
shouldFailHealth,
writeProjectHealthCiWorkflow
};

Sorry, the diff of this file is too big to display

import {
cmdWorkspace,
createWorkspaceHealthReport,
formatWorkspaceHealthMarkdown,
formatWorkspaceHealthText,
listWorkspaceCandidates,
listWorkspaceProjects,
parseWorkspaceArgs,
shouldFailWorkspaceHealth
} from "./chunk-UDBPH25I.js";
import "./chunk-EWUWVQYM.js";
import "./chunk-4WVKE2AQ.js";
export {
cmdWorkspace,
createWorkspaceHealthReport,
formatWorkspaceHealthMarkdown,
formatWorkspaceHealthText,
listWorkspaceCandidates,
listWorkspaceProjects,
parseWorkspaceArgs,
shouldFailWorkspaceHealth
};