flyee 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +134 -0
- package/bin/install.js +357 -0
- package/bridge/bridge.py +1780 -0
- package/bridge/local_tracker.py +722 -0
- package/core/agents/backend-specialist.md +266 -0
- package/core/agents/code-archaeologist.md +106 -0
- package/core/agents/database-architect.md +226 -0
- package/core/agents/debugger.md +225 -0
- package/core/agents/devops-engineer.md +323 -0
- package/core/agents/documentation-writer.md +104 -0
- package/core/agents/explorer-agent.md +73 -0
- package/core/agents/frontend-specialist.md +743 -0
- package/core/agents/game-developer.md +162 -0
- package/core/agents/mobile-developer.md +377 -0
- package/core/agents/orchestrator.md +416 -0
- package/core/agents/penetration-tester.md +188 -0
- package/core/agents/performance-optimizer.md +187 -0
- package/core/agents/product-manager.md +112 -0
- package/core/agents/product-owner.md +95 -0
- package/core/agents/project-planner.md +470 -0
- package/core/agents/qa-automation-engineer.md +103 -0
- package/core/agents/security-auditor.md +170 -0
- package/core/agents/seo-specialist.md +111 -0
- package/core/agents/stitch-designer.md +190 -0
- package/core/agents/tdd-reviewer.md +282 -0
- package/core/agents/test-engineer.md +158 -0
- package/core/scripts/auto_preview.py +148 -0
- package/core/scripts/checklist.py +243 -0
- package/core/scripts/cost_report.py +149 -0
- package/core/scripts/doc-sync-check.py +461 -0
- package/core/scripts/parse_user_stories.py +79 -0
- package/core/scripts/prepare_notion_updates.py +172 -0
- package/core/scripts/print_create_payload.py +18 -0
- package/core/scripts/session_manager.py +120 -0
- package/core/scripts/task_complete.py +127 -0
- package/core/scripts/verify_all.py +327 -0
- package/core/skills/analytics-strategy/SKILL.md +128 -0
- package/core/skills/api-patterns/SKILL.md +81 -0
- package/core/skills/api-patterns/api-style.md +42 -0
- package/core/skills/api-patterns/auth.md +24 -0
- package/core/skills/api-patterns/documentation.md +26 -0
- package/core/skills/api-patterns/graphql.md +41 -0
- package/core/skills/api-patterns/rate-limiting.md +31 -0
- package/core/skills/api-patterns/response.md +37 -0
- package/core/skills/api-patterns/rest.md +40 -0
- package/core/skills/api-patterns/scripts/api_validator.py +211 -0
- package/core/skills/api-patterns/security-testing.md +122 -0
- package/core/skills/api-patterns/trpc.md +41 -0
- package/core/skills/api-patterns/versioning.md +22 -0
- package/core/skills/app-builder/SKILL.md +75 -0
- package/core/skills/app-builder/agent-coordination.md +71 -0
- package/core/skills/app-builder/feature-building.md +53 -0
- package/core/skills/app-builder/project-detection.md +34 -0
- package/core/skills/app-builder/scaffolding.md +118 -0
- package/core/skills/app-builder/tech-stack.md +40 -0
- package/core/skills/app-builder/templates/SKILL.md +39 -0
- package/core/skills/app-builder/templates/astro-static/TEMPLATE.md +76 -0
- package/core/skills/app-builder/templates/chrome-extension/TEMPLATE.md +92 -0
- package/core/skills/app-builder/templates/cli-tool/TEMPLATE.md +88 -0
- package/core/skills/app-builder/templates/electron-desktop/TEMPLATE.md +88 -0
- package/core/skills/app-builder/templates/express-api/TEMPLATE.md +83 -0
- package/core/skills/app-builder/templates/flutter-app/TEMPLATE.md +90 -0
- package/core/skills/app-builder/templates/monorepo-turborepo/TEMPLATE.md +90 -0
- package/core/skills/app-builder/templates/nextjs-fullstack/TEMPLATE.md +82 -0
- package/core/skills/app-builder/templates/nextjs-saas/TEMPLATE.md +100 -0
- package/core/skills/app-builder/templates/nextjs-static/TEMPLATE.md +106 -0
- package/core/skills/app-builder/templates/nuxt-app/TEMPLATE.md +101 -0
- package/core/skills/app-builder/templates/python-fastapi/TEMPLATE.md +83 -0
- package/core/skills/app-builder/templates/react-native-app/TEMPLATE.md +93 -0
- package/core/skills/architecture/SKILL.md +55 -0
- package/core/skills/architecture/context-discovery.md +43 -0
- package/core/skills/architecture/examples.md +94 -0
- package/core/skills/architecture/pattern-selection.md +68 -0
- package/core/skills/architecture/patterns-reference.md +50 -0
- package/core/skills/architecture/trade-off-analysis.md +77 -0
- package/core/skills/atomic-design/SKILL.md +282 -0
- package/core/skills/atomic-design/references/classification-guide.md +132 -0
- package/core/skills/atomic-design/references/quality-checklist.md +60 -0
- package/core/skills/atomic-design/references/stacks/stack-blade.md +254 -0
- package/core/skills/atomic-design/references/stacks/stack-nextjs.md +272 -0
- package/core/skills/atomic-design/references/stacks/stack-react.md +239 -0
- package/core/skills/atomic-design/references/stacks/stack-vue.md +224 -0
- package/core/skills/bash-linux/SKILL.md +199 -0
- package/core/skills/behavioral-modes/SKILL.md +242 -0
- package/core/skills/brainstorming/SKILL.md +163 -0
- package/core/skills/brainstorming/dynamic-questioning.md +373 -0
- package/core/skills/checkpointing-patterns/SKILL.md +163 -0
- package/core/skills/clean-code/SKILL.md +201 -0
- package/core/skills/code-review-checklist/SKILL.md +109 -0
- package/core/skills/code-truth-validation/SKILL.md +149 -0
- package/core/skills/component-library-discovery/SKILL.md +154 -0
- package/core/skills/content-strategy/SKILL.md +222 -0
- package/core/skills/context-budget/SKILL.md +155 -0
- package/core/skills/context-gathering-patterns/SKILL.md +278 -0
- package/core/skills/cost-tracking/SKILL.md +206 -0
- package/core/skills/database-design/SKILL.md +52 -0
- package/core/skills/database-design/database-selection.md +43 -0
- package/core/skills/database-design/indexing.md +39 -0
- package/core/skills/database-design/migrations.md +48 -0
- package/core/skills/database-design/optimization.md +36 -0
- package/core/skills/database-design/orm-selection.md +30 -0
- package/core/skills/database-design/schema-design.md +56 -0
- package/core/skills/database-design/scripts/schema_validator.py +172 -0
- package/core/skills/deployment-procedures/SKILL.md +295 -0
- package/core/skills/design-md/README.md +34 -0
- package/core/skills/design-md/SKILL.md +172 -0
- package/core/skills/design-md/examples/DESIGN.md +154 -0
- package/core/skills/design-system-enforcement/SKILL.md +339 -0
- package/core/skills/doc.md +177 -0
- package/core/skills/document-registry/SKILL.md +130 -0
- package/core/skills/documentation-publishing/SKILL.md +174 -0
- package/core/skills/documentation-templates/SKILL.md +194 -0
- package/core/skills/enhance-prompt/README.md +34 -0
- package/core/skills/enhance-prompt/SKILL.md +204 -0
- package/core/skills/enhance-prompt/references/KEYWORDS.md +114 -0
- package/core/skills/frontend-design/SKILL.md +430 -0
- package/core/skills/frontend-design/animation-guide.md +331 -0
- package/core/skills/frontend-design/color-system.md +311 -0
- package/core/skills/frontend-design/decision-trees.md +418 -0
- package/core/skills/frontend-design/motion-graphics.md +306 -0
- package/core/skills/frontend-design/scripts/accessibility_checker.py +183 -0
- package/core/skills/frontend-design/scripts/ux_audit.py +722 -0
- package/core/skills/frontend-design/typography-system.md +345 -0
- package/core/skills/frontend-design/ux-psychology.md +541 -0
- package/core/skills/frontend-design/visual-effects.md +383 -0
- package/core/skills/game-development/2d-games/SKILL.md +119 -0
- package/core/skills/game-development/3d-games/SKILL.md +135 -0
- package/core/skills/game-development/SKILL.md +167 -0
- package/core/skills/game-development/game-art/SKILL.md +185 -0
- package/core/skills/game-development/game-audio/SKILL.md +190 -0
- package/core/skills/game-development/game-design/SKILL.md +129 -0
- package/core/skills/game-development/mobile-games/SKILL.md +108 -0
- package/core/skills/game-development/multiplayer/SKILL.md +132 -0
- package/core/skills/game-development/pc-games/SKILL.md +144 -0
- package/core/skills/game-development/vr-ar/SKILL.md +123 -0
- package/core/skills/game-development/web-games/SKILL.md +150 -0
- package/core/skills/geo-fundamentals/SKILL.md +156 -0
- package/core/skills/geo-fundamentals/scripts/geo_checker.py +289 -0
- package/core/skills/git-workflow/SKILL.md +263 -0
- package/core/skills/history-check-patterns/SKILL.md +125 -0
- package/core/skills/i18n-localization/SKILL.md +154 -0
- package/core/skills/i18n-localization/scripts/i18n_checker.py +241 -0
- package/core/skills/integration-completeness/SKILL.md +219 -0
- package/core/skills/intelligent-routing/SKILL.md +370 -0
- package/core/skills/lint-and-validate/SKILL.md +45 -0
- package/core/skills/lint-and-validate/scripts/lint_runner.py +173 -0
- package/core/skills/lint-and-validate/scripts/type_coverage.py +173 -0
- package/core/skills/local-verification/SKILL.md +195 -0
- package/core/skills/mcp-builder/SKILL.md +176 -0
- package/core/skills/mobile-design/SKILL.md +394 -0
- package/core/skills/mobile-design/decision-trees.md +516 -0
- package/core/skills/mobile-design/mobile-backend.md +491 -0
- package/core/skills/mobile-design/mobile-color-system.md +420 -0
- package/core/skills/mobile-design/mobile-debugging.md +122 -0
- package/core/skills/mobile-design/mobile-design-thinking.md +357 -0
- package/core/skills/mobile-design/mobile-navigation.md +458 -0
- package/core/skills/mobile-design/mobile-performance.md +767 -0
- package/core/skills/mobile-design/mobile-testing.md +356 -0
- package/core/skills/mobile-design/mobile-typography.md +433 -0
- package/core/skills/mobile-design/platform-android.md +666 -0
- package/core/skills/mobile-design/platform-ios.md +561 -0
- package/core/skills/mobile-design/scripts/mobile_audit.py +670 -0
- package/core/skills/mobile-design/touch-psychology.md +537 -0
- package/core/skills/nextjs-react-expert/1-async-eliminating-waterfalls.md +312 -0
- package/core/skills/nextjs-react-expert/2-bundle-bundle-size-optimization.md +240 -0
- package/core/skills/nextjs-react-expert/3-server-server-side-performance.md +490 -0
- package/core/skills/nextjs-react-expert/4-client-client-side-data-fetching.md +264 -0
- package/core/skills/nextjs-react-expert/5-rerender-re-render-optimization.md +581 -0
- package/core/skills/nextjs-react-expert/6-rendering-rendering-performance.md +432 -0
- package/core/skills/nextjs-react-expert/7-js-javascript-performance.md +684 -0
- package/core/skills/nextjs-react-expert/8-advanced-advanced-patterns.md +150 -0
- package/core/skills/nextjs-react-expert/SKILL.md +267 -0
- package/core/skills/nextjs-react-expert/scripts/convert_rules.py +222 -0
- package/core/skills/nextjs-react-expert/scripts/react_performance_checker.py +252 -0
- package/core/skills/nodejs-best-practices/SKILL.md +333 -0
- package/core/skills/notion-task-patterns/SKILL.md +2529 -0
- package/core/skills/page-specifications/SKILL.md +367 -0
- package/core/skills/parallel-agents/SKILL.md +175 -0
- package/core/skills/performance-profiling/SKILL.md +143 -0
- package/core/skills/performance-profiling/scripts/lighthouse_audit.py +76 -0
- package/core/skills/plan-writing/SKILL.md +190 -0
- package/core/skills/powershell-windows/SKILL.md +167 -0
- package/core/skills/project-foundation/SKILL.md +117 -0
- package/core/skills/project-setup/SKILL.md +141 -0
- package/core/skills/project-tracking-patterns/SKILL.md +357 -0
- package/core/skills/project-type-discovery/SKILL.md +239 -0
- package/core/skills/python-patterns/SKILL.md +441 -0
- package/core/skills/qa-test-generation/SKILL.md +156 -0
- package/core/skills/react-components/README.md +36 -0
- package/core/skills/react-components/SKILL.md +47 -0
- package/core/skills/react-components/examples/gold-standard-card.tsx +80 -0
- package/core/skills/react-components/package-lock.json +231 -0
- package/core/skills/react-components/package.json +16 -0
- package/core/skills/react-components/resources/architecture-checklist.md +15 -0
- package/core/skills/react-components/resources/component-template.tsx +37 -0
- package/core/skills/react-components/resources/stitch-api-reference.md +14 -0
- package/core/skills/react-components/resources/style-guide.json +27 -0
- package/core/skills/react-components/scripts/fetch-stitch.sh +30 -0
- package/core/skills/react-components/scripts/validate.js +68 -0
- package/core/skills/red-team-tactics/SKILL.md +199 -0
- package/core/skills/remotion/README.md +105 -0
- package/core/skills/remotion/SKILL.md +393 -0
- package/core/skills/remotion/examples/WalkthroughComposition.tsx +78 -0
- package/core/skills/remotion/examples/screens.json +56 -0
- package/core/skills/remotion/resources/composition-checklist.md +124 -0
- package/core/skills/remotion/resources/screen-slide-template.tsx +123 -0
- package/core/skills/remotion/scripts/download-stitch-asset.sh +38 -0
- package/core/skills/seo-fundamentals/SKILL.md +129 -0
- package/core/skills/seo-fundamentals/scripts/seo_checker.py +219 -0
- package/core/skills/server-management/SKILL.md +161 -0
- package/core/skills/session-resilience/SKILL.md +199 -0
- package/core/skills/shadcn-ui/README.md +248 -0
- package/core/skills/shadcn-ui/SKILL.md +326 -0
- package/core/skills/shadcn-ui/examples/auth-layout.tsx +177 -0
- package/core/skills/shadcn-ui/examples/data-table.tsx +313 -0
- package/core/skills/shadcn-ui/examples/form-pattern.tsx +177 -0
- package/core/skills/shadcn-ui/resources/component-catalog.md +481 -0
- package/core/skills/shadcn-ui/resources/customization-guide.md +516 -0
- package/core/skills/shadcn-ui/resources/migration-guide.md +463 -0
- package/core/skills/shadcn-ui/resources/setup-guide.md +412 -0
- package/core/skills/shadcn-ui/scripts/verify-setup.sh +134 -0
- package/core/skills/state-machine/SKILL.md +264 -0
- package/core/skills/stitch-loop/README.md +54 -0
- package/core/skills/stitch-loop/SKILL.md +203 -0
- package/core/skills/stitch-loop/examples/SITE.md +73 -0
- package/core/skills/stitch-loop/examples/next-prompt.md +25 -0
- package/core/skills/stitch-loop/resources/baton-schema.md +61 -0
- package/core/skills/stitch-loop/resources/site-template.md +104 -0
- package/core/skills/systematic-debugging/SKILL.md +109 -0
- package/core/skills/tailwind-patterns/SKILL.md +284 -0
- package/core/skills/tdd-validation/SKILL.md +243 -0
- package/core/skills/tdd-workflow/SKILL.md +284 -0
- package/core/skills/testing-patterns/SKILL.md +196 -0
- package/core/skills/testing-patterns/scripts/test_runner.py +219 -0
- package/core/skills/ui-ux-discovery/SKILL.md +329 -0
- package/core/skills/ui-validation/SKILL.md +190 -0
- package/core/skills/ui-validation/scripts/ui_antipattern_check.py +317 -0
- package/core/skills/verification-gate/SKILL.md +205 -0
- package/core/skills/vulnerability-scanner/SKILL.md +276 -0
- package/core/skills/vulnerability-scanner/checklists.md +121 -0
- package/core/skills/vulnerability-scanner/scripts/security_scan.py +458 -0
- package/core/skills/web-design-guidelines/SKILL.md +57 -0
- package/core/skills/webapp-testing/SKILL.md +187 -0
- package/core/skills/webapp-testing/scripts/playwright_runner.py +173 -0
- package/core/templates/ARCHITECTURE.template.md +407 -0
- package/core/templates/project-resources.example.json +71 -0
- package/core/workflows/atomic.md +182 -0
- package/core/workflows/brainstorm.md +134 -0
- package/core/workflows/check-task.md +242 -0
- package/core/workflows/copy-collect.md +306 -0
- package/core/workflows/create-agent.md +33 -0
- package/core/workflows/create-skill.md +39 -0
- package/core/workflows/create-workflow.md +33 -0
- package/core/workflows/create.md +92 -0
- package/core/workflows/debug.md +186 -0
- package/core/workflows/demand.md +443 -0
- package/core/workflows/deploy.md +260 -0
- package/core/workflows/discovery.md +267 -0
- package/core/workflows/document.md +272 -0
- package/core/workflows/ds-components.md +296 -0
- package/core/workflows/ds-init.md +58 -0
- package/core/workflows/ds-refactor.md +245 -0
- package/core/workflows/ds-references.md +197 -0
- package/core/workflows/ds-styleguide.md +237 -0
- package/core/workflows/ds-token-diff.md +103 -0
- package/core/workflows/ds-tokens.md +317 -0
- package/core/workflows/ds-validate.md +309 -0
- package/core/workflows/execute.md +483 -0
- package/core/workflows/extract-template.md +278 -0
- package/core/workflows/fix-failed-tests.md +160 -0
- package/core/workflows/init-project.md +386 -0
- package/core/workflows/legacy-project.md +849 -0
- package/core/workflows/log.md +97 -0
- package/core/workflows/new-project.md +610 -0
- package/core/workflows/new-project.md.bak +3292 -0
- package/core/workflows/new-task.md +404 -0
- package/core/workflows/orchestrate.md +237 -0
- package/core/workflows/page-build.md +296 -0
- package/core/workflows/plan.md +89 -0
- package/core/workflows/prd.md +255 -0
- package/core/workflows/preview.md +81 -0
- package/core/workflows/review-page.md +304 -0
- package/core/workflows/status.md +86 -0
- package/core/workflows/stitch.md +226 -0
- package/core/workflows/task-complete.md +473 -0
- package/core/workflows/task-update.md +163 -0
- package/core/workflows/tdd.md +344 -0
- package/core/workflows/test.md +251 -0
- package/core/workflows/ui-ux-pro-max.md +437 -0
- package/core/workflows/ux-mobile-optimize.md +262 -0
- package/core/workflows/ux-mobile-validate.md +297 -0
- package/engine-files/GEMINI.md +69 -0
- package/package.json +47 -0
- package/runtime-adapters/antigravity.js +26 -0
- package/runtime-adapters/claude.js +57 -0
- package/runtime-adapters/codex.js +51 -0
- package/runtime-adapters/copilot.js +51 -0
- package/runtime-adapters/cursor.js +51 -0
- package/runtime-adapters/gemini-cli.js +30 -0
- package/runtime-adapters/opencode.js +51 -0
- package/runtime-adapters/windsurf.js +51 -0
|
@@ -0,0 +1,461 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
doc-sync-check.py — Detecta divergências entre codebase e documentação.
|
|
4
|
+
|
|
5
|
+
Verifica:
|
|
6
|
+
1. Features em frontend/src/features/ vs PRD Sec 6.1
|
|
7
|
+
2. SDK modules em frontend/src/lib/flyee-sdk/ vs SDD Sec 3.2
|
|
8
|
+
3. Implementation Order no SDD vs estado real dos sprints
|
|
9
|
+
4. Arquivos em docs/ vs INDEX.md
|
|
10
|
+
|
|
11
|
+
Uso:
|
|
12
|
+
python3 .agent/scripts/doc-sync-check.py
|
|
13
|
+
python3 .agent/scripts/doc-sync-check.py --verbose
|
|
14
|
+
python3 .agent/scripts/doc-sync-check.py --section prd
|
|
15
|
+
python3 .agent/scripts/doc-sync-check.py --section sdd
|
|
16
|
+
python3 .agent/scripts/doc-sync-check.py --section index
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import re
|
|
21
|
+
import sys
|
|
22
|
+
import argparse
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
# ─── Paths ──────────────────────────────────────────────────────────────────
|
|
26
|
+
|
|
27
|
+
ROOT = Path(__file__).parent.parent.parent # /home/bruno/flyee
|
|
28
|
+
FEATURES_DIR = ROOT / "frontend/src/features"
|
|
29
|
+
SDK_DIR = ROOT / "frontend/src/lib/flyee-sdk"
|
|
30
|
+
PRD_PATH = ROOT / "docs/PRD-flyee.md"
|
|
31
|
+
SDD_PATH = ROOT / "docs/design/SDD-flyee.md"
|
|
32
|
+
INDEX_PATH = ROOT / "docs/INDEX.md"
|
|
33
|
+
|
|
34
|
+
# ─── ANSI colors ────────────────────────────────────────────────────────────
|
|
35
|
+
|
|
36
|
+
GREEN = "\033[92m"
|
|
37
|
+
YELLOW = "\033[93m"
|
|
38
|
+
RED = "\033[91m"
|
|
39
|
+
CYAN = "\033[96m"
|
|
40
|
+
BOLD = "\033[1m"
|
|
41
|
+
RESET = "\033[0m"
|
|
42
|
+
|
|
43
|
+
ok = f"{GREEN}✅{RESET}"
|
|
44
|
+
warn = f"{YELLOW}⚠️ {RESET}"
|
|
45
|
+
err = f"{RED}❌{RESET}"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
# ─── Helpers ────────────────────────────────────────────────────────────────
|
|
49
|
+
|
|
50
|
+
def read_file(path: Path) -> str:
|
|
51
|
+
try:
|
|
52
|
+
return path.read_text(encoding="utf-8")
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
return ""
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def section(title: str):
|
|
58
|
+
print(f"\n{BOLD}{CYAN}{'─'*60}{RESET}")
|
|
59
|
+
print(f"{BOLD}{CYAN} {title}{RESET}")
|
|
60
|
+
print(f"{BOLD}{CYAN}{'─'*60}{RESET}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ─── Check 1: Features vs PRD ───────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
def check_features_vs_prd(verbose: bool) -> int:
|
|
66
|
+
section("1. Features implementadas vs PRD Sec 6.1")
|
|
67
|
+
issues = 0
|
|
68
|
+
|
|
69
|
+
if not FEATURES_DIR.exists():
|
|
70
|
+
print(f" {warn} features dir não encontrado: {FEATURES_DIR}")
|
|
71
|
+
return 1
|
|
72
|
+
|
|
73
|
+
implemented = sorted([d.name for d in FEATURES_DIR.iterdir() if d.is_dir()])
|
|
74
|
+
prd_text = read_file(PRD_PATH)
|
|
75
|
+
|
|
76
|
+
# Extract feature table rows from PRD (lines with | F\d+ |)
|
|
77
|
+
prd_feature_rows = re.findall(r"\|\s*(F\d+)\s*\|.*?\|.*?\|\s*(✅[^|]*|⏳[^|]*|\?[^|]*)\|", prd_text)
|
|
78
|
+
prd_statuses = {f_id.strip(): status.strip() for f_id, status in prd_feature_rows}
|
|
79
|
+
|
|
80
|
+
# Map known feature dirs to F-IDs (extend as project grows)
|
|
81
|
+
feature_map = {
|
|
82
|
+
"activity": ("F7", "Activity Feed"),
|
|
83
|
+
"decisions": ("F5", "Decision Log"),
|
|
84
|
+
"dev-activity": ("F7", "Activity Feed (Dev)"),
|
|
85
|
+
"documents": ("F4", "Document Viewer + Planning History"),
|
|
86
|
+
"health": (None, "Health check (internal)"),
|
|
87
|
+
"knowledge": ("F6", "Knowledge Hub"),
|
|
88
|
+
"okrs": ("F10", "OKR Dashboard"),
|
|
89
|
+
"projects": ("F2", "Project CRUD"),
|
|
90
|
+
"proposals": ("F9", "Proposal Module"),
|
|
91
|
+
"tasks": ("F3", "Task Management + F10 QA Gate"),
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
print(f" {'Feature Dir':<20} {'F-ID':<8} {'PRD Status':<30} {'Check'}")
|
|
95
|
+
print(f" {'─'*20} {'─'*8} {'─'*30} {'─'*10}")
|
|
96
|
+
|
|
97
|
+
for feat_dir in implemented:
|
|
98
|
+
mapping = feature_map.get(feat_dir)
|
|
99
|
+
if mapping is None:
|
|
100
|
+
if verbose:
|
|
101
|
+
print(f" {warn} {feat_dir:<20} {'?':<8} {'Not in feature_map':<30} {warn} unmapped")
|
|
102
|
+
issues += 1
|
|
103
|
+
continue
|
|
104
|
+
|
|
105
|
+
f_id, label = mapping
|
|
106
|
+
if f_id is None:
|
|
107
|
+
if verbose:
|
|
108
|
+
print(f" {feat_dir:<22} {'—':<8} {'Internal':<30} {ok}")
|
|
109
|
+
continue
|
|
110
|
+
|
|
111
|
+
# Check if f_id documented in PRD
|
|
112
|
+
documented = f_id in prd_statuses
|
|
113
|
+
status_in_prd = prd_statuses.get(f_id, "NOT IN PRD")
|
|
114
|
+
has_done = "Done" in status_in_prd or "✅" in status_in_prd
|
|
115
|
+
|
|
116
|
+
if not documented:
|
|
117
|
+
symbol = err
|
|
118
|
+
issues += 1
|
|
119
|
+
elif not has_done:
|
|
120
|
+
symbol = warn
|
|
121
|
+
issues += 1
|
|
122
|
+
else:
|
|
123
|
+
symbol = ok
|
|
124
|
+
|
|
125
|
+
print(f" {feat_dir:<22} {f_id:<8} {status_in_prd[:30]:<32} {symbol}")
|
|
126
|
+
|
|
127
|
+
# Check if PRD has F-IDs that don't map to any feature dir
|
|
128
|
+
all_known_fids = {v[0] for v in feature_map.values() if v[0]}
|
|
129
|
+
for f_id, status in prd_statuses.items():
|
|
130
|
+
if f_id not in all_known_fids:
|
|
131
|
+
if verbose:
|
|
132
|
+
print(f" {warn} PRD has {f_id} but no matching feature dir")
|
|
133
|
+
|
|
134
|
+
return issues
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
# ─── Check 2: SDK modules vs SDD ────────────────────────────────────────────
|
|
138
|
+
|
|
139
|
+
def check_sdk_vs_sdd(verbose: bool) -> int:
|
|
140
|
+
section("2. SDK modules vs SDD Sec 3.2")
|
|
141
|
+
issues = 0
|
|
142
|
+
|
|
143
|
+
if not SDK_DIR.exists():
|
|
144
|
+
print(f" {warn} SDK dir não encontrado: {SDK_DIR}")
|
|
145
|
+
return 1
|
|
146
|
+
|
|
147
|
+
sdk_files = sorted([f.stem for f in SDK_DIR.glob("*.ts") if f.stem not in ("index", "client")])
|
|
148
|
+
sdd_text = read_file(SDD_PATH)
|
|
149
|
+
|
|
150
|
+
# Extract SDK module rows only from Section 3.2 of SDD
|
|
151
|
+
# Look for the table between "### 3.2 Flyee SDK Layer" and the next "###"
|
|
152
|
+
sdk_section_match = re.search(
|
|
153
|
+
r"### 3\.2 Flyee SDK Layer(.+?)(?=^###|\.\.\.END)",
|
|
154
|
+
sdd_text, re.DOTALL | re.MULTILINE
|
|
155
|
+
)
|
|
156
|
+
sdk_section = sdk_section_match.group(1) if sdk_section_match else sdd_text
|
|
157
|
+
sdd_sdk_rows = re.findall(r"\|\s*`([a-z][a-z0-9\-]+\.ts)`\s*\|", sdk_section)
|
|
158
|
+
sdd_modules = {r.replace(".ts", "") for r in sdd_sdk_rows}
|
|
159
|
+
|
|
160
|
+
# Only check files actually in flyee-sdk/ (not stores or other libs)
|
|
161
|
+
sdk_files_to_check = [f for f in sdk_files if f != "types"] # types.ts is internal
|
|
162
|
+
|
|
163
|
+
# Known stores (live in lib/stores/, NOT in flyee-sdk/) — exclude from SDD check
|
|
164
|
+
known_stores = {"auth-store", "collections", "organizations", "sources", "usage", "user", "webhooks",
|
|
165
|
+
"activityStore", "decisionsStore", "projectsStore", "sidePanelStore", "tasksStore",
|
|
166
|
+
"okrsStore", "documentsStore", "apiKeys", "authProviders", "uiStore", "index"}
|
|
167
|
+
|
|
168
|
+
print(f" {'SDK File':<30} {'In SDD':<10} {'Status'}")
|
|
169
|
+
print(f" {'─'*30} {'─'*10} {'─'*10}")
|
|
170
|
+
|
|
171
|
+
for sdk_file in sdk_files_to_check:
|
|
172
|
+
in_sdd = sdk_file in sdd_modules
|
|
173
|
+
symbol = ok if in_sdd else err
|
|
174
|
+
if not in_sdd:
|
|
175
|
+
issues += 1
|
|
176
|
+
print(f" {sdk_file+'.ts':<32} {'✓' if in_sdd else 'MISSING':<10} {symbol}")
|
|
177
|
+
|
|
178
|
+
# Modules in SDD but not in filesystem and not in known_stores
|
|
179
|
+
for sdd_mod in sorted(sdd_modules):
|
|
180
|
+
if sdd_mod in known_stores:
|
|
181
|
+
continue # skip stores — they live in lib/stores/, not lib/flyee-sdk/
|
|
182
|
+
actual_file = SDK_DIR / f"{sdd_mod}.ts"
|
|
183
|
+
if not actual_file.exists():
|
|
184
|
+
print(f" {warn} SDD documenta `{sdd_mod}.ts` mas arquivo não existe no SDK dir")
|
|
185
|
+
issues += 1
|
|
186
|
+
|
|
187
|
+
return issues
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
# ─── Check 3: Implementation Order vs sprints ────────────────────────────────
|
|
191
|
+
|
|
192
|
+
def check_impl_order(verbose: bool) -> int:
|
|
193
|
+
section("3. SDD Sec 13 — Implementation Order")
|
|
194
|
+
issues = 0
|
|
195
|
+
|
|
196
|
+
sdd_text = read_file(SDD_PATH)
|
|
197
|
+
|
|
198
|
+
# Find Implementation Order table rows: | N | Description | Status | Dependency |
|
|
199
|
+
rows = re.findall(
|
|
200
|
+
r"\|\s*(\d+)\s*\|\s*(.+?)\s*\|\s*(✅[^|]*|⏳[^|]*|\?[^|]*|[^|]+)\s*\|\s*([^|]+)\s*\|",
|
|
201
|
+
sdd_text
|
|
202
|
+
)
|
|
203
|
+
|
|
204
|
+
if not rows:
|
|
205
|
+
print(f" {warn} Não encontrei tabela de Implementation Order no SDD")
|
|
206
|
+
return 1
|
|
207
|
+
|
|
208
|
+
pending = []
|
|
209
|
+
done = []
|
|
210
|
+
for num, component, status, dep in rows:
|
|
211
|
+
status = status.strip()
|
|
212
|
+
component = component.strip()
|
|
213
|
+
if "Done" in status or "✅" in status:
|
|
214
|
+
done.append((num, component))
|
|
215
|
+
elif "⏳" in status or "Pendente" in status:
|
|
216
|
+
pending.append((num, component))
|
|
217
|
+
else:
|
|
218
|
+
pending.append((num, component))
|
|
219
|
+
issues += 1
|
|
220
|
+
|
|
221
|
+
print(f" {ok} Concluídos: {len(done)} itens")
|
|
222
|
+
print(f" {'⏳'} Pendentes: {len(pending)} itens")
|
|
223
|
+
|
|
224
|
+
if pending:
|
|
225
|
+
print(f"\n Pendentes:")
|
|
226
|
+
for num, comp in pending:
|
|
227
|
+
print(f" #{num} {comp[:60]}")
|
|
228
|
+
|
|
229
|
+
if len(done) == 0:
|
|
230
|
+
print(f" {err} Nenhum item marcado como Done — SDD pode estar desatualizado")
|
|
231
|
+
issues += 1
|
|
232
|
+
|
|
233
|
+
return issues
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# ─── Check 4: docs/ vs INDEX.md ─────────────────────────────────────────────
|
|
237
|
+
|
|
238
|
+
def check_docs_index(verbose: bool) -> int:
|
|
239
|
+
section("4. docs/ vs INDEX.md")
|
|
240
|
+
issues = 0
|
|
241
|
+
|
|
242
|
+
docs_dir = ROOT / "docs"
|
|
243
|
+
if not docs_dir.exists():
|
|
244
|
+
print(f" {warn} docs/ dir não encontrado")
|
|
245
|
+
return 1
|
|
246
|
+
|
|
247
|
+
# All .md files (excluding INDEX itself, recursively)
|
|
248
|
+
all_docs = sorted([
|
|
249
|
+
f.relative_to(ROOT)
|
|
250
|
+
for f in docs_dir.rglob("*.md")
|
|
251
|
+
if f.name != "INDEX.md"
|
|
252
|
+
])
|
|
253
|
+
|
|
254
|
+
index_text = read_file(INDEX_PATH)
|
|
255
|
+
|
|
256
|
+
print(f" {'Arquivo':<50} {'Em INDEX.md'}")
|
|
257
|
+
print(f" {'─'*50} {'─'*12}")
|
|
258
|
+
|
|
259
|
+
for doc_path in all_docs:
|
|
260
|
+
doc_name = doc_path.name
|
|
261
|
+
in_index = doc_name in index_text or str(doc_path) in index_text
|
|
262
|
+
symbol = ok if in_index else warn
|
|
263
|
+
if not in_index:
|
|
264
|
+
issues += 1
|
|
265
|
+
if verbose or not in_index:
|
|
266
|
+
print(f" {str(doc_path):<52} {symbol}")
|
|
267
|
+
|
|
268
|
+
if not verbose:
|
|
269
|
+
print(f"\n {ok} {len(all_docs) - issues}/{len(all_docs)} documentos registrados no INDEX.md")
|
|
270
|
+
if issues:
|
|
271
|
+
print(f" {warn} {issues} doc(s) não encontrados no INDEX — rode com --verbose para ver quais")
|
|
272
|
+
|
|
273
|
+
return issues
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
# ─── Check 5: BREAKDOWN-tasks.md vs Flyee ────────────────────────────────────
|
|
277
|
+
|
|
278
|
+
def check_breakdown_vs_flyee(verbose: bool) -> int:
|
|
279
|
+
section("5. BREAKDOWN-tasks.md vs Flyee")
|
|
280
|
+
issues = 0
|
|
281
|
+
|
|
282
|
+
breakdown_path = ROOT / "docs/BREAKDOWN-tasks.md"
|
|
283
|
+
if not breakdown_path.exists():
|
|
284
|
+
print(f" {warn} docs/BREAKDOWN-tasks.md não encontrado")
|
|
285
|
+
return 1
|
|
286
|
+
|
|
287
|
+
# ── Parse BREAKDOWN: extrair headers das tasks + sprint ────────────────
|
|
288
|
+
breakdown_text = read_file(breakdown_path)
|
|
289
|
+
|
|
290
|
+
sprint_pattern = re.compile(r"^## Sprint (\d+)[:\s]+(.+)$", re.MULTILINE)
|
|
291
|
+
task_pattern = re.compile(r"^### (T\d+\.\d+) — (.+)$", re.MULTILINE)
|
|
292
|
+
|
|
293
|
+
sprints = [(int(m.group(1)), m.start()) for m in sprint_pattern.finditer(breakdown_text)]
|
|
294
|
+
tasks_raw = [(m.group(1), m.group(2).strip(), m.start()) for m in task_pattern.finditer(breakdown_text)]
|
|
295
|
+
|
|
296
|
+
def find_sprint_for_pos(pos: int) -> int:
|
|
297
|
+
sprint = 0
|
|
298
|
+
for s_num, s_pos in sprints:
|
|
299
|
+
if s_pos <= pos:
|
|
300
|
+
sprint = s_num
|
|
301
|
+
return sprint
|
|
302
|
+
|
|
303
|
+
breakdown_tasks: dict = {}
|
|
304
|
+
for t_id, t_name, t_pos in tasks_raw:
|
|
305
|
+
sprint_num = find_sprint_for_pos(t_pos)
|
|
306
|
+
breakdown_tasks[t_id] = {"name": t_name, "sprint": sprint_num}
|
|
307
|
+
|
|
308
|
+
|
|
309
|
+
# ── Sprints concluídos: ler tabela Summary do próprio BREAKDOWN ─────────
|
|
310
|
+
# Busca linhas da tabela com "✅ Done" — sprints sem esse marcador são pendentes
|
|
311
|
+
pending_sprints: set = set()
|
|
312
|
+
for line in breakdown_text.splitlines():
|
|
313
|
+
# Linha da tabela: | **N** | T... | 🔲 Pendente | ... |
|
|
314
|
+
m = re.match(r"\|\s*\*\*(\d+)\*\*\s*\|.+?🔲", line)
|
|
315
|
+
if m:
|
|
316
|
+
pending_sprints.add(int(m.group(1)))
|
|
317
|
+
|
|
318
|
+
# Todos os sprints encontrados no breakdown
|
|
319
|
+
all_sprints = {info["sprint"] for info in breakdown_tasks.values() if info["sprint"] > 0}
|
|
320
|
+
completed_sprints = all_sprints - pending_sprints
|
|
321
|
+
|
|
322
|
+
|
|
323
|
+
# ── Carregar tasks do Flyee via bridge CLI ─────────────────────────────
|
|
324
|
+
import subprocess
|
|
325
|
+
bridge = ROOT / ".agent/flyee-bridge/bridge.py"
|
|
326
|
+
|
|
327
|
+
flyee_tasks: dict = {} # "T1.1" -> "completed" | "running" | ...
|
|
328
|
+
if bridge.exists():
|
|
329
|
+
try:
|
|
330
|
+
result = subprocess.run(
|
|
331
|
+
["python3", str(bridge), "--list-tasks"],
|
|
332
|
+
capture_output=True, text=True, timeout=15, cwd=str(ROOT)
|
|
333
|
+
)
|
|
334
|
+
for line in result.stdout.splitlines():
|
|
335
|
+
# Format: "N T1.1 — Name status uuid"
|
|
336
|
+
m = re.match(
|
|
337
|
+
r"\s*\d+\s+(T\d+\.\d+)[^\t]+?\s{2,}(completed|running|pending|failed|testing|cancelled)\s",
|
|
338
|
+
line
|
|
339
|
+
)
|
|
340
|
+
if m:
|
|
341
|
+
t_label, t_status = m.group(1), m.group(2)
|
|
342
|
+
flyee_tasks[t_label] = t_status # last occurrence wins
|
|
343
|
+
except Exception as e:
|
|
344
|
+
print(f" {warn} Não foi possível carregar tasks do Flyee: {e}")
|
|
345
|
+
else:
|
|
346
|
+
print(f" {warn} Bridge não encontrado — pulando verificação Flyee")
|
|
347
|
+
|
|
348
|
+
# ── Comparar ────────────────────────────────────────────────────────────
|
|
349
|
+
ghost_running = [] # sprint Done no SDD mas task ainda 'running' no Flyee
|
|
350
|
+
|
|
351
|
+
for t_id, info in sorted(breakdown_tasks.items()):
|
|
352
|
+
sprint = info["sprint"]
|
|
353
|
+
if sprint not in completed_sprints:
|
|
354
|
+
continue # sprint ainda em andamento — OK estar running
|
|
355
|
+
flyee_status = flyee_tasks.get(t_id)
|
|
356
|
+
if flyee_status is not None and flyee_status != "completed":
|
|
357
|
+
ghost_running.append((t_id, info["name"], sprint, flyee_status))
|
|
358
|
+
issues += 1
|
|
359
|
+
|
|
360
|
+
# Tasks no Flyee como 'running' cujo sprint já é Done
|
|
361
|
+
for t_label, t_status in flyee_tasks.items():
|
|
362
|
+
if t_status == "running" and t_label in breakdown_tasks:
|
|
363
|
+
sprint = breakdown_tasks[t_label]["sprint"]
|
|
364
|
+
if sprint in completed_sprints and not any(g[0] == t_label for g in ghost_running):
|
|
365
|
+
ghost_running.append((t_label, breakdown_tasks[t_label]["name"], sprint, "running"))
|
|
366
|
+
issues += 1
|
|
367
|
+
|
|
368
|
+
# ── Output ──────────────────────────────────────────────────────────────
|
|
369
|
+
total_bd = len(breakdown_tasks)
|
|
370
|
+
matched = sum(1 for t in breakdown_tasks if t in flyee_tasks)
|
|
371
|
+
|
|
372
|
+
print(f" Tasks no BREAKDOWN: {total_bd}")
|
|
373
|
+
print(f" Tasks encontradas no Flyee: {matched}/{total_bd}")
|
|
374
|
+
print(f" Sprints concluídos (SDD): {sorted(completed_sprints)}")
|
|
375
|
+
|
|
376
|
+
if not ghost_running:
|
|
377
|
+
print(f"\n {ok} Nenhuma divergência BREAKDOWN ↔ Flyee")
|
|
378
|
+
else:
|
|
379
|
+
print(f"\n {err} {len(ghost_running)} task(s) com status desatualizado no Flyee:")
|
|
380
|
+
print(f" {'Task':<12} {'Sprint':<8} {'Status Flyee':<16} {'Nome'}")
|
|
381
|
+
print(f" {'─'*12} {'─'*8} {'─'*16} {'─'*40}")
|
|
382
|
+
for t_id, name, sprint, status in sorted(ghost_running):
|
|
383
|
+
print(f" {t_id:<14} {str(sprint):<8} {status:<18} {name[:40]}")
|
|
384
|
+
print(f"\n Para corrigir (substitua <uuid> pelo ID real do Flyee):")
|
|
385
|
+
print(f" python3 .agent/flyee-bridge/bridge.py --update-task <uuid> --status completed --result success")
|
|
386
|
+
|
|
387
|
+
# Tasks de sprints concluídos não encontradas no Flyee (verbose only)
|
|
388
|
+
if verbose:
|
|
389
|
+
not_in_flyee = [
|
|
390
|
+
(t_id, info) for t_id, info in breakdown_tasks.items()
|
|
391
|
+
if t_id not in flyee_tasks and info["sprint"] in completed_sprints
|
|
392
|
+
]
|
|
393
|
+
if not_in_flyee:
|
|
394
|
+
print(f"\n {warn} Tasks de sprints concluídos não encontradas no Flyee:")
|
|
395
|
+
for t_id, info in sorted(not_in_flyee):
|
|
396
|
+
print(f" Sprint {info['sprint']}: {t_id} — {info['name']}")
|
|
397
|
+
|
|
398
|
+
return issues
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
# ─── Summary ─────────────────────────────────────────────────────────────────
|
|
402
|
+
|
|
403
|
+
def print_summary(total_issues: int):
|
|
404
|
+
section("Resumo")
|
|
405
|
+
if total_issues == 0:
|
|
406
|
+
print(f" {ok} {GREEN}{BOLD}Docs sincronizados — nenhuma divergência encontrada.{RESET}")
|
|
407
|
+
else:
|
|
408
|
+
print(f" {err} {RED}{BOLD}{total_issues} divergência(s) encontrada(s).{RESET}")
|
|
409
|
+
print(f"\n Próximos passos:")
|
|
410
|
+
print(f" 1. Corrija as divergências ANTES de marcar a task como concluída")
|
|
411
|
+
print(f" 2. Features F-level: atualizar PRD Sec 6.1 + SDD Sec 13")
|
|
412
|
+
print(f" 3. SDK changes: atualizar SDD Sec 3.2")
|
|
413
|
+
print(f" 4. Novos docs: atualizar docs/INDEX.md")
|
|
414
|
+
print(f" 5. Tasks desatualizadas: bridge.py --update-task <uuid> --status completed")
|
|
415
|
+
print()
|
|
416
|
+
print(f" Docs relevantes:")
|
|
417
|
+
print(f" PRD: docs/PRD-flyee.md")
|
|
418
|
+
print(f" SDD: docs/design/SDD-flyee.md")
|
|
419
|
+
print(f" INDEX: docs/INDEX.md")
|
|
420
|
+
print(f" BREAKDOWN: docs/BREAKDOWN-tasks.md")
|
|
421
|
+
|
|
422
|
+
|
|
423
|
+
# ─── Main ───────────────────────────────────────────────────────────────────
|
|
424
|
+
|
|
425
|
+
def main():
|
|
426
|
+
parser = argparse.ArgumentParser(
|
|
427
|
+
description="Detecta divergências entre codebase e documentação do projeto Flyee."
|
|
428
|
+
)
|
|
429
|
+
parser.add_argument("--verbose", "-v", action="store_true", help="Output detalhado")
|
|
430
|
+
parser.add_argument(
|
|
431
|
+
"--section", "-s",
|
|
432
|
+
choices=["prd", "sdd", "index", "breakdown", "all"],
|
|
433
|
+
default="all",
|
|
434
|
+
help="Rodar apenas uma seção específica (default: all)"
|
|
435
|
+
)
|
|
436
|
+
args = parser.parse_args()
|
|
437
|
+
|
|
438
|
+
print(f"\n{BOLD}🔍 doc-sync-check — Flyee Documentation Audit{RESET}")
|
|
439
|
+
print(f" Root: {ROOT}")
|
|
440
|
+
|
|
441
|
+
total_issues = 0
|
|
442
|
+
|
|
443
|
+
if args.section in ("prd", "all"):
|
|
444
|
+
total_issues += check_features_vs_prd(args.verbose)
|
|
445
|
+
|
|
446
|
+
if args.section in ("sdd", "all"):
|
|
447
|
+
total_issues += check_sdk_vs_sdd(args.verbose)
|
|
448
|
+
total_issues += check_impl_order(args.verbose)
|
|
449
|
+
|
|
450
|
+
if args.section in ("index", "all"):
|
|
451
|
+
total_issues += check_docs_index(args.verbose)
|
|
452
|
+
|
|
453
|
+
if args.section in ("breakdown", "all"):
|
|
454
|
+
total_issues += check_breakdown_vs_flyee(args.verbose)
|
|
455
|
+
|
|
456
|
+
print_summary(total_issues)
|
|
457
|
+
sys.exit(0 if total_issues == 0 else 1)
|
|
458
|
+
|
|
459
|
+
|
|
460
|
+
if __name__ == "__main__":
|
|
461
|
+
main()
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
|
|
2
|
+
import re
|
|
3
|
+
import json
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
def parse_user_stories(file_path):
|
|
9
|
+
if not os.path.exists(file_path):
|
|
10
|
+
raise FileNotFoundError(f"Input file not found: {file_path}")
|
|
11
|
+
|
|
12
|
+
with open(file_path, 'r', encoding='utf-8') as f:
|
|
13
|
+
content = f.read()
|
|
14
|
+
|
|
15
|
+
stories = []
|
|
16
|
+
# Regex to find story blocks
|
|
17
|
+
# Looking for ### Title then content until next ### or ---
|
|
18
|
+
|
|
19
|
+
sections = re.split(r'### ', content)[1:] # Skip preamble
|
|
20
|
+
|
|
21
|
+
for section in sections:
|
|
22
|
+
lines = section.split('\n')
|
|
23
|
+
title_line = lines[0].strip()
|
|
24
|
+
|
|
25
|
+
# Extract ID and Title (e.g., "1.1 Setup Inicial Next.js")
|
|
26
|
+
match = re.match(r'(\d+\.\d+)\s+(.+)', title_line)
|
|
27
|
+
if not match:
|
|
28
|
+
continue
|
|
29
|
+
|
|
30
|
+
story_id = match.group(1)
|
|
31
|
+
story_title = match.group(2)
|
|
32
|
+
full_title = f"{story_id} {story_title}"
|
|
33
|
+
|
|
34
|
+
# Parse fields
|
|
35
|
+
body = section
|
|
36
|
+
|
|
37
|
+
priority_match = re.search(r'\*\*Priority:\*\*\s*(.+)', body)
|
|
38
|
+
priority = priority_match.group(1).strip() if priority_match else "Medium"
|
|
39
|
+
|
|
40
|
+
estimate_match = re.search(r'\*\*Estimate:\*\*\s*(.+)', body)
|
|
41
|
+
estimate = estimate_match.group(1).strip() if estimate_match else "M"
|
|
42
|
+
|
|
43
|
+
agent_match = re.search(r'\*\*Agent:\*\*\s*(.+)', body)
|
|
44
|
+
agent = agent_match.group(1).strip() if agent_match else "Pending"
|
|
45
|
+
|
|
46
|
+
# Extract User Story and Acceptance Criteria for Description
|
|
47
|
+
desc_start = body.find('**User Story:**')
|
|
48
|
+
desc_end = body.find('**Verification:**')
|
|
49
|
+
if desc_end == -1:
|
|
50
|
+
desc_end = body.find('---', desc_start)
|
|
51
|
+
|
|
52
|
+
description = body[desc_start:desc_end].strip() if desc_start != -1 else body.strip()
|
|
53
|
+
|
|
54
|
+
stories.append({
|
|
55
|
+
"id": story_id,
|
|
56
|
+
"title": full_title,
|
|
57
|
+
"priority": priority,
|
|
58
|
+
"estimate": estimate,
|
|
59
|
+
"agent": agent,
|
|
60
|
+
"description": description
|
|
61
|
+
})
|
|
62
|
+
|
|
63
|
+
return stories
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
parser = argparse.ArgumentParser(description="Parse User Stories from Markdown to JSON")
|
|
67
|
+
parser.add_argument("--input", required=True, help="Input Markdown file path")
|
|
68
|
+
parser.add_argument("--output", default="stories.json", help="Output JSON file path")
|
|
69
|
+
|
|
70
|
+
args = parser.parse_args()
|
|
71
|
+
|
|
72
|
+
try:
|
|
73
|
+
results = parse_user_stories(args.input)
|
|
74
|
+
with open(args.output, 'w', encoding='utf-8') as f:
|
|
75
|
+
json.dump(results, f, indent=2, ensure_ascii=False)
|
|
76
|
+
print(f"✅ Successfully wrote {len(results)} stories to {args.output}")
|
|
77
|
+
except Exception as e:
|
|
78
|
+
print(f"❌ Error: {e}")
|
|
79
|
+
|