@voodocs/cli 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +98 -0
- package/README.md +37 -3
- package/USAGE.md +52 -2
- package/cli.py +262 -2
- package/examples/test_function_invariants.py +134 -0
- package/lib/darkarts/annotations/parser.py +64 -0
- package/lib/darkarts/context/__init__.py +84 -0
- package/lib/darkarts/context/checker.py +379 -0
- package/lib/darkarts/context/commands.py +1688 -0
- package/lib/darkarts/context/diagram.py +300 -0
- package/lib/darkarts/context/models.py +200 -0
- package/lib/darkarts/context/yaml_utils.py +342 -0
- package/lib/darkarts/plugins/voodocs/documentation_generator.py +1 -1
- package/lib/darkarts/telemetry.py +1 -1
- package/package.json +2 -1
- package/templates/CONTEXT_TEMPLATE.md +152 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"""
|
|
2
|
+
YAML Utilities for Context Files
|
|
3
|
+
|
|
4
|
+
Handles reading, writing, and formatting of .voodocs.context YAML files.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import yaml
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import Dict, Any, Optional
|
|
10
|
+
from .models import ContextFile, Versioning, Project, Architecture, Change
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContextYAMLDumper(yaml.SafeDumper):
|
|
14
|
+
"""Custom YAML dumper with better formatting for context files."""
|
|
15
|
+
|
|
16
|
+
def increase_indent(self, flow=False, indentless=False):
|
|
17
|
+
return super().increase_indent(flow, False)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _represent_none(self, _):
|
|
21
|
+
"""Represent None as empty string instead of 'null'."""
|
|
22
|
+
return self.represent_scalar('tag:yaml.org,2002:null', '')
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# Register custom representers
|
|
26
|
+
ContextYAMLDumper.add_representer(type(None), _represent_none)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def write_context_yaml(context: ContextFile, file_path: Path) -> None:
|
|
30
|
+
"""
|
|
31
|
+
Write a ContextFile to a YAML file with proper formatting.
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
context: The ContextFile object to write
|
|
35
|
+
file_path: Path to the .voodocs.context file
|
|
36
|
+
"""
|
|
37
|
+
data = context.to_dict()
|
|
38
|
+
|
|
39
|
+
# Create file with header comment
|
|
40
|
+
with open(file_path, 'w', encoding='utf-8') as f:
|
|
41
|
+
# Write header
|
|
42
|
+
f.write("# VooDocs Context File\n")
|
|
43
|
+
f.write("# This file contains structured project context for AI assistants and developers.\n")
|
|
44
|
+
f.write("# Format: YAML (DSL for machines, use 'voodocs context view' for human-readable Markdown)\n")
|
|
45
|
+
f.write("# Version: {}\n".format(context.versioning.context_version))
|
|
46
|
+
f.write("# Last Updated: {}\n".format(context.versioning.last_updated))
|
|
47
|
+
f.write("\n")
|
|
48
|
+
|
|
49
|
+
# Write YAML content
|
|
50
|
+
yaml.dump(
|
|
51
|
+
data,
|
|
52
|
+
f,
|
|
53
|
+
Dumper=ContextYAMLDumper,
|
|
54
|
+
default_flow_style=False,
|
|
55
|
+
allow_unicode=True,
|
|
56
|
+
sort_keys=False,
|
|
57
|
+
width=100,
|
|
58
|
+
indent=2
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def read_context_yaml(file_path: Path) -> Optional[Dict[str, Any]]:
|
|
63
|
+
"""
|
|
64
|
+
Read a .voodocs.context YAML file.
|
|
65
|
+
|
|
66
|
+
Args:
|
|
67
|
+
file_path: Path to the .voodocs.context file
|
|
68
|
+
|
|
69
|
+
Returns:
|
|
70
|
+
Dictionary containing the context data, or None if file doesn't exist
|
|
71
|
+
"""
|
|
72
|
+
if not file_path.exists():
|
|
73
|
+
return None
|
|
74
|
+
|
|
75
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
76
|
+
return yaml.safe_load(f)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def parse_context_file(data: Dict[str, Any]) -> ContextFile:
|
|
80
|
+
"""
|
|
81
|
+
Parse a context dictionary into a ContextFile object.
|
|
82
|
+
|
|
83
|
+
Args:
|
|
84
|
+
data: Dictionary loaded from YAML
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
ContextFile object
|
|
88
|
+
"""
|
|
89
|
+
# Parse versioning
|
|
90
|
+
versioning_data = data.get('versioning', {})
|
|
91
|
+
versioning = Versioning(
|
|
92
|
+
code_version=versioning_data.get('code_version', '0.0.0'),
|
|
93
|
+
context_version=versioning_data.get('context_version', '0.0'),
|
|
94
|
+
last_updated=versioning_data.get('last_updated', '')
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# Parse project
|
|
98
|
+
project_data = data.get('project', {})
|
|
99
|
+
project = Project(
|
|
100
|
+
name=project_data.get('name', ''),
|
|
101
|
+
purpose=project_data.get('purpose', ''),
|
|
102
|
+
repository=project_data.get('repository'),
|
|
103
|
+
homepage=project_data.get('homepage'),
|
|
104
|
+
license=project_data.get('license')
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
# Parse changes
|
|
108
|
+
changes_data = data.get('changes', [])
|
|
109
|
+
changes = []
|
|
110
|
+
for change_dict in changes_data:
|
|
111
|
+
from .models import Change
|
|
112
|
+
changes.append(Change(
|
|
113
|
+
type=change_dict.get('type', 'context'),
|
|
114
|
+
description=change_dict.get('description', ''),
|
|
115
|
+
date=change_dict.get('date', ''),
|
|
116
|
+
context_version=change_dict.get('context_version'),
|
|
117
|
+
code_version=change_dict.get('code_version'),
|
|
118
|
+
commit=change_dict.get('commit'),
|
|
119
|
+
author=change_dict.get('author')
|
|
120
|
+
))
|
|
121
|
+
|
|
122
|
+
# Parse architecture
|
|
123
|
+
arch_data = data.get('architecture', {})
|
|
124
|
+
architecture = Architecture(
|
|
125
|
+
decisions=arch_data.get('decisions', []),
|
|
126
|
+
modules=arch_data.get('modules', {}),
|
|
127
|
+
tech_stack=arch_data.get('tech_stack', {})
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
# Parse assumptions
|
|
131
|
+
assumptions_data = data.get('assumptions', [])
|
|
132
|
+
assumptions = []
|
|
133
|
+
for assumption in assumptions_data:
|
|
134
|
+
if isinstance(assumption, dict):
|
|
135
|
+
from .models import Assumption
|
|
136
|
+
assumptions.append(Assumption(
|
|
137
|
+
assumption=assumption.get('description', assumption.get('assumption', '')),
|
|
138
|
+
rationale=assumption.get('rationale'),
|
|
139
|
+
risk_if_false=assumption.get('risk_if_false')
|
|
140
|
+
))
|
|
141
|
+
elif isinstance(assumption, str):
|
|
142
|
+
from .models import Assumption
|
|
143
|
+
assumptions.append(Assumption(assumption=assumption))
|
|
144
|
+
|
|
145
|
+
# Parse known issues
|
|
146
|
+
issues_data = data.get('known_issues', [])
|
|
147
|
+
known_issues = []
|
|
148
|
+
for issue in issues_data:
|
|
149
|
+
if isinstance(issue, dict):
|
|
150
|
+
from .models import KnownIssue
|
|
151
|
+
known_issues.append(KnownIssue(
|
|
152
|
+
description=issue.get('description', ''),
|
|
153
|
+
severity=issue.get('severity'),
|
|
154
|
+
workaround=issue.get('workaround'),
|
|
155
|
+
tracking_id=issue.get('tracking_id')
|
|
156
|
+
))
|
|
157
|
+
elif isinstance(issue, str):
|
|
158
|
+
from .models import KnownIssue
|
|
159
|
+
known_issues.append(KnownIssue(description=issue))
|
|
160
|
+
|
|
161
|
+
# Parse critical paths
|
|
162
|
+
paths_data = data.get('critical_paths', [])
|
|
163
|
+
critical_paths = []
|
|
164
|
+
for path in paths_data:
|
|
165
|
+
if isinstance(path, dict):
|
|
166
|
+
from .models import CriticalPath
|
|
167
|
+
critical_paths.append(CriticalPath(
|
|
168
|
+
name=path.get('name', ''),
|
|
169
|
+
description=path.get('description', ''),
|
|
170
|
+
entry_point=path.get('entry_point'),
|
|
171
|
+
steps=path.get('steps', [])
|
|
172
|
+
))
|
|
173
|
+
|
|
174
|
+
# Parse roadmap
|
|
175
|
+
roadmap_data = data.get('roadmap', [])
|
|
176
|
+
roadmap = []
|
|
177
|
+
for item in roadmap_data:
|
|
178
|
+
if isinstance(item, dict):
|
|
179
|
+
from .models import RoadmapItem
|
|
180
|
+
roadmap.append(RoadmapItem(
|
|
181
|
+
version=item.get('version', ''),
|
|
182
|
+
description=item.get('description', ''),
|
|
183
|
+
status=item.get('status'),
|
|
184
|
+
target_date=item.get('target_date')
|
|
185
|
+
))
|
|
186
|
+
|
|
187
|
+
# Create context file
|
|
188
|
+
context = ContextFile(
|
|
189
|
+
versioning=versioning,
|
|
190
|
+
project=project,
|
|
191
|
+
invariants=data.get('invariants', {}),
|
|
192
|
+
architecture=architecture,
|
|
193
|
+
critical_paths=critical_paths,
|
|
194
|
+
known_issues=known_issues,
|
|
195
|
+
assumptions=assumptions,
|
|
196
|
+
changes=changes,
|
|
197
|
+
roadmap=roadmap,
|
|
198
|
+
performance=data.get('performance', {}),
|
|
199
|
+
security=data.get('security', {}),
|
|
200
|
+
dependencies=data.get('dependencies', {}),
|
|
201
|
+
testing=data.get('testing', {}),
|
|
202
|
+
deployment=data.get('deployment', {}),
|
|
203
|
+
team=data.get('team', {}),
|
|
204
|
+
custom=data.get('custom', {})
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
return context
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def add_to_gitignore(project_root: Path) -> bool:
|
|
211
|
+
"""
|
|
212
|
+
Add .voodocs.context to .gitignore if not already present.
|
|
213
|
+
|
|
214
|
+
Args:
|
|
215
|
+
project_root: Root directory of the project
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
True if added, False if already present or error
|
|
219
|
+
"""
|
|
220
|
+
gitignore_path = project_root / '.gitignore'
|
|
221
|
+
context_entry = '.voodocs.context'
|
|
222
|
+
|
|
223
|
+
# Check if .gitignore exists
|
|
224
|
+
if gitignore_path.exists():
|
|
225
|
+
with open(gitignore_path, 'r', encoding='utf-8') as f:
|
|
226
|
+
content = f.read()
|
|
227
|
+
|
|
228
|
+
# Check if entry already exists
|
|
229
|
+
if context_entry in content:
|
|
230
|
+
return False
|
|
231
|
+
|
|
232
|
+
# Add entry
|
|
233
|
+
with open(gitignore_path, 'a', encoding='utf-8') as f:
|
|
234
|
+
# Ensure there's a newline before our section
|
|
235
|
+
if not content.endswith('\n'):
|
|
236
|
+
f.write('\n')
|
|
237
|
+
f.write('\n# VooDocs Context File (contains internal project details)\n')
|
|
238
|
+
f.write(f'{context_entry}\n')
|
|
239
|
+
|
|
240
|
+
return True
|
|
241
|
+
else:
|
|
242
|
+
# Create .gitignore with entry
|
|
243
|
+
with open(gitignore_path, 'w', encoding='utf-8') as f:
|
|
244
|
+
f.write('# VooDocs Context File (contains internal project details)\n')
|
|
245
|
+
f.write(f'{context_entry}\n')
|
|
246
|
+
|
|
247
|
+
return True
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
def format_context_as_markdown(context: ContextFile) -> str:
|
|
251
|
+
"""
|
|
252
|
+
Convert a ContextFile to human-readable Markdown.
|
|
253
|
+
|
|
254
|
+
Args:
|
|
255
|
+
context: The ContextFile object
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
Formatted Markdown string
|
|
259
|
+
"""
|
|
260
|
+
lines = []
|
|
261
|
+
|
|
262
|
+
# Header
|
|
263
|
+
lines.append(f"# {context.project.name} Context")
|
|
264
|
+
lines.append("")
|
|
265
|
+
lines.append(f"**Version:** {context.versioning.context_version}")
|
|
266
|
+
lines.append(f"**Last Updated:** {context.versioning.last_updated}")
|
|
267
|
+
lines.append("")
|
|
268
|
+
lines.append("---")
|
|
269
|
+
lines.append("")
|
|
270
|
+
|
|
271
|
+
# Project Overview
|
|
272
|
+
lines.append("## 🎯 Project Overview")
|
|
273
|
+
lines.append("")
|
|
274
|
+
lines.append(f"**Purpose:** {context.project.purpose}")
|
|
275
|
+
lines.append("")
|
|
276
|
+
if context.project.repository:
|
|
277
|
+
lines.append(f"**Repository:** {context.project.repository}")
|
|
278
|
+
if context.project.license:
|
|
279
|
+
lines.append(f"**License:** {context.project.license}")
|
|
280
|
+
lines.append("")
|
|
281
|
+
lines.append("---")
|
|
282
|
+
lines.append("")
|
|
283
|
+
|
|
284
|
+
# Global Invariants
|
|
285
|
+
if context.invariants.get('global'):
|
|
286
|
+
lines.append("## ⚖️ Global Invariants")
|
|
287
|
+
lines.append("")
|
|
288
|
+
for invariant in context.invariants['global']:
|
|
289
|
+
lines.append(f"- `{invariant}`")
|
|
290
|
+
lines.append("")
|
|
291
|
+
lines.append("---")
|
|
292
|
+
lines.append("")
|
|
293
|
+
|
|
294
|
+
# Architecture
|
|
295
|
+
if context.architecture.decisions:
|
|
296
|
+
lines.append("## 🏛️ Architecture Decisions")
|
|
297
|
+
lines.append("")
|
|
298
|
+
for decision in context.architecture.decisions:
|
|
299
|
+
lines.append(f"### {decision.decision}")
|
|
300
|
+
lines.append("")
|
|
301
|
+
lines.append(f"**Rationale:** {decision.rationale}")
|
|
302
|
+
if decision.alternatives_considered:
|
|
303
|
+
lines.append("")
|
|
304
|
+
lines.append("**Alternatives Considered:**")
|
|
305
|
+
for alt in decision.alternatives_considered:
|
|
306
|
+
lines.append(f"- {alt}")
|
|
307
|
+
lines.append("")
|
|
308
|
+
lines.append("---")
|
|
309
|
+
lines.append("")
|
|
310
|
+
|
|
311
|
+
# Critical Paths
|
|
312
|
+
if context.critical_paths:
|
|
313
|
+
lines.append("## 🔄 Critical Paths")
|
|
314
|
+
lines.append("")
|
|
315
|
+
for path in context.critical_paths:
|
|
316
|
+
lines.append(f"### {path.name}")
|
|
317
|
+
lines.append("")
|
|
318
|
+
if path.description:
|
|
319
|
+
lines.append(path.description)
|
|
320
|
+
lines.append("")
|
|
321
|
+
for i, step in enumerate(path.steps, 1):
|
|
322
|
+
lines.append(f"{i}. {step}")
|
|
323
|
+
lines.append("")
|
|
324
|
+
lines.append("---")
|
|
325
|
+
lines.append("")
|
|
326
|
+
|
|
327
|
+
# Known Issues
|
|
328
|
+
if context.known_issues:
|
|
329
|
+
lines.append("## ⚠️ Known Issues")
|
|
330
|
+
lines.append("")
|
|
331
|
+
for issue in context.known_issues:
|
|
332
|
+
lines.append(f"### {issue.issue}")
|
|
333
|
+
lines.append("")
|
|
334
|
+
lines.append(f"**Impact:** {issue.impact}")
|
|
335
|
+
if issue.workaround:
|
|
336
|
+
lines.append(f"**Workaround:** {issue.workaround}")
|
|
337
|
+
lines.append(f"**Priority:** {issue.priority}")
|
|
338
|
+
lines.append("")
|
|
339
|
+
lines.append("---")
|
|
340
|
+
lines.append("")
|
|
341
|
+
|
|
342
|
+
return '\n'.join(lines)
|
|
@@ -567,7 +567,7 @@ class DocumentationGenerator:
|
|
|
567
567
|
|
|
568
568
|
# Documentation coverage badge
|
|
569
569
|
total_items = len(parsed.module.classes) + len(parsed.get_all_functions())
|
|
570
|
-
annotated_items = sum(1 for _ in parsed.module.classes if _.
|
|
570
|
+
annotated_items = sum(1 for _ in parsed.module.classes if _.class_invariants or _.state_transitions)
|
|
571
571
|
annotated_items += sum(1 for f in parsed.get_all_functions() if f.preconditions or f.postconditions)
|
|
572
572
|
|
|
573
573
|
if total_items > 0:
|
|
@@ -20,7 +20,7 @@ SUPABASE_URL = os.getenv("VOODOCS_SUPABASE_URL", "https://sjatkayudkbkmipubhfy.s
|
|
|
20
20
|
SUPABASE_ANON_KEY = os.getenv("VOODOCS_SUPABASE_ANON_KEY", "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InNqYXRrYXl1ZGtia21pcHViaGZ5Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjYxNjE0MTMsImV4cCI6MjA4MTczNzQxM30.Wj3dbokjKPsmWTgbPw77aPnCoZCsE5hrFfIH-R_ErzI")
|
|
21
21
|
|
|
22
22
|
# VooDocs version
|
|
23
|
-
VERSION = "0.1.
|
|
23
|
+
VERSION = "0.1.2"
|
|
24
24
|
|
|
25
25
|
class TelemetryClient:
|
|
26
26
|
"""Anonymous telemetry client for VooDocs CLI."""
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voodocs/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "AI-Native Documentation System - Generate docs, tests, and API specs from @voodocs annotations using the DarkArts language",
|
|
5
5
|
"main": "cli.py",
|
|
6
6
|
"bin": {
|
|
@@ -46,6 +46,7 @@
|
|
|
46
46
|
"files": [
|
|
47
47
|
"cli.py",
|
|
48
48
|
"lib/darkarts/annotations/",
|
|
49
|
+
"lib/darkarts/context/",
|
|
49
50
|
"lib/darkarts/core/",
|
|
50
51
|
"lib/darkarts/exceptions.py",
|
|
51
52
|
"lib/darkarts/telemetry.py",
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# [Project Name] Context
|
|
2
|
+
|
|
3
|
+
**Version:** [current version]
|
|
4
|
+
**Last Updated:** [YYYY-MM-DD]
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## 🎯 Project Overview
|
|
9
|
+
|
|
10
|
+
@voodocs
|
|
11
|
+
module_purpose: "[One-sentence description of what this project does and why it exists]"
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## ⚖️ Global Invariants
|
|
16
|
+
|
|
17
|
+
These properties must hold true across the entire codebase.
|
|
18
|
+
|
|
19
|
+
@voodocs
|
|
20
|
+
global_invariants: [
|
|
21
|
+
"[Property that must always be true, e.g., 'user.balance >= 0']",
|
|
22
|
+
"[Another invariant, e.g., '∀ transaction: transaction.amount > 0']",
|
|
23
|
+
"[System-wide constraint, e.g., 'database connections <= max_pool_size']"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 🏛️ Architecture & Key Decisions
|
|
29
|
+
|
|
30
|
+
@voodocs
|
|
31
|
+
architecture_decisions: [
|
|
32
|
+
{
|
|
33
|
+
decision: "[What was decided]",
|
|
34
|
+
rationale: "[Why this decision was made]",
|
|
35
|
+
alternatives_considered: ["[Option A]", "[Option B]"],
|
|
36
|
+
tradeoffs: "[What was sacrificed or gained]"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
modules: {
|
|
41
|
+
"[module_name]": "[Brief description of what this module does]",
|
|
42
|
+
"[another_module]": "[Description]"
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
tech_stack: {
|
|
46
|
+
"languages": ["[Language 1]", "[Language 2]"],
|
|
47
|
+
"frameworks": ["[Framework 1]"],
|
|
48
|
+
"databases": ["[Database type]"],
|
|
49
|
+
"infrastructure": ["[Cloud provider or hosting]"]
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## 🔄 Critical Paths & User Workflows
|
|
55
|
+
|
|
56
|
+
@voodocs
|
|
57
|
+
critical_paths: [
|
|
58
|
+
{
|
|
59
|
+
name: "[Workflow name, e.g., 'User Registration']",
|
|
60
|
+
steps: ["[Step 1]", "[Step 2]", "[Step 3]"]
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
name: "[Another workflow, e.g., 'Payment Processing']",
|
|
64
|
+
steps: ["[Step 1]", "[Step 2]", "[Step 3]"]
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## ⚠️ Known Issues & Assumptions
|
|
71
|
+
|
|
72
|
+
@voodocs
|
|
73
|
+
known_issues: [
|
|
74
|
+
{
|
|
75
|
+
issue: "[Description of the issue]",
|
|
76
|
+
impact: "[What breaks or degrades]",
|
|
77
|
+
workaround: "[How to work around it]",
|
|
78
|
+
priority: "[low|medium|high|critical]"
|
|
79
|
+
}
|
|
80
|
+
]
|
|
81
|
+
|
|
82
|
+
assumptions: [
|
|
83
|
+
"[Assumption 1, e.g., 'Users have internet access']",
|
|
84
|
+
"[Assumption 2, e.g., 'Database is always available']",
|
|
85
|
+
"[Assumption 3, e.g., 'Input data is validated by frontend']"
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 🗓️ Recent Changes
|
|
91
|
+
|
|
92
|
+
@voodocs
|
|
93
|
+
changes: [
|
|
94
|
+
{
|
|
95
|
+
type: "[fix|feat|docs|refactor|test]",
|
|
96
|
+
description: "[What changed]",
|
|
97
|
+
date: "[YYYY-MM-DD]",
|
|
98
|
+
commit: "[commit hash]"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
|
|
102
|
+
---
|
|
103
|
+
|
|
104
|
+
## 🔮 Future Vision
|
|
105
|
+
|
|
106
|
+
@voodocs
|
|
107
|
+
roadmap: [
|
|
108
|
+
"[Feature or improvement planned for next version]",
|
|
109
|
+
"[Another planned feature]",
|
|
110
|
+
"[Long-term goal]"
|
|
111
|
+
]
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## 📚 Additional Context
|
|
116
|
+
|
|
117
|
+
### Performance Characteristics
|
|
118
|
+
|
|
119
|
+
@voodocs
|
|
120
|
+
performance: {
|
|
121
|
+
"typical_response_time": "[e.g., '< 100ms']",
|
|
122
|
+
"max_throughput": "[e.g., '1000 requests/sec']",
|
|
123
|
+
"bottlenecks": ["[Known bottleneck 1]", "[Known bottleneck 2]"]
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
### Security Considerations
|
|
127
|
+
|
|
128
|
+
@voodocs
|
|
129
|
+
security: {
|
|
130
|
+
"authentication": "[How users are authenticated]",
|
|
131
|
+
"authorization": "[How permissions are enforced]",
|
|
132
|
+
"data_protection": "[How sensitive data is protected]",
|
|
133
|
+
"known_vulnerabilities": ["[CVE or issue description]"]
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
### Dependencies & Integrations
|
|
137
|
+
|
|
138
|
+
@voodocs
|
|
139
|
+
dependencies: {
|
|
140
|
+
"external_services": ["[Service 1]", "[Service 2]"],
|
|
141
|
+
"third_party_apis": ["[API 1]", "[API 2]"],
|
|
142
|
+
"critical_libraries": ["[Library 1]", "[Library 2]"]
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
**Note:** This context file should be updated whenever:
|
|
148
|
+
- Architecture decisions are made
|
|
149
|
+
- Global invariants change
|
|
150
|
+
- New modules are added
|
|
151
|
+
- Critical issues are discovered
|
|
152
|
+
- Major features are shipped
|