@adityaaria/spark 6.0.17 → 6.0.19
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/package.json +1 -1
- package/skills/audit/SKILL.md +30 -0
- package/skills/bug-fix/SKILL.md +30 -0
- package/skills/enhancement/SKILL.md +28 -0
- package/skills/project-scanner/SKILL.md +59 -0
- package/src/cli/index.js +6 -0
- package/src/dashboard/public/index.html +369 -0
- package/src/dashboard/server.js +86 -0
package/package.json
CHANGED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: audit
|
|
3
|
+
description: Use to audit code quality, architecture, and security against the project's documented anti-patterns and standards.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Audit (Meta-Skill)
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Use this skill to orchestrate a rigorous code review or architecture audit. You must NOT evaluate the code based on generic AI preferences or external "best practices" unless they align with the project's established rules. Your sole source of truth for the audit is the local knowledge base.
|
|
11
|
+
|
|
12
|
+
**Announce at start:** "spark detection 💥 Using audit meta-skill to enforce project standards"
|
|
13
|
+
|
|
14
|
+
## Standard Operating Procedure (SOP)
|
|
15
|
+
|
|
16
|
+
You MUST execute this checklist strictly in order. Do not skip steps.
|
|
17
|
+
|
|
18
|
+
### Phase 1: Context Grounding
|
|
19
|
+
- `[ ]` **Step 1:** Read the local knowledge base located in `.docs/` (generated by the `project-scanner` skill). Focus heavily on `STANDARDS.md`, API contracts, and the list of Legacy Traps / Anti-patterns.
|
|
20
|
+
- `[ ]` **Step 2:** Identify the exact boundaries of the code to be audited (e.g., a specific Pull Request, a newly written module, or a suspected legacy file).
|
|
21
|
+
|
|
22
|
+
### Phase 2: Tactical Delegation (Review)
|
|
23
|
+
- `[ ]` **Step 3:** Invoke the `receiving-code-review` (or `requesting-code-review` depending on your role) skill to structure your feedback.
|
|
24
|
+
- `[ ]` **Step 4:** Compare the target code strictly against the Legacy Traps found in Phase 1. Actively flag any re-introduction of:
|
|
25
|
+
- Business logic leaks.
|
|
26
|
+
- Raw SQL/ORM mixing or N+1 queries.
|
|
27
|
+
- Undocumented bypass comments (`@ts-ignore`, `// HACK`).
|
|
28
|
+
|
|
29
|
+
### Phase 3: Reporting
|
|
30
|
+
- `[ ]` **Step 5:** Output an Audit Report summarizing the violations. If the code is clean, explicitly state that it complies with the `.docs/` standards.
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bug-fix
|
|
3
|
+
description: Use to fix a bug while adhering strictly to the project's established standards and debugging protocols.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Bug Fix (Meta-Skill)
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Use this skill to orchestrate a safe, standard-compliant bug fix. You are not allowed to blindly modify code based on assumptions. You must first ground yourself in the project's context, trace the bug systematically, and execute the fix using disciplined test-driven practices.
|
|
11
|
+
|
|
12
|
+
**Announce at start:** "spark detection 💥 Using bug-fix meta-skill to coordinate this investigation"
|
|
13
|
+
|
|
14
|
+
## Standard Operating Procedure (SOP)
|
|
15
|
+
|
|
16
|
+
You MUST execute this checklist strictly in order. Do not skip steps.
|
|
17
|
+
|
|
18
|
+
### Phase 1: Context Grounding
|
|
19
|
+
Before touching any code, you must understand the rules of this repository.
|
|
20
|
+
- `[ ]` **Step 1:** Read the local knowledge base located in `.docs/` (generated by the `project-scanner` skill). Specifically, look for `PROJECT_SCAN.md`, `STANDARDS.md`, and `API_CONTRACT.md`.
|
|
21
|
+
- `[ ]` **Step 2:** Identify any Legacy Traps or Anti-Patterns documented in `.docs/`. You must NOT re-introduce these patterns while fixing the bug.
|
|
22
|
+
|
|
23
|
+
### Phase 2: Tactical Delegation (Investigation)
|
|
24
|
+
Do not guess the root cause. Delegate the investigation.
|
|
25
|
+
- `[ ]` **Step 3:** Invoke the `systematic-debugging` skill. Follow its rigorous condition-based waiting and root-cause tracing protocols to find exactly what is broken.
|
|
26
|
+
- `[ ]` *(Optional)*: If the bug spans multiple independent services, invoke `dispatching-parallel-agents` to investigate them concurrently.
|
|
27
|
+
|
|
28
|
+
### Phase 3: Execution
|
|
29
|
+
Once the root cause is proven (not guessed), fix it safely.
|
|
30
|
+
- `[ ]` **Step 4:** Invoke the `test-driven-development` skill. Write a failing test that reproduces the bug, then implement the minimal code required to make it pass. Ensure your fix aligns with the architecture rules read in Phase 1.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: enhancement
|
|
3
|
+
description: Use to build a new feature or enhancement by aligning strictly with the project's existing business flow and domain architecture.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Enhancement (Meta-Skill)
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Use this skill to orchestrate the safe addition of new features. Do not inject your own architectural preferences. You must ground yourself in the project's reality, scope the feature thoughtfully, plan it out, and execute it using the project's established conventions.
|
|
11
|
+
|
|
12
|
+
**Announce at start:** "spark detection 💥 Using enhancement meta-skill to coordinate feature development"
|
|
13
|
+
|
|
14
|
+
## Standard Operating Procedure (SOP)
|
|
15
|
+
|
|
16
|
+
You MUST execute this checklist strictly in order. Do not skip steps.
|
|
17
|
+
|
|
18
|
+
### Phase 1: Context Grounding
|
|
19
|
+
- `[ ]` **Step 1:** Read the local knowledge base located in `.docs/` (generated by the `project-scanner` skill). Focus heavily on `BUSINESS_FLOW.md` and `DOMAINS/`.
|
|
20
|
+
- `[ ]` **Step 2:** Determine which existing Domain will own this new feature, or if a completely new Domain is justified. Ensure your design respects the existing boundaries.
|
|
21
|
+
|
|
22
|
+
### Phase 2: Tactical Delegation (Scoping & Planning)
|
|
23
|
+
- `[ ]` **Step 3:** Invoke the `brainstorming` skill to map out edge cases, UX flows, and failure modes for this enhancement.
|
|
24
|
+
- `[ ]` **Step 4:** Invoke the `writing-plans` skill to create a step-by-step implementation plan (saving to `docs/spark/plans/`). The plan MUST adhere to the standards found in Phase 1.
|
|
25
|
+
|
|
26
|
+
### Phase 3: Execution
|
|
27
|
+
- `[ ]` **Step 5:** Invoke the `executing-plans` skill to write the code.
|
|
28
|
+
- `[ ]` *(Optional)*: If the enhancement is massive (e.g., touching frontend UI, backend API, and database migrations simultaneously), invoke `dispatching-parallel-agents` to delegate the sub-tasks to specialized subagents.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: project-scanner
|
|
3
|
+
description: Use when analyzing an undocumented, legacy, or new project repository to extract its architectural and operational DNA.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Project Scanner
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
Use this skill to scan and document a codebase's architecture, operational rules, and hidden legacy traps. You must rely on file reading and listing tools rather than making assumptions. Traverse the root directory and key source directories to extract the project's DNA. This skill is polyglot—it applies to any tech stack (e.g., TypeScript, Go, Python, Java, PHP, Rust).
|
|
11
|
+
|
|
12
|
+
**Announce at start:** "spark detection 💥 Using project-scanner to analyze repository DNA"
|
|
13
|
+
|
|
14
|
+
**Save findings to:** The `.docs/` directory. Create it if it does not exist. For large projects, break down the documentation logically (e.g., `.docs/PROJECT_SCAN.md`, `.docs/API_CONTRACT.md`, `.docs/DOMAINS/`) instead of creating one massive monolithic file.
|
|
15
|
+
|
|
16
|
+
## Handling Massive Codebases (Subagent Delegation)
|
|
17
|
+
If the repository is extremely large and analyzing all four pillars sequentially risks exceeding context limits or taking too long:
|
|
18
|
+
- Invoke the `dispatching-parallel-agents` skill.
|
|
19
|
+
- Delegate specific analytical tasks to specialized subagents in parallel (e.g., Subagent A extracts the API Contract, Subagent B extracts the Business Flow).
|
|
20
|
+
- Compile their findings into the final `.docs/` reports.
|
|
21
|
+
|
|
22
|
+
## Target Extraction Artifacts
|
|
23
|
+
|
|
24
|
+
Your final analysis must thoroughly document these four pillars:
|
|
25
|
+
|
|
26
|
+
### 1. API CONTRACT
|
|
27
|
+
Identify how the application communicates internally and externally.
|
|
28
|
+
- **Priority Target:** Actively search for Swagger (`swagger.yaml`, `swagger.json`) or OpenAPI specifications. These are the single source of truth for the API contract.
|
|
29
|
+
- **Secondary Targets:** Discover REST/GraphQL endpoints, gRPC protos, ORM schemas, Data Transfer Objects (DTOs), and core data models.
|
|
30
|
+
- **Legacy Traps:** Flag any endpoints that violate current conventions or bypass standard authentication/validation layers.
|
|
31
|
+
|
|
32
|
+
### 2. WORKFLOW
|
|
33
|
+
Identify how the project is built, run, tested, and deployed.
|
|
34
|
+
- **Targets:** CI/CD pipelines (e.g., `.github/workflows`, `.gitlab-ci.yml`), build scripts (Makefiles, Gradle, NPM scripts), Dockerfiles, and deployment scripts.
|
|
35
|
+
- **Testing:** Identify the test framework, strategy (Unit, Integration, E2E), and explicitly highlight critical areas that lack test coverage.
|
|
36
|
+
|
|
37
|
+
### 3. BUSINESS FLOW & DOMAINS
|
|
38
|
+
Identify the core business logic and user journeys.
|
|
39
|
+
- **Targets:** Core domains, domain services, use cases, and state machines.
|
|
40
|
+
- **Domain Mapping:** Draft a list of business domains (e.g., Auth, Checkout, Inventory). Note their complexity, risk level, and the specific modules/packages that own them. Avoid forcing a domain split if the project is small.
|
|
41
|
+
|
|
42
|
+
### 4. STANDARDS & ANTI-PATTERNS
|
|
43
|
+
Identify conventions, rules, and technical debt enforced or found in the codebase.
|
|
44
|
+
- **Targets:** Code styling conventions, linting rules, architectural patterns (e.g., MVC, Hexagonal, Clean Architecture).
|
|
45
|
+
- **Anti-Pattern Detection (CRITICAL):** Actively search for and document legacy traps:
|
|
46
|
+
- Business logic leaked into controllers, presentation, or routing layers.
|
|
47
|
+
- Inconsistent database queries (e.g., raw queries mixed with ORM) or N+1 query problems.
|
|
48
|
+
- Misleading naming (e.g., `Utils` files containing heavy domain logic).
|
|
49
|
+
- Hardcoded external URLs, API keys, or magic numbers.
|
|
50
|
+
- Bypass comments (e.g., `@ts-ignore`, `// HACK`, `// FIXME`, `#nosec`) without explanations.
|
|
51
|
+
- Duplicated logic across modules.
|
|
52
|
+
|
|
53
|
+
## Execution Checklist
|
|
54
|
+
|
|
55
|
+
- `[ ]` **Step 1:** Announce skill usage exactly as required.
|
|
56
|
+
- `[ ]` **Step 2:** Scan root configuration files and prioritize searching for Swagger/OpenAPI specifications.
|
|
57
|
+
- `[ ]` **Step 3:** Scan source directory structures to infer the language, framework, and architectural patterns.
|
|
58
|
+
- `[ ]` **Step 4:** Analyze testing frameworks, CI/CD workflows, and actively hunt for anti-patterns and legacy traps.
|
|
59
|
+
- `[ ]` **Step 5:** Write the comprehensive report(s) into the `.docs/` directory.
|
package/src/cli/index.js
CHANGED
|
@@ -14,6 +14,12 @@ export async function run(argv = [], env = process.env) {
|
|
|
14
14
|
return;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
+
if (command === 'dashboard' || command === 'ui') {
|
|
18
|
+
const { startDashboard } = await import('../dashboard/server.js');
|
|
19
|
+
startDashboard();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
17
23
|
throw new Error(`Unknown command: ${command}`);
|
|
18
24
|
}
|
|
19
25
|
|
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>SPARK | Command Center</title>
|
|
7
|
+
<!-- Google Fonts -->
|
|
8
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap" rel="stylesheet">
|
|
9
|
+
<!-- Marked.js for Markdown parsing -->
|
|
10
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
11
|
+
<!-- Mermaid.js for Diagrams -->
|
|
12
|
+
<script src="https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js"></script>
|
|
13
|
+
<style>
|
|
14
|
+
:root {
|
|
15
|
+
--bg-color: #0b0f19;
|
|
16
|
+
--glass-bg: rgba(255, 255, 255, 0.03);
|
|
17
|
+
--glass-border: rgba(255, 255, 255, 0.08);
|
|
18
|
+
--accent-glow: rgba(0, 255, 170, 0.15);
|
|
19
|
+
--accent: #00ffaa;
|
|
20
|
+
--danger: #ff4757;
|
|
21
|
+
--text-main: #f1f5f9;
|
|
22
|
+
--text-muted: #94a3b8;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
* {
|
|
26
|
+
box-sizing: border-box;
|
|
27
|
+
margin: 0;
|
|
28
|
+
padding: 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
body {
|
|
32
|
+
font-family: 'Inter', sans-serif;
|
|
33
|
+
background-color: var(--bg-color);
|
|
34
|
+
color: var(--text-main);
|
|
35
|
+
min-height: 100vh;
|
|
36
|
+
display: flex;
|
|
37
|
+
background-image:
|
|
38
|
+
radial-gradient(circle at 15% 50%, var(--accent-glow), transparent 25%),
|
|
39
|
+
radial-gradient(circle at 85% 30%, rgba(255, 71, 87, 0.1), transparent 25%);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/* Sidebar */
|
|
43
|
+
.sidebar {
|
|
44
|
+
width: 280px;
|
|
45
|
+
background: var(--glass-bg);
|
|
46
|
+
border-right: 1px solid var(--glass-border);
|
|
47
|
+
backdrop-filter: blur(12px);
|
|
48
|
+
padding: 24px;
|
|
49
|
+
display: flex;
|
|
50
|
+
flex-direction: column;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.logo {
|
|
54
|
+
font-size: 24px;
|
|
55
|
+
font-weight: 700;
|
|
56
|
+
letter-spacing: 2px;
|
|
57
|
+
margin-bottom: 40px;
|
|
58
|
+
display: flex;
|
|
59
|
+
align-items: center;
|
|
60
|
+
gap: 10px;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.logo span {
|
|
64
|
+
color: var(--accent);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
.nav-item {
|
|
68
|
+
padding: 12px 16px;
|
|
69
|
+
margin-bottom: 8px;
|
|
70
|
+
border-radius: 8px;
|
|
71
|
+
cursor: pointer;
|
|
72
|
+
transition: all 0.2s ease;
|
|
73
|
+
color: var(--text-muted);
|
|
74
|
+
font-weight: 600;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.nav-item:hover {
|
|
78
|
+
background: rgba(255,255,255,0.05);
|
|
79
|
+
color: var(--text-main);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.nav-item.active {
|
|
83
|
+
background: rgba(0, 255, 170, 0.1);
|
|
84
|
+
color: var(--accent);
|
|
85
|
+
border: 1px solid rgba(0, 255, 170, 0.2);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* Main Content */
|
|
89
|
+
.content {
|
|
90
|
+
flex: 1;
|
|
91
|
+
padding: 40px;
|
|
92
|
+
overflow-y: auto;
|
|
93
|
+
height: 100vh;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
.header {
|
|
97
|
+
display: flex;
|
|
98
|
+
justify-content: space-between;
|
|
99
|
+
align-items: center;
|
|
100
|
+
margin-bottom: 40px;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
.header h1 {
|
|
104
|
+
font-size: 32px;
|
|
105
|
+
font-weight: 700;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
.status-badge {
|
|
109
|
+
background: rgba(0, 255, 170, 0.1);
|
|
110
|
+
color: var(--accent);
|
|
111
|
+
padding: 6px 12px;
|
|
112
|
+
border-radius: 20px;
|
|
113
|
+
font-size: 14px;
|
|
114
|
+
font-weight: 600;
|
|
115
|
+
border: 1px solid rgba(0, 255, 170, 0.2);
|
|
116
|
+
display: flex;
|
|
117
|
+
align-items: center;
|
|
118
|
+
gap: 6px;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.status-badge::before {
|
|
122
|
+
content: '';
|
|
123
|
+
display: block;
|
|
124
|
+
width: 8px;
|
|
125
|
+
height: 8px;
|
|
126
|
+
border-radius: 50%;
|
|
127
|
+
background: var(--accent);
|
|
128
|
+
box-shadow: 0 0 8px var(--accent);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/* Cards / Panels */
|
|
132
|
+
.panel {
|
|
133
|
+
background: var(--glass-bg);
|
|
134
|
+
border: 1px solid var(--glass-border);
|
|
135
|
+
border-radius: 16px;
|
|
136
|
+
padding: 30px;
|
|
137
|
+
backdrop-filter: blur(10px);
|
|
138
|
+
margin-bottom: 30px;
|
|
139
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.2);
|
|
140
|
+
animation: fadeIn 0.4s ease forwards;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
@keyframes fadeIn {
|
|
144
|
+
from { opacity: 0; transform: translateY(10px); }
|
|
145
|
+
to { opacity: 1; transform: translateY(0); }
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Markdown Styling */
|
|
149
|
+
.markdown-body {
|
|
150
|
+
line-height: 1.6;
|
|
151
|
+
}
|
|
152
|
+
.markdown-body h1, .markdown-body h2, .markdown-body h3 {
|
|
153
|
+
margin-top: 24px;
|
|
154
|
+
margin-bottom: 16px;
|
|
155
|
+
font-weight: 600;
|
|
156
|
+
color: var(--text-main);
|
|
157
|
+
}
|
|
158
|
+
.markdown-body h1 { font-size: 28px; border-bottom: 1px solid var(--glass-border); padding-bottom: 8px; }
|
|
159
|
+
.markdown-body h2 { font-size: 22px; }
|
|
160
|
+
.markdown-body p { margin-bottom: 16px; color: var(--text-muted); }
|
|
161
|
+
.markdown-body ul, .markdown-body ol { margin-bottom: 16px; padding-left: 24px; color: var(--text-muted); }
|
|
162
|
+
.markdown-body li { margin-bottom: 8px; }
|
|
163
|
+
.markdown-body code {
|
|
164
|
+
background: rgba(0,0,0,0.3);
|
|
165
|
+
padding: 3px 6px;
|
|
166
|
+
border-radius: 4px;
|
|
167
|
+
font-family: monospace;
|
|
168
|
+
font-size: 14px;
|
|
169
|
+
color: #ff9e64;
|
|
170
|
+
}
|
|
171
|
+
.markdown-body pre {
|
|
172
|
+
background: #111520;
|
|
173
|
+
padding: 16px;
|
|
174
|
+
border-radius: 8px;
|
|
175
|
+
overflow-x: auto;
|
|
176
|
+
border: 1px solid var(--glass-border);
|
|
177
|
+
margin-bottom: 16px;
|
|
178
|
+
}
|
|
179
|
+
.markdown-body pre code {
|
|
180
|
+
background: none;
|
|
181
|
+
padding: 0;
|
|
182
|
+
color: #f1f5f9;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/* Empty State */
|
|
186
|
+
.empty-state {
|
|
187
|
+
text-align: center;
|
|
188
|
+
padding: 60px 20px;
|
|
189
|
+
color: var(--text-muted);
|
|
190
|
+
}
|
|
191
|
+
.empty-state h3 { margin-bottom: 10px; color: var(--text-main); }
|
|
192
|
+
.empty-state p { margin-bottom: 20px; }
|
|
193
|
+
.btn {
|
|
194
|
+
background: var(--text-main);
|
|
195
|
+
color: var(--bg-color);
|
|
196
|
+
border: none;
|
|
197
|
+
padding: 10px 24px;
|
|
198
|
+
border-radius: 6px;
|
|
199
|
+
font-weight: 600;
|
|
200
|
+
cursor: pointer;
|
|
201
|
+
transition: transform 0.1s ease;
|
|
202
|
+
}
|
|
203
|
+
.btn:hover { transform: scale(1.05); }
|
|
204
|
+
|
|
205
|
+
</style>
|
|
206
|
+
</head>
|
|
207
|
+
<body>
|
|
208
|
+
|
|
209
|
+
<div class="sidebar">
|
|
210
|
+
<div class="logo">
|
|
211
|
+
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="var(--accent)" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
212
|
+
<path d="M13 2L3 14h9l-1 8 10-12h-9l1-8z"/>
|
|
213
|
+
</svg>
|
|
214
|
+
SPARK<span>UI</span>
|
|
215
|
+
</div>
|
|
216
|
+
<div id="nav-container">
|
|
217
|
+
<!-- Nav items will be injected here -->
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div class="content">
|
|
222
|
+
<div class="header">
|
|
223
|
+
<h1 id="page-title">Dashboard Overview</h1>
|
|
224
|
+
<div class="status-badge" id="agent-status">Checking Agents...</div>
|
|
225
|
+
</div>
|
|
226
|
+
|
|
227
|
+
<div id="content-container">
|
|
228
|
+
<div class="panel empty-state">
|
|
229
|
+
<h3>Loading Knowledge Base...</h3>
|
|
230
|
+
<p>Fetching architectural DNA from .docs/</p>
|
|
231
|
+
</div>
|
|
232
|
+
</div>
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
<script>
|
|
236
|
+
// Initialize Mermaid
|
|
237
|
+
mermaid.initialize({
|
|
238
|
+
startOnLoad: false,
|
|
239
|
+
theme: 'dark',
|
|
240
|
+
fontFamily: 'Inter'
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
let docsData = [];
|
|
244
|
+
|
|
245
|
+
async function fetchDocs() {
|
|
246
|
+
try {
|
|
247
|
+
const res = await fetch('/api/docs');
|
|
248
|
+
const data = await res.json();
|
|
249
|
+
|
|
250
|
+
if (data.error) {
|
|
251
|
+
renderEmptyState(data.error);
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
docsData = data.docs;
|
|
256
|
+
renderNav();
|
|
257
|
+
|
|
258
|
+
// Show first doc by default or overview
|
|
259
|
+
if(docsData.length > 0) {
|
|
260
|
+
// Try to find PROJECT_SCAN.md first
|
|
261
|
+
const mainDoc = docsData.find(d => d.filename === 'PROJECT_SCAN.md') || docsData[0];
|
|
262
|
+
renderDoc(mainDoc.filename);
|
|
263
|
+
} else {
|
|
264
|
+
renderEmptyState("The .docs/ directory is empty. Generate artifacts using the project-scanner skill.");
|
|
265
|
+
}
|
|
266
|
+
} catch (err) {
|
|
267
|
+
renderEmptyState("Failed to connect to the SPARK local server.");
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
async function fetchStatus() {
|
|
272
|
+
try {
|
|
273
|
+
const res = await fetch('/api/status');
|
|
274
|
+
const data = await res.json();
|
|
275
|
+
const badge = document.getElementById('agent-status');
|
|
276
|
+
if (data.installed && data.agents) {
|
|
277
|
+
badge.innerHTML = `Agents Active: ${data.agents.join(', ')}`;
|
|
278
|
+
} else {
|
|
279
|
+
badge.innerHTML = `No Agents Detected`;
|
|
280
|
+
badge.style.color = 'var(--text-muted)';
|
|
281
|
+
badge.style.borderColor = 'var(--glass-border)';
|
|
282
|
+
}
|
|
283
|
+
} catch (e) {
|
|
284
|
+
console.error(e);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
function renderNav() {
|
|
289
|
+
const nav = document.getElementById('nav-container');
|
|
290
|
+
nav.innerHTML = '';
|
|
291
|
+
|
|
292
|
+
docsData.forEach(doc => {
|
|
293
|
+
const el = document.createElement('div');
|
|
294
|
+
el.className = 'nav-item';
|
|
295
|
+
el.innerText = doc.filename.replace('.md', '').replace(/_/g, ' ');
|
|
296
|
+
el.onclick = () => renderDoc(doc.filename);
|
|
297
|
+
nav.appendChild(el);
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function renderDoc(filename) {
|
|
302
|
+
// Update active state
|
|
303
|
+
document.querySelectorAll('.nav-item').forEach(el => {
|
|
304
|
+
if (el.innerText === filename.replace('.md', '').replace(/_/g, ' ')) {
|
|
305
|
+
el.classList.add('active');
|
|
306
|
+
} else {
|
|
307
|
+
el.classList.remove('active');
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
const doc = docsData.find(d => d.filename === filename);
|
|
312
|
+
if (!doc) return;
|
|
313
|
+
|
|
314
|
+
document.getElementById('page-title').innerText = filename;
|
|
315
|
+
|
|
316
|
+
// Render Markdown
|
|
317
|
+
const container = document.getElementById('content-container');
|
|
318
|
+
const parsedHTML = marked.parse(doc.content);
|
|
319
|
+
|
|
320
|
+
container.innerHTML = `
|
|
321
|
+
<div class="panel markdown-body">
|
|
322
|
+
${parsedHTML}
|
|
323
|
+
</div>
|
|
324
|
+
`;
|
|
325
|
+
|
|
326
|
+
// Render Mermaid diagrams if any
|
|
327
|
+
const codeBlocks = container.querySelectorAll('code.language-mermaid');
|
|
328
|
+
codeBlocks.forEach((block, index) => {
|
|
329
|
+
const graphDefinition = block.innerText;
|
|
330
|
+
const parent = block.parentElement; // the <pre> tag
|
|
331
|
+
|
|
332
|
+
// Create a div for mermaid to render into
|
|
333
|
+
const mermaidDiv = document.createElement('div');
|
|
334
|
+
mermaidDiv.className = 'mermaid-chart';
|
|
335
|
+
mermaidDiv.id = `mermaid-${index}`;
|
|
336
|
+
|
|
337
|
+
// Render it
|
|
338
|
+
mermaid.render(`mermaid-graph-${index}`, graphDefinition).then((result) => {
|
|
339
|
+
mermaidDiv.innerHTML = result.svg;
|
|
340
|
+
parent.replaceWith(mermaidDiv);
|
|
341
|
+
}).catch(e => {
|
|
342
|
+
console.error("Mermaid error:", e);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
function renderEmptyState(message) {
|
|
348
|
+
document.getElementById('content-container').innerHTML = `
|
|
349
|
+
<div class="panel empty-state">
|
|
350
|
+
<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="var(--text-muted)" stroke-width="1.5" style="margin-bottom: 20px;">
|
|
351
|
+
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
|
352
|
+
<polyline points="14 2 14 8 20 8"></polyline>
|
|
353
|
+
<line x1="16" y1="13" x2="8" y2="13"></line>
|
|
354
|
+
<line x1="16" y1="17" x2="8" y2="17"></line>
|
|
355
|
+
<polyline points="10 9 9 9 8 9"></polyline>
|
|
356
|
+
</svg>
|
|
357
|
+
<h3>No Data Available</h3>
|
|
358
|
+
<p>${message}</p>
|
|
359
|
+
</div>
|
|
360
|
+
`;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Init
|
|
364
|
+
fetchStatus();
|
|
365
|
+
fetchDocs();
|
|
366
|
+
|
|
367
|
+
</script>
|
|
368
|
+
</body>
|
|
369
|
+
</html>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import http from 'http';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
5
|
+
|
|
6
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
+
const __dirname = path.dirname(__filename);
|
|
8
|
+
|
|
9
|
+
const PORT = 4321;
|
|
10
|
+
const PUBLIC_DIR = path.join(__dirname, 'public');
|
|
11
|
+
const DOCS_DIR = path.join(process.cwd(), '.docs');
|
|
12
|
+
const LOCK_FILE = path.join(process.cwd(), '.spark-lock.json');
|
|
13
|
+
|
|
14
|
+
const server = http.createServer((req, res) => {
|
|
15
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
16
|
+
|
|
17
|
+
// API Route: Get all markdown docs
|
|
18
|
+
if (req.url === '/api/docs' && req.method === 'GET') {
|
|
19
|
+
res.setHeader('Content-Type', 'application/json');
|
|
20
|
+
if (!fs.existsSync(DOCS_DIR)) {
|
|
21
|
+
return res.end(JSON.stringify({ error: 'No .docs/ directory found in this project. Please run project-scanner skill first.' }));
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const files = fs.readdirSync(DOCS_DIR).filter(f => f.endsWith('.md'));
|
|
26
|
+
const docs = files.map(filename => {
|
|
27
|
+
const content = fs.readFileSync(path.join(DOCS_DIR, filename), 'utf-8');
|
|
28
|
+
return { filename, content };
|
|
29
|
+
});
|
|
30
|
+
return res.end(JSON.stringify({ docs }));
|
|
31
|
+
} catch (e) {
|
|
32
|
+
return res.end(JSON.stringify({ error: e.message }));
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// API Route: Get Spark Lock status
|
|
37
|
+
if (req.url === '/api/status' && req.method === 'GET') {
|
|
38
|
+
res.setHeader('Content-Type', 'application/json');
|
|
39
|
+
let status = { installed: false, agents: [] };
|
|
40
|
+
if (fs.existsSync(LOCK_FILE)) {
|
|
41
|
+
try {
|
|
42
|
+
status = JSON.parse(fs.readFileSync(LOCK_FILE, 'utf-8'));
|
|
43
|
+
status.installed = true;
|
|
44
|
+
} catch (e) {}
|
|
45
|
+
}
|
|
46
|
+
return res.end(JSON.stringify(status));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Static File Server
|
|
50
|
+
let filePath = req.url === '/' ? '/index.html' : req.url;
|
|
51
|
+
// Prevent path traversal
|
|
52
|
+
filePath = path.normalize(filePath).replace(/^(\.\.[\/\\])+/, '');
|
|
53
|
+
const ext = path.extname(filePath);
|
|
54
|
+
|
|
55
|
+
const contentTypes = {
|
|
56
|
+
'.html': 'text/html',
|
|
57
|
+
'.css': 'text/css',
|
|
58
|
+
'.js': 'text/javascript',
|
|
59
|
+
'.png': 'image/png',
|
|
60
|
+
'.svg': 'image/svg+xml'
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const contentType = contentTypes[ext] || 'text/plain';
|
|
64
|
+
const fullPath = path.join(PUBLIC_DIR, filePath);
|
|
65
|
+
|
|
66
|
+
if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
|
|
67
|
+
res.writeHead(200, { 'Content-Type': contentType });
|
|
68
|
+
fs.createReadStream(fullPath).pipe(res);
|
|
69
|
+
} else {
|
|
70
|
+
res.writeHead(404);
|
|
71
|
+
res.end('404 Not Found');
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
export function startDashboard() {
|
|
76
|
+
server.listen(PORT, async () => {
|
|
77
|
+
console.log(`\n🚀 SPARK Dashboard running at http://localhost:${PORT}`);
|
|
78
|
+
console.log(`Open this URL in your browser to view the AI Knowledge Base.`);
|
|
79
|
+
console.log(`Press Ctrl+C to stop.\n`);
|
|
80
|
+
|
|
81
|
+
// Attempt to open browser automatically
|
|
82
|
+
const { exec } = await import('child_process');
|
|
83
|
+
const startCmd = process.platform == 'darwin' ? 'open' : process.platform == 'win32' ? 'start' : 'xdg-open';
|
|
84
|
+
exec(`${startCmd} http://localhost:${PORT}`);
|
|
85
|
+
});
|
|
86
|
+
}
|