ai-qakit 1.0.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/README.md +240 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +208 -0
- package/dist/commands/clean.d.ts +10 -0
- package/dist/commands/clean.js +53 -0
- package/dist/commands/init.d.ts +20 -0
- package/dist/commands/init.js +195 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +2 -0
- package/dist/utils/file-operations.d.ts +18 -0
- package/dist/utils/file-operations.js +79 -0
- package/dist/utils/git.d.ts +4 -0
- package/dist/utils/git.js +27 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/logger.d.ts +22 -0
- package/dist/utils/logger.js +23 -0
- package/dist/utils/manifest.d.ts +37 -0
- package/dist/utils/manifest.js +120 -0
- package/dist/utils/package.d.ts +19 -0
- package/dist/utils/package.js +47 -0
- package/package.json +61 -0
- package/templates/.antigravity/agents/qa-agents.yaml +50 -0
- package/templates/.antigravity/prompts/qa-prompts.md +56 -0
- package/templates/.claude/.scoutignore +33 -0
- package/templates/.claude/CHANGELOG.md +587 -0
- package/templates/.claude/CLAUDE.md +335 -0
- package/templates/.claude/README.md +428 -0
- package/templates/.claude/TROUBLESHOOTING.md +789 -0
- package/templates/.claude/agents/accessibility-tester.md +29 -0
- package/templates/.claude/agents/api-tester.md +29 -0
- package/templates/.claude/agents/automation-engineer.md +30 -0
- package/templates/.claude/agents/brainstormer.md +123 -0
- package/templates/.claude/agents/bug-hunter.md +30 -0
- package/templates/.claude/agents/claude-optimizer.md +221 -0
- package/templates/.claude/agents/code-reviewer.md +176 -0
- package/templates/.claude/agents/code-simplifier.md +103 -0
- package/templates/.claude/agents/coder.md +163 -0
- package/templates/.claude/agents/context-manager.md +110 -0
- package/templates/.claude/agents/cunningham.md +134 -0
- package/templates/.claude/agents/database-admin.md +112 -0
- package/templates/.claude/agents/debugger.md +156 -0
- package/templates/.claude/agents/docs-manager.md +181 -0
- package/templates/.claude/agents/e2e-tester.md +30 -0
- package/templates/.claude/agents/performance-engineer.md +48 -0
- package/templates/.claude/agents/performance-tester.md +29 -0
- package/templates/.claude/agents/planner.md +72 -0
- package/templates/.claude/agents/project-manager.md +152 -0
- package/templates/.claude/agents/qa-scout.md +29 -0
- package/templates/.claude/agents/quality-engineer.md +48 -0
- package/templates/.claude/agents/readme-writer.md +67 -0
- package/templates/.claude/agents/researcher.md +57 -0
- package/templates/.claude/agents/scout.md +125 -0
- package/templates/.claude/agents/security-agent.md +564 -0
- package/templates/.claude/agents/security-tester.md +29 -0
- package/templates/.claude/agents/storage-path-convention.md +102 -0
- package/templates/.claude/agents/tattletale-reporter.md +77 -0
- package/templates/.claude/agents/test-architect.md +32 -0
- package/templates/.claude/agents/test-reviewer.md +29 -0
- package/templates/.claude/agents/tester.md +286 -0
- package/templates/.claude/agents/typescript-pro.md +280 -0
- package/templates/.claude/agents/ui-ux-designer.md +256 -0
- package/templates/.claude/commands/devkit/analyze.md +92 -0
- package/templates/.claude/commands/devkit/ask.md +269 -0
- package/templates/.claude/commands/devkit/backend/create-api.md +220 -0
- package/templates/.claude/commands/devkit/backend/create-migration.md +130 -0
- package/templates/.claude/commands/devkit/backend/create-schema.md +158 -0
- package/templates/.claude/commands/devkit/backend/design-graphql-api.md +302 -0
- package/templates/.claude/commands/devkit/backend/design-rest-api.md +192 -0
- package/templates/.claude/commands/devkit/config/bootstrap.md +137 -0
- package/templates/.claude/commands/devkit/config/init-backend.md +233 -0
- package/templates/.claude/commands/devkit/config/init-frontend.md +911 -0
- package/templates/.claude/commands/devkit/create-test.md +165 -0
- package/templates/.claude/commands/devkit/debug.md +481 -0
- package/templates/.claude/commands/devkit/docs/capture-knowledge.md +424 -0
- package/templates/.claude/commands/devkit/docs/init.md +203 -0
- package/templates/.claude/commands/devkit/docs/summarize.md +33 -0
- package/templates/.claude/commands/devkit/docs/update.md +207 -0
- package/templates/.claude/commands/devkit/fix/fast.md +22 -0
- package/templates/.claude/commands/devkit/fix/hard.md +44 -0
- package/templates/.claude/commands/devkit/fix.md +16 -0
- package/templates/.claude/commands/devkit/frontend/create-api-integration.md +507 -0
- package/templates/.claude/commands/devkit/frontend/create-component.md +461 -0
- package/templates/.claude/commands/devkit/frontend/create-form.md +804 -0
- package/templates/.claude/commands/devkit/frontend/create-page.md +484 -0
- package/templates/.claude/commands/devkit/frontend/create-storybook.md +899 -0
- package/templates/.claude/commands/devkit/frontend/create-table.md +1035 -0
- package/templates/.claude/commands/devkit/help.md +148 -0
- package/templates/.claude/commands/devkit/refactor.md +510 -0
- package/templates/.claude/commands/devkit/research.md +394 -0
- package/templates/.claude/commands/devkit/review/code.md +349 -0
- package/templates/.claude/commands/devkit/review/codebase.md +51 -0
- package/templates/.claude/commands/devkit/scout.md +312 -0
- package/templates/.claude/commands/devkit/skill/add.md +29 -0
- package/templates/.claude/commands/devkit/skill/create.md +22 -0
- package/templates/.claude/commands/devkit/skill/optimize.md +34 -0
- package/templates/.claude/commands/devkit/solve.md +364 -0
- package/templates/.claude/commands/devkit/spec/brainstorm.md +309 -0
- package/templates/.claude/commands/devkit/spec/capture-requirement.md +202 -0
- package/templates/.claude/commands/devkit/spec/cook.md +157 -0
- package/templates/.claude/commands/devkit/spec/deep-capture-requirement.md +393 -0
- package/templates/.claude/commands/devkit/spec/implement.md +350 -0
- package/templates/.claude/commands/devkit/spec/plan.md +269 -0
- package/templates/.claude/commands/devkit/spec/task.md +156 -0
- package/templates/.claude/commands/devkit/spec/test.md +259 -0
- package/templates/.claude/commands/devkit/spec/update-plan.md +263 -0
- package/templates/.claude/commands/devkit/spec/update-requirement.md +239 -0
- package/templates/.claude/commands/devkit/spec-lite/do.md +280 -0
- package/templates/.claude/commands/devkit/spec-lite/plan.md +236 -0
- package/templates/.claude/commands/devkit/spec-lite/task.md +141 -0
- package/templates/.claude/commands/devkit/spec-lite/test.md +171 -0
- package/templates/.claude/commands/devkit/spec-lite/update-plan.md +154 -0
- package/templates/.claude/commands/qakit/a11y/audit.md +38 -0
- package/templates/.claude/commands/qakit/a11y/fix.md +22 -0
- package/templates/.claude/commands/qakit/analyze/full.md +459 -0
- package/templates/.claude/commands/qakit/analyze/quick.md +84 -0
- package/templates/.claude/commands/qakit/api/analyze.md +34 -0
- package/templates/.claude/commands/qakit/api/contract.md +22 -0
- package/templates/.claude/commands/qakit/api/generate.md +20 -0
- package/templates/.claude/commands/qakit/api/run.md +16 -0
- package/templates/.claude/commands/qakit/ask.md +57 -0
- package/templates/.claude/commands/qakit/automation/fix-flaky.md +233 -0
- package/templates/.claude/commands/qakit/automation/health-check.md +192 -0
- package/templates/.claude/commands/qakit/automation/maintenance.md +311 -0
- package/templates/.claude/commands/qakit/automation/scaffold-playwright.md +689 -0
- package/templates/.claude/commands/qakit/automation/scaffold-webdriverio.md +802 -0
- package/templates/.claude/commands/qakit/automation/scaffold.md +447 -0
- package/templates/.claude/commands/qakit/automation/update-deps.md +219 -0
- package/templates/.claude/commands/qakit/bug/investigate.md +96 -0
- package/templates/.claude/commands/qakit/bug/report.md +147 -0
- package/templates/.claude/commands/qakit/bug/reproduce.md +101 -0
- package/templates/.claude/commands/qakit/bug/triage.md +101 -0
- package/templates/.claude/commands/qakit/bug/verify.md +106 -0
- package/templates/.claude/commands/qakit/config/init-qa.md +107 -0
- package/templates/.claude/commands/qakit/confluence/fetch.md +192 -0
- package/templates/.claude/commands/qakit/confluence/search.md +201 -0
- package/templates/.claude/commands/qakit/docs/capture-test-knowledge.md +451 -0
- package/templates/.claude/commands/qakit/docs/update-changelog.md +207 -0
- package/templates/.claude/commands/qakit/docs/update-readme.md +296 -0
- package/templates/.claude/commands/qakit/e2e/generate.md +18 -0
- package/templates/.claude/commands/qakit/e2e/page-object.md +37 -0
- package/templates/.claude/commands/qakit/e2e/run.md +17 -0
- package/templates/.claude/commands/qakit/e2e/scenario.md +20 -0
- package/templates/.claude/commands/qakit/feature/create.md +178 -0
- package/templates/.claude/commands/qakit/feature/enrich-api.md +352 -0
- package/templates/.claude/commands/qakit/feature/enrich-tests.md +243 -0
- package/templates/.claude/commands/qakit/feature/enrich-ui.md +217 -0
- package/templates/.claude/commands/qakit/feature/enrich.md +246 -0
- package/templates/.claude/commands/qakit/feature/from-jira.md +184 -0
- package/templates/.claude/commands/qakit/feature/update.md +136 -0
- package/templates/.claude/commands/qakit/help.md +98 -0
- package/templates/.claude/commands/qakit/jira/analyze.md +329 -0
- package/templates/.claude/commands/qakit/jira/create-tests.md +319 -0
- package/templates/.claude/commands/qakit/kb/add-design.md +258 -0
- package/templates/.claude/commands/qakit/kb/capture.md +325 -0
- package/templates/.claude/commands/qakit/kb/clean.md +157 -0
- package/templates/.claude/commands/qakit/kb/dedupe.md +281 -0
- package/templates/.claude/commands/qakit/kb/init.md +234 -0
- package/templates/.claude/commands/qakit/kb/link.md +124 -0
- package/templates/.claude/commands/qakit/kb/list.md +185 -0
- package/templates/.claude/commands/qakit/kb/resolve.md +234 -0
- package/templates/.claude/commands/qakit/kb/search.md +202 -0
- package/templates/.claude/commands/qakit/kb/sync-confluence.md +474 -0
- package/templates/.claude/commands/qakit/metrics/coverage.md +46 -0
- package/templates/.claude/commands/qakit/metrics/dashboard.md +62 -0
- package/templates/.claude/commands/qakit/metrics/trends.md +42 -0
- package/templates/.claude/commands/qakit/perf/analyze.md +31 -0
- package/templates/.claude/commands/qakit/perf/plan.md +18 -0
- package/templates/.claude/commands/qakit/perf/run.md +17 -0
- package/templates/.claude/commands/qakit/perf/script.md +38 -0
- package/templates/.claude/commands/qakit/rca/analyze.md +166 -0
- package/templates/.claude/commands/qakit/regression/analyze.md +36 -0
- package/templates/.claude/commands/qakit/regression/report.md +30 -0
- package/templates/.claude/commands/qakit/regression/run.md +17 -0
- package/templates/.claude/commands/qakit/regression/select.md +34 -0
- package/templates/.claude/commands/qakit/research/deep.md +328 -0
- package/templates/.claude/commands/qakit/review/coverage.md +116 -0
- package/templates/.claude/commands/qakit/review/quality-gate.md +126 -0
- package/templates/.claude/commands/qakit/review/test-code.md +320 -0
- package/templates/.claude/commands/qakit/review/test-review.md +109 -0
- package/templates/.claude/commands/qakit/scout.md +195 -0
- package/templates/.claude/commands/qakit/setup/confluence.md +140 -0
- package/templates/.claude/commands/qakit/setup/jira.md +154 -0
- package/templates/.claude/commands/qakit/spec/analyze.md +46 -0
- package/templates/.claude/commands/qakit/spec/cook.md +344 -0
- package/templates/.claude/commands/qakit/spec/execute.md +38 -0
- package/templates/.claude/commands/qakit/spec/plan.md +46 -0
- package/templates/.claude/commands/qakit/spec/report.md +40 -0
- package/templates/.claude/commands/qakit/spec/task.md +40 -0
- package/templates/.claude/commands/qakit/test/create-test-case.md +151 -0
- package/templates/.claude/commands/qakit/test/create-test-suite.md +84 -0
- package/templates/.claude/commands/qakit/test/generate-api-tests.md +82 -0
- package/templates/.claude/commands/qakit/test/generate-data.md +114 -0
- package/templates/.claude/commands/qakit/test/generate-e2e.md +99 -0
- package/templates/.claude/commands/qakit/test/generate-ui-tests.md +97 -0
- package/templates/.claude/commands/qakit/testmo/report.md +22 -0
- package/templates/.claude/commands/qakit/testmo/sync.md +31 -0
- package/templates/.claude/doc-templates/bug-report-template.md +247 -0
- package/templates/.claude/doc-templates/code-standards.md +486 -0
- package/templates/.claude/doc-templates/codebase-summary.md +426 -0
- package/templates/.claude/doc-templates/development-rules.md +553 -0
- package/templates/.claude/doc-templates/feature-spec-template.md +167 -0
- package/templates/.claude/doc-templates/performance-report-template.md +49 -0
- package/templates/.claude/doc-templates/project-overview-pdr.md +494 -0
- package/templates/.claude/doc-templates/quality-report-template.md +44 -0
- package/templates/.claude/doc-templates/regression-report-template.md +41 -0
- package/templates/.claude/doc-templates/system-architecture.md +615 -0
- package/templates/.claude/doc-templates/technical-documents.md +127 -0
- package/templates/.claude/doc-templates/test-case-template.md +198 -0
- package/templates/.claude/doc-templates/test-plan-template.md +366 -0
- package/templates/.claude/doc-templates/test-summary-report.md +401 -0
- package/templates/.claude/hooks/dev-rules-reminder.cjs +101 -0
- package/templates/.claude/hooks/dev-rules-reminder.js +96 -0
- package/templates/.claude/hooks/executable_force-agent-skills-eval.sh +38 -0
- package/templates/.claude/hooks/scout-block/scout-block.ps1 +92 -0
- package/templates/.claude/hooks/scout-block/scout-block.sh +119 -0
- package/templates/.claude/hooks/scout-block.cjs +100 -0
- package/templates/.claude/hooks/scout-block.js +104 -0
- package/templates/.claude/repomix.config.json +34 -0
- package/templates/.claude/rules/01-core-principles.md +81 -0
- package/templates/.claude/rules/02-naming-and-constants.md +115 -0
- package/templates/.claude/rules/03-function-design.md +167 -0
- package/templates/.claude/rules/04-error-handling.md +83 -0
- package/templates/.claude/rules/05-testing.md +86 -0
- package/templates/.claude/rules/06-code-quality.md +107 -0
- package/templates/.claude/rules/07-architecture.md +105 -0
- package/templates/.claude/rules/08-performance-security.md +93 -0
- package/templates/.claude/rules/index.md +77 -0
- package/templates/.claude/scripts/README.md +153 -0
- package/templates/.claude/scripts/commands_data.yaml +336 -0
- package/templates/.claude/scripts/generate_catalogs.py +163 -0
- package/templates/.claude/scripts/helper.py +723 -0
- package/templates/.claude/scripts/scan_commands.py +101 -0
- package/templates/.claude/scripts/scan_skills.py +187 -0
- package/templates/.claude/scripts/skills_data.yaml +348 -0
- package/templates/.claude/settings.json +54 -0
- package/templates/.claude/settings.local.README.md +650 -0
- package/templates/.claude/settings.local.json +41 -0
- package/templates/.claude/setup/dependencies/setup-claude-mem.ps1 +534 -0
- package/templates/.claude/setup/dependencies/setup-claude-mem.sh +477 -0
- package/templates/.claude/setup/dependencies/setup-mcp-servers.ps1 +562 -0
- package/templates/.claude/setup/dependencies/setup-mcp-servers.sh +511 -0
- package/templates/.claude/setup/jira-env.example.sh +22 -0
- package/templates/.claude/setup/setup.ps1 +240 -0
- package/templates/.claude/setup/setup.sh +222 -0
- package/templates/.claude/skills/accessibility-testing/SKILL.md +34 -0
- package/templates/.claude/skills/aesthetic/SKILL.md +121 -0
- package/templates/.claude/skills/aesthetic/assets/design-guideline-template.md +163 -0
- package/templates/.claude/skills/aesthetic/assets/design-story-template.md +135 -0
- package/templates/.claude/skills/aesthetic/references/design-principles.md +62 -0
- package/templates/.claude/skills/aesthetic/references/design-resources.md +75 -0
- package/templates/.claude/skills/aesthetic/references/micro-interactions.md +53 -0
- package/templates/.claude/skills/aesthetic/references/storytelling-design.md +50 -0
- package/templates/.claude/skills/aesthetic/skill-creator/LICENSE.txt +202 -0
- package/templates/.claude/skills/aesthetic/skill-creator/SKILL.md +266 -0
- package/templates/.claude/skills/aesthetic/skill-creator/scripts/init_skill.py +303 -0
- package/templates/.claude/skills/aesthetic/skill-creator/scripts/package_skill.py +110 -0
- package/templates/.claude/skills/aesthetic/skill-creator/scripts/quick_validate.py +65 -0
- package/templates/.claude/skills/agent_skills_spec.md +51 -0
- package/templates/.claude/skills/ai-multimodal/.env.example +185 -0
- package/templates/.claude/skills/ai-multimodal/SKILL.md +69 -0
- package/templates/.claude/skills/ai-multimodal/references/audio-processing.md +386 -0
- package/templates/.claude/skills/ai-multimodal/references/image-generation.md +722 -0
- package/templates/.claude/skills/ai-multimodal/references/video-analysis.md +515 -0
- package/templates/.claude/skills/ai-multimodal/references/video-generation.md +457 -0
- package/templates/.claude/skills/ai-multimodal/references/vision-understanding.md +492 -0
- package/templates/.claude/skills/ai-multimodal/scripts/check_setup.py +299 -0
- package/templates/.claude/skills/ai-multimodal/scripts/document_converter.py +395 -0
- package/templates/.claude/skills/ai-multimodal/scripts/gemini_batch_process.py +1067 -0
- package/templates/.claude/skills/ai-multimodal/scripts/media_optimizer.py +506 -0
- package/templates/.claude/skills/ai-multimodal/scripts/requirements.txt +26 -0
- package/templates/.claude/skills/ai-multimodal/scripts/tests/requirements.txt +20 -0
- package/templates/.claude/skills/ai-multimodal/scripts/tests/test_document_converter.py +74 -0
- package/templates/.claude/skills/ai-multimodal/scripts/tests/test_gemini_batch_process.py +362 -0
- package/templates/.claude/skills/ai-multimodal/scripts/tests/test_media_optimizer.py +373 -0
- package/templates/.claude/skills/api-design-principles/SKILL.md +527 -0
- package/templates/.claude/skills/api-design-principles/assets/api-design-checklist.md +136 -0
- package/templates/.claude/skills/api-design-principles/assets/rest-api-template.py +165 -0
- package/templates/.claude/skills/api-design-principles/references/graphql-schema-design.md +566 -0
- package/templates/.claude/skills/api-design-principles/references/rest-best-practices.md +385 -0
- package/templates/.claude/skills/api-testing/SKILL.md +24 -0
- package/templates/.claude/skills/backend-development/SKILL.md +95 -0
- package/templates/.claude/skills/backend-development/references/backend-api-design.md +495 -0
- package/templates/.claude/skills/backend-development/references/backend-architecture.md +454 -0
- package/templates/.claude/skills/backend-development/references/backend-code-quality.md +659 -0
- package/templates/.claude/skills/backend-development/references/backend-debugging.md +904 -0
- package/templates/.claude/skills/backend-development/references/backend-devops.md +494 -0
- package/templates/.claude/skills/backend-development/references/backend-mindset.md +387 -0
- package/templates/.claude/skills/backend-development/references/backend-performance.md +397 -0
- package/templates/.claude/skills/backend-development/references/backend-security.md +290 -0
- package/templates/.claude/skills/backend-development/references/backend-technologies.md +256 -0
- package/templates/.claude/skills/backend-development/references/backend-testing.md +429 -0
- package/templates/.claude/skills/bug-investigation/SKILL.md +24 -0
- package/templates/.claude/skills/chrome-devtools/SKILL.md +472 -0
- package/templates/.claude/skills/chrome-devtools/references/cdp-domains.md +694 -0
- package/templates/.claude/skills/chrome-devtools/references/performance-guide.md +940 -0
- package/templates/.claude/skills/chrome-devtools/references/puppeteer-reference.md +953 -0
- package/templates/.claude/skills/chrome-devtools/scripts/README.md +228 -0
- package/templates/.claude/skills/chrome-devtools/scripts/__tests__/selector.test.js +210 -0
- package/templates/.claude/skills/chrome-devtools/scripts/aria-snapshot.js +362 -0
- package/templates/.claude/skills/chrome-devtools/scripts/click.js +83 -0
- package/templates/.claude/skills/chrome-devtools/scripts/console.js +79 -0
- package/templates/.claude/skills/chrome-devtools/scripts/evaluate.js +53 -0
- package/templates/.claude/skills/chrome-devtools/scripts/fill.js +76 -0
- package/templates/.claude/skills/chrome-devtools/scripts/install-deps.sh +181 -0
- package/templates/.claude/skills/chrome-devtools/scripts/install.sh +83 -0
- package/templates/.claude/skills/chrome-devtools/scripts/lib/browser.js +217 -0
- package/templates/.claude/skills/chrome-devtools/scripts/lib/selector.js +178 -0
- package/templates/.claude/skills/chrome-devtools/scripts/navigate.js +54 -0
- package/templates/.claude/skills/chrome-devtools/scripts/network.js +106 -0
- package/templates/.claude/skills/chrome-devtools/scripts/package.json +16 -0
- package/templates/.claude/skills/chrome-devtools/scripts/performance.js +149 -0
- package/templates/.claude/skills/chrome-devtools/scripts/screenshot.js +194 -0
- package/templates/.claude/skills/chrome-devtools/scripts/select-ref.js +131 -0
- package/templates/.claude/skills/chrome-devtools/scripts/snapshot.js +135 -0
- package/templates/.claude/skills/claude-code-analyzer/SKILL.md +299 -0
- package/templates/.claude/skills/claude-code-analyzer/references/best-practices.md +290 -0
- package/templates/.claude/skills/claude-code-analyzer/scripts/analyze-claude-md.sh +239 -0
- package/templates/.claude/skills/claude-code-analyzer/scripts/analyze.sh +274 -0
- package/templates/.claude/skills/claude-code-analyzer/scripts/fetch-features.sh +69 -0
- package/templates/.claude/skills/claude-code-analyzer/scripts/github-discovery.sh +178 -0
- package/templates/.claude/skills/code-review/SKILL.md +155 -0
- package/templates/.claude/skills/code-review/references/code-review-reception.md +209 -0
- package/templates/.claude/skills/code-review/references/human-code-review-practices.md +370 -0
- package/templates/.claude/skills/code-review/references/requesting-code-review.md +105 -0
- package/templates/.claude/skills/code-review/references/verification-before-completion.md +139 -0
- package/templates/.claude/skills/command-development/README.md +272 -0
- package/templates/.claude/skills/command-development/SKILL.md +834 -0
- package/templates/.claude/skills/command-development/examples/plugin-commands.md +557 -0
- package/templates/.claude/skills/command-development/examples/simple-commands.md +504 -0
- package/templates/.claude/skills/command-development/references/advanced-workflows.md +722 -0
- package/templates/.claude/skills/command-development/references/documentation-patterns.md +739 -0
- package/templates/.claude/skills/command-development/references/frontmatter-reference.md +463 -0
- package/templates/.claude/skills/command-development/references/interactive-commands.md +920 -0
- package/templates/.claude/skills/command-development/references/marketplace-considerations.md +904 -0
- package/templates/.claude/skills/command-development/references/plugin-features-reference.md +609 -0
- package/templates/.claude/skills/command-development/references/testing-strategies.md +702 -0
- package/templates/.claude/skills/confluence-integration/confluence-integration.md +260 -0
- package/templates/.claude/skills/databases/SKILL.md +232 -0
- package/templates/.claude/skills/databases/references/mongodb-aggregation.md +447 -0
- package/templates/.claude/skills/databases/references/mongodb-atlas.md +465 -0
- package/templates/.claude/skills/databases/references/mongodb-crud.md +408 -0
- package/templates/.claude/skills/databases/references/mongodb-indexing.md +442 -0
- package/templates/.claude/skills/databases/references/postgresql-administration.md +594 -0
- package/templates/.claude/skills/databases/references/postgresql-performance.md +527 -0
- package/templates/.claude/skills/databases/references/postgresql-psql-cli.md +467 -0
- package/templates/.claude/skills/databases/references/postgresql-queries.md +475 -0
- package/templates/.claude/skills/databases/scripts/db_backup.py +502 -0
- package/templates/.claude/skills/databases/scripts/db_migrate.py +414 -0
- package/templates/.claude/skills/databases/scripts/db_performance_check.py +444 -0
- package/templates/.claude/skills/databases/scripts/dot_coverage +0 -0
- package/templates/.claude/skills/databases/scripts/requirements.txt +20 -0
- package/templates/.claude/skills/databases/scripts/tests/coverage-db.json +1 -0
- package/templates/.claude/skills/databases/scripts/tests/requirements.txt +4 -0
- package/templates/.claude/skills/databases/scripts/tests/test_db_backup.py +340 -0
- package/templates/.claude/skills/databases/scripts/tests/test_db_migrate.py +277 -0
- package/templates/.claude/skills/databases/scripts/tests/test_db_performance_check.py +370 -0
- package/templates/.claude/skills/debugging/SKILL.md +84 -0
- package/templates/.claude/skills/debugging/references/defense-in-depth.md +124 -0
- package/templates/.claude/skills/debugging/references/root-cause-tracing.md +122 -0
- package/templates/.claude/skills/debugging/references/systematic-debugging.md +102 -0
- package/templates/.claude/skills/debugging/references/verification.md +123 -0
- package/templates/.claude/skills/debugging/scripts/find-polluter.sh +63 -0
- package/templates/.claude/skills/debugging/scripts/find-polluter.test.md +102 -0
- package/templates/.claude/skills/docs-discovery/COMPARISON.md +313 -0
- package/templates/.claude/skills/docs-discovery/README.md +53 -0
- package/templates/.claude/skills/docs-discovery/REFACTORING_SUMMARY.md +144 -0
- package/templates/.claude/skills/docs-discovery/SKILL.md +64 -0
- package/templates/.claude/skills/docs-discovery/archive/dot_env.example +15 -0
- package/templates/.claude/skills/docs-discovery/archive/package.json +24 -0
- package/templates/.claude/skills/docs-discovery/archive/references/advanced.md +79 -0
- package/templates/.claude/skills/docs-discovery/archive/references/context7-patterns.md +68 -0
- package/templates/.claude/skills/docs-discovery/archive/references/errors.md +68 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/analyze-llms-txt.js +211 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/detect-topic.js +172 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/fetch-docs.js +213 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/tests/run-tests.js +72 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/tests/test-analyze-llms.js +119 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/tests/test-detect-topic.js +112 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/tests/test-fetch-docs.js +84 -0
- package/templates/.claude/skills/docs-discovery/archive/scripts/utils/env-loader.js +94 -0
- package/templates/.claude/skills/docs-discovery/archive/workflows/library-search.md +87 -0
- package/templates/.claude/skills/docs-discovery/archive/workflows/repo-analysis.md +91 -0
- package/templates/.claude/skills/docs-discovery/archive/workflows/topic-search.md +77 -0
- package/templates/.claude/skills/docs-seeker/.env.example +15 -0
- package/templates/.claude/skills/docs-seeker/SKILL.md +97 -0
- package/templates/.claude/skills/docs-seeker/package.json +24 -0
- package/templates/.claude/skills/docs-seeker/references/advanced.md +79 -0
- package/templates/.claude/skills/docs-seeker/references/context7-patterns.md +68 -0
- package/templates/.claude/skills/docs-seeker/references/errors.md +68 -0
- package/templates/.claude/skills/docs-seeker/scripts/analyze-llms-txt.js +211 -0
- package/templates/.claude/skills/docs-seeker/scripts/detect-topic.js +172 -0
- package/templates/.claude/skills/docs-seeker/scripts/fetch-docs.js +213 -0
- package/templates/.claude/skills/docs-seeker/scripts/tests/run-tests.js +72 -0
- package/templates/.claude/skills/docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
- package/templates/.claude/skills/docs-seeker/scripts/tests/test-detect-topic.js +112 -0
- package/templates/.claude/skills/docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
- package/templates/.claude/skills/docs-seeker/scripts/utils/env-loader.js +94 -0
- package/templates/.claude/skills/docs-seeker/workflows/library-search.md +87 -0
- package/templates/.claude/skills/docs-seeker/workflows/repo-analysis.md +91 -0
- package/templates/.claude/skills/docs-seeker/workflows/topic-search.md +77 -0
- package/templates/.claude/skills/document-skills/docx/LICENSE.txt +30 -0
- package/templates/.claude/skills/document-skills/docx/SKILL.md +197 -0
- package/templates/.claude/skills/document-skills/docx/docx-js.md +350 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/mce/mc.xsd +75 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/pack.py +159 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/unpack.py +29 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/validate.py +69 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/validation/base.py +951 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/.claude/skills/document-skills/docx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/.claude/skills/document-skills/docx/ooxml.md +610 -0
- package/templates/.claude/skills/document-skills/docx/scripts/__init__.py +1 -0
- package/templates/.claude/skills/document-skills/docx/scripts/document.py +1276 -0
- package/templates/.claude/skills/document-skills/docx/scripts/templates/comments.xml +3 -0
- package/templates/.claude/skills/document-skills/docx/scripts/templates/commentsExtended.xml +3 -0
- package/templates/.claude/skills/document-skills/docx/scripts/templates/commentsExtensible.xml +3 -0
- package/templates/.claude/skills/document-skills/docx/scripts/templates/commentsIds.xml +3 -0
- package/templates/.claude/skills/document-skills/docx/scripts/templates/people.xml +3 -0
- package/templates/.claude/skills/document-skills/docx/scripts/utilities.py +374 -0
- package/templates/.claude/skills/document-skills/pdf/LICENSE.txt +30 -0
- package/templates/.claude/skills/document-skills/pdf/SKILL.md +294 -0
- package/templates/.claude/skills/document-skills/pdf/forms.md +205 -0
- package/templates/.claude/skills/document-skills/pdf/reference.md +612 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/check_bounding_boxes.py +70 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/check_bounding_boxes_test.py +226 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/check_fillable_fields.py +12 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/convert_pdf_to_images.py +35 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/create_validation_image.py +41 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/extract_form_field_info.py +152 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/fill_fillable_fields.py +114 -0
- package/templates/.claude/skills/document-skills/pdf/scripts/fill_pdf_form_with_annotations.py +108 -0
- package/templates/.claude/skills/document-skills/pptx/LICENSE.txt +30 -0
- package/templates/.claude/skills/document-skills/pptx/SKILL.md +484 -0
- package/templates/.claude/skills/document-skills/pptx/html2pptx.md +625 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chart.xsd +1499 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-chartDrawing.xsd +146 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-diagram.xsd +1085 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-lockedCanvas.xsd +11 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-main.xsd +3081 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-picture.xsd +23 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-spreadsheetDrawing.xsd +185 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/dml-wordprocessingDrawing.xsd +287 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/pml.xsd +1676 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-additionalCharacteristics.xsd +28 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-bibliography.xsd +144 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-commonSimpleTypes.xsd +174 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlDataProperties.xsd +25 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-customXmlSchemaProperties.xsd +18 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesCustom.xsd +59 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesExtended.xsd +56 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-documentPropertiesVariantTypes.xsd +195 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-math.xsd +582 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/shared-relationshipReference.xsd +25 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/sml.xsd +4439 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-main.xsd +570 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-officeDrawing.xsd +509 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-presentationDrawing.xsd +12 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-spreadsheetDrawing.xsd +108 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/vml-wordprocessingDrawing.xsd +96 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/wml.xsd +3646 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ISO-IEC29500-4_2016/xml.xsd +116 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-contentTypes.xsd +42 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-coreProperties.xsd +50 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-digSig.xsd +49 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/ecma/fouth-edition/opc-relationships.xsd +33 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/mce/mc.xsd +75 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2010.xsd +560 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2012.xsd +67 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-2018.xsd +14 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-cex-2018.xsd +20 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-cid-2016.xsd +13 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-sdtdatahash-2020.xsd +4 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/schemas/microsoft/wml-symex-2015.xsd +8 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/pack.py +159 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/unpack.py +29 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/validate.py +69 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/validation/__init__.py +15 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/validation/base.py +951 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/validation/docx.py +274 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/validation/pptx.py +315 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml/scripts/validation/redlining.py +279 -0
- package/templates/.claude/skills/document-skills/pptx/ooxml.md +427 -0
- package/templates/.claude/skills/document-skills/pptx/scripts/html2pptx.js +979 -0
- package/templates/.claude/skills/document-skills/pptx/scripts/inventory.py +1020 -0
- package/templates/.claude/skills/document-skills/pptx/scripts/rearrange.py +231 -0
- package/templates/.claude/skills/document-skills/pptx/scripts/replace.py +385 -0
- package/templates/.claude/skills/document-skills/pptx/scripts/thumbnail.py +450 -0
- package/templates/.claude/skills/document-skills/xlsx/LICENSE.txt +30 -0
- package/templates/.claude/skills/document-skills/xlsx/SKILL.md +289 -0
- package/templates/.claude/skills/document-skills/xlsx/recalc.py +178 -0
- package/templates/.claude/skills/frontend-development/SKILL.md +208 -0
- package/templates/.claude/skills/frontend-development/references/accessibility.md +720 -0
- package/templates/.claude/skills/frontend-development/references/data-fetching.md +412 -0
- package/templates/.claude/skills/frontend-development/references/forms.md +809 -0
- package/templates/.claude/skills/frontend-development/references/fsd-architecture.md +442 -0
- package/templates/.claude/skills/frontend-development/references/graphql-apollo.md +731 -0
- package/templates/.claude/skills/frontend-development/references/performance.md +417 -0
- package/templates/.claude/skills/frontend-development/references/react-patterns.md +451 -0
- package/templates/.claude/skills/frontend-development/references/routing.md +436 -0
- package/templates/.claude/skills/frontend-development/references/state-management.md +510 -0
- package/templates/.claude/skills/frontend-development/references/tailwind-css.md +397 -0
- package/templates/.claude/skills/frontend-development/references/testing.md +630 -0
- package/templates/.claude/skills/frontend-development/references/typescript-standards.md +363 -0
- package/templates/.claude/skills/frontend-development/references/ui-libraries.md +586 -0
- package/templates/.claude/skills/infra-engineer/SKILL.md +642 -0
- package/templates/.claude/skills/infra-engineer/dot_env.example +76 -0
- package/templates/.claude/skills/infra-engineer/references/aws-overview.md +171 -0
- package/templates/.claude/skills/infra-engineer/references/browser-rendering.md +305 -0
- package/templates/.claude/skills/infra-engineer/references/cicd-github-actions.md +439 -0
- package/templates/.claude/skills/infra-engineer/references/cloudflare-d1-kv.md +123 -0
- package/templates/.claude/skills/infra-engineer/references/cloudflare-platform.md +271 -0
- package/templates/.claude/skills/infra-engineer/references/cloudflare-r2-storage.md +280 -0
- package/templates/.claude/skills/infra-engineer/references/cloudflare-workers-advanced.md +312 -0
- package/templates/.claude/skills/infra-engineer/references/cloudflare-workers-apis.md +309 -0
- package/templates/.claude/skills/infra-engineer/references/cloudflare-workers-basics.md +418 -0
- package/templates/.claude/skills/infra-engineer/references/devsecops-basics.md +361 -0
- package/templates/.claude/skills/infra-engineer/references/docker-basics.md +297 -0
- package/templates/.claude/skills/infra-engineer/references/docker-compose.md +292 -0
- package/templates/.claude/skills/infra-engineer/references/finops-basics.md +278 -0
- package/templates/.claude/skills/infra-engineer/references/gcloud-platform.md +297 -0
- package/templates/.claude/skills/infra-engineer/references/gcloud-services.md +304 -0
- package/templates/.claude/skills/infra-engineer/references/kubernetes-basics.md +295 -0
- package/templates/.claude/skills/infra-engineer/scripts/cloudflare_deploy.py +269 -0
- package/templates/.claude/skills/infra-engineer/scripts/docker_optimize.py +320 -0
- package/templates/.claude/skills/infra-engineer/scripts/requirements.txt +20 -0
- package/templates/.claude/skills/infra-engineer/scripts/tests/requirements.txt +3 -0
- package/templates/.claude/skills/infra-engineer/scripts/tests/test_cloudflare_deploy.py +285 -0
- package/templates/.claude/skills/infra-engineer/scripts/tests/test_docker_optimize.py +436 -0
- package/templates/.claude/skills/javascript-pro/SKILL.md +96 -0
- package/templates/.claude/skills/javascript-pro/references/async-patterns.md +96 -0
- package/templates/.claude/skills/javascript-pro/references/best-practices.md +98 -0
- package/templates/.claude/skills/javascript-pro/references/design-patterns.md +100 -0
- package/templates/.claude/skills/javascript-pro/references/error-handling.md +100 -0
- package/templates/.claude/skills/javascript-pro/references/functional-patterns.md +93 -0
- package/templates/.claude/skills/javascript-pro/references/modern-syntax.md +100 -0
- package/templates/.claude/skills/javascript-pro/references/performance.md +98 -0
- package/templates/.claude/skills/jira-integration/adf-parser.md +338 -0
- package/templates/.claude/skills/jira-integration/jira-integration.md +353 -0
- package/templates/.claude/skills/knowledge-base/knowledge-base.md +212 -0
- package/templates/.claude/skills/mobile-development/SKILL.md +212 -0
- package/templates/.claude/skills/mobile-development/references/mobile-android.md +604 -0
- package/templates/.claude/skills/mobile-development/references/mobile-best-practices.md +545 -0
- package/templates/.claude/skills/mobile-development/references/mobile-debugging.md +1089 -0
- package/templates/.claude/skills/mobile-development/references/mobile-frameworks.md +465 -0
- package/templates/.claude/skills/mobile-development/references/mobile-ios.md +496 -0
- package/templates/.claude/skills/mobile-development/references/mobile-mindset.md +544 -0
- package/templates/.claude/skills/mobile-testing/SKILL.md +24 -0
- package/templates/.claude/skills/performance-testing/SKILL.md +30 -0
- package/templates/.claude/skills/planning/SKILL.md +143 -0
- package/templates/.claude/skills/planning/references/codebase-understanding.md +62 -0
- package/templates/.claude/skills/planning/references/output-standards.md +87 -0
- package/templates/.claude/skills/planning/references/plan-organization.md +137 -0
- package/templates/.claude/skills/planning/references/research-phase.md +47 -0
- package/templates/.claude/skills/planning/references/solution-design.md +63 -0
- package/templates/.claude/skills/playwright/SKILL.md +303 -0
- package/templates/.claude/skills/playwright/examples/auth.fixture.ts +172 -0
- package/templates/.claude/skills/playwright/examples/login.page.ts +232 -0
- package/templates/.claude/skills/playwright/references/config.md +391 -0
- package/templates/.claude/skills/problem-solving/SKILL.md +96 -0
- package/templates/.claude/skills/problem-solving/references/attribution.md +69 -0
- package/templates/.claude/skills/problem-solving/references/collision-zone-thinking.md +79 -0
- package/templates/.claude/skills/problem-solving/references/inversion-exercise.md +91 -0
- package/templates/.claude/skills/problem-solving/references/meta-pattern-recognition.md +87 -0
- package/templates/.claude/skills/problem-solving/references/scale-game.md +95 -0
- package/templates/.claude/skills/problem-solving/references/simplification-cascades.md +80 -0
- package/templates/.claude/skills/problem-solving/references/when-stuck.md +72 -0
- package/templates/.claude/skills/prompt-enhancer/SKILL.md +380 -0
- package/templates/.claude/skills/python-pro/SKILL.md +59 -0
- package/templates/.claude/skills/python-pro/references/async-patterns.md +96 -0
- package/templates/.claude/skills/python-pro/references/error-handling.md +84 -0
- package/templates/.claude/skills/python-pro/references/performance.md +71 -0
- package/templates/.claude/skills/python-pro/references/project-setup.md +78 -0
- package/templates/.claude/skills/python-pro/references/security.md +73 -0
- package/templates/.claude/skills/python-pro/references/testing.md +90 -0
- package/templates/.claude/skills/python-pro/references/type-system.md +88 -0
- package/templates/.claude/skills/qa-standards/SKILL.md +176 -0
- package/templates/.claude/skills/qa-standards/references/naming-conventions.md +262 -0
- package/templates/.claude/skills/quality-metrics/SKILL.md +27 -0
- package/templates/.claude/skills/repomix/SKILL.md +247 -0
- package/templates/.claude/skills/repomix/references/configuration.md +211 -0
- package/templates/.claude/skills/repomix/references/usage-patterns.md +232 -0
- package/templates/.claude/skills/repomix/scripts/README.md +179 -0
- package/templates/.claude/skills/repomix/scripts/repomix_batch.py +455 -0
- package/templates/.claude/skills/repomix/scripts/repos.example.json +15 -0
- package/templates/.claude/skills/repomix/scripts/requirements.txt +15 -0
- package/templates/.claude/skills/repomix/scripts/tests/test_repomix_batch.py +531 -0
- package/templates/.claude/skills/research/SKILL.md +168 -0
- package/templates/.claude/skills/sequential-thinking/.env.example +8 -0
- package/templates/.claude/skills/sequential-thinking/README.md +183 -0
- package/templates/.claude/skills/sequential-thinking/SKILL.md +94 -0
- package/templates/.claude/skills/sequential-thinking/package.json +31 -0
- package/templates/.claude/skills/sequential-thinking/references/advanced-strategies.md +79 -0
- package/templates/.claude/skills/sequential-thinking/references/advanced-techniques.md +76 -0
- package/templates/.claude/skills/sequential-thinking/references/core-patterns.md +95 -0
- package/templates/.claude/skills/sequential-thinking/references/examples-api.md +88 -0
- package/templates/.claude/skills/sequential-thinking/references/examples-architecture.md +94 -0
- package/templates/.claude/skills/sequential-thinking/references/examples-debug.md +90 -0
- package/templates/.claude/skills/sequential-thinking/scripts/format-thought.js +159 -0
- package/templates/.claude/skills/sequential-thinking/scripts/process-thought.js +236 -0
- package/templates/.claude/skills/sequential-thinking/tests/format-thought.test.js +133 -0
- package/templates/.claude/skills/sequential-thinking/tests/process-thought.test.js +215 -0
- package/templates/.claude/skills/test-automation/SKILL.md +93 -0
- package/templates/.claude/skills/test-automation/examples/page-object.ts +44 -0
- package/templates/.claude/skills/test-automation/references/frameworks.md +96 -0
- package/templates/.claude/skills/test-data-management/SKILL.md +24 -0
- package/templates/.claude/skills/test-design/SKILL.md +25 -0
- package/templates/.claude/skills/typescript-pro/SKILL.md +84 -0
- package/templates/.claude/skills/typescript-pro/references/advanced-patterns.md +100 -0
- package/templates/.claude/skills/typescript-pro/references/best-practices.md +96 -0
- package/templates/.claude/skills/typescript-pro/references/conditional-types.md +85 -0
- package/templates/.claude/skills/typescript-pro/references/generics.md +97 -0
- package/templates/.claude/skills/typescript-pro/references/mapped-types.md +99 -0
- package/templates/.claude/skills/typescript-pro/references/template-literals.md +85 -0
- package/templates/.claude/skills/typescript-pro/references/type-inference.md +94 -0
- package/templates/.claude/skills/typescript-pro/references/utility-types.md +96 -0
- package/templates/.claude/skills/ui-testing/SKILL.md +24 -0
- package/templates/.claude/skills/ui-ux-pro-max/SKILL.md +229 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/colors.csv +97 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/landing.csv +31 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/products.csv +97 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/prompts.csv +24 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/styles.csv +59 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/typography.csv +58 -0
- package/templates/.claude/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/templates/.claude/skills/ui-ux-pro-max/scripts/core.py +236 -0
- package/templates/.claude/skills/ui-ux-pro-max/scripts/search.py +61 -0
- package/templates/.claude/skills/webdriverio/SKILL.md +433 -0
- package/templates/.claude/skills/webdriverio/examples/mobile.page.ts +369 -0
- package/templates/.claude/skills/webdriverio/references/capabilities.md +377 -0
- package/templates/.claude/statusline.cjs +105 -0
- package/templates/.claude/statusline.ps1 +187 -0
- package/templates/.claude/statusline.sh +161 -0
- package/templates/.claude/workflows/api-testing-workflow.md +36 -0
- package/templates/.claude/workflows/bug-investigation-workflow.md +42 -0
- package/templates/.claude/workflows/development-rules.md +366 -0
- package/templates/.claude/workflows/documentation-management.md +128 -0
- package/templates/.claude/workflows/e2e-testing-workflow.md +36 -0
- package/templates/.claude/workflows/orchestration-protocol.md +16 -0
- package/templates/.claude/workflows/performance-testing-workflow.md +42 -0
- package/templates/.claude/workflows/primary-workflow.md +45 -0
- package/templates/.claude/workflows/qa-primary-workflow.md +48 -0
- package/templates/.claude/workflows/qa-rules.md +42 -0
- package/templates/.claude/workflows/regression-testing-workflow.md +36 -0
- package/templates/.claude/workflows/test-driven-qa-workflow.md +37 -0
- package/templates/.codex/config.toml.template +29 -0
- package/templates/.codex/prompts/qa-commands.md +32 -0
- package/templates/.codex/prompts/test-generation.md +29 -0
- package/templates/.cursor/CHANGELOG.md +540 -0
- package/templates/.cursor/README.md +261 -0
- package/templates/.cursor/commands/devkit/ask.md +71 -0
- package/templates/.cursor/commands/devkit/backend/create-api.md +207 -0
- package/templates/.cursor/commands/devkit/backend/create-migration.md +169 -0
- package/templates/.cursor/commands/devkit/backend/create-schema.md +183 -0
- package/templates/.cursor/commands/devkit/backend/design-graphql-api.md +287 -0
- package/templates/.cursor/commands/devkit/backend/design-rest-api.md +219 -0
- package/templates/.cursor/commands/devkit/config/init-backend.md +219 -0
- package/templates/.cursor/commands/devkit/config/init-frontend.md +114 -0
- package/templates/.cursor/commands/devkit/create-api-integration.md +196 -0
- package/templates/.cursor/commands/devkit/create-component.md +104 -0
- package/templates/.cursor/commands/devkit/create-form.md +189 -0
- package/templates/.cursor/commands/devkit/create-page.md +111 -0
- package/templates/.cursor/commands/devkit/create-storybook.md +235 -0
- package/templates/.cursor/commands/devkit/create-table.md +251 -0
- package/templates/.cursor/commands/devkit/create-test.md +174 -0
- package/templates/.cursor/commands/devkit/debug.md +192 -0
- package/templates/.cursor/commands/devkit/docs/capture-knowledge.md +252 -0
- package/templates/.cursor/commands/devkit/docs/init.md +153 -0
- package/templates/.cursor/commands/devkit/docs/summarize.md +82 -0
- package/templates/.cursor/commands/devkit/docs/update.md +174 -0
- package/templates/.cursor/commands/devkit/fix.md +24 -0
- package/templates/.cursor/commands/devkit/help.md +148 -0
- package/templates/.cursor/commands/devkit/refactor.md +476 -0
- package/templates/.cursor/commands/devkit/research.md +135 -0
- package/templates/.cursor/commands/devkit/review/code.md +297 -0
- package/templates/.cursor/commands/devkit/review/codebase.md +37 -0
- package/templates/.cursor/commands/devkit/scout.md +103 -0
- package/templates/.cursor/commands/devkit/solve.md +355 -0
- package/templates/.cursor/commands/devkit/spec/brainstorm.md +184 -0
- package/templates/.cursor/commands/devkit/spec/capture-requirement.md +164 -0
- package/templates/.cursor/commands/devkit/spec/cook.md +101 -0
- package/templates/.cursor/commands/devkit/spec/deep-capture-requirement.md +149 -0
- package/templates/.cursor/commands/devkit/spec/implement.md +354 -0
- package/templates/.cursor/commands/devkit/spec/plan.md +317 -0
- package/templates/.cursor/commands/devkit/spec/task.md +150 -0
- package/templates/.cursor/commands/devkit/spec/test.md +203 -0
- package/templates/.cursor/commands/devkit/spec/update-plan.md +234 -0
- package/templates/.cursor/commands/devkit/spec/update-requirement.md +222 -0
- package/templates/.cursor/commands/devkit/spec-lite/do.md +154 -0
- package/templates/.cursor/commands/devkit/spec-lite/plan.md +197 -0
- package/templates/.cursor/commands/devkit/spec-lite/task.md +138 -0
- package/templates/.cursor/commands/devkit/spec-lite/test.md +129 -0
- package/templates/.cursor/commands/devkit/spec-lite/update-plan.md +133 -0
- package/templates/.cursor/commands/devkit/ui-ux-pro-max.md +226 -0
- package/templates/.cursor/doc-templates/code-standards.md +486 -0
- package/templates/.cursor/doc-templates/codebase-summary.md +426 -0
- package/templates/.cursor/doc-templates/development-rules.md +553 -0
- package/templates/.cursor/doc-templates/project-overview-pdr.md +494 -0
- package/templates/.cursor/doc-templates/system-architecture.md +615 -0
- package/templates/.cursor/doc-templates/technical-documents.md +127 -0
- package/templates/.cursor/repomix.config.json +34 -0
- package/templates/.cursor/rules/backend-development/backend-development.mdc +94 -0
- package/templates/.cursor/rules/backend-development/references/backend-api-design.md +495 -0
- package/templates/.cursor/rules/backend-development/references/backend-architecture.md +454 -0
- package/templates/.cursor/rules/backend-development/references/backend-code-quality.md +659 -0
- package/templates/.cursor/rules/backend-development/references/backend-debugging.md +904 -0
- package/templates/.cursor/rules/backend-development/references/backend-devops.md +494 -0
- package/templates/.cursor/rules/backend-development/references/backend-mindset.md +387 -0
- package/templates/.cursor/rules/backend-development/references/backend-performance.md +397 -0
- package/templates/.cursor/rules/backend-development/references/backend-security.md +290 -0
- package/templates/.cursor/rules/backend-development/references/backend-technologies.md +256 -0
- package/templates/.cursor/rules/backend-development/references/backend-testing.md +429 -0
- package/templates/.cursor/rules/brainstormer.mdc +147 -0
- package/templates/.cursor/rules/code-optimizer.mdc +132 -0
- package/templates/.cursor/rules/code-review/code-revew.mdc +144 -0
- package/templates/.cursor/rules/code-review/references/code-review-reception.md +209 -0
- package/templates/.cursor/rules/code-review/references/requesting-code-review.md +105 -0
- package/templates/.cursor/rules/code-review/references/verification-before-completion.md +139 -0
- package/templates/.cursor/rules/code-reviewer.mdc +85 -0
- package/templates/.cursor/rules/code-simplifier.mdc +121 -0
- package/templates/.cursor/rules/coder.mdc +86 -0
- package/templates/.cursor/rules/community-rules.mdc +38 -0
- package/templates/.cursor/rules/cunningham.mdc +114 -0
- package/templates/.cursor/rules/debugger.mdc +108 -0
- package/templates/.cursor/rules/development-rules.mdc +208 -0
- package/templates/.cursor/rules/docs-manager.mdc +121 -0
- package/templates/.cursor/rules/docs-seeker/docs-seeker.mdc +57 -0
- package/templates/.cursor/rules/docs-seeker/references/advanced.md +80 -0
- package/templates/.cursor/rules/docs-seeker/references/context7-patterns.md +69 -0
- package/templates/.cursor/rules/docs-seeker/references/errors.md +69 -0
- package/templates/.cursor/rules/docs-seeker/scripts/analyze-llms-txt.js +211 -0
- package/templates/.cursor/rules/docs-seeker/scripts/detect-topic.js +172 -0
- package/templates/.cursor/rules/docs-seeker/scripts/fetch-docs.js +213 -0
- package/templates/.cursor/rules/docs-seeker/scripts/tests/run-tests.js +72 -0
- package/templates/.cursor/rules/docs-seeker/scripts/tests/test-analyze-llms.js +119 -0
- package/templates/.cursor/rules/docs-seeker/scripts/tests/test-detect-topic.js +112 -0
- package/templates/.cursor/rules/docs-seeker/scripts/tests/test-fetch-docs.js +84 -0
- package/templates/.cursor/rules/docs-seeker/scripts/utils/env-loader.js +94 -0
- package/templates/.cursor/rules/docs-seeker/workflows/library-search.md +88 -0
- package/templates/.cursor/rules/docs-seeker/workflows/repo-analysis.md +92 -0
- package/templates/.cursor/rules/docs-seeker/workflows/topic-search.md +78 -0
- package/templates/.cursor/rules/frontend-development/frontend-development.mdc +131 -0
- package/templates/.cursor/rules/frontend-development/references/accessibility.md +719 -0
- package/templates/.cursor/rules/frontend-development/references/data-fetching.md +412 -0
- package/templates/.cursor/rules/frontend-development/references/forms.md +808 -0
- package/templates/.cursor/rules/frontend-development/references/fsd-architecture.md +442 -0
- package/templates/.cursor/rules/frontend-development/references/graphql-apollo.md +730 -0
- package/templates/.cursor/rules/frontend-development/references/performance.md +417 -0
- package/templates/.cursor/rules/frontend-development/references/react-patterns.md +451 -0
- package/templates/.cursor/rules/frontend-development/references/routing.md +436 -0
- package/templates/.cursor/rules/frontend-development/references/state-management.md +511 -0
- package/templates/.cursor/rules/frontend-development/references/tailwind-css.md +398 -0
- package/templates/.cursor/rules/frontend-development/references/testing.md +629 -0
- package/templates/.cursor/rules/frontend-development/references/typescript-standards.md +364 -0
- package/templates/.cursor/rules/frontend-development/references/ui-libraries.md +587 -0
- package/templates/.cursor/rules/javascript-pro/javascript-pro.mdc +98 -0
- package/templates/.cursor/rules/javascript-pro/references/async-patterns.md +96 -0
- package/templates/.cursor/rules/javascript-pro/references/best-practices.md +98 -0
- package/templates/.cursor/rules/javascript-pro/references/design-patterns.md +100 -0
- package/templates/.cursor/rules/javascript-pro/references/error-handling.md +100 -0
- package/templates/.cursor/rules/javascript-pro/references/functional-patterns.md +93 -0
- package/templates/.cursor/rules/javascript-pro/references/modern-syntax.md +100 -0
- package/templates/.cursor/rules/javascript-pro/references/performance.md +98 -0
- package/templates/.cursor/rules/planner.mdc +87 -0
- package/templates/.cursor/rules/problem-solving/problem-solving.mdc +96 -0
- package/templates/.cursor/rules/problem-solving/references/attribution.md +69 -0
- package/templates/.cursor/rules/problem-solving/references/collision-zone-thinking.md +79 -0
- package/templates/.cursor/rules/problem-solving/references/inversion-exercise.md +91 -0
- package/templates/.cursor/rules/problem-solving/references/meta-pattern-recognition.md +87 -0
- package/templates/.cursor/rules/problem-solving/references/scale-game.md +95 -0
- package/templates/.cursor/rules/problem-solving/references/simplification-cascades.md +80 -0
- package/templates/.cursor/rules/problem-solving/references/when-stuck.md +72 -0
- package/templates/.cursor/rules/project-manager.mdc +116 -0
- package/templates/.cursor/rules/project-profile.mdc +54 -0
- package/templates/.cursor/rules/python-pro/python-pro.mdc +83 -0
- package/templates/.cursor/rules/python-pro/references/async-patterns.md +96 -0
- package/templates/.cursor/rules/python-pro/references/error-handling.md +84 -0
- package/templates/.cursor/rules/python-pro/references/performance.md +71 -0
- package/templates/.cursor/rules/python-pro/references/project-setup.md +78 -0
- package/templates/.cursor/rules/python-pro/references/security.md +73 -0
- package/templates/.cursor/rules/python-pro/references/testing.md +90 -0
- package/templates/.cursor/rules/python-pro/references/type-system.md +88 -0
- package/templates/.cursor/rules/qa-api-testing.mdc +25 -0
- package/templates/.cursor/rules/qa-automation.mdc +28 -0
- package/templates/.cursor/rules/qa-testing.mdc +28 -0
- package/templates/.cursor/rules/repomix/references/configuration.md +211 -0
- package/templates/.cursor/rules/repomix/references/usage-patterns.md +232 -0
- package/templates/.cursor/rules/repomix/repomix.mdc +248 -0
- package/templates/.cursor/rules/repomix/scripts/README.md +179 -0
- package/templates/.cursor/rules/repomix/scripts/repomix_batch.py +455 -0
- package/templates/.cursor/rules/repomix/scripts/repos.example.json +15 -0
- package/templates/.cursor/rules/repomix/scripts/requirements.txt +15 -0
- package/templates/.cursor/rules/repomix/scripts/tests/test_repomix_batch.py +531 -0
- package/templates/.cursor/rules/research/research.mdc +111 -0
- package/templates/.cursor/rules/researcher.mdc +54 -0
- package/templates/.cursor/rules/scout.mdc +105 -0
- package/templates/.cursor/rules/security-agent.mdc +122 -0
- package/templates/.cursor/rules/security.mdc +90 -0
- package/templates/.cursor/rules/sequential-thinking/references/advanced-strategies.md +80 -0
- package/templates/.cursor/rules/sequential-thinking/references/advanced-techniques.md +77 -0
- package/templates/.cursor/rules/sequential-thinking/references/core-patterns.md +96 -0
- package/templates/.cursor/rules/sequential-thinking/references/examples-api.md +89 -0
- package/templates/.cursor/rules/sequential-thinking/references/examples-architecture.md +95 -0
- package/templates/.cursor/rules/sequential-thinking/references/examples-debug.md +91 -0
- package/templates/.cursor/rules/sequential-thinking/scripts/format-thought.js +159 -0
- package/templates/.cursor/rules/sequential-thinking/scripts/process-thought.js +236 -0
- package/templates/.cursor/rules/sequential-thinking/sequential-thinking.mdc +86 -0
- package/templates/.cursor/rules/tattletale-reporter.mdc +64 -0
- package/templates/.cursor/rules/tester.mdc +221 -0
- package/templates/.cursor/rules/testing.mdc +215 -0
- package/templates/.cursor/rules/typescript-pro/references/advanced-patterns.md +100 -0
- package/templates/.cursor/rules/typescript-pro/references/best-practices.md +96 -0
- package/templates/.cursor/rules/typescript-pro/references/conditional-types.md +85 -0
- package/templates/.cursor/rules/typescript-pro/references/generics.md +97 -0
- package/templates/.cursor/rules/typescript-pro/references/mapped-types.md +99 -0
- package/templates/.cursor/rules/typescript-pro/references/template-literals.md +85 -0
- package/templates/.cursor/rules/typescript-pro/references/type-inference.md +94 -0
- package/templates/.cursor/rules/typescript-pro/references/utility-types.md +96 -0
- package/templates/.cursor/rules/typescript-pro/typescript-pro.mdc +101 -0
- package/templates/.cursor/rules/typescript-pro.mdc +129 -0
- package/templates/.cursor/rules/ui-ux-designer.mdc +117 -0
- package/templates/.cursor/scripts/helper.py +723 -0
- package/templates/.cursor/settings.json +5 -0
- package/templates/.kiro/steering/qa-standards.md +84 -0
- package/templates/.kiro/steering/test-patterns.md +62 -0
- package/templates/.kiro/steering/tool-integration.md +73 -0
|
@@ -0,0 +1,1276 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Library for working with Word documents: comments, tracked changes, and editing.
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
from skills.docx.scripts.document import Document
|
|
7
|
+
|
|
8
|
+
# Initialize
|
|
9
|
+
doc = Document('workspace/unpacked')
|
|
10
|
+
doc = Document('workspace/unpacked', author="John Doe", initials="JD")
|
|
11
|
+
|
|
12
|
+
# Find nodes
|
|
13
|
+
node = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "1"})
|
|
14
|
+
node = doc["word/document.xml"].get_node(tag="w:p", line_number=10)
|
|
15
|
+
|
|
16
|
+
# Add comments
|
|
17
|
+
doc.add_comment(start=node, end=node, text="Comment text")
|
|
18
|
+
doc.reply_to_comment(parent_comment_id=0, text="Reply text")
|
|
19
|
+
|
|
20
|
+
# Suggest tracked changes
|
|
21
|
+
doc["word/document.xml"].suggest_deletion(node) # Delete content
|
|
22
|
+
doc["word/document.xml"].revert_insertion(ins_node) # Reject insertion
|
|
23
|
+
doc["word/document.xml"].revert_deletion(del_node) # Reject deletion
|
|
24
|
+
|
|
25
|
+
# Save
|
|
26
|
+
doc.save()
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
import html
|
|
30
|
+
import random
|
|
31
|
+
import shutil
|
|
32
|
+
import tempfile
|
|
33
|
+
from datetime import datetime, timezone
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
from defusedxml import minidom
|
|
37
|
+
from ooxml.scripts.pack import pack_document
|
|
38
|
+
from ooxml.scripts.validation.docx import DOCXSchemaValidator
|
|
39
|
+
from ooxml.scripts.validation.redlining import RedliningValidator
|
|
40
|
+
|
|
41
|
+
from .utilities import XMLEditor
|
|
42
|
+
|
|
43
|
+
# Path to template files
|
|
44
|
+
TEMPLATE_DIR = Path(__file__).parent / "templates"
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class DocxXMLEditor(XMLEditor):
|
|
48
|
+
"""XMLEditor that automatically applies RSID, author, and date to new elements.
|
|
49
|
+
|
|
50
|
+
Automatically adds attributes to elements that support them when inserting new content:
|
|
51
|
+
- w:rsidR, w:rsidRDefault, w:rsidP (for w:p and w:r elements)
|
|
52
|
+
- w:author and w:date (for w:ins, w:del, w:comment elements)
|
|
53
|
+
- w:id (for w:ins and w:del elements)
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
dom (defusedxml.minidom.Document): The DOM document for direct manipulation
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def __init__(
|
|
60
|
+
self, xml_path, rsid: str, author: str = "Claude", initials: str = "C"
|
|
61
|
+
):
|
|
62
|
+
"""Initialize with required RSID and optional author.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
xml_path: Path to XML file to edit
|
|
66
|
+
rsid: RSID to automatically apply to new elements
|
|
67
|
+
author: Author name for tracked changes and comments (default: "Claude")
|
|
68
|
+
initials: Author initials (default: "C")
|
|
69
|
+
"""
|
|
70
|
+
super().__init__(xml_path)
|
|
71
|
+
self.rsid = rsid
|
|
72
|
+
self.author = author
|
|
73
|
+
self.initials = initials
|
|
74
|
+
|
|
75
|
+
def _get_next_change_id(self):
|
|
76
|
+
"""Get the next available change ID by checking all tracked change elements."""
|
|
77
|
+
max_id = -1
|
|
78
|
+
for tag in ("w:ins", "w:del"):
|
|
79
|
+
elements = self.dom.getElementsByTagName(tag)
|
|
80
|
+
for elem in elements:
|
|
81
|
+
change_id = elem.getAttribute("w:id")
|
|
82
|
+
if change_id:
|
|
83
|
+
try:
|
|
84
|
+
max_id = max(max_id, int(change_id))
|
|
85
|
+
except ValueError:
|
|
86
|
+
pass
|
|
87
|
+
return max_id + 1
|
|
88
|
+
|
|
89
|
+
def _ensure_w16du_namespace(self):
|
|
90
|
+
"""Ensure w16du namespace is declared on the root element."""
|
|
91
|
+
root = self.dom.documentElement
|
|
92
|
+
if not root.hasAttribute("xmlns:w16du"): # type: ignore
|
|
93
|
+
root.setAttribute( # type: ignore
|
|
94
|
+
"xmlns:w16du",
|
|
95
|
+
"http://schemas.microsoft.com/office/word/2023/wordml/word16du",
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
def _ensure_w16cex_namespace(self):
|
|
99
|
+
"""Ensure w16cex namespace is declared on the root element."""
|
|
100
|
+
root = self.dom.documentElement
|
|
101
|
+
if not root.hasAttribute("xmlns:w16cex"): # type: ignore
|
|
102
|
+
root.setAttribute( # type: ignore
|
|
103
|
+
"xmlns:w16cex",
|
|
104
|
+
"http://schemas.microsoft.com/office/word/2018/wordml/cex",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
def _ensure_w14_namespace(self):
|
|
108
|
+
"""Ensure w14 namespace is declared on the root element."""
|
|
109
|
+
root = self.dom.documentElement
|
|
110
|
+
if not root.hasAttribute("xmlns:w14"): # type: ignore
|
|
111
|
+
root.setAttribute( # type: ignore
|
|
112
|
+
"xmlns:w14",
|
|
113
|
+
"http://schemas.microsoft.com/office/word/2010/wordml",
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
def _inject_attributes_to_nodes(self, nodes):
|
|
117
|
+
"""Inject RSID, author, and date attributes into DOM nodes where applicable.
|
|
118
|
+
|
|
119
|
+
Adds attributes to elements that support them:
|
|
120
|
+
- w:r: gets w:rsidR (or w:rsidDel if inside w:del)
|
|
121
|
+
- w:p: gets w:rsidR, w:rsidRDefault, w:rsidP, w14:paraId, w14:textId
|
|
122
|
+
- w:t: gets xml:space="preserve" if text has leading/trailing whitespace
|
|
123
|
+
- w:ins, w:del: get w:id, w:author, w:date, w16du:dateUtc
|
|
124
|
+
- w:comment: gets w:author, w:date, w:initials
|
|
125
|
+
- w16cex:commentExtensible: gets w16cex:dateUtc
|
|
126
|
+
|
|
127
|
+
Args:
|
|
128
|
+
nodes: List of DOM nodes to process
|
|
129
|
+
"""
|
|
130
|
+
from datetime import datetime, timezone
|
|
131
|
+
|
|
132
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
133
|
+
|
|
134
|
+
def is_inside_deletion(elem):
|
|
135
|
+
"""Check if element is inside a w:del element."""
|
|
136
|
+
parent = elem.parentNode
|
|
137
|
+
while parent:
|
|
138
|
+
if parent.nodeType == parent.ELEMENT_NODE and parent.tagName == "w:del":
|
|
139
|
+
return True
|
|
140
|
+
parent = parent.parentNode
|
|
141
|
+
return False
|
|
142
|
+
|
|
143
|
+
def add_rsid_to_p(elem):
|
|
144
|
+
if not elem.hasAttribute("w:rsidR"):
|
|
145
|
+
elem.setAttribute("w:rsidR", self.rsid)
|
|
146
|
+
if not elem.hasAttribute("w:rsidRDefault"):
|
|
147
|
+
elem.setAttribute("w:rsidRDefault", self.rsid)
|
|
148
|
+
if not elem.hasAttribute("w:rsidP"):
|
|
149
|
+
elem.setAttribute("w:rsidP", self.rsid)
|
|
150
|
+
# Add w14:paraId and w14:textId if not present
|
|
151
|
+
if not elem.hasAttribute("w14:paraId"):
|
|
152
|
+
self._ensure_w14_namespace()
|
|
153
|
+
elem.setAttribute("w14:paraId", _generate_hex_id())
|
|
154
|
+
if not elem.hasAttribute("w14:textId"):
|
|
155
|
+
self._ensure_w14_namespace()
|
|
156
|
+
elem.setAttribute("w14:textId", _generate_hex_id())
|
|
157
|
+
|
|
158
|
+
def add_rsid_to_r(elem):
|
|
159
|
+
# Use w:rsidDel for <w:r> inside <w:del>, otherwise w:rsidR
|
|
160
|
+
if is_inside_deletion(elem):
|
|
161
|
+
if not elem.hasAttribute("w:rsidDel"):
|
|
162
|
+
elem.setAttribute("w:rsidDel", self.rsid)
|
|
163
|
+
else:
|
|
164
|
+
if not elem.hasAttribute("w:rsidR"):
|
|
165
|
+
elem.setAttribute("w:rsidR", self.rsid)
|
|
166
|
+
|
|
167
|
+
def add_tracked_change_attrs(elem):
|
|
168
|
+
# Auto-assign w:id if not present
|
|
169
|
+
if not elem.hasAttribute("w:id"):
|
|
170
|
+
elem.setAttribute("w:id", str(self._get_next_change_id()))
|
|
171
|
+
if not elem.hasAttribute("w:author"):
|
|
172
|
+
elem.setAttribute("w:author", self.author)
|
|
173
|
+
if not elem.hasAttribute("w:date"):
|
|
174
|
+
elem.setAttribute("w:date", timestamp)
|
|
175
|
+
# Add w16du:dateUtc for tracked changes (same as w:date since we generate UTC timestamps)
|
|
176
|
+
if elem.tagName in ("w:ins", "w:del") and not elem.hasAttribute(
|
|
177
|
+
"w16du:dateUtc"
|
|
178
|
+
):
|
|
179
|
+
self._ensure_w16du_namespace()
|
|
180
|
+
elem.setAttribute("w16du:dateUtc", timestamp)
|
|
181
|
+
|
|
182
|
+
def add_comment_attrs(elem):
|
|
183
|
+
if not elem.hasAttribute("w:author"):
|
|
184
|
+
elem.setAttribute("w:author", self.author)
|
|
185
|
+
if not elem.hasAttribute("w:date"):
|
|
186
|
+
elem.setAttribute("w:date", timestamp)
|
|
187
|
+
if not elem.hasAttribute("w:initials"):
|
|
188
|
+
elem.setAttribute("w:initials", self.initials)
|
|
189
|
+
|
|
190
|
+
def add_comment_extensible_date(elem):
|
|
191
|
+
# Add w16cex:dateUtc for comment extensible elements
|
|
192
|
+
if not elem.hasAttribute("w16cex:dateUtc"):
|
|
193
|
+
self._ensure_w16cex_namespace()
|
|
194
|
+
elem.setAttribute("w16cex:dateUtc", timestamp)
|
|
195
|
+
|
|
196
|
+
def add_xml_space_to_t(elem):
|
|
197
|
+
# Add xml:space="preserve" to w:t if text has leading/trailing whitespace
|
|
198
|
+
if (
|
|
199
|
+
elem.firstChild
|
|
200
|
+
and elem.firstChild.nodeType == elem.firstChild.TEXT_NODE
|
|
201
|
+
):
|
|
202
|
+
text = elem.firstChild.data
|
|
203
|
+
if text and (text[0].isspace() or text[-1].isspace()):
|
|
204
|
+
if not elem.hasAttribute("xml:space"):
|
|
205
|
+
elem.setAttribute("xml:space", "preserve")
|
|
206
|
+
|
|
207
|
+
for node in nodes:
|
|
208
|
+
if node.nodeType != node.ELEMENT_NODE:
|
|
209
|
+
continue
|
|
210
|
+
|
|
211
|
+
# Handle the node itself
|
|
212
|
+
if node.tagName == "w:p":
|
|
213
|
+
add_rsid_to_p(node)
|
|
214
|
+
elif node.tagName == "w:r":
|
|
215
|
+
add_rsid_to_r(node)
|
|
216
|
+
elif node.tagName == "w:t":
|
|
217
|
+
add_xml_space_to_t(node)
|
|
218
|
+
elif node.tagName in ("w:ins", "w:del"):
|
|
219
|
+
add_tracked_change_attrs(node)
|
|
220
|
+
elif node.tagName == "w:comment":
|
|
221
|
+
add_comment_attrs(node)
|
|
222
|
+
elif node.tagName == "w16cex:commentExtensible":
|
|
223
|
+
add_comment_extensible_date(node)
|
|
224
|
+
|
|
225
|
+
# Process descendants (getElementsByTagName doesn't return the element itself)
|
|
226
|
+
for elem in node.getElementsByTagName("w:p"):
|
|
227
|
+
add_rsid_to_p(elem)
|
|
228
|
+
for elem in node.getElementsByTagName("w:r"):
|
|
229
|
+
add_rsid_to_r(elem)
|
|
230
|
+
for elem in node.getElementsByTagName("w:t"):
|
|
231
|
+
add_xml_space_to_t(elem)
|
|
232
|
+
for tag in ("w:ins", "w:del"):
|
|
233
|
+
for elem in node.getElementsByTagName(tag):
|
|
234
|
+
add_tracked_change_attrs(elem)
|
|
235
|
+
for elem in node.getElementsByTagName("w:comment"):
|
|
236
|
+
add_comment_attrs(elem)
|
|
237
|
+
for elem in node.getElementsByTagName("w16cex:commentExtensible"):
|
|
238
|
+
add_comment_extensible_date(elem)
|
|
239
|
+
|
|
240
|
+
def replace_node(self, elem, new_content):
|
|
241
|
+
"""Replace node with automatic attribute injection."""
|
|
242
|
+
nodes = super().replace_node(elem, new_content)
|
|
243
|
+
self._inject_attributes_to_nodes(nodes)
|
|
244
|
+
return nodes
|
|
245
|
+
|
|
246
|
+
def insert_after(self, elem, xml_content):
|
|
247
|
+
"""Insert after with automatic attribute injection."""
|
|
248
|
+
nodes = super().insert_after(elem, xml_content)
|
|
249
|
+
self._inject_attributes_to_nodes(nodes)
|
|
250
|
+
return nodes
|
|
251
|
+
|
|
252
|
+
def insert_before(self, elem, xml_content):
|
|
253
|
+
"""Insert before with automatic attribute injection."""
|
|
254
|
+
nodes = super().insert_before(elem, xml_content)
|
|
255
|
+
self._inject_attributes_to_nodes(nodes)
|
|
256
|
+
return nodes
|
|
257
|
+
|
|
258
|
+
def append_to(self, elem, xml_content):
|
|
259
|
+
"""Append to with automatic attribute injection."""
|
|
260
|
+
nodes = super().append_to(elem, xml_content)
|
|
261
|
+
self._inject_attributes_to_nodes(nodes)
|
|
262
|
+
return nodes
|
|
263
|
+
|
|
264
|
+
def revert_insertion(self, elem):
|
|
265
|
+
"""Reject an insertion by wrapping its content in a deletion.
|
|
266
|
+
|
|
267
|
+
Wraps all runs inside w:ins in w:del, converting w:t to w:delText.
|
|
268
|
+
Can process a single w:ins element or a container element with multiple w:ins.
|
|
269
|
+
|
|
270
|
+
Args:
|
|
271
|
+
elem: Element to process (w:ins, w:p, w:body, etc.)
|
|
272
|
+
|
|
273
|
+
Returns:
|
|
274
|
+
list: List containing the processed element(s)
|
|
275
|
+
|
|
276
|
+
Raises:
|
|
277
|
+
ValueError: If the element contains no w:ins elements
|
|
278
|
+
|
|
279
|
+
Example:
|
|
280
|
+
# Reject a single insertion
|
|
281
|
+
ins = doc["word/document.xml"].get_node(tag="w:ins", attrs={"w:id": "5"})
|
|
282
|
+
doc["word/document.xml"].revert_insertion(ins)
|
|
283
|
+
|
|
284
|
+
# Reject all insertions in a paragraph
|
|
285
|
+
para = doc["word/document.xml"].get_node(tag="w:p", line_number=42)
|
|
286
|
+
doc["word/document.xml"].revert_insertion(para)
|
|
287
|
+
"""
|
|
288
|
+
# Collect insertions
|
|
289
|
+
ins_elements = []
|
|
290
|
+
if elem.tagName == "w:ins":
|
|
291
|
+
ins_elements.append(elem)
|
|
292
|
+
else:
|
|
293
|
+
ins_elements.extend(elem.getElementsByTagName("w:ins"))
|
|
294
|
+
|
|
295
|
+
# Validate that there are insertions to reject
|
|
296
|
+
if not ins_elements:
|
|
297
|
+
raise ValueError(
|
|
298
|
+
f"revert_insertion requires w:ins elements. "
|
|
299
|
+
f"The provided element <{elem.tagName}> contains no insertions. "
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
# Process all insertions - wrap all children in w:del
|
|
303
|
+
for ins_elem in ins_elements:
|
|
304
|
+
runs = list(ins_elem.getElementsByTagName("w:r"))
|
|
305
|
+
if not runs:
|
|
306
|
+
continue
|
|
307
|
+
|
|
308
|
+
# Create deletion wrapper
|
|
309
|
+
del_wrapper = self.dom.createElement("w:del")
|
|
310
|
+
|
|
311
|
+
# Process each run
|
|
312
|
+
for run in runs:
|
|
313
|
+
# Convert w:t → w:delText and w:rsidR → w:rsidDel
|
|
314
|
+
if run.hasAttribute("w:rsidR"):
|
|
315
|
+
run.setAttribute("w:rsidDel", run.getAttribute("w:rsidR"))
|
|
316
|
+
run.removeAttribute("w:rsidR")
|
|
317
|
+
elif not run.hasAttribute("w:rsidDel"):
|
|
318
|
+
run.setAttribute("w:rsidDel", self.rsid)
|
|
319
|
+
|
|
320
|
+
for t_elem in list(run.getElementsByTagName("w:t")):
|
|
321
|
+
del_text = self.dom.createElement("w:delText")
|
|
322
|
+
# Copy ALL child nodes (not just firstChild) to handle entities
|
|
323
|
+
while t_elem.firstChild:
|
|
324
|
+
del_text.appendChild(t_elem.firstChild)
|
|
325
|
+
for i in range(t_elem.attributes.length):
|
|
326
|
+
attr = t_elem.attributes.item(i)
|
|
327
|
+
del_text.setAttribute(attr.name, attr.value)
|
|
328
|
+
t_elem.parentNode.replaceChild(del_text, t_elem)
|
|
329
|
+
|
|
330
|
+
# Move all children from ins to del wrapper
|
|
331
|
+
while ins_elem.firstChild:
|
|
332
|
+
del_wrapper.appendChild(ins_elem.firstChild)
|
|
333
|
+
|
|
334
|
+
# Add del wrapper back to ins
|
|
335
|
+
ins_elem.appendChild(del_wrapper)
|
|
336
|
+
|
|
337
|
+
# Inject attributes to the deletion wrapper
|
|
338
|
+
self._inject_attributes_to_nodes([del_wrapper])
|
|
339
|
+
|
|
340
|
+
return [elem]
|
|
341
|
+
|
|
342
|
+
def revert_deletion(self, elem):
|
|
343
|
+
"""Reject a deletion by re-inserting the deleted content.
|
|
344
|
+
|
|
345
|
+
Creates w:ins elements after each w:del, copying deleted content and
|
|
346
|
+
converting w:delText back to w:t.
|
|
347
|
+
Can process a single w:del element or a container element with multiple w:del.
|
|
348
|
+
|
|
349
|
+
Args:
|
|
350
|
+
elem: Element to process (w:del, w:p, w:body, etc.)
|
|
351
|
+
|
|
352
|
+
Returns:
|
|
353
|
+
list: If elem is w:del, returns [elem, new_ins]. Otherwise returns [elem].
|
|
354
|
+
|
|
355
|
+
Raises:
|
|
356
|
+
ValueError: If the element contains no w:del elements
|
|
357
|
+
|
|
358
|
+
Example:
|
|
359
|
+
# Reject a single deletion - returns [w:del, w:ins]
|
|
360
|
+
del_elem = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "3"})
|
|
361
|
+
nodes = doc["word/document.xml"].revert_deletion(del_elem)
|
|
362
|
+
|
|
363
|
+
# Reject all deletions in a paragraph - returns [para]
|
|
364
|
+
para = doc["word/document.xml"].get_node(tag="w:p", line_number=42)
|
|
365
|
+
nodes = doc["word/document.xml"].revert_deletion(para)
|
|
366
|
+
"""
|
|
367
|
+
# Collect deletions FIRST - before we modify the DOM
|
|
368
|
+
del_elements = []
|
|
369
|
+
is_single_del = elem.tagName == "w:del"
|
|
370
|
+
|
|
371
|
+
if is_single_del:
|
|
372
|
+
del_elements.append(elem)
|
|
373
|
+
else:
|
|
374
|
+
del_elements.extend(elem.getElementsByTagName("w:del"))
|
|
375
|
+
|
|
376
|
+
# Validate that there are deletions to reject
|
|
377
|
+
if not del_elements:
|
|
378
|
+
raise ValueError(
|
|
379
|
+
f"revert_deletion requires w:del elements. "
|
|
380
|
+
f"The provided element <{elem.tagName}> contains no deletions. "
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
# Track created insertion (only relevant if elem is a single w:del)
|
|
384
|
+
created_insertion = None
|
|
385
|
+
|
|
386
|
+
# Process all deletions - create insertions that copy the deleted content
|
|
387
|
+
for del_elem in del_elements:
|
|
388
|
+
# Clone the deleted runs and convert them to insertions
|
|
389
|
+
runs = list(del_elem.getElementsByTagName("w:r"))
|
|
390
|
+
if not runs:
|
|
391
|
+
continue
|
|
392
|
+
|
|
393
|
+
# Create insertion wrapper
|
|
394
|
+
ins_elem = self.dom.createElement("w:ins")
|
|
395
|
+
|
|
396
|
+
for run in runs:
|
|
397
|
+
# Clone the run
|
|
398
|
+
new_run = run.cloneNode(True)
|
|
399
|
+
|
|
400
|
+
# Convert w:delText → w:t
|
|
401
|
+
for del_text in list(new_run.getElementsByTagName("w:delText")):
|
|
402
|
+
t_elem = self.dom.createElement("w:t")
|
|
403
|
+
# Copy ALL child nodes (not just firstChild) to handle entities
|
|
404
|
+
while del_text.firstChild:
|
|
405
|
+
t_elem.appendChild(del_text.firstChild)
|
|
406
|
+
for i in range(del_text.attributes.length):
|
|
407
|
+
attr = del_text.attributes.item(i)
|
|
408
|
+
t_elem.setAttribute(attr.name, attr.value)
|
|
409
|
+
del_text.parentNode.replaceChild(t_elem, del_text)
|
|
410
|
+
|
|
411
|
+
# Update run attributes: w:rsidDel → w:rsidR
|
|
412
|
+
if new_run.hasAttribute("w:rsidDel"):
|
|
413
|
+
new_run.setAttribute("w:rsidR", new_run.getAttribute("w:rsidDel"))
|
|
414
|
+
new_run.removeAttribute("w:rsidDel")
|
|
415
|
+
elif not new_run.hasAttribute("w:rsidR"):
|
|
416
|
+
new_run.setAttribute("w:rsidR", self.rsid)
|
|
417
|
+
|
|
418
|
+
ins_elem.appendChild(new_run)
|
|
419
|
+
|
|
420
|
+
# Insert the new insertion after the deletion
|
|
421
|
+
nodes = self.insert_after(del_elem, ins_elem.toxml())
|
|
422
|
+
|
|
423
|
+
# If processing a single w:del, track the created insertion
|
|
424
|
+
if is_single_del and nodes:
|
|
425
|
+
created_insertion = nodes[0]
|
|
426
|
+
|
|
427
|
+
# Return based on input type
|
|
428
|
+
if is_single_del and created_insertion:
|
|
429
|
+
return [elem, created_insertion]
|
|
430
|
+
else:
|
|
431
|
+
return [elem]
|
|
432
|
+
|
|
433
|
+
@staticmethod
|
|
434
|
+
def suggest_paragraph(xml_content: str) -> str:
|
|
435
|
+
"""Transform paragraph XML to add tracked change wrapping for insertion.
|
|
436
|
+
|
|
437
|
+
Wraps runs in <w:ins> and adds <w:ins/> to w:rPr in w:pPr for numbered lists.
|
|
438
|
+
|
|
439
|
+
Args:
|
|
440
|
+
xml_content: XML string containing a <w:p> element
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
str: Transformed XML with tracked change wrapping
|
|
444
|
+
"""
|
|
445
|
+
wrapper = f'<root xmlns:w="http://schemas.openxmlformats.org/wordprocessingml/2006/main">{xml_content}</root>'
|
|
446
|
+
doc = minidom.parseString(wrapper)
|
|
447
|
+
para = doc.getElementsByTagName("w:p")[0]
|
|
448
|
+
|
|
449
|
+
# Ensure w:pPr exists
|
|
450
|
+
pPr_list = para.getElementsByTagName("w:pPr")
|
|
451
|
+
if not pPr_list:
|
|
452
|
+
pPr = doc.createElement("w:pPr")
|
|
453
|
+
para.insertBefore(
|
|
454
|
+
pPr, para.firstChild
|
|
455
|
+
) if para.firstChild else para.appendChild(pPr)
|
|
456
|
+
else:
|
|
457
|
+
pPr = pPr_list[0]
|
|
458
|
+
|
|
459
|
+
# Ensure w:rPr exists in w:pPr
|
|
460
|
+
rPr_list = pPr.getElementsByTagName("w:rPr")
|
|
461
|
+
if not rPr_list:
|
|
462
|
+
rPr = doc.createElement("w:rPr")
|
|
463
|
+
pPr.appendChild(rPr)
|
|
464
|
+
else:
|
|
465
|
+
rPr = rPr_list[0]
|
|
466
|
+
|
|
467
|
+
# Add <w:ins/> to w:rPr
|
|
468
|
+
ins_marker = doc.createElement("w:ins")
|
|
469
|
+
rPr.insertBefore(
|
|
470
|
+
ins_marker, rPr.firstChild
|
|
471
|
+
) if rPr.firstChild else rPr.appendChild(ins_marker)
|
|
472
|
+
|
|
473
|
+
# Wrap all non-pPr children in <w:ins>
|
|
474
|
+
ins_wrapper = doc.createElement("w:ins")
|
|
475
|
+
for child in [c for c in para.childNodes if c.nodeName != "w:pPr"]:
|
|
476
|
+
para.removeChild(child)
|
|
477
|
+
ins_wrapper.appendChild(child)
|
|
478
|
+
para.appendChild(ins_wrapper)
|
|
479
|
+
|
|
480
|
+
return para.toxml()
|
|
481
|
+
|
|
482
|
+
def suggest_deletion(self, elem):
|
|
483
|
+
"""Mark a w:r or w:p element as deleted with tracked changes (in-place DOM manipulation).
|
|
484
|
+
|
|
485
|
+
For w:r: wraps in <w:del>, converts <w:t> to <w:delText>, preserves w:rPr
|
|
486
|
+
For w:p (regular): wraps content in <w:del>, converts <w:t> to <w:delText>
|
|
487
|
+
For w:p (numbered list): adds <w:del/> to w:rPr in w:pPr, wraps content in <w:del>
|
|
488
|
+
|
|
489
|
+
Args:
|
|
490
|
+
elem: A w:r or w:p DOM element without existing tracked changes
|
|
491
|
+
|
|
492
|
+
Returns:
|
|
493
|
+
Element: The modified element
|
|
494
|
+
|
|
495
|
+
Raises:
|
|
496
|
+
ValueError: If element has existing tracked changes or invalid structure
|
|
497
|
+
"""
|
|
498
|
+
if elem.nodeName == "w:r":
|
|
499
|
+
# Check for existing w:delText
|
|
500
|
+
if elem.getElementsByTagName("w:delText"):
|
|
501
|
+
raise ValueError("w:r element already contains w:delText")
|
|
502
|
+
|
|
503
|
+
# Convert w:t → w:delText
|
|
504
|
+
for t_elem in list(elem.getElementsByTagName("w:t")):
|
|
505
|
+
del_text = self.dom.createElement("w:delText")
|
|
506
|
+
# Copy ALL child nodes (not just firstChild) to handle entities
|
|
507
|
+
while t_elem.firstChild:
|
|
508
|
+
del_text.appendChild(t_elem.firstChild)
|
|
509
|
+
# Preserve attributes like xml:space
|
|
510
|
+
for i in range(t_elem.attributes.length):
|
|
511
|
+
attr = t_elem.attributes.item(i)
|
|
512
|
+
del_text.setAttribute(attr.name, attr.value)
|
|
513
|
+
t_elem.parentNode.replaceChild(del_text, t_elem)
|
|
514
|
+
|
|
515
|
+
# Update run attributes: w:rsidR → w:rsidDel
|
|
516
|
+
if elem.hasAttribute("w:rsidR"):
|
|
517
|
+
elem.setAttribute("w:rsidDel", elem.getAttribute("w:rsidR"))
|
|
518
|
+
elem.removeAttribute("w:rsidR")
|
|
519
|
+
elif not elem.hasAttribute("w:rsidDel"):
|
|
520
|
+
elem.setAttribute("w:rsidDel", self.rsid)
|
|
521
|
+
|
|
522
|
+
# Wrap in w:del
|
|
523
|
+
del_wrapper = self.dom.createElement("w:del")
|
|
524
|
+
parent = elem.parentNode
|
|
525
|
+
parent.insertBefore(del_wrapper, elem)
|
|
526
|
+
parent.removeChild(elem)
|
|
527
|
+
del_wrapper.appendChild(elem)
|
|
528
|
+
|
|
529
|
+
# Inject attributes to the deletion wrapper
|
|
530
|
+
self._inject_attributes_to_nodes([del_wrapper])
|
|
531
|
+
|
|
532
|
+
return del_wrapper
|
|
533
|
+
|
|
534
|
+
elif elem.nodeName == "w:p":
|
|
535
|
+
# Check for existing tracked changes
|
|
536
|
+
if elem.getElementsByTagName("w:ins") or elem.getElementsByTagName("w:del"):
|
|
537
|
+
raise ValueError("w:p element already contains tracked changes")
|
|
538
|
+
|
|
539
|
+
# Check if it's a numbered list item
|
|
540
|
+
pPr_list = elem.getElementsByTagName("w:pPr")
|
|
541
|
+
is_numbered = pPr_list and pPr_list[0].getElementsByTagName("w:numPr")
|
|
542
|
+
|
|
543
|
+
if is_numbered:
|
|
544
|
+
# Add <w:del/> to w:rPr in w:pPr
|
|
545
|
+
pPr = pPr_list[0]
|
|
546
|
+
rPr_list = pPr.getElementsByTagName("w:rPr")
|
|
547
|
+
|
|
548
|
+
if not rPr_list:
|
|
549
|
+
rPr = self.dom.createElement("w:rPr")
|
|
550
|
+
pPr.appendChild(rPr)
|
|
551
|
+
else:
|
|
552
|
+
rPr = rPr_list[0]
|
|
553
|
+
|
|
554
|
+
# Add <w:del/> marker
|
|
555
|
+
del_marker = self.dom.createElement("w:del")
|
|
556
|
+
rPr.insertBefore(
|
|
557
|
+
del_marker, rPr.firstChild
|
|
558
|
+
) if rPr.firstChild else rPr.appendChild(del_marker)
|
|
559
|
+
|
|
560
|
+
# Convert w:t → w:delText in all runs
|
|
561
|
+
for t_elem in list(elem.getElementsByTagName("w:t")):
|
|
562
|
+
del_text = self.dom.createElement("w:delText")
|
|
563
|
+
# Copy ALL child nodes (not just firstChild) to handle entities
|
|
564
|
+
while t_elem.firstChild:
|
|
565
|
+
del_text.appendChild(t_elem.firstChild)
|
|
566
|
+
# Preserve attributes like xml:space
|
|
567
|
+
for i in range(t_elem.attributes.length):
|
|
568
|
+
attr = t_elem.attributes.item(i)
|
|
569
|
+
del_text.setAttribute(attr.name, attr.value)
|
|
570
|
+
t_elem.parentNode.replaceChild(del_text, t_elem)
|
|
571
|
+
|
|
572
|
+
# Update run attributes: w:rsidR → w:rsidDel
|
|
573
|
+
for run in elem.getElementsByTagName("w:r"):
|
|
574
|
+
if run.hasAttribute("w:rsidR"):
|
|
575
|
+
run.setAttribute("w:rsidDel", run.getAttribute("w:rsidR"))
|
|
576
|
+
run.removeAttribute("w:rsidR")
|
|
577
|
+
elif not run.hasAttribute("w:rsidDel"):
|
|
578
|
+
run.setAttribute("w:rsidDel", self.rsid)
|
|
579
|
+
|
|
580
|
+
# Wrap all non-pPr children in <w:del>
|
|
581
|
+
del_wrapper = self.dom.createElement("w:del")
|
|
582
|
+
for child in [c for c in elem.childNodes if c.nodeName != "w:pPr"]:
|
|
583
|
+
elem.removeChild(child)
|
|
584
|
+
del_wrapper.appendChild(child)
|
|
585
|
+
elem.appendChild(del_wrapper)
|
|
586
|
+
|
|
587
|
+
# Inject attributes to the deletion wrapper
|
|
588
|
+
self._inject_attributes_to_nodes([del_wrapper])
|
|
589
|
+
|
|
590
|
+
return elem
|
|
591
|
+
|
|
592
|
+
else:
|
|
593
|
+
raise ValueError(f"Element must be w:r or w:p, got {elem.nodeName}")
|
|
594
|
+
|
|
595
|
+
|
|
596
|
+
def _generate_hex_id() -> str:
|
|
597
|
+
"""Generate random 8-character hex ID for para/durable IDs.
|
|
598
|
+
|
|
599
|
+
Values are constrained to be less than 0x7FFFFFFF per OOXML spec:
|
|
600
|
+
- paraId must be < 0x80000000
|
|
601
|
+
- durableId must be < 0x7FFFFFFF
|
|
602
|
+
We use the stricter constraint (0x7FFFFFFF) for both.
|
|
603
|
+
"""
|
|
604
|
+
return f"{random.randint(1, 0x7FFFFFFE):08X}"
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def _generate_rsid() -> str:
|
|
608
|
+
"""Generate random 8-character hex RSID."""
|
|
609
|
+
return "".join(random.choices("0123456789ABCDEF", k=8))
|
|
610
|
+
|
|
611
|
+
|
|
612
|
+
class Document:
|
|
613
|
+
"""Manages comments in unpacked Word documents."""
|
|
614
|
+
|
|
615
|
+
def __init__(
|
|
616
|
+
self,
|
|
617
|
+
unpacked_dir,
|
|
618
|
+
rsid=None,
|
|
619
|
+
track_revisions=False,
|
|
620
|
+
author="Claude",
|
|
621
|
+
initials="C",
|
|
622
|
+
):
|
|
623
|
+
"""
|
|
624
|
+
Initialize with path to unpacked Word document directory.
|
|
625
|
+
Automatically sets up comment infrastructure (people.xml, RSIDs).
|
|
626
|
+
|
|
627
|
+
Args:
|
|
628
|
+
unpacked_dir: Path to unpacked DOCX directory (must contain word/ subdirectory)
|
|
629
|
+
rsid: Optional RSID to use for all comment elements. If not provided, one will be generated.
|
|
630
|
+
track_revisions: If True, enables track revisions in settings.xml (default: False)
|
|
631
|
+
author: Default author name for comments (default: "Claude")
|
|
632
|
+
initials: Default author initials for comments (default: "C")
|
|
633
|
+
"""
|
|
634
|
+
self.original_path = Path(unpacked_dir)
|
|
635
|
+
|
|
636
|
+
if not self.original_path.exists() or not self.original_path.is_dir():
|
|
637
|
+
raise ValueError(f"Directory not found: {unpacked_dir}")
|
|
638
|
+
|
|
639
|
+
# Create temporary directory with subdirectories for unpacked content and baseline
|
|
640
|
+
self.temp_dir = tempfile.mkdtemp(prefix="docx_")
|
|
641
|
+
self.unpacked_path = Path(self.temp_dir) / "unpacked"
|
|
642
|
+
shutil.copytree(self.original_path, self.unpacked_path)
|
|
643
|
+
|
|
644
|
+
# Pack original directory into temporary .docx for validation baseline (outside unpacked dir)
|
|
645
|
+
self.original_docx = Path(self.temp_dir) / "original.docx"
|
|
646
|
+
pack_document(self.original_path, self.original_docx, validate=False)
|
|
647
|
+
|
|
648
|
+
self.word_path = self.unpacked_path / "word"
|
|
649
|
+
|
|
650
|
+
# Generate RSID if not provided
|
|
651
|
+
self.rsid = rsid if rsid else _generate_rsid()
|
|
652
|
+
print(f"Using RSID: {self.rsid}")
|
|
653
|
+
|
|
654
|
+
# Set default author and initials
|
|
655
|
+
self.author = author
|
|
656
|
+
self.initials = initials
|
|
657
|
+
|
|
658
|
+
# Cache for lazy-loaded editors
|
|
659
|
+
self._editors = {}
|
|
660
|
+
|
|
661
|
+
# Comment file paths
|
|
662
|
+
self.comments_path = self.word_path / "comments.xml"
|
|
663
|
+
self.comments_extended_path = self.word_path / "commentsExtended.xml"
|
|
664
|
+
self.comments_ids_path = self.word_path / "commentsIds.xml"
|
|
665
|
+
self.comments_extensible_path = self.word_path / "commentsExtensible.xml"
|
|
666
|
+
|
|
667
|
+
# Load existing comments and determine next ID (before setup modifies files)
|
|
668
|
+
self.existing_comments = self._load_existing_comments()
|
|
669
|
+
self.next_comment_id = self._get_next_comment_id()
|
|
670
|
+
|
|
671
|
+
# Convenient access to document.xml editor (semi-private)
|
|
672
|
+
self._document = self["word/document.xml"]
|
|
673
|
+
|
|
674
|
+
# Setup tracked changes infrastructure
|
|
675
|
+
self._setup_tracking(track_revisions=track_revisions)
|
|
676
|
+
|
|
677
|
+
# Add author to people.xml
|
|
678
|
+
self._add_author_to_people(author)
|
|
679
|
+
|
|
680
|
+
def __getitem__(self, xml_path: str) -> DocxXMLEditor:
|
|
681
|
+
"""
|
|
682
|
+
Get or create a DocxXMLEditor for the specified XML file.
|
|
683
|
+
|
|
684
|
+
Enables lazy-loaded editors with bracket notation:
|
|
685
|
+
node = doc["word/document.xml"].get_node(tag="w:p", line_number=42)
|
|
686
|
+
|
|
687
|
+
Args:
|
|
688
|
+
xml_path: Relative path to XML file (e.g., "word/document.xml", "word/comments.xml")
|
|
689
|
+
|
|
690
|
+
Returns:
|
|
691
|
+
DocxXMLEditor instance for the specified file
|
|
692
|
+
|
|
693
|
+
Raises:
|
|
694
|
+
ValueError: If the file does not exist
|
|
695
|
+
|
|
696
|
+
Example:
|
|
697
|
+
# Get node from document.xml
|
|
698
|
+
node = doc["word/document.xml"].get_node(tag="w:del", attrs={"w:id": "1"})
|
|
699
|
+
|
|
700
|
+
# Get node from comments.xml
|
|
701
|
+
comment = doc["word/comments.xml"].get_node(tag="w:comment", attrs={"w:id": "0"})
|
|
702
|
+
"""
|
|
703
|
+
if xml_path not in self._editors:
|
|
704
|
+
file_path = self.unpacked_path / xml_path
|
|
705
|
+
if not file_path.exists():
|
|
706
|
+
raise ValueError(f"XML file not found: {xml_path}")
|
|
707
|
+
# Use DocxXMLEditor with RSID, author, and initials for all editors
|
|
708
|
+
self._editors[xml_path] = DocxXMLEditor(
|
|
709
|
+
file_path, rsid=self.rsid, author=self.author, initials=self.initials
|
|
710
|
+
)
|
|
711
|
+
return self._editors[xml_path]
|
|
712
|
+
|
|
713
|
+
def add_comment(self, start, end, text: str) -> int:
|
|
714
|
+
"""
|
|
715
|
+
Add a comment spanning from one element to another.
|
|
716
|
+
|
|
717
|
+
Args:
|
|
718
|
+
start: DOM element for the starting point
|
|
719
|
+
end: DOM element for the ending point
|
|
720
|
+
text: Comment content
|
|
721
|
+
|
|
722
|
+
Returns:
|
|
723
|
+
The comment ID that was created
|
|
724
|
+
|
|
725
|
+
Example:
|
|
726
|
+
start_node = cm.get_document_node(tag="w:del", id="1")
|
|
727
|
+
end_node = cm.get_document_node(tag="w:ins", id="2")
|
|
728
|
+
cm.add_comment(start=start_node, end=end_node, text="Explanation")
|
|
729
|
+
"""
|
|
730
|
+
comment_id = self.next_comment_id
|
|
731
|
+
para_id = _generate_hex_id()
|
|
732
|
+
durable_id = _generate_hex_id()
|
|
733
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
734
|
+
|
|
735
|
+
# Add comment ranges to document.xml immediately
|
|
736
|
+
self._document.insert_before(start, self._comment_range_start_xml(comment_id))
|
|
737
|
+
|
|
738
|
+
# If end node is a paragraph, append comment markup inside it
|
|
739
|
+
# Otherwise insert after it (for run-level anchors)
|
|
740
|
+
if end.tagName == "w:p":
|
|
741
|
+
self._document.append_to(end, self._comment_range_end_xml(comment_id))
|
|
742
|
+
else:
|
|
743
|
+
self._document.insert_after(end, self._comment_range_end_xml(comment_id))
|
|
744
|
+
|
|
745
|
+
# Add to comments.xml immediately
|
|
746
|
+
self._add_to_comments_xml(
|
|
747
|
+
comment_id, para_id, text, self.author, self.initials, timestamp
|
|
748
|
+
)
|
|
749
|
+
|
|
750
|
+
# Add to commentsExtended.xml immediately
|
|
751
|
+
self._add_to_comments_extended_xml(para_id, parent_para_id=None)
|
|
752
|
+
|
|
753
|
+
# Add to commentsIds.xml immediately
|
|
754
|
+
self._add_to_comments_ids_xml(para_id, durable_id)
|
|
755
|
+
|
|
756
|
+
# Add to commentsExtensible.xml immediately
|
|
757
|
+
self._add_to_comments_extensible_xml(durable_id)
|
|
758
|
+
|
|
759
|
+
# Update existing_comments so replies work
|
|
760
|
+
self.existing_comments[comment_id] = {"para_id": para_id}
|
|
761
|
+
|
|
762
|
+
self.next_comment_id += 1
|
|
763
|
+
return comment_id
|
|
764
|
+
|
|
765
|
+
def reply_to_comment(
|
|
766
|
+
self,
|
|
767
|
+
parent_comment_id: int,
|
|
768
|
+
text: str,
|
|
769
|
+
) -> int:
|
|
770
|
+
"""
|
|
771
|
+
Add a reply to an existing comment.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
parent_comment_id: The w:id of the parent comment to reply to
|
|
775
|
+
text: Reply text
|
|
776
|
+
|
|
777
|
+
Returns:
|
|
778
|
+
The comment ID that was created for the reply
|
|
779
|
+
|
|
780
|
+
Example:
|
|
781
|
+
cm.reply_to_comment(parent_comment_id=0, text="I agree with this change")
|
|
782
|
+
"""
|
|
783
|
+
if parent_comment_id not in self.existing_comments:
|
|
784
|
+
raise ValueError(f"Parent comment with id={parent_comment_id} not found")
|
|
785
|
+
|
|
786
|
+
parent_info = self.existing_comments[parent_comment_id]
|
|
787
|
+
comment_id = self.next_comment_id
|
|
788
|
+
para_id = _generate_hex_id()
|
|
789
|
+
durable_id = _generate_hex_id()
|
|
790
|
+
timestamp = datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
|
|
791
|
+
|
|
792
|
+
# Add comment ranges to document.xml immediately
|
|
793
|
+
parent_start_elem = self._document.get_node(
|
|
794
|
+
tag="w:commentRangeStart", attrs={"w:id": str(parent_comment_id)}
|
|
795
|
+
)
|
|
796
|
+
parent_ref_elem = self._document.get_node(
|
|
797
|
+
tag="w:commentReference", attrs={"w:id": str(parent_comment_id)}
|
|
798
|
+
)
|
|
799
|
+
|
|
800
|
+
self._document.insert_after(
|
|
801
|
+
parent_start_elem, self._comment_range_start_xml(comment_id)
|
|
802
|
+
)
|
|
803
|
+
parent_ref_run = parent_ref_elem.parentNode
|
|
804
|
+
self._document.insert_after(
|
|
805
|
+
parent_ref_run, f'<w:commentRangeEnd w:id="{comment_id}"/>'
|
|
806
|
+
)
|
|
807
|
+
self._document.insert_after(
|
|
808
|
+
parent_ref_run, self._comment_ref_run_xml(comment_id)
|
|
809
|
+
)
|
|
810
|
+
|
|
811
|
+
# Add to comments.xml immediately
|
|
812
|
+
self._add_to_comments_xml(
|
|
813
|
+
comment_id, para_id, text, self.author, self.initials, timestamp
|
|
814
|
+
)
|
|
815
|
+
|
|
816
|
+
# Add to commentsExtended.xml immediately (with parent)
|
|
817
|
+
self._add_to_comments_extended_xml(
|
|
818
|
+
para_id, parent_para_id=parent_info["para_id"]
|
|
819
|
+
)
|
|
820
|
+
|
|
821
|
+
# Add to commentsIds.xml immediately
|
|
822
|
+
self._add_to_comments_ids_xml(para_id, durable_id)
|
|
823
|
+
|
|
824
|
+
# Add to commentsExtensible.xml immediately
|
|
825
|
+
self._add_to_comments_extensible_xml(durable_id)
|
|
826
|
+
|
|
827
|
+
# Update existing_comments so replies work
|
|
828
|
+
self.existing_comments[comment_id] = {"para_id": para_id}
|
|
829
|
+
|
|
830
|
+
self.next_comment_id += 1
|
|
831
|
+
return comment_id
|
|
832
|
+
|
|
833
|
+
def __del__(self):
|
|
834
|
+
"""Clean up temporary directory on deletion."""
|
|
835
|
+
if hasattr(self, "temp_dir") and Path(self.temp_dir).exists():
|
|
836
|
+
shutil.rmtree(self.temp_dir)
|
|
837
|
+
|
|
838
|
+
def validate(self) -> None:
|
|
839
|
+
"""
|
|
840
|
+
Validate the document against XSD schema and redlining rules.
|
|
841
|
+
|
|
842
|
+
Raises:
|
|
843
|
+
ValueError: If validation fails.
|
|
844
|
+
"""
|
|
845
|
+
# Create validators with current state
|
|
846
|
+
schema_validator = DOCXSchemaValidator(
|
|
847
|
+
self.unpacked_path, self.original_docx, verbose=False
|
|
848
|
+
)
|
|
849
|
+
redlining_validator = RedliningValidator(
|
|
850
|
+
self.unpacked_path, self.original_docx, verbose=False
|
|
851
|
+
)
|
|
852
|
+
|
|
853
|
+
# Run validations
|
|
854
|
+
if not schema_validator.validate():
|
|
855
|
+
raise ValueError("Schema validation failed")
|
|
856
|
+
if not redlining_validator.validate():
|
|
857
|
+
raise ValueError("Redlining validation failed")
|
|
858
|
+
|
|
859
|
+
def save(self, destination=None, validate=True) -> None:
|
|
860
|
+
"""
|
|
861
|
+
Save all modified XML files to disk and copy to destination directory.
|
|
862
|
+
|
|
863
|
+
This persists all changes made via add_comment() and reply_to_comment().
|
|
864
|
+
|
|
865
|
+
Args:
|
|
866
|
+
destination: Optional path to save to. If None, saves back to original directory.
|
|
867
|
+
validate: If True, validates document before saving (default: True).
|
|
868
|
+
"""
|
|
869
|
+
# Only ensure comment relationships and content types if comment files exist
|
|
870
|
+
if self.comments_path.exists():
|
|
871
|
+
self._ensure_comment_relationships()
|
|
872
|
+
self._ensure_comment_content_types()
|
|
873
|
+
|
|
874
|
+
# Save all modified XML files in temp directory
|
|
875
|
+
for editor in self._editors.values():
|
|
876
|
+
editor.save()
|
|
877
|
+
|
|
878
|
+
# Validate by default
|
|
879
|
+
if validate:
|
|
880
|
+
self.validate()
|
|
881
|
+
|
|
882
|
+
# Copy contents from temp directory to destination (or original directory)
|
|
883
|
+
target_path = Path(destination) if destination else self.original_path
|
|
884
|
+
shutil.copytree(self.unpacked_path, target_path, dirs_exist_ok=True)
|
|
885
|
+
|
|
886
|
+
# ==================== Private: Initialization ====================
|
|
887
|
+
|
|
888
|
+
def _get_next_comment_id(self):
|
|
889
|
+
"""Get the next available comment ID."""
|
|
890
|
+
if not self.comments_path.exists():
|
|
891
|
+
return 0
|
|
892
|
+
|
|
893
|
+
editor = self["word/comments.xml"]
|
|
894
|
+
max_id = -1
|
|
895
|
+
for comment_elem in editor.dom.getElementsByTagName("w:comment"):
|
|
896
|
+
comment_id = comment_elem.getAttribute("w:id")
|
|
897
|
+
if comment_id:
|
|
898
|
+
try:
|
|
899
|
+
max_id = max(max_id, int(comment_id))
|
|
900
|
+
except ValueError:
|
|
901
|
+
pass
|
|
902
|
+
return max_id + 1
|
|
903
|
+
|
|
904
|
+
def _load_existing_comments(self):
|
|
905
|
+
"""Load existing comments from files to enable replies."""
|
|
906
|
+
if not self.comments_path.exists():
|
|
907
|
+
return {}
|
|
908
|
+
|
|
909
|
+
editor = self["word/comments.xml"]
|
|
910
|
+
existing = {}
|
|
911
|
+
|
|
912
|
+
for comment_elem in editor.dom.getElementsByTagName("w:comment"):
|
|
913
|
+
comment_id = comment_elem.getAttribute("w:id")
|
|
914
|
+
if not comment_id:
|
|
915
|
+
continue
|
|
916
|
+
|
|
917
|
+
# Find para_id from the w:p element within the comment
|
|
918
|
+
para_id = None
|
|
919
|
+
for p_elem in comment_elem.getElementsByTagName("w:p"):
|
|
920
|
+
para_id = p_elem.getAttribute("w14:paraId")
|
|
921
|
+
if para_id:
|
|
922
|
+
break
|
|
923
|
+
|
|
924
|
+
if not para_id:
|
|
925
|
+
continue
|
|
926
|
+
|
|
927
|
+
existing[int(comment_id)] = {"para_id": para_id}
|
|
928
|
+
|
|
929
|
+
return existing
|
|
930
|
+
|
|
931
|
+
# ==================== Private: Setup Methods ====================
|
|
932
|
+
|
|
933
|
+
def _setup_tracking(self, track_revisions=False):
|
|
934
|
+
"""Set up comment infrastructure in unpacked directory.
|
|
935
|
+
|
|
936
|
+
Args:
|
|
937
|
+
track_revisions: If True, enables track revisions in settings.xml
|
|
938
|
+
"""
|
|
939
|
+
# Create or update word/people.xml
|
|
940
|
+
people_file = self.word_path / "people.xml"
|
|
941
|
+
self._update_people_xml(people_file)
|
|
942
|
+
|
|
943
|
+
# Update XML files
|
|
944
|
+
self._add_content_type_for_people(self.unpacked_path / "[Content_Types].xml")
|
|
945
|
+
self._add_relationship_for_people(
|
|
946
|
+
self.word_path / "_rels" / "document.xml.rels"
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
# Always add RSID to settings.xml, optionally enable trackRevisions
|
|
950
|
+
self._update_settings(
|
|
951
|
+
self.word_path / "settings.xml", track_revisions=track_revisions
|
|
952
|
+
)
|
|
953
|
+
|
|
954
|
+
def _update_people_xml(self, path):
|
|
955
|
+
"""Create people.xml if it doesn't exist."""
|
|
956
|
+
if not path.exists():
|
|
957
|
+
# Copy from template
|
|
958
|
+
shutil.copy(TEMPLATE_DIR / "people.xml", path)
|
|
959
|
+
|
|
960
|
+
def _add_content_type_for_people(self, path):
|
|
961
|
+
"""Add people.xml content type to [Content_Types].xml if not already present."""
|
|
962
|
+
editor = self["[Content_Types].xml"]
|
|
963
|
+
|
|
964
|
+
if self._has_override(editor, "/word/people.xml"):
|
|
965
|
+
return
|
|
966
|
+
|
|
967
|
+
# Add Override element
|
|
968
|
+
root = editor.dom.documentElement
|
|
969
|
+
override_xml = '<Override PartName="/word/people.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.people+xml"/>'
|
|
970
|
+
editor.append_to(root, override_xml)
|
|
971
|
+
|
|
972
|
+
def _add_relationship_for_people(self, path):
|
|
973
|
+
"""Add people.xml relationship to document.xml.rels if not already present."""
|
|
974
|
+
editor = self["word/_rels/document.xml.rels"]
|
|
975
|
+
|
|
976
|
+
if self._has_relationship(editor, "people.xml"):
|
|
977
|
+
return
|
|
978
|
+
|
|
979
|
+
root = editor.dom.documentElement
|
|
980
|
+
root_tag = root.tagName # type: ignore
|
|
981
|
+
prefix = root_tag.split(":")[0] + ":" if ":" in root_tag else ""
|
|
982
|
+
next_rid = editor.get_next_rid()
|
|
983
|
+
|
|
984
|
+
# Create the relationship entry
|
|
985
|
+
rel_xml = f'<{prefix}Relationship Id="{next_rid}" Type="http://schemas.microsoft.com/office/2011/relationships/people" Target="people.xml"/>'
|
|
986
|
+
editor.append_to(root, rel_xml)
|
|
987
|
+
|
|
988
|
+
def _update_settings(self, path, track_revisions=False):
|
|
989
|
+
"""Add RSID and optionally enable track revisions in settings.xml.
|
|
990
|
+
|
|
991
|
+
Args:
|
|
992
|
+
path: Path to settings.xml
|
|
993
|
+
track_revisions: If True, adds trackRevisions element
|
|
994
|
+
|
|
995
|
+
Places elements per OOXML schema order:
|
|
996
|
+
- trackRevisions: early (before defaultTabStop)
|
|
997
|
+
- rsids: late (after compat)
|
|
998
|
+
"""
|
|
999
|
+
editor = self["word/settings.xml"]
|
|
1000
|
+
root = editor.get_node(tag="w:settings")
|
|
1001
|
+
prefix = root.tagName.split(":")[0] if ":" in root.tagName else "w"
|
|
1002
|
+
|
|
1003
|
+
# Conditionally add trackRevisions if requested
|
|
1004
|
+
if track_revisions:
|
|
1005
|
+
track_revisions_exists = any(
|
|
1006
|
+
elem.tagName == f"{prefix}:trackRevisions"
|
|
1007
|
+
for elem in editor.dom.getElementsByTagName(f"{prefix}:trackRevisions")
|
|
1008
|
+
)
|
|
1009
|
+
|
|
1010
|
+
if not track_revisions_exists:
|
|
1011
|
+
track_rev_xml = f"<{prefix}:trackRevisions/>"
|
|
1012
|
+
# Try to insert before documentProtection, defaultTabStop, or at start
|
|
1013
|
+
inserted = False
|
|
1014
|
+
for tag in [f"{prefix}:documentProtection", f"{prefix}:defaultTabStop"]:
|
|
1015
|
+
elements = editor.dom.getElementsByTagName(tag)
|
|
1016
|
+
if elements:
|
|
1017
|
+
editor.insert_before(elements[0], track_rev_xml)
|
|
1018
|
+
inserted = True
|
|
1019
|
+
break
|
|
1020
|
+
if not inserted:
|
|
1021
|
+
# Insert as first child of settings
|
|
1022
|
+
if root.firstChild:
|
|
1023
|
+
editor.insert_before(root.firstChild, track_rev_xml)
|
|
1024
|
+
else:
|
|
1025
|
+
editor.append_to(root, track_rev_xml)
|
|
1026
|
+
|
|
1027
|
+
# Always check if rsids section exists
|
|
1028
|
+
rsids_elements = editor.dom.getElementsByTagName(f"{prefix}:rsids")
|
|
1029
|
+
|
|
1030
|
+
if not rsids_elements:
|
|
1031
|
+
# Add new rsids section
|
|
1032
|
+
rsids_xml = f'''<{prefix}:rsids>
|
|
1033
|
+
<{prefix}:rsidRoot {prefix}:val="{self.rsid}"/>
|
|
1034
|
+
<{prefix}:rsid {prefix}:val="{self.rsid}"/>
|
|
1035
|
+
</{prefix}:rsids>'''
|
|
1036
|
+
|
|
1037
|
+
# Try to insert after compat, before clrSchemeMapping, or before closing tag
|
|
1038
|
+
inserted = False
|
|
1039
|
+
compat_elements = editor.dom.getElementsByTagName(f"{prefix}:compat")
|
|
1040
|
+
if compat_elements:
|
|
1041
|
+
editor.insert_after(compat_elements[0], rsids_xml)
|
|
1042
|
+
inserted = True
|
|
1043
|
+
|
|
1044
|
+
if not inserted:
|
|
1045
|
+
clr_elements = editor.dom.getElementsByTagName(
|
|
1046
|
+
f"{prefix}:clrSchemeMapping"
|
|
1047
|
+
)
|
|
1048
|
+
if clr_elements:
|
|
1049
|
+
editor.insert_before(clr_elements[0], rsids_xml)
|
|
1050
|
+
inserted = True
|
|
1051
|
+
|
|
1052
|
+
if not inserted:
|
|
1053
|
+
editor.append_to(root, rsids_xml)
|
|
1054
|
+
else:
|
|
1055
|
+
# Check if this rsid already exists
|
|
1056
|
+
rsids_elem = rsids_elements[0]
|
|
1057
|
+
rsid_exists = any(
|
|
1058
|
+
elem.getAttribute(f"{prefix}:val") == self.rsid
|
|
1059
|
+
for elem in rsids_elem.getElementsByTagName(f"{prefix}:rsid")
|
|
1060
|
+
)
|
|
1061
|
+
|
|
1062
|
+
if not rsid_exists:
|
|
1063
|
+
rsid_xml = f'<{prefix}:rsid {prefix}:val="{self.rsid}"/>'
|
|
1064
|
+
editor.append_to(rsids_elem, rsid_xml)
|
|
1065
|
+
|
|
1066
|
+
# ==================== Private: XML File Creation ====================
|
|
1067
|
+
|
|
1068
|
+
def _add_to_comments_xml(
|
|
1069
|
+
self, comment_id, para_id, text, author, initials, timestamp
|
|
1070
|
+
):
|
|
1071
|
+
"""Add a single comment to comments.xml."""
|
|
1072
|
+
if not self.comments_path.exists():
|
|
1073
|
+
shutil.copy(TEMPLATE_DIR / "comments.xml", self.comments_path)
|
|
1074
|
+
|
|
1075
|
+
editor = self["word/comments.xml"]
|
|
1076
|
+
root = editor.get_node(tag="w:comments")
|
|
1077
|
+
|
|
1078
|
+
escaped_text = (
|
|
1079
|
+
text.replace("&", "&").replace("<", "<").replace(">", ">")
|
|
1080
|
+
)
|
|
1081
|
+
# Note: w:rsidR, w:rsidRDefault, w:rsidP on w:p, w:rsidR on w:r,
|
|
1082
|
+
# and w:author, w:date, w:initials on w:comment are automatically added by DocxXMLEditor
|
|
1083
|
+
comment_xml = f'''<w:comment w:id="{comment_id}">
|
|
1084
|
+
<w:p w14:paraId="{para_id}" w14:textId="77777777">
|
|
1085
|
+
<w:r><w:rPr><w:rStyle w:val="CommentReference"/></w:rPr><w:annotationRef/></w:r>
|
|
1086
|
+
<w:r><w:rPr><w:color w:val="000000"/><w:sz w:val="20"/><w:szCs w:val="20"/></w:rPr><w:t>{escaped_text}</w:t></w:r>
|
|
1087
|
+
</w:p>
|
|
1088
|
+
</w:comment>'''
|
|
1089
|
+
editor.append_to(root, comment_xml)
|
|
1090
|
+
|
|
1091
|
+
def _add_to_comments_extended_xml(self, para_id, parent_para_id):
|
|
1092
|
+
"""Add a single comment to commentsExtended.xml."""
|
|
1093
|
+
if not self.comments_extended_path.exists():
|
|
1094
|
+
shutil.copy(
|
|
1095
|
+
TEMPLATE_DIR / "commentsExtended.xml", self.comments_extended_path
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
editor = self["word/commentsExtended.xml"]
|
|
1099
|
+
root = editor.get_node(tag="w15:commentsEx")
|
|
1100
|
+
|
|
1101
|
+
if parent_para_id:
|
|
1102
|
+
xml = f'<w15:commentEx w15:paraId="{para_id}" w15:paraIdParent="{parent_para_id}" w15:done="0"/>'
|
|
1103
|
+
else:
|
|
1104
|
+
xml = f'<w15:commentEx w15:paraId="{para_id}" w15:done="0"/>'
|
|
1105
|
+
editor.append_to(root, xml)
|
|
1106
|
+
|
|
1107
|
+
def _add_to_comments_ids_xml(self, para_id, durable_id):
|
|
1108
|
+
"""Add a single comment to commentsIds.xml."""
|
|
1109
|
+
if not self.comments_ids_path.exists():
|
|
1110
|
+
shutil.copy(TEMPLATE_DIR / "commentsIds.xml", self.comments_ids_path)
|
|
1111
|
+
|
|
1112
|
+
editor = self["word/commentsIds.xml"]
|
|
1113
|
+
root = editor.get_node(tag="w16cid:commentsIds")
|
|
1114
|
+
|
|
1115
|
+
xml = f'<w16cid:commentId w16cid:paraId="{para_id}" w16cid:durableId="{durable_id}"/>'
|
|
1116
|
+
editor.append_to(root, xml)
|
|
1117
|
+
|
|
1118
|
+
def _add_to_comments_extensible_xml(self, durable_id):
|
|
1119
|
+
"""Add a single comment to commentsExtensible.xml."""
|
|
1120
|
+
if not self.comments_extensible_path.exists():
|
|
1121
|
+
shutil.copy(
|
|
1122
|
+
TEMPLATE_DIR / "commentsExtensible.xml", self.comments_extensible_path
|
|
1123
|
+
)
|
|
1124
|
+
|
|
1125
|
+
editor = self["word/commentsExtensible.xml"]
|
|
1126
|
+
root = editor.get_node(tag="w16cex:commentsExtensible")
|
|
1127
|
+
|
|
1128
|
+
xml = f'<w16cex:commentExtensible w16cex:durableId="{durable_id}"/>'
|
|
1129
|
+
editor.append_to(root, xml)
|
|
1130
|
+
|
|
1131
|
+
# ==================== Private: XML Fragments ====================
|
|
1132
|
+
|
|
1133
|
+
def _comment_range_start_xml(self, comment_id):
|
|
1134
|
+
"""Generate XML for comment range start."""
|
|
1135
|
+
return f'<w:commentRangeStart w:id="{comment_id}"/>'
|
|
1136
|
+
|
|
1137
|
+
def _comment_range_end_xml(self, comment_id):
|
|
1138
|
+
"""Generate XML for comment range end with reference run.
|
|
1139
|
+
|
|
1140
|
+
Note: w:rsidR is automatically added by DocxXMLEditor.
|
|
1141
|
+
"""
|
|
1142
|
+
return f'''<w:commentRangeEnd w:id="{comment_id}"/>
|
|
1143
|
+
<w:r>
|
|
1144
|
+
<w:rPr><w:rStyle w:val="CommentReference"/></w:rPr>
|
|
1145
|
+
<w:commentReference w:id="{comment_id}"/>
|
|
1146
|
+
</w:r>'''
|
|
1147
|
+
|
|
1148
|
+
def _comment_ref_run_xml(self, comment_id):
|
|
1149
|
+
"""Generate XML for comment reference run.
|
|
1150
|
+
|
|
1151
|
+
Note: w:rsidR is automatically added by DocxXMLEditor.
|
|
1152
|
+
"""
|
|
1153
|
+
return f'''<w:r>
|
|
1154
|
+
<w:rPr><w:rStyle w:val="CommentReference"/></w:rPr>
|
|
1155
|
+
<w:commentReference w:id="{comment_id}"/>
|
|
1156
|
+
</w:r>'''
|
|
1157
|
+
|
|
1158
|
+
# ==================== Private: Metadata Updates ====================
|
|
1159
|
+
|
|
1160
|
+
def _has_relationship(self, editor, target):
|
|
1161
|
+
"""Check if a relationship with given target exists."""
|
|
1162
|
+
for rel_elem in editor.dom.getElementsByTagName("Relationship"):
|
|
1163
|
+
if rel_elem.getAttribute("Target") == target:
|
|
1164
|
+
return True
|
|
1165
|
+
return False
|
|
1166
|
+
|
|
1167
|
+
def _has_override(self, editor, part_name):
|
|
1168
|
+
"""Check if an override with given part name exists."""
|
|
1169
|
+
for override_elem in editor.dom.getElementsByTagName("Override"):
|
|
1170
|
+
if override_elem.getAttribute("PartName") == part_name:
|
|
1171
|
+
return True
|
|
1172
|
+
return False
|
|
1173
|
+
|
|
1174
|
+
def _has_author(self, editor, author):
|
|
1175
|
+
"""Check if an author already exists in people.xml."""
|
|
1176
|
+
for person_elem in editor.dom.getElementsByTagName("w15:person"):
|
|
1177
|
+
if person_elem.getAttribute("w15:author") == author:
|
|
1178
|
+
return True
|
|
1179
|
+
return False
|
|
1180
|
+
|
|
1181
|
+
def _add_author_to_people(self, author):
|
|
1182
|
+
"""Add author to people.xml (called during initialization)."""
|
|
1183
|
+
people_path = self.word_path / "people.xml"
|
|
1184
|
+
|
|
1185
|
+
# people.xml should already exist from _setup_tracking
|
|
1186
|
+
if not people_path.exists():
|
|
1187
|
+
raise ValueError("people.xml should exist after _setup_tracking")
|
|
1188
|
+
|
|
1189
|
+
editor = self["word/people.xml"]
|
|
1190
|
+
root = editor.get_node(tag="w15:people")
|
|
1191
|
+
|
|
1192
|
+
# Check if author already exists
|
|
1193
|
+
if self._has_author(editor, author):
|
|
1194
|
+
return
|
|
1195
|
+
|
|
1196
|
+
# Add author with proper XML escaping to prevent injection
|
|
1197
|
+
escaped_author = html.escape(author, quote=True)
|
|
1198
|
+
person_xml = f'''<w15:person w15:author="{escaped_author}">
|
|
1199
|
+
<w15:presenceInfo w15:providerId="None" w15:userId="{escaped_author}"/>
|
|
1200
|
+
</w15:person>'''
|
|
1201
|
+
editor.append_to(root, person_xml)
|
|
1202
|
+
|
|
1203
|
+
def _ensure_comment_relationships(self):
|
|
1204
|
+
"""Ensure word/_rels/document.xml.rels has comment relationships."""
|
|
1205
|
+
editor = self["word/_rels/document.xml.rels"]
|
|
1206
|
+
|
|
1207
|
+
if self._has_relationship(editor, "comments.xml"):
|
|
1208
|
+
return
|
|
1209
|
+
|
|
1210
|
+
root = editor.dom.documentElement
|
|
1211
|
+
root_tag = root.tagName # type: ignore
|
|
1212
|
+
prefix = root_tag.split(":")[0] + ":" if ":" in root_tag else ""
|
|
1213
|
+
next_rid_num = int(editor.get_next_rid()[3:])
|
|
1214
|
+
|
|
1215
|
+
# Add relationship elements
|
|
1216
|
+
rels = [
|
|
1217
|
+
(
|
|
1218
|
+
next_rid_num,
|
|
1219
|
+
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/comments",
|
|
1220
|
+
"comments.xml",
|
|
1221
|
+
),
|
|
1222
|
+
(
|
|
1223
|
+
next_rid_num + 1,
|
|
1224
|
+
"http://schemas.microsoft.com/office/2011/relationships/commentsExtended",
|
|
1225
|
+
"commentsExtended.xml",
|
|
1226
|
+
),
|
|
1227
|
+
(
|
|
1228
|
+
next_rid_num + 2,
|
|
1229
|
+
"http://schemas.microsoft.com/office/2016/09/relationships/commentsIds",
|
|
1230
|
+
"commentsIds.xml",
|
|
1231
|
+
),
|
|
1232
|
+
(
|
|
1233
|
+
next_rid_num + 3,
|
|
1234
|
+
"http://schemas.microsoft.com/office/2018/08/relationships/commentsExtensible",
|
|
1235
|
+
"commentsExtensible.xml",
|
|
1236
|
+
),
|
|
1237
|
+
]
|
|
1238
|
+
|
|
1239
|
+
for rel_id, rel_type, target in rels:
|
|
1240
|
+
rel_xml = f'<{prefix}Relationship Id="rId{rel_id}" Type="{rel_type}" Target="{target}"/>'
|
|
1241
|
+
editor.append_to(root, rel_xml)
|
|
1242
|
+
|
|
1243
|
+
def _ensure_comment_content_types(self):
|
|
1244
|
+
"""Ensure [Content_Types].xml has comment content types."""
|
|
1245
|
+
editor = self["[Content_Types].xml"]
|
|
1246
|
+
|
|
1247
|
+
if self._has_override(editor, "/word/comments.xml"):
|
|
1248
|
+
return
|
|
1249
|
+
|
|
1250
|
+
root = editor.dom.documentElement
|
|
1251
|
+
|
|
1252
|
+
# Add Override elements
|
|
1253
|
+
overrides = [
|
|
1254
|
+
(
|
|
1255
|
+
"/word/comments.xml",
|
|
1256
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.comments+xml",
|
|
1257
|
+
),
|
|
1258
|
+
(
|
|
1259
|
+
"/word/commentsExtended.xml",
|
|
1260
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtended+xml",
|
|
1261
|
+
),
|
|
1262
|
+
(
|
|
1263
|
+
"/word/commentsIds.xml",
|
|
1264
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsIds+xml",
|
|
1265
|
+
),
|
|
1266
|
+
(
|
|
1267
|
+
"/word/commentsExtensible.xml",
|
|
1268
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.commentsExtensible+xml",
|
|
1269
|
+
),
|
|
1270
|
+
]
|
|
1271
|
+
|
|
1272
|
+
for part_name, content_type in overrides:
|
|
1273
|
+
override_xml = (
|
|
1274
|
+
f'<Override PartName="{part_name}" ContentType="{content_type}"/>'
|
|
1275
|
+
)
|
|
1276
|
+
editor.append_to(root, override_xml)
|