@jakkrichm/create-nexus-devflow 2.0.25 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -8
- package/dist/bin/create-nexus-devflow.d.ts +22 -1
- package/dist/bin/create-nexus-devflow.js +658 -11
- package/dist/bin/create-nexus-devflow.js.map +1 -1
- package/dist/lib/branch-context.d.ts +39 -0
- package/dist/lib/branch-context.js +196 -0
- package/dist/lib/branch-context.js.map +1 -0
- package/dist/lib/code-graph.d.ts +30 -0
- package/dist/lib/code-graph.js +209 -0
- package/dist/lib/code-graph.js.map +1 -0
- package/dist/lib/command-catalog.d.ts +11 -0
- package/dist/lib/command-catalog.js +63 -0
- package/dist/lib/command-catalog.js.map +1 -0
- package/dist/lib/context-slicer.d.ts +31 -0
- package/dist/lib/context-slicer.js +234 -0
- package/dist/lib/context-slicer.js.map +1 -0
- package/dist/lib/current-work.js +97 -49
- package/dist/lib/current-work.js.map +1 -1
- package/dist/lib/dashboard-page.d.ts +2 -0
- package/dist/lib/dashboard-page.js +285 -0
- package/dist/lib/dashboard-page.js.map +1 -0
- package/dist/lib/dashboard-snapshot.d.ts +62 -0
- package/dist/lib/dashboard-snapshot.js +114 -0
- package/dist/lib/dashboard-snapshot.js.map +1 -0
- package/dist/lib/dashboard.d.ts +2 -0
- package/dist/lib/dashboard.js +398 -8
- package/dist/lib/dashboard.js.map +1 -1
- package/dist/lib/discoveries.d.ts +19 -0
- package/dist/lib/discoveries.js +81 -0
- package/dist/lib/discoveries.js.map +1 -0
- package/dist/lib/doctor.d.ts +26 -0
- package/dist/lib/doctor.js +474 -0
- package/dist/lib/doctor.js.map +1 -0
- package/dist/lib/drift-reconciler.d.ts +38 -0
- package/dist/lib/drift-reconciler.js +155 -0
- package/dist/lib/drift-reconciler.js.map +1 -0
- package/dist/lib/findings.d.ts +26 -2
- package/dist/lib/findings.js +165 -1
- package/dist/lib/findings.js.map +1 -1
- package/dist/lib/gatekeeper.d.ts +20 -0
- package/dist/lib/gatekeeper.js +106 -0
- package/dist/lib/gatekeeper.js.map +1 -0
- package/dist/lib/git-hooks.d.ts +16 -0
- package/dist/lib/git-hooks.js +103 -0
- package/dist/lib/git-hooks.js.map +1 -0
- package/dist/lib/history.d.ts +6 -1
- package/dist/lib/history.js +98 -11
- package/dist/lib/history.js.map +1 -1
- package/dist/lib/ide-extension.d.ts +38 -0
- package/dist/lib/ide-extension.js +61 -0
- package/dist/lib/ide-extension.js.map +1 -0
- package/dist/lib/ideas.d.ts +26 -0
- package/dist/lib/ideas.js +179 -0
- package/dist/lib/ideas.js.map +1 -0
- package/dist/lib/mcp.d.ts +36 -0
- package/dist/lib/mcp.js +653 -0
- package/dist/lib/mcp.js.map +1 -0
- package/dist/lib/status.d.ts +3 -1
- package/dist/lib/status.js +60 -6
- package/dist/lib/status.js.map +1 -1
- package/dist/lib/swarm-orchestrator.d.ts +31 -0
- package/dist/lib/swarm-orchestrator.js +134 -0
- package/dist/lib/swarm-orchestrator.js.map +1 -0
- package/dist/lib/update.js +2 -2
- package/dist/lib/update.js.map +1 -1
- package/dist/lib/version-check.d.ts +22 -0
- package/dist/lib/version-check.js +59 -0
- package/dist/lib/version-check.js.map +1 -0
- package/dist/lib/webview-studio.d.ts +8 -0
- package/dist/lib/webview-studio.js +463 -0
- package/dist/lib/webview-studio.js.map +1 -0
- package/dist/lib/workflow-state.d.ts +23 -0
- package/dist/lib/workflow-state.js +128 -0
- package/dist/lib/workflow-state.js.map +1 -0
- package/dist/scripts/prepare-template.js +3 -3
- package/package.json +1 -1
- package/template/{.claude/skills/00-discover → .agents/skills/00-explore}/SKILL.md +10 -10
- package/template/.agents/skills/10-define/SKILL.md +4 -4
- package/template/.agents/skills/20-spec/SKILL.md +2 -3
- package/template/.agents/skills/30-plan/SKILL.md +2 -3
- package/template/.agents/skills/40-execute/SKILL.md +2 -2
- package/template/.agents/skills/50-verify/SKILL.md +2 -2
- package/template/.agents/skills/60-report/SKILL.md +3 -3
- package/template/{.claude/skills/70-release → .agents/skills/70-deliver}/SKILL.md +7 -13
- package/template/.agents/skills/adopt/SKILL.md +136 -73
- package/template/.agents/skills/audit/SKILL.md +275 -0
- package/template/.agents/skills/autopilot/SKILL.md +228 -147
- package/template/.agents/skills/brainstorm/SKILL.md +3 -3
- package/template/.agents/skills/brief/SKILL.md +74 -51
- package/template/.agents/skills/check/SKILL.md +96 -76
- package/template/.agents/skills/ci/SKILL.md +140 -61
- package/template/.agents/skills/complete/SKILL.md +156 -101
- package/template/.agents/skills/convert-any-to-md/SKILL.md +2 -2
- package/template/.agents/skills/debug/SKILL.md +124 -49
- package/template/.agents/skills/devflow/SKILL.md +16 -10
- package/template/.agents/skills/discovery/SKILL.md +166 -0
- package/template/.agents/skills/doctor/SKILL.md +188 -72
- package/template/.agents/skills/feature/SKILL.md +195 -102
- package/template/.agents/skills/fix/SKILL.md +41 -90
- package/template/.agents/skills/idea/SKILL.md +3 -3
- package/template/.agents/skills/implement/SKILL.md +189 -46
- package/template/.agents/skills/onboard/SKILL.md +215 -85
- package/template/.agents/skills/overview/SKILL.md +51 -85
- package/template/.agents/skills/prototype/SKILL.md +82 -27
- package/template/.agents/skills/release/SKILL.md +160 -0
- package/template/.agents/skills/report-html/SKILL.md +2 -2
- package/template/.agents/skills/rollback/SKILL.md +123 -77
- package/template/.agents/skills/status/SKILL.md +109 -0
- package/template/.agents/skills/test/SKILL.md +2 -2
- package/template/.agents/skills/tests/SKILL.md +126 -0
- package/template/.agents/skills/try/SKILL.md +77 -65
- package/template/{.agents/skills/00-discover → .claude/skills/00-explore}/SKILL.md +9 -9
- package/template/.claude/skills/10-define/SKILL.md +2 -2
- package/template/.claude/skills/20-spec/SKILL.md +1 -1
- package/template/.claude/skills/30-plan/SKILL.md +1 -1
- package/template/.claude/skills/60-report/SKILL.md +1 -1
- package/template/{.agents/skills/70-release → .claude/skills/70-deliver}/SKILL.md +6 -12
- package/template/.claude/skills/adopt/SKILL.md +2 -2
- package/template/.claude/skills/audit/SKILL.md +141 -0
- package/template/.claude/skills/autopilot/SKILL.md +74 -52
- package/template/.claude/skills/brief/SKILL.md +67 -45
- package/template/.claude/skills/ci/SKILL.md +1 -1
- package/template/.claude/skills/complete/SKILL.md +1 -1
- package/template/.claude/skills/debug/SKILL.md +1 -1
- package/template/.claude/skills/devflow/SKILL.md +7 -7
- package/template/.claude/skills/discovery/SKILL.md +145 -0
- package/template/.claude/skills/doctor/SKILL.md +1 -1
- package/template/.claude/skills/feature/SKILL.md +67 -18
- package/template/.claude/skills/fix/SKILL.md +1 -1
- package/template/.claude/skills/idea/SKILL.md +2 -2
- package/template/.claude/skills/onboard/SKILL.md +3 -3
- package/template/.claude/skills/overview/SKILL.md +39 -88
- package/template/.claude/skills/prototype/SKILL.md +1 -1
- package/template/.claude/skills/release/SKILL.md +171 -0
- package/template/.claude/skills/rollback/SKILL.md +2 -2
- package/template/AGENTS.md +5 -5
- package/template/devflow/build-plan.md +62 -0
- package/template/devflow/context/ai-interaction.md +4 -4
- package/template/devflow/context/coding-standards.md +3 -3
- package/template/devflow/context/current-stage.md +1 -1
- package/template/devflow/context/findings.md +1 -1
- package/template/devflow/history/HISTORY.md +2 -2
- package/template/devflow/project-plan.md +79 -0
- package/template/devflow/reference/mockup.html +476 -0
- package/template/devflow/reference/project-overview-template.md +39 -0
- package/template/devflow/reference/running-id-contract.md +24 -2
- package/template/devflow/reference/studio.html +504 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { readCommandCatalog } from "./command-catalog.js";
|
|
2
|
+
import { readDiscoveries } from "./discoveries.js";
|
|
3
|
+
import { runDoctor } from "./doctor.js";
|
|
4
|
+
import { readHistory } from "./history.js";
|
|
5
|
+
import { readProjectStatus } from "./status.js";
|
|
6
|
+
import { checkPackageVersion } from "./version-check.js";
|
|
7
|
+
import { readWorkflowState } from "./workflow-state.js";
|
|
8
|
+
import { evaluateGate } from "./gatekeeper.js";
|
|
9
|
+
import { detectGitDrift } from "./drift-reconciler.js";
|
|
10
|
+
import { generateSwarmPlan } from "./swarm-orchestrator.js";
|
|
11
|
+
import { buildCodeGraph } from "./code-graph.js";
|
|
12
|
+
import { DEVFLOW_MCP_TOOLS } from "./mcp.js";
|
|
13
|
+
const slowCache = new Map();
|
|
14
|
+
async function readDashboardSnapshot(startPath = process.cwd(), options = {}) {
|
|
15
|
+
const now = options.now || Date.now;
|
|
16
|
+
const status = await readProjectStatus(startPath);
|
|
17
|
+
const projectRoot = status.project.root;
|
|
18
|
+
const workflow = await readWorkflowState(projectRoot, status.currentWork);
|
|
19
|
+
const [history, slow, update, gatekeeper, drift, swarm] = await Promise.all([
|
|
20
|
+
readHistory(projectRoot),
|
|
21
|
+
readSlowData(projectRoot, workflow.activeDiscoveryId, now, options.slowTtlMs ?? 15_000),
|
|
22
|
+
checkPackageVersion({
|
|
23
|
+
installedVersion: status.devflow.version,
|
|
24
|
+
fetchImpl: options.fetchImpl,
|
|
25
|
+
timeoutMs: options.versionTimeoutMs,
|
|
26
|
+
cacheTtlMs: options.versionCacheTtlMs,
|
|
27
|
+
now
|
|
28
|
+
}),
|
|
29
|
+
evaluateGate(projectRoot, { strict: false }),
|
|
30
|
+
detectGitDrift(projectRoot),
|
|
31
|
+
generateSwarmPlan(projectRoot)
|
|
32
|
+
]);
|
|
33
|
+
return {
|
|
34
|
+
schemaVersion: 1,
|
|
35
|
+
generatedAt: new Date(now()).toISOString(),
|
|
36
|
+
status,
|
|
37
|
+
workflow,
|
|
38
|
+
history,
|
|
39
|
+
doctor: selectDoctorSummary(slow.doctor),
|
|
40
|
+
discoveries: slow.discoveries,
|
|
41
|
+
update,
|
|
42
|
+
commands: slow.commands,
|
|
43
|
+
adapters: buildAdapterItems(status.devflow.adapters, slow.doctor),
|
|
44
|
+
nextAction: selectDashboardNextAction(status, workflow),
|
|
45
|
+
gatekeeper,
|
|
46
|
+
drift,
|
|
47
|
+
swarm,
|
|
48
|
+
graph: {
|
|
49
|
+
totalFiles: slow.graph.totalFiles,
|
|
50
|
+
totalEdges: slow.graph.totalEdges,
|
|
51
|
+
files: Object.keys(slow.graph.nodes)
|
|
52
|
+
},
|
|
53
|
+
mcpTools: DEVFLOW_MCP_TOOLS
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
async function readSlowData(projectRoot, activeDiscoveryId, now, ttlMs) {
|
|
57
|
+
const cached = slowCache.get(projectRoot);
|
|
58
|
+
if (cached && cached.expiresAt > now() && cached.activeDiscoveryId === activeDiscoveryId) {
|
|
59
|
+
return cached.value;
|
|
60
|
+
}
|
|
61
|
+
const [doctor, discoveries, commands, graph] = await Promise.all([
|
|
62
|
+
runDoctor(projectRoot, { fix: false }),
|
|
63
|
+
readDiscoveries(projectRoot, activeDiscoveryId),
|
|
64
|
+
readCommandCatalog(projectRoot),
|
|
65
|
+
buildCodeGraph(projectRoot)
|
|
66
|
+
]);
|
|
67
|
+
const value = { doctor, discoveries, commands, graph };
|
|
68
|
+
slowCache.set(projectRoot, { expiresAt: now() + ttlMs, activeDiscoveryId, value });
|
|
69
|
+
return value;
|
|
70
|
+
}
|
|
71
|
+
function selectDoctorSummary(report) {
|
|
72
|
+
return {
|
|
73
|
+
checks: report.checks,
|
|
74
|
+
totalChecks: report.totalChecks,
|
|
75
|
+
passCount: report.passCount,
|
|
76
|
+
warnCount: report.warnCount,
|
|
77
|
+
failCount: report.failCount
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
function buildAdapterItems(adapters, doctor) {
|
|
81
|
+
const adapterCheck = doctor.checks.find((check) => check.id === "ai_adapters");
|
|
82
|
+
const state = adapterCheck?.status === "fail"
|
|
83
|
+
? "warning"
|
|
84
|
+
: adapterCheck?.status === "pass"
|
|
85
|
+
? "healthy"
|
|
86
|
+
: "configured";
|
|
87
|
+
const paths = {
|
|
88
|
+
codex: ".agents/skills/",
|
|
89
|
+
copilot: ".agents/skills/",
|
|
90
|
+
antigravity: ".agents/skills/",
|
|
91
|
+
claude: ".claude/skills/"
|
|
92
|
+
};
|
|
93
|
+
return adapters.map((name) => ({
|
|
94
|
+
name,
|
|
95
|
+
path: paths[name] || "AGENTS.md",
|
|
96
|
+
state,
|
|
97
|
+
message: adapterCheck?.message || "Configured in the Nexus-DevFlow manifest."
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
function clearDashboardSnapshotCache() {
|
|
101
|
+
slowCache.clear();
|
|
102
|
+
}
|
|
103
|
+
function selectDashboardNextAction(status, workflow) {
|
|
104
|
+
if (workflow.track !== "deep" || !workflow.currentStage) {
|
|
105
|
+
return status.nextAction;
|
|
106
|
+
}
|
|
107
|
+
const suffix = workflow.activeRunId ? ` ${workflow.activeRunId}` : "";
|
|
108
|
+
return {
|
|
109
|
+
command: `/${workflow.currentStage}${suffix}`,
|
|
110
|
+
reason: `Continue the active Deep-Track run at ${workflow.currentStage}.`
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
export { clearDashboardSnapshotCache, readDashboardSnapshot, selectDashboardNextAction };
|
|
114
|
+
//# sourceMappingURL=dashboard-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dashboard-snapshot.js","sourceRoot":"","sources":["../../lib/dashboard-snapshot.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/dashboard.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import type { DashboardSnapshotOptions } from "./dashboard-snapshot.js";
|
|
1
2
|
interface DashboardServer {
|
|
2
3
|
close: () => Promise<void>;
|
|
3
4
|
url: string;
|
|
4
5
|
}
|
|
5
6
|
interface DashboardServerOptions {
|
|
6
7
|
port?: number;
|
|
8
|
+
snapshotOptions?: DashboardSnapshotOptions;
|
|
7
9
|
}
|
|
8
10
|
declare function startDashboardServer(startPath?: string, options?: DashboardServerOptions): Promise<DashboardServer>;
|
|
9
11
|
declare function openDashboard(url: string): Promise<void>;
|
package/dist/lib/dashboard.js
CHANGED
|
@@ -1,13 +1,17 @@
|
|
|
1
1
|
import { spawn } from "node:child_process";
|
|
2
2
|
import http from "node:http";
|
|
3
|
+
import { DASHBOARD_PAGE_HTML } from "./dashboard-page.js";
|
|
4
|
+
import { readDashboardSnapshot } from "./dashboard-snapshot.js";
|
|
3
5
|
import { readHistory } from "./history.js";
|
|
4
6
|
import { readProjectStatus } from "./status.js";
|
|
7
|
+
import { buildCodeGraph, calculateBlastRadius } from "./code-graph.js";
|
|
8
|
+
import { reconcileState } from "./drift-reconciler.js";
|
|
5
9
|
const DASHBOARD_HOST = "127.0.0.1";
|
|
6
10
|
async function startDashboardServer(startPath = process.cwd(), options = {}) {
|
|
7
11
|
const initialStatus = await readProjectStatus(startPath);
|
|
8
12
|
const projectRoot = initialStatus.project.root;
|
|
9
13
|
const server = http.createServer((request, response) => {
|
|
10
|
-
void handleRequest(projectRoot, request, response);
|
|
14
|
+
void handleRequest(projectRoot, request, response, options.snapshotOptions);
|
|
11
15
|
});
|
|
12
16
|
await new Promise((resolve, reject) => {
|
|
13
17
|
const onError = (error) => {
|
|
@@ -32,7 +36,7 @@ async function startDashboardServer(startPath = process.cwd(), options = {}) {
|
|
|
32
36
|
close: () => closeServer(server)
|
|
33
37
|
};
|
|
34
38
|
}
|
|
35
|
-
async function handleRequest(projectRoot, request, response) {
|
|
39
|
+
async function handleRequest(projectRoot, request, response, snapshotOptions) {
|
|
36
40
|
const method = request.method || "GET";
|
|
37
41
|
if (method !== "GET" && method !== "HEAD") {
|
|
38
42
|
response.setHeader("Allow", "GET, HEAD");
|
|
@@ -41,8 +45,8 @@ async function handleRequest(projectRoot, request, response) {
|
|
|
41
45
|
}
|
|
42
46
|
const pathname = new URL(request.url || "/", `http://${DASHBOARD_HOST}`).pathname;
|
|
43
47
|
if (pathname === "/") {
|
|
44
|
-
response.setHeader("Content-Security-Policy", "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src
|
|
45
|
-
sendResponse(response, method, 200, "text/html; charset=utf-8",
|
|
48
|
+
response.setHeader("Content-Security-Policy", "default-src 'none'; script-src 'unsafe-inline'; style-src 'unsafe-inline' https://fonts.googleapis.com; font-src https://fonts.gstatic.com; connect-src 'self'; img-src 'self' data:; base-uri 'none'; form-action 'none'; frame-ancestors 'none'");
|
|
49
|
+
sendResponse(response, method, 200, "text/html; charset=utf-8", DASHBOARD_PAGE_HTML);
|
|
46
50
|
return;
|
|
47
51
|
}
|
|
48
52
|
if (pathname === "/api/status") {
|
|
@@ -57,6 +61,18 @@ async function handleRequest(projectRoot, request, response) {
|
|
|
57
61
|
}
|
|
58
62
|
return;
|
|
59
63
|
}
|
|
64
|
+
if (pathname === "/api/dashboard") {
|
|
65
|
+
try {
|
|
66
|
+
const snapshot = await readDashboardSnapshot(projectRoot, snapshotOptions);
|
|
67
|
+
sendResponse(response, method, 200, "application/json; charset=utf-8", `${JSON.stringify(snapshot)}\n`);
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
sendResponse(response, method, 500, "application/json; charset=utf-8", `${JSON.stringify({
|
|
71
|
+
error: error instanceof Error ? error.message : "Unable to read dashboard snapshot."
|
|
72
|
+
})}\n`);
|
|
73
|
+
}
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
60
76
|
if (pathname === "/api/history") {
|
|
61
77
|
try {
|
|
62
78
|
const history = await readHistory(projectRoot);
|
|
@@ -69,6 +85,42 @@ async function handleRequest(projectRoot, request, response) {
|
|
|
69
85
|
}
|
|
70
86
|
return;
|
|
71
87
|
}
|
|
88
|
+
if (pathname === "/api/graph") {
|
|
89
|
+
try {
|
|
90
|
+
const urlObj = new URL(request.url || "/", `http://${DASHBOARD_HOST}`);
|
|
91
|
+
const targetFile = urlObj.searchParams.get("file");
|
|
92
|
+
const graph = await buildCodeGraph(projectRoot);
|
|
93
|
+
if (targetFile && targetFile.trim().length > 0) {
|
|
94
|
+
const blast = calculateBlastRadius(graph, targetFile.trim());
|
|
95
|
+
sendResponse(response, method, 200, "application/json; charset=utf-8", `${JSON.stringify(blast)}\n`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
sendResponse(response, method, 200, "application/json; charset=utf-8", `${JSON.stringify({
|
|
99
|
+
totalFiles: graph.totalFiles,
|
|
100
|
+
totalEdges: graph.totalEdges,
|
|
101
|
+
files: Object.keys(graph.nodes)
|
|
102
|
+
})}\n`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
catch (error) {
|
|
106
|
+
sendResponse(response, method, 500, "application/json; charset=utf-8", `${JSON.stringify({
|
|
107
|
+
error: error instanceof Error ? error.message : "Unable to query code graph."
|
|
108
|
+
})}\n`);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
if (pathname === "/api/reconcile") {
|
|
113
|
+
try {
|
|
114
|
+
const result = await reconcileState(projectRoot, { healStage: true });
|
|
115
|
+
sendResponse(response, method, 200, "application/json; charset=utf-8", `${JSON.stringify(result)}\n`);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
sendResponse(response, method, 500, "application/json; charset=utf-8", `${JSON.stringify({
|
|
119
|
+
error: error instanceof Error ? error.message : "Unable to reconcile state."
|
|
120
|
+
})}\n`);
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
72
124
|
if (pathname === "/favicon.ico") {
|
|
73
125
|
sendResponse(response, method, 204, "text/plain; charset=utf-8", "");
|
|
74
126
|
return;
|
|
@@ -432,11 +484,55 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
432
484
|
|
|
433
485
|
.next-action { padding: 24px; }
|
|
434
486
|
|
|
487
|
+
.next-action-header {
|
|
488
|
+
display: flex;
|
|
489
|
+
align-items: center;
|
|
490
|
+
justify-content: space-between;
|
|
491
|
+
gap: 12px;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.next-copy-btn {
|
|
495
|
+
display: inline-flex;
|
|
496
|
+
align-items: center;
|
|
497
|
+
gap: 6px;
|
|
498
|
+
padding: 5px 12px;
|
|
499
|
+
border-radius: 6px;
|
|
500
|
+
background: rgba(118, 168, 255, 0.12);
|
|
501
|
+
color: var(--code-blue);
|
|
502
|
+
border: 1px solid rgba(118, 168, 255, 0.3);
|
|
503
|
+
font: 600 11px/1.3 var(--font-mono);
|
|
504
|
+
cursor: pointer;
|
|
505
|
+
transition: background-color 0.2s ease, border-color 0.2s ease, transform 0.15s ease, color 0.2s ease;
|
|
506
|
+
user-select: none;
|
|
507
|
+
}
|
|
508
|
+
.next-copy-btn:hover {
|
|
509
|
+
background: var(--blue);
|
|
510
|
+
color: #ffffff;
|
|
511
|
+
border-color: var(--blue);
|
|
512
|
+
transform: translateY(-1px);
|
|
513
|
+
}
|
|
514
|
+
.next-copy-btn:active {
|
|
515
|
+
transform: translateY(0);
|
|
516
|
+
}
|
|
517
|
+
.next-copy-btn.copied {
|
|
518
|
+
background: var(--green);
|
|
519
|
+
color: #ffffff;
|
|
520
|
+
border-color: var(--green);
|
|
521
|
+
}
|
|
522
|
+
|
|
435
523
|
.command {
|
|
436
524
|
margin: 13px 0 8px;
|
|
437
525
|
color: var(--code-blue);
|
|
438
526
|
font: 600 clamp(20px, 3vw, 29px)/1.3 var(--font-mono);
|
|
439
527
|
overflow-wrap: anywhere;
|
|
528
|
+
cursor: pointer;
|
|
529
|
+
border-radius: 6px;
|
|
530
|
+
padding: 4px 6px;
|
|
531
|
+
margin-left: -6px;
|
|
532
|
+
transition: background-color 0.2s ease;
|
|
533
|
+
}
|
|
534
|
+
.command:hover {
|
|
535
|
+
background: rgba(118, 168, 255, 0.08);
|
|
440
536
|
}
|
|
441
537
|
|
|
442
538
|
.command-flip { animation: commandFlip 300ms ease-out; }
|
|
@@ -465,7 +561,135 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
465
561
|
li:last-child { border-bottom: 0; }
|
|
466
562
|
li.empty { color: var(--ink-muted); }
|
|
467
563
|
li.empty:hover { background-color: transparent; transform: none; }
|
|
468
|
-
|
|
564
|
+
/* Markdown Inline Formatting */
|
|
565
|
+
.inline-code, code.inline-code {
|
|
566
|
+
display: inline-block;
|
|
567
|
+
padding: 1.5px 6px;
|
|
568
|
+
margin: 0 2px;
|
|
569
|
+
border-radius: 5px;
|
|
570
|
+
background: var(--surface-muted);
|
|
571
|
+
color: var(--blue-dark);
|
|
572
|
+
font-family: var(--font-mono);
|
|
573
|
+
font-size: 0.88em;
|
|
574
|
+
font-weight: 600;
|
|
575
|
+
border: 1px solid var(--line);
|
|
576
|
+
line-height: 1.35;
|
|
577
|
+
vertical-align: baseline;
|
|
578
|
+
word-break: break-word;
|
|
579
|
+
}
|
|
580
|
+
strong {
|
|
581
|
+
color: var(--ink);
|
|
582
|
+
font-weight: 700;
|
|
583
|
+
}
|
|
584
|
+
em {
|
|
585
|
+
font-style: italic;
|
|
586
|
+
color: var(--ink-soft);
|
|
587
|
+
}
|
|
588
|
+
del {
|
|
589
|
+
text-decoration: line-through;
|
|
590
|
+
color: var(--ink-muted);
|
|
591
|
+
}
|
|
592
|
+
.inline-link {
|
|
593
|
+
color: var(--blue);
|
|
594
|
+
text-decoration: none;
|
|
595
|
+
font-weight: 600;
|
|
596
|
+
}
|
|
597
|
+
.inline-link:hover {
|
|
598
|
+
text-decoration: underline;
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
/* Idea Inbox Item Styling */
|
|
602
|
+
.idea-item {
|
|
603
|
+
display: flex;
|
|
604
|
+
flex-direction: column;
|
|
605
|
+
gap: 8px;
|
|
606
|
+
padding: 14px 16px;
|
|
607
|
+
border: 1px solid var(--line);
|
|
608
|
+
border-radius: 10px;
|
|
609
|
+
background: var(--surface-solid);
|
|
610
|
+
margin-bottom: 12px;
|
|
611
|
+
transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.2s ease;
|
|
612
|
+
}
|
|
613
|
+
.idea-item:hover {
|
|
614
|
+
border-color: var(--blue);
|
|
615
|
+
box-shadow: 0 4px 16px rgba(21, 94, 239, 0.09);
|
|
616
|
+
transform: translateY(-1px);
|
|
617
|
+
}
|
|
618
|
+
.idea-header {
|
|
619
|
+
display: flex;
|
|
620
|
+
align-items: center;
|
|
621
|
+
justify-content: space-between;
|
|
622
|
+
gap: 12px;
|
|
623
|
+
}
|
|
624
|
+
.idea-id-badge {
|
|
625
|
+
display: inline-flex;
|
|
626
|
+
align-items: center;
|
|
627
|
+
padding: 4px 9px;
|
|
628
|
+
border-radius: 6px;
|
|
629
|
+
background: rgba(21, 94, 239, 0.08);
|
|
630
|
+
color: var(--blue-dark);
|
|
631
|
+
font: 700 11px/1 var(--font-mono);
|
|
632
|
+
letter-spacing: .04em;
|
|
633
|
+
}
|
|
634
|
+
.idea-title-text {
|
|
635
|
+
color: var(--ink);
|
|
636
|
+
font-size: 14px;
|
|
637
|
+
font-weight: 700;
|
|
638
|
+
letter-spacing: -.01em;
|
|
639
|
+
line-height: 1.45;
|
|
640
|
+
}
|
|
641
|
+
.idea-title-text .inline-code {
|
|
642
|
+
font-size: 0.85em;
|
|
643
|
+
background: rgba(21, 94, 239, 0.06);
|
|
644
|
+
border-color: rgba(21, 94, 239, 0.2);
|
|
645
|
+
}
|
|
646
|
+
.idea-desc-text {
|
|
647
|
+
color: var(--ink-soft);
|
|
648
|
+
font-size: 13px;
|
|
649
|
+
line-height: 1.55;
|
|
650
|
+
}
|
|
651
|
+
.idea-meta-row {
|
|
652
|
+
display: flex;
|
|
653
|
+
align-items: center;
|
|
654
|
+
justify-content: space-between;
|
|
655
|
+
flex-wrap: wrap;
|
|
656
|
+
gap: 10px;
|
|
657
|
+
margin-top: 2px;
|
|
658
|
+
padding-top: 8px;
|
|
659
|
+
border-top: 1px dashed var(--line);
|
|
660
|
+
}
|
|
661
|
+
.idea-feasibility-tag {
|
|
662
|
+
color: var(--ink-soft);
|
|
663
|
+
font-size: 12px;
|
|
664
|
+
background: var(--surface-muted);
|
|
665
|
+
padding: 4px 9px;
|
|
666
|
+
border-radius: 6px;
|
|
667
|
+
line-height: 1.4;
|
|
668
|
+
}
|
|
669
|
+
.idea-feasibility-tag strong {
|
|
670
|
+
color: var(--ink);
|
|
671
|
+
}
|
|
672
|
+
.idea-feasibility-tag .inline-code {
|
|
673
|
+
font-size: 0.85em;
|
|
674
|
+
background: rgba(0, 0, 0, 0.04);
|
|
675
|
+
}
|
|
676
|
+
.idea-cmd-code {
|
|
677
|
+
display: inline-block;
|
|
678
|
+
padding: 4px 10px;
|
|
679
|
+
border-radius: 6px;
|
|
680
|
+
background: var(--surface-muted);
|
|
681
|
+
color: var(--blue-dark);
|
|
682
|
+
font: 600 11px/1.3 var(--font-mono);
|
|
683
|
+
cursor: pointer;
|
|
684
|
+
border: 1px solid var(--line);
|
|
685
|
+
transition: background-color 0.15s ease, color 0.15s ease, border-color 0.15s ease;
|
|
686
|
+
user-select: none;
|
|
687
|
+
}
|
|
688
|
+
.idea-cmd-code:hover {
|
|
689
|
+
background: var(--blue);
|
|
690
|
+
color: #ffffff;
|
|
691
|
+
border-color: var(--blue);
|
|
692
|
+
}
|
|
469
693
|
|
|
470
694
|
footer { margin-top: 24px; color: var(--ink-muted); font: 11px/1.6 var(--font-mono); }
|
|
471
695
|
|
|
@@ -551,6 +775,12 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
551
775
|
<ul id="warnings-list" aria-live="polite"><li class="empty">Loading warnings...</li></ul>
|
|
552
776
|
</article>
|
|
553
777
|
|
|
778
|
+
<article class="card full card-enter" style="animation-delay: 0.26s;">
|
|
779
|
+
<div class="card-head"><span class="label">Idea Inbox & Backlog</span><span class="value" id="ideas-count">-</span></div>
|
|
780
|
+
<div class="muted">Pending ideas from devflow/ideas.md. Start any idea with /feature IDEA-xxx or /00-explore IDEA-xxx.</div>
|
|
781
|
+
<ul id="ideas-list" tabindex="0" aria-label="Idea Inbox"><li class="empty">Loading ideas...</li></ul>
|
|
782
|
+
</article>
|
|
783
|
+
|
|
554
784
|
<article class="card full card-enter" style="animation-delay: 0.28s;">
|
|
555
785
|
<div class="card-head"><span class="label">Completed Work Archive</span><span class="value" id="history-count">-</span></div>
|
|
556
786
|
<div class="muted">Categorized history of features, fixes, and rollbacks.</div>
|
|
@@ -558,8 +788,17 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
558
788
|
</article>
|
|
559
789
|
|
|
560
790
|
<article class="card full code-panel next-action card-enter" style="animation-delay: 0.32s;" id="next-panel">
|
|
561
|
-
<
|
|
562
|
-
|
|
791
|
+
<div class="next-action-header">
|
|
792
|
+
<span class="label">Next Action</span>
|
|
793
|
+
<button class="next-copy-btn" id="next-copy-btn" type="button" aria-label="Copy next command" title="Click to copy next action command">
|
|
794
|
+
<svg viewBox="0 0 24 24" width="13" height="13" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
795
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
796
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
797
|
+
</svg>
|
|
798
|
+
<span id="next-copy-text">Copy Command</span>
|
|
799
|
+
</button>
|
|
800
|
+
</div>
|
|
801
|
+
<div class="command" id="next-command" aria-live="polite" title="Click to copy command">Loading...</div>
|
|
563
802
|
<div class="muted" id="next-reason"></div>
|
|
564
803
|
</article>
|
|
565
804
|
</section>
|
|
@@ -619,6 +858,40 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
619
858
|
setTimeout(() => node.classList.remove(className), 600);
|
|
620
859
|
}
|
|
621
860
|
|
|
861
|
+
function escapeHtml(str) {
|
|
862
|
+
if (typeof str !== 'string') return '';
|
|
863
|
+
return str
|
|
864
|
+
.replace(/&/g, '&')
|
|
865
|
+
.replace(/</g, '<')
|
|
866
|
+
.replace(/>/g, '>')
|
|
867
|
+
.replace(/"/g, '"')
|
|
868
|
+
.replace(/'/g, ''');
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
function renderMarkdownInline(text) {
|
|
872
|
+
if (!text) return '';
|
|
873
|
+
var str = escapeHtml(text);
|
|
874
|
+
|
|
875
|
+
// Inline code
|
|
876
|
+
str = str.replace(new RegExp('\\x60([^\\x60]+)\\x60', 'g'), '<code class="inline-code">$1</code>');
|
|
877
|
+
|
|
878
|
+
// Bold
|
|
879
|
+
str = str.replace(new RegExp('\\\\*\\\\*(.+?)\\\\*\\\\*', 'g'), '<strong>$1</strong>');
|
|
880
|
+
str = str.replace(new RegExp('__(.+?)__', 'g'), '<strong>$1</strong>');
|
|
881
|
+
|
|
882
|
+
// Strikethrough
|
|
883
|
+
str = str.replace(new RegExp('~~(.+?)~~', 'g'), '<del>$1</del>');
|
|
884
|
+
|
|
885
|
+
// Italic
|
|
886
|
+
str = str.replace(new RegExp('(^|[^*])\\\\*([^*\\\\r\\\\n]+)\\\\*([^*]|$)', 'g'), '$1<em>$2</em>$3');
|
|
887
|
+
str = str.replace(new RegExp('(^|[^_])_([^_\\\\r\\\\n]+)_([^_]|$)', 'g'), '$1<em>$2</em>$3');
|
|
888
|
+
|
|
889
|
+
// Links
|
|
890
|
+
str = str.replace(new RegExp('\\\\[([^\\\\]]+)\\\\]\\\\((https?:\\\\/\\\\/[^\\\\s\\\\)]+|#[^\\\\s\\\\)]+)\\\\)', 'g'), '<a href="$2" target="_blank" rel="noopener noreferrer" class="inline-link">$1</a>');
|
|
891
|
+
|
|
892
|
+
return str;
|
|
893
|
+
}
|
|
894
|
+
|
|
622
895
|
function setList(id, values, emptyMessage) {
|
|
623
896
|
const list = byId(id);
|
|
624
897
|
if (!list) return;
|
|
@@ -628,7 +901,7 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
628
901
|
|
|
629
902
|
items.forEach((value, idx) => {
|
|
630
903
|
const item = document.createElement("li");
|
|
631
|
-
item.
|
|
904
|
+
item.innerHTML = renderMarkdownInline(value);
|
|
632
905
|
if (values.length === 0) {
|
|
633
906
|
item.className = "empty";
|
|
634
907
|
} else if (!isFirstLoad && !prevItemsText.includes(value)) {
|
|
@@ -639,6 +912,86 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
639
912
|
});
|
|
640
913
|
}
|
|
641
914
|
|
|
915
|
+
function setIdeas(ideas) {
|
|
916
|
+
const list = byId('ideas-list');
|
|
917
|
+
if (!list) return;
|
|
918
|
+
list.replaceChildren();
|
|
919
|
+
|
|
920
|
+
const items = ideas && ideas.pending && ideas.pending.length > 0 ? ideas.pending : [];
|
|
921
|
+
if (items.length === 0) {
|
|
922
|
+
const emptyItem = document.createElement('li');
|
|
923
|
+
emptyItem.className = 'empty';
|
|
924
|
+
emptyItem.textContent = 'No pending ideas in devflow/ideas.md';
|
|
925
|
+
list.append(emptyItem);
|
|
926
|
+
return;
|
|
927
|
+
}
|
|
928
|
+
|
|
929
|
+
items.forEach((idea, idx) => {
|
|
930
|
+
const li = document.createElement('li');
|
|
931
|
+
li.className = 'idea-item';
|
|
932
|
+
if (!isFirstLoad) {
|
|
933
|
+
li.classList.add('fade-slide-in');
|
|
934
|
+
li.style.animationDelay = (idx * 40) + 'ms';
|
|
935
|
+
}
|
|
936
|
+
|
|
937
|
+
const headerDiv = document.createElement('div');
|
|
938
|
+
headerDiv.className = 'idea-header';
|
|
939
|
+
|
|
940
|
+
const badgeSpan = document.createElement('span');
|
|
941
|
+
badgeSpan.className = 'idea-id-badge';
|
|
942
|
+
badgeSpan.textContent = idea.id;
|
|
943
|
+
|
|
944
|
+
const cmdCode = document.createElement('code');
|
|
945
|
+
cmdCode.className = 'idea-cmd-code';
|
|
946
|
+
cmdCode.textContent = '/feature ' + idea.id;
|
|
947
|
+
cmdCode.title = 'Click to copy command';
|
|
948
|
+
cmdCode.onclick = () => {
|
|
949
|
+
if (navigator.clipboard) {
|
|
950
|
+
navigator.clipboard.writeText('/feature ' + idea.id);
|
|
951
|
+
const orig = cmdCode.textContent;
|
|
952
|
+
cmdCode.textContent = 'Copied!';
|
|
953
|
+
setTimeout(() => { cmdCode.textContent = orig; }, 1200);
|
|
954
|
+
}
|
|
955
|
+
};
|
|
956
|
+
|
|
957
|
+
headerDiv.append(badgeSpan, cmdCode);
|
|
958
|
+
|
|
959
|
+
const titleDiv = document.createElement('div');
|
|
960
|
+
titleDiv.className = 'idea-title-text';
|
|
961
|
+
titleDiv.innerHTML = renderMarkdownInline(idea.title);
|
|
962
|
+
|
|
963
|
+
li.append(headerDiv, titleDiv);
|
|
964
|
+
|
|
965
|
+
if (idea.rawInput) {
|
|
966
|
+
const descDiv = document.createElement('div');
|
|
967
|
+
descDiv.className = 'idea-desc-text';
|
|
968
|
+
descDiv.innerHTML = renderMarkdownInline(idea.rawInput);
|
|
969
|
+
li.append(descDiv);
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
if (idea.feasibility || idea.value || idea.date) {
|
|
973
|
+
const metaDiv = document.createElement('div');
|
|
974
|
+
metaDiv.className = 'idea-meta-row';
|
|
975
|
+
if (idea.feasibility) {
|
|
976
|
+
const feasSpan = document.createElement('span');
|
|
977
|
+
feasSpan.className = 'idea-feasibility-tag';
|
|
978
|
+
feasSpan.innerHTML = '⚡ ' + renderMarkdownInline(idea.feasibility);
|
|
979
|
+
metaDiv.append(feasSpan);
|
|
980
|
+
}
|
|
981
|
+
if (idea.date) {
|
|
982
|
+
const dateSpan = document.createElement('span');
|
|
983
|
+
dateSpan.className = 'muted';
|
|
984
|
+
dateSpan.style.fontSize = '11px';
|
|
985
|
+
dateSpan.textContent = idea.date;
|
|
986
|
+
metaDiv.append(dateSpan);
|
|
987
|
+
}
|
|
988
|
+
li.append(metaDiv);
|
|
989
|
+
}
|
|
990
|
+
|
|
991
|
+
list.append(li);
|
|
992
|
+
});
|
|
993
|
+
}
|
|
994
|
+
|
|
642
995
|
async function refreshStatus() {
|
|
643
996
|
if (refreshing) return;
|
|
644
997
|
refreshing = true;
|
|
@@ -732,6 +1085,17 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
732
1085
|
}
|
|
733
1086
|
setList('warnings-list', warnings.map(w => w.message), 'No active warnings or drift');
|
|
734
1087
|
|
|
1088
|
+
const ideas = data.ideas || { totalPending: 0, pending: [] };
|
|
1089
|
+
const prevIdeasCount = prevData?.ideas?.totalPending ?? 0;
|
|
1090
|
+
const ideasNode = byId('ideas-count');
|
|
1091
|
+
if (!isFirstLoad && ideas.totalPending !== prevIdeasCount) {
|
|
1092
|
+
animateCount(ideasNode, prevIdeasCount, ideas.totalPending);
|
|
1093
|
+
triggerFlash(ideasNode, 'green');
|
|
1094
|
+
} else {
|
|
1095
|
+
ideasNode.textContent = ideas.totalPending + ' pending';
|
|
1096
|
+
}
|
|
1097
|
+
setIdeas(ideas);
|
|
1098
|
+
|
|
735
1099
|
const historyItems = historyData.items || [];
|
|
736
1100
|
const prevHistoryCount = prevData?.historyCount ?? 0;
|
|
737
1101
|
const historyNode = byId('history-count');
|
|
@@ -778,6 +1142,32 @@ const DASHBOARD_HTML = `<!doctype html>
|
|
|
778
1142
|
}
|
|
779
1143
|
}
|
|
780
1144
|
|
|
1145
|
+
function setupNextActionCopy() {
|
|
1146
|
+
const copyBtn = byId('next-copy-btn');
|
|
1147
|
+
const cmdNode = byId('next-command');
|
|
1148
|
+
const copyText = byId('next-copy-text');
|
|
1149
|
+
|
|
1150
|
+
const performCopy = () => {
|
|
1151
|
+
const textToCopy = cmdNode ? cmdNode.textContent.trim() : '';
|
|
1152
|
+
if (!textToCopy || textToCopy === 'Loading...') return;
|
|
1153
|
+
if (navigator.clipboard) {
|
|
1154
|
+
navigator.clipboard.writeText(textToCopy);
|
|
1155
|
+
if (copyBtn && copyText) {
|
|
1156
|
+
copyBtn.classList.add('copied');
|
|
1157
|
+
copyText.textContent = '✓ Copied!';
|
|
1158
|
+
setTimeout(() => {
|
|
1159
|
+
copyBtn.classList.remove('copied');
|
|
1160
|
+
copyText.textContent = 'Copy Command';
|
|
1161
|
+
}, 1400);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
1164
|
+
};
|
|
1165
|
+
|
|
1166
|
+
if (copyBtn) copyBtn.onclick = performCopy;
|
|
1167
|
+
if (cmdNode) cmdNode.onclick = performCopy;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
setupNextActionCopy();
|
|
781
1171
|
refreshStatus();
|
|
782
1172
|
setInterval(refreshStatus, 2000);
|
|
783
1173
|
</script>
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../lib/dashboard.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAC3C,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"dashboard.js","sourceRoot":"","sources":["../../lib/dashboard.ts"],"names":[],"mappings":"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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;QA0+BvB,CAAC;AAET,OAAO,EAAE,aAAa,EAAE,oBAAoB,EAAE,CAAC"}
|