@nextsparkjs/ai-workflow 0.1.0-beta.100
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 +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,357 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Validate Scope Script
|
|
4
|
+
|
|
5
|
+
Validates file modifications against session scope.json configuration.
|
|
6
|
+
Part of the scope-enforcement skill.
|
|
7
|
+
|
|
8
|
+
Usage:
|
|
9
|
+
python validate-scope.py --session ".claude/sessions/2025-12-30-feature-v1"
|
|
10
|
+
python validate-scope.py -s ".claude/sessions/2025-12-30-feature-v1" --files "core/lib/x.ts,app/api/y/route.ts"
|
|
11
|
+
python validate-scope.py -s ".claude/sessions/2025-12-30-feature-v1" --git # Check git changes
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import argparse
|
|
15
|
+
import fnmatch
|
|
16
|
+
import json
|
|
17
|
+
import os
|
|
18
|
+
import subprocess
|
|
19
|
+
import sys
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import List, Dict, Any, Optional
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def get_project_root() -> Path:
|
|
25
|
+
"""Find the project root by looking for .claude directory."""
|
|
26
|
+
current = Path(__file__).resolve()
|
|
27
|
+
|
|
28
|
+
# Walk up the directory tree
|
|
29
|
+
for parent in current.parents:
|
|
30
|
+
if (parent / ".claude").is_dir():
|
|
31
|
+
return parent
|
|
32
|
+
|
|
33
|
+
# Fallback: check current working directory
|
|
34
|
+
cwd = Path.cwd()
|
|
35
|
+
if (cwd / ".claude").is_dir():
|
|
36
|
+
return cwd
|
|
37
|
+
|
|
38
|
+
print("Error: Could not find project root (.claude directory)")
|
|
39
|
+
sys.exit(1)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def read_scope_config(session_path: Path) -> Dict[str, Any]:
|
|
43
|
+
"""Read and parse scope.json from session folder."""
|
|
44
|
+
scope_file = session_path / "scope.json"
|
|
45
|
+
|
|
46
|
+
if not scope_file.exists():
|
|
47
|
+
print(f"Error: scope.json not found in {session_path}")
|
|
48
|
+
sys.exit(1)
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
with open(scope_file, "r") as f:
|
|
52
|
+
return json.load(f)
|
|
53
|
+
except json.JSONDecodeError as e:
|
|
54
|
+
print(f"Error: Invalid JSON in scope.json: {e}")
|
|
55
|
+
sys.exit(1)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def build_allowed_paths(scope_config: Dict[str, Any]) -> List[str]:
|
|
59
|
+
"""Build list of allowed paths from scope configuration."""
|
|
60
|
+
allowed_paths = [".claude/sessions/**/*"]
|
|
61
|
+
|
|
62
|
+
scope = scope_config.get("scope", {})
|
|
63
|
+
|
|
64
|
+
# Core paths
|
|
65
|
+
if scope.get("core", False):
|
|
66
|
+
allowed_paths.extend([
|
|
67
|
+
"core/**/*",
|
|
68
|
+
"app/**/*",
|
|
69
|
+
"scripts/**/*",
|
|
70
|
+
"migrations/**/*",
|
|
71
|
+
"core/migrations/**/*"
|
|
72
|
+
])
|
|
73
|
+
|
|
74
|
+
# Theme paths
|
|
75
|
+
theme = scope.get("theme")
|
|
76
|
+
if theme and theme != False:
|
|
77
|
+
allowed_paths.append(f"contents/themes/{theme}/**/*")
|
|
78
|
+
|
|
79
|
+
# Plugin paths
|
|
80
|
+
plugins = scope.get("plugins")
|
|
81
|
+
if isinstance(plugins, list):
|
|
82
|
+
for plugin in plugins:
|
|
83
|
+
allowed_paths.append(f"contents/plugins/{plugin}/**/*")
|
|
84
|
+
|
|
85
|
+
# Exceptions
|
|
86
|
+
exceptions = scope_config.get("exceptions", [])
|
|
87
|
+
allowed_paths.extend(exceptions)
|
|
88
|
+
|
|
89
|
+
# Also check allowedPaths if explicitly defined
|
|
90
|
+
explicit_paths = scope_config.get("allowedPaths", [])
|
|
91
|
+
allowed_paths.extend(explicit_paths)
|
|
92
|
+
|
|
93
|
+
return allowed_paths
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def matches_pattern(file_path: str, pattern: str) -> bool:
|
|
97
|
+
"""Check if file path matches glob pattern."""
|
|
98
|
+
# Normalize paths
|
|
99
|
+
file_path = file_path.replace("\\", "/")
|
|
100
|
+
pattern = pattern.replace("\\", "/")
|
|
101
|
+
|
|
102
|
+
# Handle ** patterns
|
|
103
|
+
if "**" in pattern:
|
|
104
|
+
# Split pattern into parts
|
|
105
|
+
parts = pattern.split("**")
|
|
106
|
+
if len(parts) == 2:
|
|
107
|
+
prefix, suffix = parts
|
|
108
|
+
prefix = prefix.rstrip("/")
|
|
109
|
+
suffix = suffix.lstrip("/")
|
|
110
|
+
|
|
111
|
+
# Check if file starts with prefix
|
|
112
|
+
if prefix and not file_path.startswith(prefix):
|
|
113
|
+
return False
|
|
114
|
+
|
|
115
|
+
# If suffix is "*", match anything after prefix
|
|
116
|
+
if suffix == "*" or suffix == "/*":
|
|
117
|
+
return file_path.startswith(prefix)
|
|
118
|
+
|
|
119
|
+
# Check if file ends with suffix pattern
|
|
120
|
+
if suffix:
|
|
121
|
+
remaining = file_path[len(prefix):].lstrip("/")
|
|
122
|
+
return fnmatch.fnmatch(remaining, f"**/{suffix}") or fnmatch.fnmatch(remaining, suffix)
|
|
123
|
+
|
|
124
|
+
return file_path.startswith(prefix)
|
|
125
|
+
|
|
126
|
+
# Standard glob matching
|
|
127
|
+
return fnmatch.fnmatch(file_path, pattern)
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def validate_files(files: List[str], allowed_paths: List[str]) -> Dict[str, List[str]]:
|
|
131
|
+
"""Validate files against allowed paths."""
|
|
132
|
+
valid_files = []
|
|
133
|
+
violations = []
|
|
134
|
+
|
|
135
|
+
for file_path in files:
|
|
136
|
+
# Normalize path
|
|
137
|
+
file_path = file_path.strip()
|
|
138
|
+
if not file_path:
|
|
139
|
+
continue
|
|
140
|
+
|
|
141
|
+
# Check against all allowed patterns
|
|
142
|
+
is_allowed = False
|
|
143
|
+
for pattern in allowed_paths:
|
|
144
|
+
if matches_pattern(file_path, pattern):
|
|
145
|
+
is_allowed = True
|
|
146
|
+
break
|
|
147
|
+
|
|
148
|
+
if is_allowed:
|
|
149
|
+
valid_files.append(file_path)
|
|
150
|
+
else:
|
|
151
|
+
violations.append(file_path)
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
"valid": valid_files,
|
|
155
|
+
"violations": violations
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def get_git_changed_files(project_root: Path) -> List[str]:
|
|
160
|
+
"""Get list of changed files from git."""
|
|
161
|
+
try:
|
|
162
|
+
# Get staged and unstaged changes
|
|
163
|
+
result = subprocess.run(
|
|
164
|
+
["git", "diff", "--name-only", "HEAD"],
|
|
165
|
+
cwd=project_root,
|
|
166
|
+
capture_output=True,
|
|
167
|
+
text=True
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
if result.returncode != 0:
|
|
171
|
+
# Try without HEAD (for initial commits)
|
|
172
|
+
result = subprocess.run(
|
|
173
|
+
["git", "status", "--porcelain"],
|
|
174
|
+
cwd=project_root,
|
|
175
|
+
capture_output=True,
|
|
176
|
+
text=True
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
if result.returncode != 0:
|
|
180
|
+
print("Error: Could not get git changes")
|
|
181
|
+
return []
|
|
182
|
+
|
|
183
|
+
# Parse porcelain output
|
|
184
|
+
files = []
|
|
185
|
+
for line in result.stdout.strip().split("\n"):
|
|
186
|
+
if line:
|
|
187
|
+
# Format: XY filename
|
|
188
|
+
files.append(line[3:].strip())
|
|
189
|
+
return files
|
|
190
|
+
|
|
191
|
+
return [f.strip() for f in result.stdout.strip().split("\n") if f.strip()]
|
|
192
|
+
|
|
193
|
+
except FileNotFoundError:
|
|
194
|
+
print("Error: git not found")
|
|
195
|
+
return []
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def print_scope_summary(scope_config: Dict[str, Any]) -> None:
|
|
199
|
+
"""Print a summary of the scope configuration."""
|
|
200
|
+
scope = scope_config.get("scope", {})
|
|
201
|
+
|
|
202
|
+
print("\n" + "=" * 60)
|
|
203
|
+
print("SCOPE CONFIGURATION")
|
|
204
|
+
print("=" * 60)
|
|
205
|
+
|
|
206
|
+
print(f"\nSession: {scope_config.get('session', 'Unknown')}")
|
|
207
|
+
print(f"Defined by: {scope_config.get('definedBy', 'Unknown')}")
|
|
208
|
+
print(f"Date: {scope_config.get('date', 'Unknown')}")
|
|
209
|
+
|
|
210
|
+
print("\nScope Settings:")
|
|
211
|
+
print(f" Core: {'ALLOWED' if scope.get('core') else 'DENIED'}")
|
|
212
|
+
print(f" Theme: {scope.get('theme') or 'NONE'}")
|
|
213
|
+
print(f" Plugins: {scope.get('plugins') if isinstance(scope.get('plugins'), list) else 'NONE'}")
|
|
214
|
+
|
|
215
|
+
exceptions = scope_config.get("exceptions", [])
|
|
216
|
+
if exceptions:
|
|
217
|
+
print(f"\nExceptions ({len(exceptions)}):")
|
|
218
|
+
for exc in exceptions:
|
|
219
|
+
print(f" + {exc}")
|
|
220
|
+
|
|
221
|
+
|
|
222
|
+
def print_validation_result(result: Dict[str, List[str]], allowed_paths: List[str]) -> None:
|
|
223
|
+
"""Print validation results."""
|
|
224
|
+
violations = result["violations"]
|
|
225
|
+
valid_files = result["valid"]
|
|
226
|
+
|
|
227
|
+
print("\n" + "=" * 60)
|
|
228
|
+
print("VALIDATION RESULT")
|
|
229
|
+
print("=" * 60)
|
|
230
|
+
|
|
231
|
+
if violations:
|
|
232
|
+
print("\n SCOPE VIOLATIONS DETECTED")
|
|
233
|
+
print("-" * 40)
|
|
234
|
+
for file in violations:
|
|
235
|
+
print(f" {file}")
|
|
236
|
+
|
|
237
|
+
print("\nAllowed Paths:")
|
|
238
|
+
for path in allowed_paths:
|
|
239
|
+
print(f" {path}")
|
|
240
|
+
|
|
241
|
+
print("\nRequired Actions:")
|
|
242
|
+
print(" 1. Revert modifications to files outside scope")
|
|
243
|
+
print(" 2. OR request scope expansion via /task:scope-change")
|
|
244
|
+
print(" 3. OR move logic to an allowed path")
|
|
245
|
+
else:
|
|
246
|
+
print("\n All files are within scope")
|
|
247
|
+
print(f"\nValidated {len(valid_files)} file(s)")
|
|
248
|
+
|
|
249
|
+
if valid_files:
|
|
250
|
+
print("\nValid Files:")
|
|
251
|
+
for file in valid_files:
|
|
252
|
+
print(f" {file}")
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
def main():
|
|
256
|
+
parser = argparse.ArgumentParser(
|
|
257
|
+
description="Validate file modifications against session scope",
|
|
258
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
259
|
+
epilog="""
|
|
260
|
+
Examples:
|
|
261
|
+
python validate-scope.py --session ".claude/sessions/2025-12-30-feature-v1"
|
|
262
|
+
python validate-scope.py -s ".claude/sessions/2025-12-30-feature-v1" --files "core/lib/x.ts"
|
|
263
|
+
python validate-scope.py -s ".claude/sessions/2025-12-30-feature-v1" --git
|
|
264
|
+
"""
|
|
265
|
+
)
|
|
266
|
+
|
|
267
|
+
parser.add_argument(
|
|
268
|
+
"-s", "--session",
|
|
269
|
+
required=True,
|
|
270
|
+
help="Path to session folder (relative to project root)"
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
parser.add_argument(
|
|
274
|
+
"-f", "--files",
|
|
275
|
+
help="Comma-separated list of files to validate"
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
parser.add_argument(
|
|
279
|
+
"-g", "--git",
|
|
280
|
+
action="store_true",
|
|
281
|
+
help="Validate git changed files instead of explicit list"
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
parser.add_argument(
|
|
285
|
+
"-q", "--quiet",
|
|
286
|
+
action="store_true",
|
|
287
|
+
help="Only output violations (exit code indicates result)"
|
|
288
|
+
)
|
|
289
|
+
|
|
290
|
+
parser.add_argument(
|
|
291
|
+
"--json",
|
|
292
|
+
action="store_true",
|
|
293
|
+
help="Output results as JSON"
|
|
294
|
+
)
|
|
295
|
+
|
|
296
|
+
args = parser.parse_args()
|
|
297
|
+
|
|
298
|
+
project_root = get_project_root()
|
|
299
|
+
session_path = project_root / args.session
|
|
300
|
+
|
|
301
|
+
# Validate session exists
|
|
302
|
+
if not session_path.exists():
|
|
303
|
+
print(f"Error: Session not found: {session_path}")
|
|
304
|
+
sys.exit(1)
|
|
305
|
+
|
|
306
|
+
# Read scope configuration
|
|
307
|
+
scope_config = read_scope_config(session_path)
|
|
308
|
+
|
|
309
|
+
# Build allowed paths
|
|
310
|
+
allowed_paths = build_allowed_paths(scope_config)
|
|
311
|
+
|
|
312
|
+
# Get files to validate
|
|
313
|
+
if args.git:
|
|
314
|
+
files = get_git_changed_files(project_root)
|
|
315
|
+
if not files:
|
|
316
|
+
if not args.quiet:
|
|
317
|
+
print("No git changes detected")
|
|
318
|
+
sys.exit(0)
|
|
319
|
+
elif args.files:
|
|
320
|
+
files = [f.strip() for f in args.files.split(",") if f.strip()]
|
|
321
|
+
else:
|
|
322
|
+
print("Error: Either --files or --git must be specified")
|
|
323
|
+
sys.exit(1)
|
|
324
|
+
|
|
325
|
+
# Validate files
|
|
326
|
+
result = validate_files(files, allowed_paths)
|
|
327
|
+
|
|
328
|
+
# Output results
|
|
329
|
+
if args.json:
|
|
330
|
+
output = {
|
|
331
|
+
"session": str(session_path),
|
|
332
|
+
"scope": scope_config.get("scope", {}),
|
|
333
|
+
"allowedPaths": allowed_paths,
|
|
334
|
+
"result": {
|
|
335
|
+
"valid": len(result["violations"]) == 0,
|
|
336
|
+
"validFiles": result["valid"],
|
|
337
|
+
"violations": result["violations"]
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
print(json.dumps(output, indent=2))
|
|
341
|
+
elif args.quiet:
|
|
342
|
+
if result["violations"]:
|
|
343
|
+
for violation in result["violations"]:
|
|
344
|
+
print(violation)
|
|
345
|
+
else:
|
|
346
|
+
print_scope_summary(scope_config)
|
|
347
|
+
print_validation_result(result, allowed_paths)
|
|
348
|
+
|
|
349
|
+
# Exit with appropriate code
|
|
350
|
+
if result["violations"]:
|
|
351
|
+
sys.exit(1) # Violations found
|
|
352
|
+
else:
|
|
353
|
+
sys.exit(0) # All valid
|
|
354
|
+
|
|
355
|
+
|
|
356
|
+
if __name__ == "__main__":
|
|
357
|
+
main()
|