@forgelore/core 0.2.1 → 0.2.3
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/dist/index.d.ts +7 -1
- package/dist/index.js +74 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -132,6 +132,12 @@ declare function getChangesDir(projectRoot: string): string;
|
|
|
132
132
|
declare function getArchiveDir(projectRoot: string): string;
|
|
133
133
|
declare function getChangePath(projectRoot: string, changeName: string): string;
|
|
134
134
|
declare function scaffoldSpecDirs(projectRoot: string): Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Scaffold the forgelore skill — agent-agnostic instructions for using
|
|
137
|
+
* the forgelore CLI. This goes into the project root so any agent system
|
|
138
|
+
* (OpenCode, Claude Code, Cursor, etc.) can discover it.
|
|
139
|
+
*/
|
|
140
|
+
declare function scaffoldSkill(projectRoot: string): Promise<void>;
|
|
135
141
|
declare function createChange(projectRoot: string, name: string, proposalContent: string): Promise<Change>;
|
|
136
142
|
declare function readChange(projectRoot: string, name: string): Promise<Change>;
|
|
137
143
|
declare function readChangeFile(projectRoot: string, changeName: string, fileName: string): Promise<string>;
|
|
@@ -226,4 +232,4 @@ declare function getProjectSummary(projectRoot: string): Promise<ProjectSummary>
|
|
|
226
232
|
*/
|
|
227
233
|
declare function analyzeDrift(projectRoot: string): Promise<DriftReport>;
|
|
228
234
|
|
|
229
|
-
export { type Capability, type Change, type ChangeStatus, type Decision, type DriftItem, type DriftReport, type DriftSeverity, type EnforcementConfig, type ForgeloreConfig, type ForgeloreProject, type GlobalSpecConfig, type KnowledgeBase, type OrchestrationConfig, type ProjectSummary, type RequirementValidation, type SpecMode, type Task, type TaskStatus, type TaskSummary, type ValidationResult, type ValidationVerdict, addCapability, addDecision, analyzeDrift, archiveChange, configExists, createCapability, createChange, createDefaultConfig, fileExists, getArchiveDir, getCapabilitiesDir, getCapability, getChangePath, getChangesDir, getConfigPath, getConfigValue, getDecisionsDir, getForgeloreDir, getKnowledgeDir, getProjectSummary, listCapabilities, listChanges, listDecisions, readChange, readChangeFile, readConfig, readKnowledge, readOutcome, registerCapability, scaffoldKnowledge, scaffoldSpecDirs, setConfigValue, slugify, summarizeTasks, updateChangeStatus, updateTaskStatus, writeConfig, writeOutcome };
|
|
235
|
+
export { type Capability, type Change, type ChangeStatus, type Decision, type DriftItem, type DriftReport, type DriftSeverity, type EnforcementConfig, type ForgeloreConfig, type ForgeloreProject, type GlobalSpecConfig, type KnowledgeBase, type OrchestrationConfig, type ProjectSummary, type RequirementValidation, type SpecMode, type Task, type TaskStatus, type TaskSummary, type ValidationResult, type ValidationVerdict, addCapability, addDecision, analyzeDrift, archiveChange, configExists, createCapability, createChange, createDefaultConfig, fileExists, getArchiveDir, getCapabilitiesDir, getCapability, getChangePath, getChangesDir, getConfigPath, getConfigValue, getDecisionsDir, getForgeloreDir, getKnowledgeDir, getProjectSummary, listCapabilities, listChanges, listDecisions, readChange, readChangeFile, readConfig, readKnowledge, readOutcome, registerCapability, scaffoldKnowledge, scaffoldSkill, scaffoldSpecDirs, setConfigValue, slugify, summarizeTasks, updateChangeStatus, updateTaskStatus, writeConfig, writeOutcome };
|
package/dist/index.js
CHANGED
|
@@ -99,6 +99,79 @@ async function scaffoldSpecDirs(projectRoot) {
|
|
|
99
99
|
await mkdir(join2(forgeloreDir, "knowledge", "capabilities"), { recursive: true });
|
|
100
100
|
await mkdir(join2(forgeloreDir, "knowledge", "decisions"), { recursive: true });
|
|
101
101
|
}
|
|
102
|
+
async function scaffoldSkill(projectRoot) {
|
|
103
|
+
const skillDir = join2(projectRoot, "skills", "forgelore");
|
|
104
|
+
await mkdir(skillDir, { recursive: true });
|
|
105
|
+
const skillPath = join2(skillDir, "SKILL.md");
|
|
106
|
+
if (await fileExists(skillPath)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const content = `# Forgelore \u2014 Spec-Driven Development
|
|
110
|
+
|
|
111
|
+
This project uses **forgelore** for spec-driven development. All significant changes
|
|
112
|
+
should go through the spec workflow before implementation begins.
|
|
113
|
+
|
|
114
|
+
## When to Use Forgelore
|
|
115
|
+
|
|
116
|
+
- Before starting any new feature or significant change
|
|
117
|
+
- When requirements are unclear and need to be formalized
|
|
118
|
+
- When multiple agents or developers will collaborate on a change
|
|
119
|
+
- Before refactoring that touches multiple modules
|
|
120
|
+
|
|
121
|
+
## CLI Commands
|
|
122
|
+
|
|
123
|
+
| Command | Purpose |
|
|
124
|
+
|---------|---------|
|
|
125
|
+
| \`forgelore status\` | Show project status dashboard \u2014 start here |
|
|
126
|
+
| \`forgelore list\` | List all changes and their states |
|
|
127
|
+
| \`forgelore propose "idea"\` | Create a new change proposal |
|
|
128
|
+
| \`forgelore clarify <change>\` | Refine requirements interactively |
|
|
129
|
+
| \`forgelore verify <change>\` | Check spec completeness (structural) |
|
|
130
|
+
| \`forgelore diff <change>\` | Show drift between specs and code |
|
|
131
|
+
| \`forgelore archive <change>\` | Archive a completed change, extract knowledge |
|
|
132
|
+
| \`forgelore doctor\` | Health check for the forgelore setup |
|
|
133
|
+
| \`forgelore capabilities\` | List all registered capabilities |
|
|
134
|
+
| \`forgelore config [key] [value]\` | Get or set configuration |
|
|
135
|
+
|
|
136
|
+
## Workflow
|
|
137
|
+
|
|
138
|
+
1. **Propose** \u2014 \`forgelore propose "add user authentication"\`
|
|
139
|
+
2. **Plan** \u2014 Fill in \`forgelore/changes/<name>/specs/requirements.md\`, \`scenarios.md\`, \`design.md\`, and \`tasks.md\`
|
|
140
|
+
3. **Verify** \u2014 \`forgelore verify <name>\` to check spec completeness
|
|
141
|
+
4. **Build** \u2014 Implement tasks, updating status as you go
|
|
142
|
+
5. **Validate** \u2014 Review implementation against specs
|
|
143
|
+
6. **Archive** \u2014 \`forgelore archive <name>\` to capture knowledge
|
|
144
|
+
|
|
145
|
+
## Key Directories
|
|
146
|
+
|
|
147
|
+
\`\`\`
|
|
148
|
+
forgelore/
|
|
149
|
+
\u251C\u2500\u2500 forgelore.json # Configuration
|
|
150
|
+
\u251C\u2500\u2500 changes/ # Active change specs
|
|
151
|
+
\u2502 \u2514\u2500\u2500 <change-name>/
|
|
152
|
+
\u2502 \u251C\u2500\u2500 proposal.md # Original idea
|
|
153
|
+
\u2502 \u251C\u2500\u2500 specs/
|
|
154
|
+
\u2502 \u2502 \u251C\u2500\u2500 requirements.md # What to build
|
|
155
|
+
\u2502 \u2502 \u2514\u2500\u2500 scenarios.md # How it should work
|
|
156
|
+
\u2502 \u251C\u2500\u2500 design.md # Technical approach
|
|
157
|
+
\u2502 \u2514\u2500\u2500 tasks.md # Atomic task breakdown
|
|
158
|
+
\u2514\u2500\u2500 knowledge/ # Project knowledge base
|
|
159
|
+
\u251C\u2500\u2500 architecture.md # System architecture
|
|
160
|
+
\u251C\u2500\u2500 patterns.md # Code patterns and conventions
|
|
161
|
+
\u251C\u2500\u2500 glossary.md # Domain terminology
|
|
162
|
+
\u251C\u2500\u2500 capabilities/ # Extracted capabilities (JSON)
|
|
163
|
+
\u2514\u2500\u2500 decisions/ # Architecture decision records
|
|
164
|
+
\`\`\`
|
|
165
|
+
|
|
166
|
+
## Rules
|
|
167
|
+
|
|
168
|
+
- **Spec first.** Do not start coding without a spec for non-trivial changes.
|
|
169
|
+
- **Follow patterns.** Read \`forgelore/knowledge/patterns.md\` before writing code.
|
|
170
|
+
- **Update tasks.** Mark task status as you work (\`pending\` \u2192 \`in-progress\` \u2192 \`implemented\`).
|
|
171
|
+
- **Knowledge compounds.** After completing a change, archive it to capture capabilities and update the knowledge base.
|
|
172
|
+
`;
|
|
173
|
+
await writeFile2(skillPath, content, "utf-8");
|
|
174
|
+
}
|
|
102
175
|
async function createChange(projectRoot, name, proposalContent) {
|
|
103
176
|
const changePath = getChangePath(projectRoot, name);
|
|
104
177
|
if (await fileExists(changePath)) {
|
|
@@ -728,6 +801,7 @@ export {
|
|
|
728
801
|
readOutcome,
|
|
729
802
|
registerCapability,
|
|
730
803
|
scaffoldKnowledge,
|
|
804
|
+
scaffoldSkill,
|
|
731
805
|
scaffoldSpecDirs,
|
|
732
806
|
setConfigValue,
|
|
733
807
|
slugify,
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/config/index.ts","../src/spec/index.ts","../src/knowledge/index.ts","../src/capabilities/index.ts","../src/progress/index.ts","../src/drift/index.ts"],"sourcesContent":["/**\n * forgelore configuration management\n * Handles reading/writing forgelore.json config files\n */\n\nimport { readFile, writeFile, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ForgeloreConfig, SpecMode } from \"../types/index.js\";\n\nconst CONFIG_FILENAME = \"forgelore.json\";\nconst FORGELORE_DIR = \"forgelore\";\n\nconst DEFAULT_CONFIG: ForgeloreConfig = {\n $schema: \"https://forgelore.dev/config.json\",\n version: \"0.1.0\",\n mode: \"local\",\n orchestration: {\n defaultMode: \"sequential\",\n maxRetries: 3,\n parallelTracks: 3,\n },\n enforcement: {\n requireSpecForChanges: true,\n warnOnUnspeccedEdits: true,\n blockArchiveOnDrift: true,\n autoInjectContext: true,\n },\n};\n\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getForgeloreDir(projectRoot: string): string {\n return join(projectRoot, FORGELORE_DIR);\n}\n\nexport function getConfigPath(projectRoot: string): string {\n return join(getForgeloreDir(projectRoot), CONFIG_FILENAME);\n}\n\nexport async function readConfig(projectRoot: string): Promise<ForgeloreConfig> {\n const configPath = getConfigPath(projectRoot);\n if (!(await fileExists(configPath))) {\n throw new Error(\n `No forgelore config found at ${configPath}. Run 'forgelore init' to initialize.`\n );\n }\n\n const raw = await readFile(configPath, \"utf-8\");\n return JSON.parse(raw) as ForgeloreConfig;\n}\n\nexport async function writeConfig(\n projectRoot: string,\n config: ForgeloreConfig\n): Promise<void> {\n const configPath = getConfigPath(projectRoot);\n await writeFile(configPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nexport function createDefaultConfig(mode: SpecMode): ForgeloreConfig {\n return { ...DEFAULT_CONFIG, mode };\n}\n\nexport async function configExists(projectRoot: string): Promise<boolean> {\n return fileExists(getConfigPath(projectRoot));\n}\n\nexport async function getConfigValue(\n projectRoot: string,\n key: string\n): Promise<unknown> {\n const config = await readConfig(projectRoot);\n const keys = key.split(\".\");\n let current: unknown = config;\n for (const k of keys) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[k];\n }\n return current;\n}\n\nexport async function setConfigValue(\n projectRoot: string,\n key: string,\n value: unknown\n): Promise<void> {\n const config = await readConfig(projectRoot);\n const keys = key.split(\".\");\n let current: Record<string, unknown> = config as unknown as Record<string, unknown>;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!(k in current) || typeof current[k] !== \"object\") {\n current[k] = {};\n }\n current = current[k] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = value;\n await writeConfig(projectRoot, config);\n}\n","/**\n * Spec CRUD operations\n * Create, read, update, list, and archive spec changes\n */\n\nimport { readFile, writeFile, readdir, mkdir, rename, rm } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { fileExists, getForgeloreDir } from \"../config/index.js\";\nimport type { Change, ChangeStatus, Task, TaskStatus } from \"../types/index.js\";\n\n// --- Paths ---\n\nexport function getChangesDir(projectRoot: string): string {\n return join(getForgeloreDir(projectRoot), \"changes\");\n}\n\nexport function getArchiveDir(projectRoot: string): string {\n return join(getChangesDir(projectRoot), \"archive\");\n}\n\nexport function getChangePath(projectRoot: string, changeName: string): string {\n return join(getChangesDir(projectRoot), changeName);\n}\n\n// --- Scaffold ---\n\nexport async function scaffoldSpecDirs(projectRoot: string): Promise<void> {\n const forgeloreDir = getForgeloreDir(projectRoot);\n await mkdir(join(forgeloreDir, \"changes\"), { recursive: true });\n await mkdir(join(forgeloreDir, \"changes\", \"archive\"), { recursive: true });\n await mkdir(join(forgeloreDir, \"knowledge\", \"capabilities\"), { recursive: true });\n await mkdir(join(forgeloreDir, \"knowledge\", \"decisions\"), { recursive: true });\n}\n\n// --- Create ---\n\nexport async function createChange(\n projectRoot: string,\n name: string,\n proposalContent: string\n): Promise<Change> {\n const changePath = getChangePath(projectRoot, name);\n\n if (await fileExists(changePath)) {\n throw new Error(`Change '${name}' already exists at ${changePath}`);\n }\n\n await mkdir(changePath, { recursive: true });\n await mkdir(join(changePath, \"specs\"), { recursive: true });\n\n const now = new Date().toISOString();\n\n // Write proposal\n await writeFile(join(changePath, \"proposal.md\"), proposalContent, \"utf-8\");\n\n // Write empty spec files from templates\n await writeFile(\n join(changePath, \"specs\", \"requirements.md\"),\n `# Requirements: ${name}\\n\\n<!-- Define the requirements for this change -->\\n\\n## Functional Requirements\\n\\n1. \\n\\n## Non-Functional Requirements\\n\\n1. \\n`,\n \"utf-8\"\n );\n\n await writeFile(\n join(changePath, \"specs\", \"scenarios.md\"),\n `# Scenarios: ${name}\\n\\n<!-- Define acceptance scenarios -->\\n\\n## Happy Path\\n\\n1. \\n\\n## Edge Cases\\n\\n1. \\n\\n## Error Cases\\n\\n1. \\n`,\n \"utf-8\"\n );\n\n await writeFile(\n join(changePath, \"design.md\"),\n `# Design: ${name}\\n\\n<!-- Technical approach and architecture decisions -->\\n\\n## Approach\\n\\n\\n## Key Decisions\\n\\n\\n## Files to Modify\\n\\n- \\n\\n## Dependencies\\n\\n- \\n`,\n \"utf-8\"\n );\n\n await writeFile(\n join(changePath, \"tasks.md\"),\n `# Tasks: ${name}\\n\\n<!-- Implementation checklist -->\\n\\n| ID | Task | Status | Category | Notes |\\n|----|------|--------|----------|-------|\\n| 1 | | pending | | |\\n`,\n \"utf-8\"\n );\n\n const change: Change = {\n name,\n status: \"proposed\",\n createdAt: now,\n updatedAt: now,\n path: changePath,\n tasks: [],\n };\n\n // Write metadata\n await writeFile(\n join(changePath, \".forge-meta.json\"),\n JSON.stringify(change, null, 2) + \"\\n\",\n \"utf-8\"\n );\n\n return change;\n}\n\n// --- Read ---\n\nexport async function readChange(\n projectRoot: string,\n name: string\n): Promise<Change> {\n const changePath = getChangePath(projectRoot, name);\n const metaPath = join(changePath, \".forge-meta.json\");\n\n if (!(await fileExists(metaPath))) {\n throw new Error(`Change '${name}' not found at ${changePath}`);\n }\n\n const raw = await readFile(metaPath, \"utf-8\");\n return JSON.parse(raw) as Change;\n}\n\nexport async function readChangeFile(\n projectRoot: string,\n changeName: string,\n fileName: string\n): Promise<string> {\n const filePath = join(getChangePath(projectRoot, changeName), fileName);\n if (!(await fileExists(filePath))) {\n throw new Error(`File '${fileName}' not found in change '${changeName}'`);\n }\n return readFile(filePath, \"utf-8\");\n}\n\n// --- List ---\n\nexport async function listChanges(\n projectRoot: string,\n includeArchived = false\n): Promise<Change[]> {\n const changesDir = getChangesDir(projectRoot);\n\n if (!(await fileExists(changesDir))) {\n return [];\n }\n\n const entries = await readdir(changesDir, { withFileTypes: true });\n const changes: Change[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory() || entry.name === \"archive\") continue;\n\n const metaPath = join(changesDir, entry.name, \".forge-meta.json\");\n if (await fileExists(metaPath)) {\n const raw = await readFile(metaPath, \"utf-8\");\n changes.push(JSON.parse(raw) as Change);\n }\n }\n\n if (includeArchived) {\n const archiveDir = getArchiveDir(projectRoot);\n if (await fileExists(archiveDir)) {\n const archiveEntries = await readdir(archiveDir, { withFileTypes: true });\n for (const entry of archiveEntries) {\n if (!entry.isDirectory()) continue;\n const metaPath = join(archiveDir, entry.name, \".forge-meta.json\");\n if (await fileExists(metaPath)) {\n const raw = await readFile(metaPath, \"utf-8\");\n changes.push(JSON.parse(raw) as Change);\n }\n }\n }\n }\n\n return changes.sort(\n (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()\n );\n}\n\n// --- Update ---\n\nexport async function updateChangeStatus(\n projectRoot: string,\n name: string,\n status: ChangeStatus\n): Promise<Change> {\n const change = await readChange(projectRoot, name);\n change.status = status;\n change.updatedAt = new Date().toISOString();\n\n const metaPath = join(change.path, \".forge-meta.json\");\n await writeFile(metaPath, JSON.stringify(change, null, 2) + \"\\n\", \"utf-8\");\n\n return change;\n}\n\nexport async function updateTaskStatus(\n projectRoot: string,\n changeName: string,\n taskId: string,\n status: TaskStatus\n): Promise<void> {\n const change = await readChange(projectRoot, changeName);\n const task = change.tasks.find((t) => t.id === taskId);\n if (task) {\n task.status = status;\n }\n change.updatedAt = new Date().toISOString();\n\n const metaPath = join(change.path, \".forge-meta.json\");\n await writeFile(metaPath, JSON.stringify(change, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// --- Archive ---\n\nexport async function archiveChange(\n projectRoot: string,\n name: string\n): Promise<string> {\n const changePath = getChangePath(projectRoot, name);\n if (!(await fileExists(changePath))) {\n throw new Error(`Change '${name}' not found`);\n }\n\n const archiveDir = getArchiveDir(projectRoot);\n const datestamp = new Date().toISOString().slice(0, 10);\n const archiveName = `${datestamp}-${name}`;\n const archivePath = join(archiveDir, archiveName);\n\n // Update status before archiving\n await updateChangeStatus(projectRoot, name, \"archived\");\n\n // Move to archive\n await rename(changePath, archivePath);\n\n return archivePath;\n}\n","/**\n * Knowledge base management\n * Capabilities, decisions, architecture docs, patterns\n */\n\nimport { readFile, writeFile, readdir, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists, getForgeloreDir } from \"../config/index.js\";\nimport type { Capability, Decision, KnowledgeBase } from \"../types/index.js\";\n\n// --- Paths ---\n\nexport function getKnowledgeDir(projectRoot: string): string {\n return join(getForgeloreDir(projectRoot), \"knowledge\");\n}\n\nexport function getCapabilitiesDir(projectRoot: string): string {\n return join(getKnowledgeDir(projectRoot), \"capabilities\");\n}\n\nexport function getDecisionsDir(projectRoot: string): string {\n return join(getKnowledgeDir(projectRoot), \"decisions\");\n}\n\n// --- Read Knowledge Base ---\n\nexport async function readKnowledge(projectRoot: string): Promise<KnowledgeBase> {\n const knowledgeDir = getKnowledgeDir(projectRoot);\n\n const kb: KnowledgeBase = {\n capabilities: await listCapabilities(projectRoot),\n decisions: await listDecisions(projectRoot),\n };\n\n // Read optional markdown files\n const archPath = join(knowledgeDir, \"architecture.md\");\n if (await fileExists(archPath)) {\n kb.architecture = await readFile(archPath, \"utf-8\");\n }\n\n const patternsPath = join(knowledgeDir, \"patterns.md\");\n if (await fileExists(patternsPath)) {\n kb.patterns = await readFile(patternsPath, \"utf-8\");\n }\n\n const glossaryPath = join(knowledgeDir, \"glossary.md\");\n if (await fileExists(glossaryPath)) {\n kb.glossary = await readFile(glossaryPath, \"utf-8\");\n }\n\n return kb;\n}\n\n// --- Capabilities ---\n\nexport async function listCapabilities(projectRoot: string): Promise<Capability[]> {\n const dir = getCapabilitiesDir(projectRoot);\n if (!(await fileExists(dir))) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n const capabilities: Capability[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n const raw = await readFile(join(dir, entry.name), \"utf-8\");\n capabilities.push(JSON.parse(raw) as Capability);\n }\n\n return capabilities.sort(\n (a, b) => new Date(b.archivedAt).getTime() - new Date(a.archivedAt).getTime()\n );\n}\n\nexport async function addCapability(\n projectRoot: string,\n capability: Capability\n): Promise<void> {\n const dir = getCapabilitiesDir(projectRoot);\n await mkdir(dir, { recursive: true });\n\n const fileName = `${capability.id}.json`;\n await writeFile(\n join(dir, fileName),\n JSON.stringify(capability, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\nexport async function getCapability(\n projectRoot: string,\n id: string\n): Promise<Capability | null> {\n const filePath = join(getCapabilitiesDir(projectRoot), `${id}.json`);\n if (!(await fileExists(filePath))) return null;\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as Capability;\n}\n\n// --- Decisions ---\n\nexport async function listDecisions(projectRoot: string): Promise<Decision[]> {\n const dir = getDecisionsDir(projectRoot);\n if (!(await fileExists(dir))) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n const decisions: Decision[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n const raw = await readFile(join(dir, entry.name), \"utf-8\");\n decisions.push(JSON.parse(raw) as Decision);\n }\n\n return decisions.sort((a, b) => a.id.localeCompare(b.id));\n}\n\nexport async function addDecision(\n projectRoot: string,\n decision: Decision\n): Promise<void> {\n const dir = getDecisionsDir(projectRoot);\n await mkdir(dir, { recursive: true });\n\n const fileName = `${decision.id}.json`;\n await writeFile(\n join(dir, fileName),\n JSON.stringify(decision, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\n// --- Scaffold Knowledge ---\n\nexport async function scaffoldKnowledge(projectRoot: string): Promise<void> {\n const knowledgeDir = getKnowledgeDir(projectRoot);\n await mkdir(join(knowledgeDir, \"capabilities\"), { recursive: true });\n await mkdir(join(knowledgeDir, \"decisions\"), { recursive: true });\n\n const archPath = join(knowledgeDir, \"architecture.md\");\n if (!(await fileExists(archPath))) {\n await writeFile(\n archPath,\n `# Architecture\\n\\n<!-- Living architecture documentation. Updated as the system evolves. -->\\n\\n## Overview\\n\\n\\n## Components\\n\\n\\n## Data Flow\\n\\n\\n## Key Integrations\\n\\n`,\n \"utf-8\"\n );\n }\n\n const patternsPath = join(knowledgeDir, \"patterns.md\");\n if (!(await fileExists(patternsPath))) {\n await writeFile(\n patternsPath,\n `# Patterns\\n\\n<!-- Established patterns in this codebase. Agents should follow these. -->\\n\\n## Code Patterns\\n\\n\\n## Naming Conventions\\n\\n\\n## Error Handling\\n\\n\\n## Testing Patterns\\n\\n`,\n \"utf-8\"\n );\n }\n\n const glossaryPath = join(knowledgeDir, \"glossary.md\");\n if (!(await fileExists(glossaryPath))) {\n await writeFile(\n glossaryPath,\n `# Glossary\\n\\n<!-- Domain-specific terms used in this project -->\\n\\n| Term | Definition |\\n|------|------------|\\n| | |\\n`,\n \"utf-8\"\n );\n }\n}\n","/**\n * Capability extraction\n * Extract capabilities from archived changes for the knowledge base\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists } from \"../config/index.js\";\nimport { addCapability } from \"../knowledge/index.js\";\nimport type { Capability } from \"../types/index.js\";\n\n/**\n * Generate a URL-safe slug from a string\n */\nexport function slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n\n/**\n * Create a capability record from extracted data\n */\nexport function createCapability(\n name: string,\n description: string,\n sourceChange: string,\n files: string[] = [],\n tags: string[] = []\n): Capability {\n return {\n id: slugify(name),\n name,\n description,\n sourceChange,\n archivedAt: new Date().toISOString(),\n files,\n tags,\n };\n}\n\n/**\n * Register a capability in the project's knowledge base\n */\nexport async function registerCapability(\n projectRoot: string,\n capability: Capability\n): Promise<void> {\n await addCapability(projectRoot, capability);\n}\n\n/**\n * Read the outcome.md from an archived change (if it exists)\n */\nexport async function readOutcome(archivePath: string): Promise<string | null> {\n const outcomePath = join(archivePath, \"outcome.md\");\n if (!(await fileExists(outcomePath))) return null;\n return readFile(outcomePath, \"utf-8\");\n}\n\n/**\n * Write outcome.md to a change directory (step 1 of archive)\n */\nexport async function writeOutcome(\n changePath: string,\n content: string\n): Promise<void> {\n const { writeFile: wf } = await import(\"node:fs/promises\");\n await wf(join(changePath, \"outcome.md\"), content, \"utf-8\");\n}\n","/**\n * Progress tracking\n * Calculate completion percentages and status summaries\n */\n\nimport type { Change, Task, TaskStatus, ForgeloreProject } from \"../types/index.js\";\nimport { listChanges } from \"../spec/index.js\";\nimport { listCapabilities } from \"../knowledge/index.js\";\n\nexport interface TaskSummary {\n total: number;\n pending: number;\n inProgress: number;\n passed: number;\n failed: number;\n blocked: number;\n completionPercent: number;\n}\n\nexport interface ProjectSummary {\n activeChanges: number;\n archivedChanges: number;\n totalCapabilities: number;\n taskSummary: TaskSummary;\n changes: Array<{\n name: string;\n status: string;\n taskSummary: TaskSummary;\n }>;\n}\n\nexport function summarizeTasks(tasks: Task[]): TaskSummary {\n const total = tasks.length;\n if (total === 0) {\n return {\n total: 0,\n pending: 0,\n inProgress: 0,\n passed: 0,\n failed: 0,\n blocked: 0,\n completionPercent: 0,\n };\n }\n\n const pending = tasks.filter((t) => t.status === \"pending\").length;\n const inProgress = tasks.filter(\n (t) =>\n t.status === \"claimed\" ||\n t.status === \"in-progress\" ||\n t.status === \"implemented\" ||\n t.status === \"validating\"\n ).length;\n const passed = tasks.filter((t) => t.status === \"passed\").length;\n const failed = tasks.filter((t) => t.status === \"failed\").length;\n const blocked = tasks.filter((t) => t.status === \"blocked\").length;\n\n const completionPercent = Math.round((passed / total) * 100);\n\n return { total, pending, inProgress, passed, failed, blocked, completionPercent };\n}\n\nexport async function getProjectSummary(\n projectRoot: string\n): Promise<ProjectSummary> {\n const activeChanges = await listChanges(projectRoot, false);\n const allChanges = await listChanges(projectRoot, true);\n const archivedChanges = allChanges.filter((c) => c.status === \"archived\");\n const capabilities = await listCapabilities(projectRoot);\n\n const allTasks = activeChanges.flatMap((c) => c.tasks);\n\n return {\n activeChanges: activeChanges.length,\n archivedChanges: archivedChanges.length,\n totalCapabilities: capabilities.length,\n taskSummary: summarizeTasks(allTasks),\n changes: activeChanges.map((c) => ({\n name: c.name,\n status: c.status,\n taskSummary: summarizeTasks(c.tasks),\n })),\n };\n}\n","/**\n * Drift detection\n * Identify where specs and implementation have diverged\n *\n * Drift categories:\n * - unspecced-change: files modified that aren't in any active spec\n * - stale-spec: specs with no progress or activity\n * - missing-capability: archived changes without capabilities extracted\n * - outdated-knowledge: knowledge base files that may be stale\n */\n\nimport { readFile, readdir, stat } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { fileExists, getForgeloreDir } from \"../config/index.js\";\nimport { listChanges, readChange, readChangeFile, getArchiveDir } from \"../spec/index.js\";\nimport { listCapabilities } from \"../knowledge/index.js\";\nimport type { DriftItem, DriftReport, DriftSeverity, Change } from \"../types/index.js\";\n\n/**\n * Run a full drift analysis on the project\n */\nexport async function analyzeDrift(projectRoot: string): Promise<DriftReport> {\n const items: DriftItem[] = [];\n\n await detectStaleSpecs(projectRoot, items);\n await detectMissingCapabilities(projectRoot, items);\n await detectIncompleteSpecs(projectRoot, items);\n await detectStaleKnowledge(projectRoot, items);\n\n const score = calculateScore(items);\n\n return {\n score,\n items,\n timestamp: new Date().toISOString(),\n };\n}\n\n/**\n * Detect changes that have gone stale (no updates in a while)\n */\nasync function detectStaleSpecs(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const changes = await listChanges(projectRoot, false);\n const now = Date.now();\n\n for (const change of changes) {\n const daysSinceUpdate = Math.floor(\n (now - new Date(change.updatedAt).getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (change.status === \"proposed\" && daysSinceUpdate > 14) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" proposed ${daysSinceUpdate} days ago with no progress — consider clarifying or closing`,\n });\n } else if (change.status === \"proposed\" && daysSinceUpdate > 7) {\n items.push({\n type: \"stale-spec\",\n severity: \"info\",\n spec: change.name,\n message: `\"${change.name}\" proposed ${daysSinceUpdate} days ago — consider running \\`forgelore clarify ${change.name}\\``,\n });\n }\n\n if (change.status === \"in-progress\" && daysSinceUpdate > 7) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" in progress for ${daysSinceUpdate} days since last update`,\n });\n }\n\n if (change.status === \"validating\" && daysSinceUpdate > 3) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" awaiting validation for ${daysSinceUpdate} days`,\n });\n }\n\n // Check for stale tasks: status is in-progress but all tasks still pending\n if (\n change.status === \"in-progress\" &&\n change.tasks.length > 0 &&\n change.tasks.every((t) => t.status === \"pending\")\n ) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" is in-progress but all ${change.tasks.length} tasks are still pending`,\n });\n }\n }\n}\n\n/**\n * Detect archived changes that don't have capabilities extracted\n */\nasync function detectMissingCapabilities(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const archiveDir = getArchiveDir(projectRoot);\n if (!(await fileExists(archiveDir))) return;\n\n const capabilities = await listCapabilities(projectRoot);\n const capSources = new Set(capabilities.map((c) => c.sourceChange));\n\n const entries = await readdir(archiveDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n // Archive names are like \"2025-01-15-feature-name\"\n // The original change name is everything after the date prefix\n const match = entry.name.match(/^\\d{4}-\\d{2}-\\d{2}-(.+)$/);\n const changeName = match ? match[1] : entry.name;\n\n // Check for outcome.md\n const outcomePath = join(archiveDir, entry.name, \"outcome.md\");\n if (!(await fileExists(outcomePath))) {\n items.push({\n type: \"missing-capability\",\n severity: \"warning\",\n spec: changeName,\n message: `Archived change \"${changeName}\" has no outcome.md — knowledge not captured`,\n });\n continue;\n }\n\n // Check if capabilities were extracted\n if (!capSources.has(changeName)) {\n items.push({\n type: \"missing-capability\",\n severity: \"info\",\n spec: changeName,\n message: `Archived change \"${changeName}\" has outcome.md but no capabilities extracted`,\n });\n }\n }\n}\n\n/**\n * Detect active changes with incomplete spec files\n */\nasync function detectIncompleteSpecs(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const changes = await listChanges(projectRoot, false);\n\n for (const change of changes) {\n // Only flag incomplete specs for changes that are past the proposal stage\n if (change.status === \"proposed\") continue;\n\n const specFiles = [\n { path: \"specs/requirements.md\", name: \"requirements\" },\n { path: \"specs/scenarios.md\", name: \"scenarios\" },\n { path: \"design.md\", name: \"design\" },\n { path: \"tasks.md\", name: \"tasks\" },\n ];\n\n for (const spec of specFiles) {\n try {\n const content = await readChangeFile(projectRoot, change.name, spec.path);\n const stripped = content.replace(/<!--.*?-->/gs, \"\").trim();\n if (stripped.length < 50) {\n const severity: DriftSeverity =\n change.status === \"in-progress\" ? \"warning\" : \"info\";\n items.push({\n type: \"stale-spec\",\n severity,\n spec: change.name,\n file: spec.path,\n message: `\"${change.name}/${spec.path}\" has minimal content (status: ${change.status})`,\n });\n }\n } catch {\n items.push({\n type: \"stale-spec\",\n severity: \"critical\",\n spec: change.name,\n file: spec.path,\n message: `\"${change.name}/${spec.path}\" is missing`,\n });\n }\n }\n }\n}\n\n/**\n * Detect knowledge base files that may be outdated\n */\nasync function detectStaleKnowledge(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const forgeloreDir = getForgeloreDir(projectRoot);\n const knowledgeDir = join(forgeloreDir, \"knowledge\");\n\n const knowledgeFiles = [\n { path: \"architecture.md\", name: \"Architecture\" },\n { path: \"patterns.md\", name: \"Patterns\" },\n { path: \"glossary.md\", name: \"Glossary\" },\n ];\n\n for (const kb of knowledgeFiles) {\n const filePath = join(knowledgeDir, kb.path);\n if (!(await fileExists(filePath))) {\n items.push({\n type: \"outdated-knowledge\",\n severity: \"info\",\n file: `knowledge/${kb.path}`,\n message: `${kb.name} documentation is missing`,\n });\n continue;\n }\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const stripped = content.replace(/<!--.*?-->/gs, \"\").trim();\n if (stripped.length < 50) {\n items.push({\n type: \"outdated-knowledge\",\n severity: \"info\",\n file: `knowledge/${kb.path}`,\n message: `${kb.name} documentation has minimal content`,\n });\n }\n\n // Check staleness by file modification time\n const stats = await stat(filePath);\n const daysSinceModified = Math.floor(\n (Date.now() - stats.mtimeMs) / (1000 * 60 * 60 * 24)\n );\n\n // Only flag if there have been archived changes since the last update\n const capabilities = await listCapabilities(projectRoot);\n const recentCaps = capabilities.filter((c) => {\n const archiveTime = new Date(c.archivedAt).getTime();\n return archiveTime > stats.mtimeMs;\n });\n\n if (recentCaps.length > 0 && daysSinceModified > 7) {\n items.push({\n type: \"outdated-knowledge\",\n severity: \"warning\",\n file: `knowledge/${kb.path}`,\n message: `${kb.name} not updated since ${recentCaps.length} capabilities were archived`,\n });\n }\n } catch {\n // Ignore read errors\n }\n }\n}\n\n/**\n * Calculate drift score (0-100, where 100 = no drift)\n */\nfunction calculateScore(items: DriftItem[]): number {\n if (items.length === 0) return 100;\n\n let penalty = 0;\n for (const item of items) {\n switch (item.severity) {\n case \"critical\":\n penalty += 15;\n break;\n case \"warning\":\n penalty += 5;\n break;\n case \"info\":\n penalty += 1;\n break;\n }\n }\n\n return Math.max(0, 100 - penalty);\n}\n"],"mappings":"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","names":["readFile","writeFile","join","join","writeFile","readFile","readFile","writeFile","readdir","mkdir","join","join","readFile","readdir","mkdir","writeFile","readFile","join","join","readFile","readFile","readdir","join","readdir","join","readFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/config/index.ts","../src/spec/index.ts","../src/knowledge/index.ts","../src/capabilities/index.ts","../src/progress/index.ts","../src/drift/index.ts"],"sourcesContent":["/**\n * forgelore configuration management\n * Handles reading/writing forgelore.json config files\n */\n\nimport { readFile, writeFile, access } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type { ForgeloreConfig, SpecMode } from \"../types/index.js\";\n\nconst CONFIG_FILENAME = \"forgelore.json\";\nconst FORGELORE_DIR = \"forgelore\";\n\nconst DEFAULT_CONFIG: ForgeloreConfig = {\n $schema: \"https://forgelore.dev/config.json\",\n version: \"0.1.0\",\n mode: \"local\",\n orchestration: {\n defaultMode: \"sequential\",\n maxRetries: 3,\n parallelTracks: 3,\n },\n enforcement: {\n requireSpecForChanges: true,\n warnOnUnspeccedEdits: true,\n blockArchiveOnDrift: true,\n autoInjectContext: true,\n },\n};\n\nexport async function fileExists(path: string): Promise<boolean> {\n try {\n await access(path);\n return true;\n } catch {\n return false;\n }\n}\n\nexport function getForgeloreDir(projectRoot: string): string {\n return join(projectRoot, FORGELORE_DIR);\n}\n\nexport function getConfigPath(projectRoot: string): string {\n return join(getForgeloreDir(projectRoot), CONFIG_FILENAME);\n}\n\nexport async function readConfig(projectRoot: string): Promise<ForgeloreConfig> {\n const configPath = getConfigPath(projectRoot);\n if (!(await fileExists(configPath))) {\n throw new Error(\n `No forgelore config found at ${configPath}. Run 'forgelore init' to initialize.`\n );\n }\n\n const raw = await readFile(configPath, \"utf-8\");\n return JSON.parse(raw) as ForgeloreConfig;\n}\n\nexport async function writeConfig(\n projectRoot: string,\n config: ForgeloreConfig\n): Promise<void> {\n const configPath = getConfigPath(projectRoot);\n await writeFile(configPath, JSON.stringify(config, null, 2) + \"\\n\", \"utf-8\");\n}\n\nexport function createDefaultConfig(mode: SpecMode): ForgeloreConfig {\n return { ...DEFAULT_CONFIG, mode };\n}\n\nexport async function configExists(projectRoot: string): Promise<boolean> {\n return fileExists(getConfigPath(projectRoot));\n}\n\nexport async function getConfigValue(\n projectRoot: string,\n key: string\n): Promise<unknown> {\n const config = await readConfig(projectRoot);\n const keys = key.split(\".\");\n let current: unknown = config;\n for (const k of keys) {\n if (current === null || current === undefined || typeof current !== \"object\") {\n return undefined;\n }\n current = (current as Record<string, unknown>)[k];\n }\n return current;\n}\n\nexport async function setConfigValue(\n projectRoot: string,\n key: string,\n value: unknown\n): Promise<void> {\n const config = await readConfig(projectRoot);\n const keys = key.split(\".\");\n let current: Record<string, unknown> = config as unknown as Record<string, unknown>;\n for (let i = 0; i < keys.length - 1; i++) {\n const k = keys[i];\n if (!(k in current) || typeof current[k] !== \"object\") {\n current[k] = {};\n }\n current = current[k] as Record<string, unknown>;\n }\n current[keys[keys.length - 1]] = value;\n await writeConfig(projectRoot, config);\n}\n","/**\n * Spec CRUD operations\n * Create, read, update, list, and archive spec changes\n */\n\nimport { readFile, writeFile, readdir, mkdir, rename, rm } from \"node:fs/promises\";\nimport { join, basename } from \"node:path\";\nimport { fileExists, getForgeloreDir } from \"../config/index.js\";\nimport type { Change, ChangeStatus, Task, TaskStatus } from \"../types/index.js\";\n\n// --- Paths ---\n\nexport function getChangesDir(projectRoot: string): string {\n return join(getForgeloreDir(projectRoot), \"changes\");\n}\n\nexport function getArchiveDir(projectRoot: string): string {\n return join(getChangesDir(projectRoot), \"archive\");\n}\n\nexport function getChangePath(projectRoot: string, changeName: string): string {\n return join(getChangesDir(projectRoot), changeName);\n}\n\n// --- Scaffold ---\n\nexport async function scaffoldSpecDirs(projectRoot: string): Promise<void> {\n const forgeloreDir = getForgeloreDir(projectRoot);\n await mkdir(join(forgeloreDir, \"changes\"), { recursive: true });\n await mkdir(join(forgeloreDir, \"changes\", \"archive\"), { recursive: true });\n await mkdir(join(forgeloreDir, \"knowledge\", \"capabilities\"), { recursive: true });\n await mkdir(join(forgeloreDir, \"knowledge\", \"decisions\"), { recursive: true });\n}\n\n/**\n * Scaffold the forgelore skill — agent-agnostic instructions for using\n * the forgelore CLI. This goes into the project root so any agent system\n * (OpenCode, Claude Code, Cursor, etc.) can discover it.\n */\nexport async function scaffoldSkill(projectRoot: string): Promise<void> {\n const skillDir = join(projectRoot, \"skills\", \"forgelore\");\n await mkdir(skillDir, { recursive: true });\n\n const skillPath = join(skillDir, \"SKILL.md\");\n if (await fileExists(skillPath)) {\n return; // Don't overwrite existing skill\n }\n\n const content = `# Forgelore — Spec-Driven Development\n\nThis project uses **forgelore** for spec-driven development. All significant changes\nshould go through the spec workflow before implementation begins.\n\n## When to Use Forgelore\n\n- Before starting any new feature or significant change\n- When requirements are unclear and need to be formalized\n- When multiple agents or developers will collaborate on a change\n- Before refactoring that touches multiple modules\n\n## CLI Commands\n\n| Command | Purpose |\n|---------|---------|\n| \\`forgelore status\\` | Show project status dashboard — start here |\n| \\`forgelore list\\` | List all changes and their states |\n| \\`forgelore propose \"idea\"\\` | Create a new change proposal |\n| \\`forgelore clarify <change>\\` | Refine requirements interactively |\n| \\`forgelore verify <change>\\` | Check spec completeness (structural) |\n| \\`forgelore diff <change>\\` | Show drift between specs and code |\n| \\`forgelore archive <change>\\` | Archive a completed change, extract knowledge |\n| \\`forgelore doctor\\` | Health check for the forgelore setup |\n| \\`forgelore capabilities\\` | List all registered capabilities |\n| \\`forgelore config [key] [value]\\` | Get or set configuration |\n\n## Workflow\n\n1. **Propose** — \\`forgelore propose \"add user authentication\"\\`\n2. **Plan** — Fill in \\`forgelore/changes/<name>/specs/requirements.md\\`, \\`scenarios.md\\`, \\`design.md\\`, and \\`tasks.md\\`\n3. **Verify** — \\`forgelore verify <name>\\` to check spec completeness\n4. **Build** — Implement tasks, updating status as you go\n5. **Validate** — Review implementation against specs\n6. **Archive** — \\`forgelore archive <name>\\` to capture knowledge\n\n## Key Directories\n\n\\`\\`\\`\nforgelore/\n├── forgelore.json # Configuration\n├── changes/ # Active change specs\n│ └── <change-name>/\n│ ├── proposal.md # Original idea\n│ ├── specs/\n│ │ ├── requirements.md # What to build\n│ │ └── scenarios.md # How it should work\n│ ├── design.md # Technical approach\n│ └── tasks.md # Atomic task breakdown\n└── knowledge/ # Project knowledge base\n ├── architecture.md # System architecture\n ├── patterns.md # Code patterns and conventions\n ├── glossary.md # Domain terminology\n ├── capabilities/ # Extracted capabilities (JSON)\n └── decisions/ # Architecture decision records\n\\`\\`\\`\n\n## Rules\n\n- **Spec first.** Do not start coding without a spec for non-trivial changes.\n- **Follow patterns.** Read \\`forgelore/knowledge/patterns.md\\` before writing code.\n- **Update tasks.** Mark task status as you work (\\`pending\\` → \\`in-progress\\` → \\`implemented\\`).\n- **Knowledge compounds.** After completing a change, archive it to capture capabilities and update the knowledge base.\n`;\n\n await writeFile(skillPath, content, \"utf-8\");\n}\n\n// --- Create ---\n\nexport async function createChange(\n projectRoot: string,\n name: string,\n proposalContent: string\n): Promise<Change> {\n const changePath = getChangePath(projectRoot, name);\n\n if (await fileExists(changePath)) {\n throw new Error(`Change '${name}' already exists at ${changePath}`);\n }\n\n await mkdir(changePath, { recursive: true });\n await mkdir(join(changePath, \"specs\"), { recursive: true });\n\n const now = new Date().toISOString();\n\n // Write proposal\n await writeFile(join(changePath, \"proposal.md\"), proposalContent, \"utf-8\");\n\n // Write empty spec files from templates\n await writeFile(\n join(changePath, \"specs\", \"requirements.md\"),\n `# Requirements: ${name}\\n\\n<!-- Define the requirements for this change -->\\n\\n## Functional Requirements\\n\\n1. \\n\\n## Non-Functional Requirements\\n\\n1. \\n`,\n \"utf-8\"\n );\n\n await writeFile(\n join(changePath, \"specs\", \"scenarios.md\"),\n `# Scenarios: ${name}\\n\\n<!-- Define acceptance scenarios -->\\n\\n## Happy Path\\n\\n1. \\n\\n## Edge Cases\\n\\n1. \\n\\n## Error Cases\\n\\n1. \\n`,\n \"utf-8\"\n );\n\n await writeFile(\n join(changePath, \"design.md\"),\n `# Design: ${name}\\n\\n<!-- Technical approach and architecture decisions -->\\n\\n## Approach\\n\\n\\n## Key Decisions\\n\\n\\n## Files to Modify\\n\\n- \\n\\n## Dependencies\\n\\n- \\n`,\n \"utf-8\"\n );\n\n await writeFile(\n join(changePath, \"tasks.md\"),\n `# Tasks: ${name}\\n\\n<!-- Implementation checklist -->\\n\\n| ID | Task | Status | Category | Notes |\\n|----|------|--------|----------|-------|\\n| 1 | | pending | | |\\n`,\n \"utf-8\"\n );\n\n const change: Change = {\n name,\n status: \"proposed\",\n createdAt: now,\n updatedAt: now,\n path: changePath,\n tasks: [],\n };\n\n // Write metadata\n await writeFile(\n join(changePath, \".forge-meta.json\"),\n JSON.stringify(change, null, 2) + \"\\n\",\n \"utf-8\"\n );\n\n return change;\n}\n\n// --- Read ---\n\nexport async function readChange(\n projectRoot: string,\n name: string\n): Promise<Change> {\n const changePath = getChangePath(projectRoot, name);\n const metaPath = join(changePath, \".forge-meta.json\");\n\n if (!(await fileExists(metaPath))) {\n throw new Error(`Change '${name}' not found at ${changePath}`);\n }\n\n const raw = await readFile(metaPath, \"utf-8\");\n return JSON.parse(raw) as Change;\n}\n\nexport async function readChangeFile(\n projectRoot: string,\n changeName: string,\n fileName: string\n): Promise<string> {\n const filePath = join(getChangePath(projectRoot, changeName), fileName);\n if (!(await fileExists(filePath))) {\n throw new Error(`File '${fileName}' not found in change '${changeName}'`);\n }\n return readFile(filePath, \"utf-8\");\n}\n\n// --- List ---\n\nexport async function listChanges(\n projectRoot: string,\n includeArchived = false\n): Promise<Change[]> {\n const changesDir = getChangesDir(projectRoot);\n\n if (!(await fileExists(changesDir))) {\n return [];\n }\n\n const entries = await readdir(changesDir, { withFileTypes: true });\n const changes: Change[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory() || entry.name === \"archive\") continue;\n\n const metaPath = join(changesDir, entry.name, \".forge-meta.json\");\n if (await fileExists(metaPath)) {\n const raw = await readFile(metaPath, \"utf-8\");\n changes.push(JSON.parse(raw) as Change);\n }\n }\n\n if (includeArchived) {\n const archiveDir = getArchiveDir(projectRoot);\n if (await fileExists(archiveDir)) {\n const archiveEntries = await readdir(archiveDir, { withFileTypes: true });\n for (const entry of archiveEntries) {\n if (!entry.isDirectory()) continue;\n const metaPath = join(archiveDir, entry.name, \".forge-meta.json\");\n if (await fileExists(metaPath)) {\n const raw = await readFile(metaPath, \"utf-8\");\n changes.push(JSON.parse(raw) as Change);\n }\n }\n }\n }\n\n return changes.sort(\n (a, b) => new Date(b.updatedAt).getTime() - new Date(a.updatedAt).getTime()\n );\n}\n\n// --- Update ---\n\nexport async function updateChangeStatus(\n projectRoot: string,\n name: string,\n status: ChangeStatus\n): Promise<Change> {\n const change = await readChange(projectRoot, name);\n change.status = status;\n change.updatedAt = new Date().toISOString();\n\n const metaPath = join(change.path, \".forge-meta.json\");\n await writeFile(metaPath, JSON.stringify(change, null, 2) + \"\\n\", \"utf-8\");\n\n return change;\n}\n\nexport async function updateTaskStatus(\n projectRoot: string,\n changeName: string,\n taskId: string,\n status: TaskStatus\n): Promise<void> {\n const change = await readChange(projectRoot, changeName);\n const task = change.tasks.find((t) => t.id === taskId);\n if (task) {\n task.status = status;\n }\n change.updatedAt = new Date().toISOString();\n\n const metaPath = join(change.path, \".forge-meta.json\");\n await writeFile(metaPath, JSON.stringify(change, null, 2) + \"\\n\", \"utf-8\");\n}\n\n// --- Archive ---\n\nexport async function archiveChange(\n projectRoot: string,\n name: string\n): Promise<string> {\n const changePath = getChangePath(projectRoot, name);\n if (!(await fileExists(changePath))) {\n throw new Error(`Change '${name}' not found`);\n }\n\n const archiveDir = getArchiveDir(projectRoot);\n const datestamp = new Date().toISOString().slice(0, 10);\n const archiveName = `${datestamp}-${name}`;\n const archivePath = join(archiveDir, archiveName);\n\n // Update status before archiving\n await updateChangeStatus(projectRoot, name, \"archived\");\n\n // Move to archive\n await rename(changePath, archivePath);\n\n return archivePath;\n}\n","/**\n * Knowledge base management\n * Capabilities, decisions, architecture docs, patterns\n */\n\nimport { readFile, writeFile, readdir, mkdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists, getForgeloreDir } from \"../config/index.js\";\nimport type { Capability, Decision, KnowledgeBase } from \"../types/index.js\";\n\n// --- Paths ---\n\nexport function getKnowledgeDir(projectRoot: string): string {\n return join(getForgeloreDir(projectRoot), \"knowledge\");\n}\n\nexport function getCapabilitiesDir(projectRoot: string): string {\n return join(getKnowledgeDir(projectRoot), \"capabilities\");\n}\n\nexport function getDecisionsDir(projectRoot: string): string {\n return join(getKnowledgeDir(projectRoot), \"decisions\");\n}\n\n// --- Read Knowledge Base ---\n\nexport async function readKnowledge(projectRoot: string): Promise<KnowledgeBase> {\n const knowledgeDir = getKnowledgeDir(projectRoot);\n\n const kb: KnowledgeBase = {\n capabilities: await listCapabilities(projectRoot),\n decisions: await listDecisions(projectRoot),\n };\n\n // Read optional markdown files\n const archPath = join(knowledgeDir, \"architecture.md\");\n if (await fileExists(archPath)) {\n kb.architecture = await readFile(archPath, \"utf-8\");\n }\n\n const patternsPath = join(knowledgeDir, \"patterns.md\");\n if (await fileExists(patternsPath)) {\n kb.patterns = await readFile(patternsPath, \"utf-8\");\n }\n\n const glossaryPath = join(knowledgeDir, \"glossary.md\");\n if (await fileExists(glossaryPath)) {\n kb.glossary = await readFile(glossaryPath, \"utf-8\");\n }\n\n return kb;\n}\n\n// --- Capabilities ---\n\nexport async function listCapabilities(projectRoot: string): Promise<Capability[]> {\n const dir = getCapabilitiesDir(projectRoot);\n if (!(await fileExists(dir))) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n const capabilities: Capability[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n const raw = await readFile(join(dir, entry.name), \"utf-8\");\n capabilities.push(JSON.parse(raw) as Capability);\n }\n\n return capabilities.sort(\n (a, b) => new Date(b.archivedAt).getTime() - new Date(a.archivedAt).getTime()\n );\n}\n\nexport async function addCapability(\n projectRoot: string,\n capability: Capability\n): Promise<void> {\n const dir = getCapabilitiesDir(projectRoot);\n await mkdir(dir, { recursive: true });\n\n const fileName = `${capability.id}.json`;\n await writeFile(\n join(dir, fileName),\n JSON.stringify(capability, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\nexport async function getCapability(\n projectRoot: string,\n id: string\n): Promise<Capability | null> {\n const filePath = join(getCapabilitiesDir(projectRoot), `${id}.json`);\n if (!(await fileExists(filePath))) return null;\n const raw = await readFile(filePath, \"utf-8\");\n return JSON.parse(raw) as Capability;\n}\n\n// --- Decisions ---\n\nexport async function listDecisions(projectRoot: string): Promise<Decision[]> {\n const dir = getDecisionsDir(projectRoot);\n if (!(await fileExists(dir))) return [];\n\n const entries = await readdir(dir, { withFileTypes: true });\n const decisions: Decision[] = [];\n\n for (const entry of entries) {\n if (!entry.isFile() || !entry.name.endsWith(\".json\")) continue;\n const raw = await readFile(join(dir, entry.name), \"utf-8\");\n decisions.push(JSON.parse(raw) as Decision);\n }\n\n return decisions.sort((a, b) => a.id.localeCompare(b.id));\n}\n\nexport async function addDecision(\n projectRoot: string,\n decision: Decision\n): Promise<void> {\n const dir = getDecisionsDir(projectRoot);\n await mkdir(dir, { recursive: true });\n\n const fileName = `${decision.id}.json`;\n await writeFile(\n join(dir, fileName),\n JSON.stringify(decision, null, 2) + \"\\n\",\n \"utf-8\"\n );\n}\n\n// --- Scaffold Knowledge ---\n\nexport async function scaffoldKnowledge(projectRoot: string): Promise<void> {\n const knowledgeDir = getKnowledgeDir(projectRoot);\n await mkdir(join(knowledgeDir, \"capabilities\"), { recursive: true });\n await mkdir(join(knowledgeDir, \"decisions\"), { recursive: true });\n\n const archPath = join(knowledgeDir, \"architecture.md\");\n if (!(await fileExists(archPath))) {\n await writeFile(\n archPath,\n `# Architecture\\n\\n<!-- Living architecture documentation. Updated as the system evolves. -->\\n\\n## Overview\\n\\n\\n## Components\\n\\n\\n## Data Flow\\n\\n\\n## Key Integrations\\n\\n`,\n \"utf-8\"\n );\n }\n\n const patternsPath = join(knowledgeDir, \"patterns.md\");\n if (!(await fileExists(patternsPath))) {\n await writeFile(\n patternsPath,\n `# Patterns\\n\\n<!-- Established patterns in this codebase. Agents should follow these. -->\\n\\n## Code Patterns\\n\\n\\n## Naming Conventions\\n\\n\\n## Error Handling\\n\\n\\n## Testing Patterns\\n\\n`,\n \"utf-8\"\n );\n }\n\n const glossaryPath = join(knowledgeDir, \"glossary.md\");\n if (!(await fileExists(glossaryPath))) {\n await writeFile(\n glossaryPath,\n `# Glossary\\n\\n<!-- Domain-specific terms used in this project -->\\n\\n| Term | Definition |\\n|------|------------|\\n| | |\\n`,\n \"utf-8\"\n );\n }\n}\n","/**\n * Capability extraction\n * Extract capabilities from archived changes for the knowledge base\n */\n\nimport { readFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { fileExists } from \"../config/index.js\";\nimport { addCapability } from \"../knowledge/index.js\";\nimport type { Capability } from \"../types/index.js\";\n\n/**\n * Generate a URL-safe slug from a string\n */\nexport function slugify(text: string): string {\n return text\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n\n/**\n * Create a capability record from extracted data\n */\nexport function createCapability(\n name: string,\n description: string,\n sourceChange: string,\n files: string[] = [],\n tags: string[] = []\n): Capability {\n return {\n id: slugify(name),\n name,\n description,\n sourceChange,\n archivedAt: new Date().toISOString(),\n files,\n tags,\n };\n}\n\n/**\n * Register a capability in the project's knowledge base\n */\nexport async function registerCapability(\n projectRoot: string,\n capability: Capability\n): Promise<void> {\n await addCapability(projectRoot, capability);\n}\n\n/**\n * Read the outcome.md from an archived change (if it exists)\n */\nexport async function readOutcome(archivePath: string): Promise<string | null> {\n const outcomePath = join(archivePath, \"outcome.md\");\n if (!(await fileExists(outcomePath))) return null;\n return readFile(outcomePath, \"utf-8\");\n}\n\n/**\n * Write outcome.md to a change directory (step 1 of archive)\n */\nexport async function writeOutcome(\n changePath: string,\n content: string\n): Promise<void> {\n const { writeFile: wf } = await import(\"node:fs/promises\");\n await wf(join(changePath, \"outcome.md\"), content, \"utf-8\");\n}\n","/**\n * Progress tracking\n * Calculate completion percentages and status summaries\n */\n\nimport type { Change, Task, TaskStatus, ForgeloreProject } from \"../types/index.js\";\nimport { listChanges } from \"../spec/index.js\";\nimport { listCapabilities } from \"../knowledge/index.js\";\n\nexport interface TaskSummary {\n total: number;\n pending: number;\n inProgress: number;\n passed: number;\n failed: number;\n blocked: number;\n completionPercent: number;\n}\n\nexport interface ProjectSummary {\n activeChanges: number;\n archivedChanges: number;\n totalCapabilities: number;\n taskSummary: TaskSummary;\n changes: Array<{\n name: string;\n status: string;\n taskSummary: TaskSummary;\n }>;\n}\n\nexport function summarizeTasks(tasks: Task[]): TaskSummary {\n const total = tasks.length;\n if (total === 0) {\n return {\n total: 0,\n pending: 0,\n inProgress: 0,\n passed: 0,\n failed: 0,\n blocked: 0,\n completionPercent: 0,\n };\n }\n\n const pending = tasks.filter((t) => t.status === \"pending\").length;\n const inProgress = tasks.filter(\n (t) =>\n t.status === \"claimed\" ||\n t.status === \"in-progress\" ||\n t.status === \"implemented\" ||\n t.status === \"validating\"\n ).length;\n const passed = tasks.filter((t) => t.status === \"passed\").length;\n const failed = tasks.filter((t) => t.status === \"failed\").length;\n const blocked = tasks.filter((t) => t.status === \"blocked\").length;\n\n const completionPercent = Math.round((passed / total) * 100);\n\n return { total, pending, inProgress, passed, failed, blocked, completionPercent };\n}\n\nexport async function getProjectSummary(\n projectRoot: string\n): Promise<ProjectSummary> {\n const activeChanges = await listChanges(projectRoot, false);\n const allChanges = await listChanges(projectRoot, true);\n const archivedChanges = allChanges.filter((c) => c.status === \"archived\");\n const capabilities = await listCapabilities(projectRoot);\n\n const allTasks = activeChanges.flatMap((c) => c.tasks);\n\n return {\n activeChanges: activeChanges.length,\n archivedChanges: archivedChanges.length,\n totalCapabilities: capabilities.length,\n taskSummary: summarizeTasks(allTasks),\n changes: activeChanges.map((c) => ({\n name: c.name,\n status: c.status,\n taskSummary: summarizeTasks(c.tasks),\n })),\n };\n}\n","/**\n * Drift detection\n * Identify where specs and implementation have diverged\n *\n * Drift categories:\n * - unspecced-change: files modified that aren't in any active spec\n * - stale-spec: specs with no progress or activity\n * - missing-capability: archived changes without capabilities extracted\n * - outdated-knowledge: knowledge base files that may be stale\n */\n\nimport { readFile, readdir, stat } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { fileExists, getForgeloreDir } from \"../config/index.js\";\nimport { listChanges, readChange, readChangeFile, getArchiveDir } from \"../spec/index.js\";\nimport { listCapabilities } from \"../knowledge/index.js\";\nimport type { DriftItem, DriftReport, DriftSeverity, Change } from \"../types/index.js\";\n\n/**\n * Run a full drift analysis on the project\n */\nexport async function analyzeDrift(projectRoot: string): Promise<DriftReport> {\n const items: DriftItem[] = [];\n\n await detectStaleSpecs(projectRoot, items);\n await detectMissingCapabilities(projectRoot, items);\n await detectIncompleteSpecs(projectRoot, items);\n await detectStaleKnowledge(projectRoot, items);\n\n const score = calculateScore(items);\n\n return {\n score,\n items,\n timestamp: new Date().toISOString(),\n };\n}\n\n/**\n * Detect changes that have gone stale (no updates in a while)\n */\nasync function detectStaleSpecs(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const changes = await listChanges(projectRoot, false);\n const now = Date.now();\n\n for (const change of changes) {\n const daysSinceUpdate = Math.floor(\n (now - new Date(change.updatedAt).getTime()) / (1000 * 60 * 60 * 24)\n );\n\n if (change.status === \"proposed\" && daysSinceUpdate > 14) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" proposed ${daysSinceUpdate} days ago with no progress — consider clarifying or closing`,\n });\n } else if (change.status === \"proposed\" && daysSinceUpdate > 7) {\n items.push({\n type: \"stale-spec\",\n severity: \"info\",\n spec: change.name,\n message: `\"${change.name}\" proposed ${daysSinceUpdate} days ago — consider running \\`forgelore clarify ${change.name}\\``,\n });\n }\n\n if (change.status === \"in-progress\" && daysSinceUpdate > 7) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" in progress for ${daysSinceUpdate} days since last update`,\n });\n }\n\n if (change.status === \"validating\" && daysSinceUpdate > 3) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" awaiting validation for ${daysSinceUpdate} days`,\n });\n }\n\n // Check for stale tasks: status is in-progress but all tasks still pending\n if (\n change.status === \"in-progress\" &&\n change.tasks.length > 0 &&\n change.tasks.every((t) => t.status === \"pending\")\n ) {\n items.push({\n type: \"stale-spec\",\n severity: \"warning\",\n spec: change.name,\n message: `\"${change.name}\" is in-progress but all ${change.tasks.length} tasks are still pending`,\n });\n }\n }\n}\n\n/**\n * Detect archived changes that don't have capabilities extracted\n */\nasync function detectMissingCapabilities(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const archiveDir = getArchiveDir(projectRoot);\n if (!(await fileExists(archiveDir))) return;\n\n const capabilities = await listCapabilities(projectRoot);\n const capSources = new Set(capabilities.map((c) => c.sourceChange));\n\n const entries = await readdir(archiveDir, { withFileTypes: true });\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n // Archive names are like \"2025-01-15-feature-name\"\n // The original change name is everything after the date prefix\n const match = entry.name.match(/^\\d{4}-\\d{2}-\\d{2}-(.+)$/);\n const changeName = match ? match[1] : entry.name;\n\n // Check for outcome.md\n const outcomePath = join(archiveDir, entry.name, \"outcome.md\");\n if (!(await fileExists(outcomePath))) {\n items.push({\n type: \"missing-capability\",\n severity: \"warning\",\n spec: changeName,\n message: `Archived change \"${changeName}\" has no outcome.md — knowledge not captured`,\n });\n continue;\n }\n\n // Check if capabilities were extracted\n if (!capSources.has(changeName)) {\n items.push({\n type: \"missing-capability\",\n severity: \"info\",\n spec: changeName,\n message: `Archived change \"${changeName}\" has outcome.md but no capabilities extracted`,\n });\n }\n }\n}\n\n/**\n * Detect active changes with incomplete spec files\n */\nasync function detectIncompleteSpecs(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const changes = await listChanges(projectRoot, false);\n\n for (const change of changes) {\n // Only flag incomplete specs for changes that are past the proposal stage\n if (change.status === \"proposed\") continue;\n\n const specFiles = [\n { path: \"specs/requirements.md\", name: \"requirements\" },\n { path: \"specs/scenarios.md\", name: \"scenarios\" },\n { path: \"design.md\", name: \"design\" },\n { path: \"tasks.md\", name: \"tasks\" },\n ];\n\n for (const spec of specFiles) {\n try {\n const content = await readChangeFile(projectRoot, change.name, spec.path);\n const stripped = content.replace(/<!--.*?-->/gs, \"\").trim();\n if (stripped.length < 50) {\n const severity: DriftSeverity =\n change.status === \"in-progress\" ? \"warning\" : \"info\";\n items.push({\n type: \"stale-spec\",\n severity,\n spec: change.name,\n file: spec.path,\n message: `\"${change.name}/${spec.path}\" has minimal content (status: ${change.status})`,\n });\n }\n } catch {\n items.push({\n type: \"stale-spec\",\n severity: \"critical\",\n spec: change.name,\n file: spec.path,\n message: `\"${change.name}/${spec.path}\" is missing`,\n });\n }\n }\n }\n}\n\n/**\n * Detect knowledge base files that may be outdated\n */\nasync function detectStaleKnowledge(\n projectRoot: string,\n items: DriftItem[]\n): Promise<void> {\n const forgeloreDir = getForgeloreDir(projectRoot);\n const knowledgeDir = join(forgeloreDir, \"knowledge\");\n\n const knowledgeFiles = [\n { path: \"architecture.md\", name: \"Architecture\" },\n { path: \"patterns.md\", name: \"Patterns\" },\n { path: \"glossary.md\", name: \"Glossary\" },\n ];\n\n for (const kb of knowledgeFiles) {\n const filePath = join(knowledgeDir, kb.path);\n if (!(await fileExists(filePath))) {\n items.push({\n type: \"outdated-knowledge\",\n severity: \"info\",\n file: `knowledge/${kb.path}`,\n message: `${kb.name} documentation is missing`,\n });\n continue;\n }\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n const stripped = content.replace(/<!--.*?-->/gs, \"\").trim();\n if (stripped.length < 50) {\n items.push({\n type: \"outdated-knowledge\",\n severity: \"info\",\n file: `knowledge/${kb.path}`,\n message: `${kb.name} documentation has minimal content`,\n });\n }\n\n // Check staleness by file modification time\n const stats = await stat(filePath);\n const daysSinceModified = Math.floor(\n (Date.now() - stats.mtimeMs) / (1000 * 60 * 60 * 24)\n );\n\n // Only flag if there have been archived changes since the last update\n const capabilities = await listCapabilities(projectRoot);\n const recentCaps = capabilities.filter((c) => {\n const archiveTime = new Date(c.archivedAt).getTime();\n return archiveTime > stats.mtimeMs;\n });\n\n if (recentCaps.length > 0 && daysSinceModified > 7) {\n items.push({\n type: \"outdated-knowledge\",\n severity: \"warning\",\n file: `knowledge/${kb.path}`,\n message: `${kb.name} not updated since ${recentCaps.length} capabilities were archived`,\n });\n }\n } catch {\n // Ignore read errors\n }\n }\n}\n\n/**\n * Calculate drift score (0-100, where 100 = no drift)\n */\nfunction calculateScore(items: DriftItem[]): number {\n if (items.length === 0) return 100;\n\n let penalty = 0;\n for (const item of items) {\n switch (item.severity) {\n case \"critical\":\n penalty += 15;\n break;\n case \"warning\":\n penalty += 5;\n break;\n case \"info\":\n penalty += 1;\n break;\n }\n }\n\n return Math.max(0, 100 - penalty);\n}\n"],"mappings":";AAKA,SAAS,UAAU,WAAW,cAAc;AAC5C,SAAS,YAAY;AAGrB,IAAM,kBAAkB;AACxB,IAAM,gBAAgB;AAEtB,IAAM,iBAAkC;AAAA,EACtC,SAAS;AAAA,EACT,SAAS;AAAA,EACT,MAAM;AAAA,EACN,eAAe;AAAA,IACb,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,gBAAgB;AAAA,EAClB;AAAA,EACA,aAAa;AAAA,IACX,uBAAuB;AAAA,IACvB,sBAAsB;AAAA,IACtB,qBAAqB;AAAA,IACrB,mBAAmB;AAAA,EACrB;AACF;AAEA,eAAsB,WAAW,MAAgC;AAC/D,MAAI;AACF,UAAM,OAAO,IAAI;AACjB,WAAO;AAAA,EACT,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEO,SAAS,gBAAgB,aAA6B;AAC3D,SAAO,KAAK,aAAa,aAAa;AACxC;AAEO,SAAS,cAAc,aAA6B;AACzD,SAAO,KAAK,gBAAgB,WAAW,GAAG,eAAe;AAC3D;AAEA,eAAsB,WAAW,aAA+C;AAC9E,QAAM,aAAa,cAAc,WAAW;AAC5C,MAAI,CAAE,MAAM,WAAW,UAAU,GAAI;AACnC,UAAM,IAAI;AAAA,MACR,gCAAgC,UAAU;AAAA,IAC5C;AAAA,EACF;AAEA,QAAM,MAAM,MAAM,SAAS,YAAY,OAAO;AAC9C,SAAO,KAAK,MAAM,GAAG;AACvB;AAEA,eAAsB,YACpB,aACA,QACe;AACf,QAAM,aAAa,cAAc,WAAW;AAC5C,QAAM,UAAU,YAAY,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC7E;AAEO,SAAS,oBAAoB,MAAiC;AACnE,SAAO,EAAE,GAAG,gBAAgB,KAAK;AACnC;AAEA,eAAsB,aAAa,aAAuC;AACxE,SAAO,WAAW,cAAc,WAAW,CAAC;AAC9C;AAEA,eAAsB,eACpB,aACA,KACkB;AAClB,QAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,QAAM,OAAO,IAAI,MAAM,GAAG;AAC1B,MAAI,UAAmB;AACvB,aAAW,KAAK,MAAM;AACpB,QAAI,YAAY,QAAQ,YAAY,UAAa,OAAO,YAAY,UAAU;AAC5E,aAAO;AAAA,IACT;AACA,cAAW,QAAoC,CAAC;AAAA,EAClD;AACA,SAAO;AACT;AAEA,eAAsB,eACpB,aACA,KACA,OACe;AACf,QAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,QAAM,OAAO,IAAI,MAAM,GAAG;AAC1B,MAAI,UAAmC;AACvC,WAAS,IAAI,GAAG,IAAI,KAAK,SAAS,GAAG,KAAK;AACxC,UAAM,IAAI,KAAK,CAAC;AAChB,QAAI,EAAE,KAAK,YAAY,OAAO,QAAQ,CAAC,MAAM,UAAU;AACrD,cAAQ,CAAC,IAAI,CAAC;AAAA,IAChB;AACA,cAAU,QAAQ,CAAC;AAAA,EACrB;AACA,UAAQ,KAAK,KAAK,SAAS,CAAC,CAAC,IAAI;AACjC,QAAM,YAAY,aAAa,MAAM;AACvC;;;ACtGA,SAAS,YAAAA,WAAU,aAAAC,YAAW,SAAS,OAAO,cAAkB;AAChE,SAAS,QAAAC,aAAsB;AAMxB,SAAS,cAAc,aAA6B;AACzD,SAAOC,MAAK,gBAAgB,WAAW,GAAG,SAAS;AACrD;AAEO,SAAS,cAAc,aAA6B;AACzD,SAAOA,MAAK,cAAc,WAAW,GAAG,SAAS;AACnD;AAEO,SAAS,cAAc,aAAqB,YAA4B;AAC7E,SAAOA,MAAK,cAAc,WAAW,GAAG,UAAU;AACpD;AAIA,eAAsB,iBAAiB,aAAoC;AACzE,QAAM,eAAe,gBAAgB,WAAW;AAChD,QAAM,MAAMA,MAAK,cAAc,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,QAAM,MAAMA,MAAK,cAAc,WAAW,SAAS,GAAG,EAAE,WAAW,KAAK,CAAC;AACzE,QAAM,MAAMA,MAAK,cAAc,aAAa,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;AAChF,QAAM,MAAMA,MAAK,cAAc,aAAa,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAC/E;AAOA,eAAsB,cAAc,aAAoC;AACtE,QAAM,WAAWA,MAAK,aAAa,UAAU,WAAW;AACxD,QAAM,MAAM,UAAU,EAAE,WAAW,KAAK,CAAC;AAEzC,QAAM,YAAYA,MAAK,UAAU,UAAU;AAC3C,MAAI,MAAM,WAAW,SAAS,GAAG;AAC/B;AAAA,EACF;AAEA,QAAM,UAAU;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAiEhB,QAAMC,WAAU,WAAW,SAAS,OAAO;AAC7C;AAIA,eAAsB,aACpB,aACA,MACA,iBACiB;AACjB,QAAM,aAAa,cAAc,aAAa,IAAI;AAElD,MAAI,MAAM,WAAW,UAAU,GAAG;AAChC,UAAM,IAAI,MAAM,WAAW,IAAI,uBAAuB,UAAU,EAAE;AAAA,EACpE;AAEA,QAAM,MAAM,YAAY,EAAE,WAAW,KAAK,CAAC;AAC3C,QAAM,MAAMD,MAAK,YAAY,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAE1D,QAAM,OAAM,oBAAI,KAAK,GAAE,YAAY;AAGnC,QAAMC,WAAUD,MAAK,YAAY,aAAa,GAAG,iBAAiB,OAAO;AAGzE,QAAMC;AAAA,IACJD,MAAK,YAAY,SAAS,iBAAiB;AAAA,IAC3C,mBAAmB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IACvB;AAAA,EACF;AAEA,QAAMC;AAAA,IACJD,MAAK,YAAY,SAAS,cAAc;AAAA,IACxC,gBAAgB,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IACpB;AAAA,EACF;AAEA,QAAMC;AAAA,IACJD,MAAK,YAAY,WAAW;AAAA,IAC5B,aAAa,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IACjB;AAAA,EACF;AAEA,QAAMC;AAAA,IACJD,MAAK,YAAY,UAAU;AAAA,IAC3B,YAAY,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAChB;AAAA,EACF;AAEA,QAAM,SAAiB;AAAA,IACrB;AAAA,IACA,QAAQ;AAAA,IACR,WAAW;AAAA,IACX,WAAW;AAAA,IACX,MAAM;AAAA,IACN,OAAO,CAAC;AAAA,EACV;AAGA,QAAMC;AAAA,IACJD,MAAK,YAAY,kBAAkB;AAAA,IACnC,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI;AAAA,IAClC;AAAA,EACF;AAEA,SAAO;AACT;AAIA,eAAsB,WACpB,aACA,MACiB;AACjB,QAAM,aAAa,cAAc,aAAa,IAAI;AAClD,QAAM,WAAWA,MAAK,YAAY,kBAAkB;AAEpD,MAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;AACjC,UAAM,IAAI,MAAM,WAAW,IAAI,kBAAkB,UAAU,EAAE;AAAA,EAC/D;AAEA,QAAM,MAAM,MAAME,UAAS,UAAU,OAAO;AAC5C,SAAO,KAAK,MAAM,GAAG;AACvB;AAEA,eAAsB,eACpB,aACA,YACA,UACiB;AACjB,QAAM,WAAWF,MAAK,cAAc,aAAa,UAAU,GAAG,QAAQ;AACtE,MAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;AACjC,UAAM,IAAI,MAAM,SAAS,QAAQ,0BAA0B,UAAU,GAAG;AAAA,EAC1E;AACA,SAAOE,UAAS,UAAU,OAAO;AACnC;AAIA,eAAsB,YACpB,aACA,kBAAkB,OACC;AACnB,QAAM,aAAa,cAAc,WAAW;AAE5C,MAAI,CAAE,MAAM,WAAW,UAAU,GAAI;AACnC,WAAO,CAAC;AAAA,EACV;AAEA,QAAM,UAAU,MAAM,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AACjE,QAAM,UAAoB,CAAC;AAE3B,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,YAAY,KAAK,MAAM,SAAS,UAAW;AAEtD,UAAM,WAAWF,MAAK,YAAY,MAAM,MAAM,kBAAkB;AAChE,QAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,YAAM,MAAM,MAAME,UAAS,UAAU,OAAO;AAC5C,cAAQ,KAAK,KAAK,MAAM,GAAG,CAAW;AAAA,IACxC;AAAA,EACF;AAEA,MAAI,iBAAiB;AACnB,UAAM,aAAa,cAAc,WAAW;AAC5C,QAAI,MAAM,WAAW,UAAU,GAAG;AAChC,YAAM,iBAAiB,MAAM,QAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AACxE,iBAAW,SAAS,gBAAgB;AAClC,YAAI,CAAC,MAAM,YAAY,EAAG;AAC1B,cAAM,WAAWF,MAAK,YAAY,MAAM,MAAM,kBAAkB;AAChE,YAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,gBAAM,MAAM,MAAME,UAAS,UAAU,OAAO;AAC5C,kBAAQ,KAAK,KAAK,MAAM,GAAG,CAAW;AAAA,QACxC;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,SAAO,QAAQ;AAAA,IACb,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,SAAS,EAAE,QAAQ;AAAA,EAC5E;AACF;AAIA,eAAsB,mBACpB,aACA,MACA,QACiB;AACjB,QAAM,SAAS,MAAM,WAAW,aAAa,IAAI;AACjD,SAAO,SAAS;AAChB,SAAO,aAAY,oBAAI,KAAK,GAAE,YAAY;AAE1C,QAAM,WAAWF,MAAK,OAAO,MAAM,kBAAkB;AACrD,QAAMC,WAAU,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAEzE,SAAO;AACT;AAEA,eAAsB,iBACpB,aACA,YACA,QACA,QACe;AACf,QAAM,SAAS,MAAM,WAAW,aAAa,UAAU;AACvD,QAAM,OAAO,OAAO,MAAM,KAAK,CAAC,MAAM,EAAE,OAAO,MAAM;AACrD,MAAI,MAAM;AACR,SAAK,SAAS;AAAA,EAChB;AACA,SAAO,aAAY,oBAAI,KAAK,GAAE,YAAY;AAE1C,QAAM,WAAWD,MAAK,OAAO,MAAM,kBAAkB;AACrD,QAAMC,WAAU,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC,IAAI,MAAM,OAAO;AAC3E;AAIA,eAAsB,cACpB,aACA,MACiB;AACjB,QAAM,aAAa,cAAc,aAAa,IAAI;AAClD,MAAI,CAAE,MAAM,WAAW,UAAU,GAAI;AACnC,UAAM,IAAI,MAAM,WAAW,IAAI,aAAa;AAAA,EAC9C;AAEA,QAAM,aAAa,cAAc,WAAW;AAC5C,QAAM,aAAY,oBAAI,KAAK,GAAE,YAAY,EAAE,MAAM,GAAG,EAAE;AACtD,QAAM,cAAc,GAAG,SAAS,IAAI,IAAI;AACxC,QAAM,cAAcD,MAAK,YAAY,WAAW;AAGhD,QAAM,mBAAmB,aAAa,MAAM,UAAU;AAGtD,QAAM,OAAO,YAAY,WAAW;AAEpC,SAAO;AACT;;;ACnTA,SAAS,YAAAG,WAAU,aAAAC,YAAW,WAAAC,UAAS,SAAAC,cAAa;AACpD,SAAS,QAAAC,aAAY;AAMd,SAAS,gBAAgB,aAA6B;AAC3D,SAAOC,MAAK,gBAAgB,WAAW,GAAG,WAAW;AACvD;AAEO,SAAS,mBAAmB,aAA6B;AAC9D,SAAOA,MAAK,gBAAgB,WAAW,GAAG,cAAc;AAC1D;AAEO,SAAS,gBAAgB,aAA6B;AAC3D,SAAOA,MAAK,gBAAgB,WAAW,GAAG,WAAW;AACvD;AAIA,eAAsB,cAAc,aAA6C;AAC/E,QAAM,eAAe,gBAAgB,WAAW;AAEhD,QAAM,KAAoB;AAAA,IACxB,cAAc,MAAM,iBAAiB,WAAW;AAAA,IAChD,WAAW,MAAM,cAAc,WAAW;AAAA,EAC5C;AAGA,QAAM,WAAWA,MAAK,cAAc,iBAAiB;AACrD,MAAI,MAAM,WAAW,QAAQ,GAAG;AAC9B,OAAG,eAAe,MAAMC,UAAS,UAAU,OAAO;AAAA,EACpD;AAEA,QAAM,eAAeD,MAAK,cAAc,aAAa;AACrD,MAAI,MAAM,WAAW,YAAY,GAAG;AAClC,OAAG,WAAW,MAAMC,UAAS,cAAc,OAAO;AAAA,EACpD;AAEA,QAAM,eAAeD,MAAK,cAAc,aAAa;AACrD,MAAI,MAAM,WAAW,YAAY,GAAG;AAClC,OAAG,WAAW,MAAMC,UAAS,cAAc,OAAO;AAAA,EACpD;AAEA,SAAO;AACT;AAIA,eAAsB,iBAAiB,aAA4C;AACjF,QAAM,MAAM,mBAAmB,WAAW;AAC1C,MAAI,CAAE,MAAM,WAAW,GAAG,EAAI,QAAO,CAAC;AAEtC,QAAM,UAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,QAAM,eAA6B,CAAC;AAEpC,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,OAAO,EAAG;AACtD,UAAM,MAAM,MAAMD,UAASD,MAAK,KAAK,MAAM,IAAI,GAAG,OAAO;AACzD,iBAAa,KAAK,KAAK,MAAM,GAAG,CAAe;AAAA,EACjD;AAEA,SAAO,aAAa;AAAA,IAClB,CAAC,GAAG,MAAM,IAAI,KAAK,EAAE,UAAU,EAAE,QAAQ,IAAI,IAAI,KAAK,EAAE,UAAU,EAAE,QAAQ;AAAA,EAC9E;AACF;AAEA,eAAsB,cACpB,aACA,YACe;AACf,QAAM,MAAM,mBAAmB,WAAW;AAC1C,QAAMG,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAEpC,QAAM,WAAW,GAAG,WAAW,EAAE;AACjC,QAAMC;AAAA,IACJJ,MAAK,KAAK,QAAQ;AAAA,IAClB,KAAK,UAAU,YAAY,MAAM,CAAC,IAAI;AAAA,IACtC;AAAA,EACF;AACF;AAEA,eAAsB,cACpB,aACA,IAC4B;AAC5B,QAAM,WAAWA,MAAK,mBAAmB,WAAW,GAAG,GAAG,EAAE,OAAO;AACnE,MAAI,CAAE,MAAM,WAAW,QAAQ,EAAI,QAAO;AAC1C,QAAM,MAAM,MAAMC,UAAS,UAAU,OAAO;AAC5C,SAAO,KAAK,MAAM,GAAG;AACvB;AAIA,eAAsB,cAAc,aAA0C;AAC5E,QAAM,MAAM,gBAAgB,WAAW;AACvC,MAAI,CAAE,MAAM,WAAW,GAAG,EAAI,QAAO,CAAC;AAEtC,QAAM,UAAU,MAAMC,SAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAC1D,QAAM,YAAwB,CAAC;AAE/B,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,OAAO,KAAK,CAAC,MAAM,KAAK,SAAS,OAAO,EAAG;AACtD,UAAM,MAAM,MAAMD,UAASD,MAAK,KAAK,MAAM,IAAI,GAAG,OAAO;AACzD,cAAU,KAAK,KAAK,MAAM,GAAG,CAAa;AAAA,EAC5C;AAEA,SAAO,UAAU,KAAK,CAAC,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,EAAE,CAAC;AAC1D;AAEA,eAAsB,YACpB,aACA,UACe;AACf,QAAM,MAAM,gBAAgB,WAAW;AACvC,QAAMG,OAAM,KAAK,EAAE,WAAW,KAAK,CAAC;AAEpC,QAAM,WAAW,GAAG,SAAS,EAAE;AAC/B,QAAMC;AAAA,IACJJ,MAAK,KAAK,QAAQ;AAAA,IAClB,KAAK,UAAU,UAAU,MAAM,CAAC,IAAI;AAAA,IACpC;AAAA,EACF;AACF;AAIA,eAAsB,kBAAkB,aAAoC;AAC1E,QAAM,eAAe,gBAAgB,WAAW;AAChD,QAAMG,OAAMH,MAAK,cAAc,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;AACnE,QAAMG,OAAMH,MAAK,cAAc,WAAW,GAAG,EAAE,WAAW,KAAK,CAAC;AAEhE,QAAM,WAAWA,MAAK,cAAc,iBAAiB;AACrD,MAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;AACjC,UAAMI;AAAA,MACJ;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAeJ,MAAK,cAAc,aAAa;AACrD,MAAI,CAAE,MAAM,WAAW,YAAY,GAAI;AACrC,UAAMI;AAAA,MACJ;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAEA,QAAM,eAAeJ,MAAK,cAAc,aAAa;AACrD,MAAI,CAAE,MAAM,WAAW,YAAY,GAAI;AACrC,UAAMI;AAAA,MACJ;AAAA,MACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AACF;;;AC/JA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,QAAAC,aAAY;AAQd,SAAS,QAAQ,MAAsB;AAC5C,SAAO,KACJ,YAAY,EACZ,QAAQ,eAAe,GAAG,EAC1B,QAAQ,YAAY,EAAE;AAC3B;AAKO,SAAS,iBACd,MACA,aACA,cACA,QAAkB,CAAC,GACnB,OAAiB,CAAC,GACN;AACZ,SAAO;AAAA,IACL,IAAI,QAAQ,IAAI;AAAA,IAChB;AAAA,IACA;AAAA,IACA;AAAA,IACA,aAAY,oBAAI,KAAK,GAAE,YAAY;AAAA,IACnC;AAAA,IACA;AAAA,EACF;AACF;AAKA,eAAsB,mBACpB,aACA,YACe;AACf,QAAM,cAAc,aAAa,UAAU;AAC7C;AAKA,eAAsB,YAAY,aAA6C;AAC7E,QAAM,cAAcC,MAAK,aAAa,YAAY;AAClD,MAAI,CAAE,MAAM,WAAW,WAAW,EAAI,QAAO;AAC7C,SAAOC,UAAS,aAAa,OAAO;AACtC;AAKA,eAAsB,aACpB,YACA,SACe;AACf,QAAM,EAAE,WAAW,GAAG,IAAI,MAAM,OAAO,aAAkB;AACzD,QAAM,GAAGD,MAAK,YAAY,YAAY,GAAG,SAAS,OAAO;AAC3D;;;ACvCO,SAAS,eAAe,OAA4B;AACzD,QAAM,QAAQ,MAAM;AACpB,MAAI,UAAU,GAAG;AACf,WAAO;AAAA,MACL,OAAO;AAAA,MACP,SAAS;AAAA,MACT,YAAY;AAAA,MACZ,QAAQ;AAAA,MACR,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,mBAAmB;AAAA,IACrB;AAAA,EACF;AAEA,QAAM,UAAU,MAAM,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE;AAC5D,QAAM,aAAa,MAAM;AAAA,IACvB,CAAC,MACC,EAAE,WAAW,aACb,EAAE,WAAW,iBACb,EAAE,WAAW,iBACb,EAAE,WAAW;AAAA,EACjB,EAAE;AACF,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE;AAC1D,QAAM,SAAS,MAAM,OAAO,CAAC,MAAM,EAAE,WAAW,QAAQ,EAAE;AAC1D,QAAM,UAAU,MAAM,OAAO,CAAC,MAAM,EAAE,WAAW,SAAS,EAAE;AAE5D,QAAM,oBAAoB,KAAK,MAAO,SAAS,QAAS,GAAG;AAE3D,SAAO,EAAE,OAAO,SAAS,YAAY,QAAQ,QAAQ,SAAS,kBAAkB;AAClF;AAEA,eAAsB,kBACpB,aACyB;AACzB,QAAM,gBAAgB,MAAM,YAAY,aAAa,KAAK;AAC1D,QAAM,aAAa,MAAM,YAAY,aAAa,IAAI;AACtD,QAAM,kBAAkB,WAAW,OAAO,CAAC,MAAM,EAAE,WAAW,UAAU;AACxE,QAAM,eAAe,MAAM,iBAAiB,WAAW;AAEvD,QAAM,WAAW,cAAc,QAAQ,CAAC,MAAM,EAAE,KAAK;AAErD,SAAO;AAAA,IACL,eAAe,cAAc;AAAA,IAC7B,iBAAiB,gBAAgB;AAAA,IACjC,mBAAmB,aAAa;AAAA,IAChC,aAAa,eAAe,QAAQ;AAAA,IACpC,SAAS,cAAc,IAAI,CAAC,OAAO;AAAA,MACjC,MAAM,EAAE;AAAA,MACR,QAAQ,EAAE;AAAA,MACV,aAAa,eAAe,EAAE,KAAK;AAAA,IACrC,EAAE;AAAA,EACJ;AACF;;;ACxEA,SAAS,YAAAE,WAAU,WAAAC,UAAS,YAAY;AACxC,SAAS,QAAAC,aAAsB;AAS/B,eAAsB,aAAa,aAA2C;AAC5E,QAAM,QAAqB,CAAC;AAE5B,QAAM,iBAAiB,aAAa,KAAK;AACzC,QAAM,0BAA0B,aAAa,KAAK;AAClD,QAAM,sBAAsB,aAAa,KAAK;AAC9C,QAAM,qBAAqB,aAAa,KAAK;AAE7C,QAAM,QAAQ,eAAe,KAAK;AAElC,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,EACpC;AACF;AAKA,eAAe,iBACb,aACA,OACe;AACf,QAAM,UAAU,MAAM,YAAY,aAAa,KAAK;AACpD,QAAM,MAAM,KAAK,IAAI;AAErB,aAAW,UAAU,SAAS;AAC5B,UAAM,kBAAkB,KAAK;AAAA,OAC1B,MAAM,IAAI,KAAK,OAAO,SAAS,EAAE,QAAQ,MAAM,MAAO,KAAK,KAAK;AAAA,IACnE;AAEA,QAAI,OAAO,WAAW,cAAc,kBAAkB,IAAI;AACxD,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,OAAO;AAAA,QACb,SAAS,IAAI,OAAO,IAAI,cAAc,eAAe;AAAA,MACvD,CAAC;AAAA,IACH,WAAW,OAAO,WAAW,cAAc,kBAAkB,GAAG;AAC9D,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,OAAO;AAAA,QACb,SAAS,IAAI,OAAO,IAAI,cAAc,eAAe,yDAAoD,OAAO,IAAI;AAAA,MACtH,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,WAAW,iBAAiB,kBAAkB,GAAG;AAC1D,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,OAAO;AAAA,QACb,SAAS,IAAI,OAAO,IAAI,qBAAqB,eAAe;AAAA,MAC9D,CAAC;AAAA,IACH;AAEA,QAAI,OAAO,WAAW,gBAAgB,kBAAkB,GAAG;AACzD,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,OAAO;AAAA,QACb,SAAS,IAAI,OAAO,IAAI,6BAA6B,eAAe;AAAA,MACtE,CAAC;AAAA,IACH;AAGA,QACE,OAAO,WAAW,iBAClB,OAAO,MAAM,SAAS,KACtB,OAAO,MAAM,MAAM,CAAC,MAAM,EAAE,WAAW,SAAS,GAChD;AACA,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,OAAO;AAAA,QACb,SAAS,IAAI,OAAO,IAAI,4BAA4B,OAAO,MAAM,MAAM;AAAA,MACzE,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKA,eAAe,0BACb,aACA,OACe;AACf,QAAM,aAAa,cAAc,WAAW;AAC5C,MAAI,CAAE,MAAM,WAAW,UAAU,EAAI;AAErC,QAAM,eAAe,MAAM,iBAAiB,WAAW;AACvD,QAAM,aAAa,IAAI,IAAI,aAAa,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AAElE,QAAM,UAAU,MAAMC,SAAQ,YAAY,EAAE,eAAe,KAAK,CAAC;AACjE,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,YAAY,EAAG;AAI1B,UAAM,QAAQ,MAAM,KAAK,MAAM,0BAA0B;AACzD,UAAM,aAAa,QAAQ,MAAM,CAAC,IAAI,MAAM;AAG5C,UAAM,cAAcC,MAAK,YAAY,MAAM,MAAM,YAAY;AAC7D,QAAI,CAAE,MAAM,WAAW,WAAW,GAAI;AACpC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM;AAAA,QACN,SAAS,oBAAoB,UAAU;AAAA,MACzC,CAAC;AACD;AAAA,IACF;AAGA,QAAI,CAAC,WAAW,IAAI,UAAU,GAAG;AAC/B,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM;AAAA,QACN,SAAS,oBAAoB,UAAU;AAAA,MACzC,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKA,eAAe,sBACb,aACA,OACe;AACf,QAAM,UAAU,MAAM,YAAY,aAAa,KAAK;AAEpD,aAAW,UAAU,SAAS;AAE5B,QAAI,OAAO,WAAW,WAAY;AAElC,UAAM,YAAY;AAAA,MAChB,EAAE,MAAM,yBAAyB,MAAM,eAAe;AAAA,MACtD,EAAE,MAAM,sBAAsB,MAAM,YAAY;AAAA,MAChD,EAAE,MAAM,aAAa,MAAM,SAAS;AAAA,MACpC,EAAE,MAAM,YAAY,MAAM,QAAQ;AAAA,IACpC;AAEA,eAAW,QAAQ,WAAW;AAC5B,UAAI;AACF,cAAM,UAAU,MAAM,eAAe,aAAa,OAAO,MAAM,KAAK,IAAI;AACxE,cAAM,WAAW,QAAQ,QAAQ,gBAAgB,EAAE,EAAE,KAAK;AAC1D,YAAI,SAAS,SAAS,IAAI;AACxB,gBAAM,WACJ,OAAO,WAAW,gBAAgB,YAAY;AAChD,gBAAM,KAAK;AAAA,YACT,MAAM;AAAA,YACN;AAAA,YACA,MAAM,OAAO;AAAA,YACb,MAAM,KAAK;AAAA,YACX,SAAS,IAAI,OAAO,IAAI,IAAI,KAAK,IAAI,kCAAkC,OAAO,MAAM;AAAA,UACtF,CAAC;AAAA,QACH;AAAA,MACF,QAAQ;AACN,cAAM,KAAK;AAAA,UACT,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM,OAAO;AAAA,UACb,MAAM,KAAK;AAAA,UACX,SAAS,IAAI,OAAO,IAAI,IAAI,KAAK,IAAI;AAAA,QACvC,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AACF;AAKA,eAAe,qBACb,aACA,OACe;AACf,QAAM,eAAe,gBAAgB,WAAW;AAChD,QAAM,eAAeA,MAAK,cAAc,WAAW;AAEnD,QAAM,iBAAiB;AAAA,IACrB,EAAE,MAAM,mBAAmB,MAAM,eAAe;AAAA,IAChD,EAAE,MAAM,eAAe,MAAM,WAAW;AAAA,IACxC,EAAE,MAAM,eAAe,MAAM,WAAW;AAAA,EAC1C;AAEA,aAAW,MAAM,gBAAgB;AAC/B,UAAM,WAAWA,MAAK,cAAc,GAAG,IAAI;AAC3C,QAAI,CAAE,MAAM,WAAW,QAAQ,GAAI;AACjC,YAAM,KAAK;AAAA,QACT,MAAM;AAAA,QACN,UAAU;AAAA,QACV,MAAM,aAAa,GAAG,IAAI;AAAA,QAC1B,SAAS,GAAG,GAAG,IAAI;AAAA,MACrB,CAAC;AACD;AAAA,IACF;AAEA,QAAI;AACF,YAAM,UAAU,MAAMC,UAAS,UAAU,OAAO;AAChD,YAAM,WAAW,QAAQ,QAAQ,gBAAgB,EAAE,EAAE,KAAK;AAC1D,UAAI,SAAS,SAAS,IAAI;AACxB,cAAM,KAAK;AAAA,UACT,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM,aAAa,GAAG,IAAI;AAAA,UAC1B,SAAS,GAAG,GAAG,IAAI;AAAA,QACrB,CAAC;AAAA,MACH;AAGA,YAAM,QAAQ,MAAM,KAAK,QAAQ;AACjC,YAAM,oBAAoB,KAAK;AAAA,SAC5B,KAAK,IAAI,IAAI,MAAM,YAAY,MAAO,KAAK,KAAK;AAAA,MACnD;AAGA,YAAM,eAAe,MAAM,iBAAiB,WAAW;AACvD,YAAM,aAAa,aAAa,OAAO,CAAC,MAAM;AAC5C,cAAM,cAAc,IAAI,KAAK,EAAE,UAAU,EAAE,QAAQ;AACnD,eAAO,cAAc,MAAM;AAAA,MAC7B,CAAC;AAED,UAAI,WAAW,SAAS,KAAK,oBAAoB,GAAG;AAClD,cAAM,KAAK;AAAA,UACT,MAAM;AAAA,UACN,UAAU;AAAA,UACV,MAAM,aAAa,GAAG,IAAI;AAAA,UAC1B,SAAS,GAAG,GAAG,IAAI,sBAAsB,WAAW,MAAM;AAAA,QAC5D,CAAC;AAAA,MACH;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AAKA,SAAS,eAAe,OAA4B;AAClD,MAAI,MAAM,WAAW,EAAG,QAAO;AAE/B,MAAI,UAAU;AACd,aAAW,QAAQ,OAAO;AACxB,YAAQ,KAAK,UAAU;AAAA,MACrB,KAAK;AACH,mBAAW;AACX;AAAA,MACF,KAAK;AACH,mBAAW;AACX;AAAA,MACF,KAAK;AACH,mBAAW;AACX;AAAA,IACJ;AAAA,EACF;AAEA,SAAO,KAAK,IAAI,GAAG,MAAM,OAAO;AAClC;","names":["readFile","writeFile","join","join","writeFile","readFile","readFile","writeFile","readdir","mkdir","join","join","readFile","readdir","mkdir","writeFile","readFile","join","join","readFile","readFile","readdir","join","readdir","join","readFile"]}
|