ite-agent 0.0.23__tar.gz
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.
- ite_agent-0.0.23/.agents/skills/docx/SKILL.md +107 -0
- ite_agent-0.0.23/.agents/skills/docx/references/document-recipes.md +12 -0
- ite_agent-0.0.23/.agents/skills/docx/references/ooxml-notes.md +10 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/__init__.py +1 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/_docx_core.py +223 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/extract_docx.py +40 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/inspect_docx.py +20 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/pack_docx.py +21 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/render_docx.py +70 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/replace_text.py +39 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/unpack_docx.py +21 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/validate_docx.py +24 -0
- ite_agent-0.0.23/.agents/skills/docx/scripts/write_docx.py +40 -0
- ite_agent-0.0.23/.agents/skills/docx/templates/letter.md +11 -0
- ite_agent-0.0.23/.agents/skills/docx/templates/memo.md +13 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/SKILL.md +292 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/agents/openai.yaml +4 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/color-and-contrast.md +132 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/interaction-design.md +123 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/motion-design.md +99 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/responsive-design.md +114 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/spatial-design.md +100 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/typography.md +133 -0
- ite_agent-0.0.23/.agents/skills/frontend-design/references/ux-writing.md +107 -0
- ite_agent-0.0.23/.agents/skills/pdf/LICENSE.txt +201 -0
- ite_agent-0.0.23/.agents/skills/pdf/SKILL.md +78 -0
- ite_agent-0.0.23/.agents/skills/pdf/references/layout-review.md +13 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/__init__.py +1 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/_pdf_core.py +113 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/extract_pdf_text.py +24 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/inspect_pdf.py +20 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/render_pdf.py +33 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/validate_pdf.py +24 -0
- ite_agent-0.0.23/.agents/skills/pdf/scripts/write_pdf.py +26 -0
- ite_agent-0.0.23/.agents/skills/pdf/templates/report.txt +13 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/SKILL.md +111 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/agents/openai.yaml +4 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/references/common_bug_patterns.md +342 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/references/security_patterns.md +250 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/references/test_guidelines.md +394 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/scripts/audit_test_coverage.py +155 -0
- ite_agent-0.0.23/.agents/skills/repo-audit/scripts/find_common_bugs.py +304 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/LICENSE.txt +202 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/SKILL.md +519 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/agents/openai.yaml +4 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/references/openai_yaml.md +43 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/scripts/generate_openai_yaml.py +225 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/scripts/init_skill.py +397 -0
- ite_agent-0.0.23/.agents/skills/skill-creator/scripts/quick_validate.py +101 -0
- ite_agent-0.0.23/.gitignore +51 -0
- ite_agent-0.0.23/DESIGN.md +719 -0
- ite_agent-0.0.23/INSTALLATION.md +93 -0
- ite_agent-0.0.23/OVERVIEW.md +352 -0
- ite_agent-0.0.23/PKG-INFO +250 -0
- ite_agent-0.0.23/README.md +215 -0
- ite_agent-0.0.23/docs/BYOK_QUICKSTART.md +71 -0
- ite_agent-0.0.23/docs/CONTINUATION_HARDENING_PLAN_2026-04-16.md +263 -0
- ite_agent-0.0.23/docs/PRODUCTION_DEPLOYMENT.md +145 -0
- ite_agent-0.0.23/docs/PROD_LAUNCH_BYOK.md +136 -0
- ite_agent-0.0.23/docs/RELEASE_BUMP_WORKFLOW.md +202 -0
- ite_agent-0.0.23/docs/SHELL_AND_WEB_CAPABILITIES.md +111 -0
- ite_agent-0.0.23/docs/SHIP_TODAY_PLAN_2026-04-16.md +231 -0
- ite_agent-0.0.23/docs/SKILLS.md +133 -0
- ite_agent-0.0.23/docs/SUBAGENT_PRODUCTION_READINESS.md +244 -0
- ite_agent-0.0.23/docs/TOOLS.md +386 -0
- ite_agent-0.0.23/docs/WORK_LOG_2026-04-14.md +40 -0
- ite_agent-0.0.23/docs/architecture/context-runtime-roadmap.md +256 -0
- ite_agent-0.0.23/docs/architecture/csrc-vs-ite-runtime-handoff.md +243 -0
- ite_agent-0.0.23/docs/bugs/interrupt-clears-content.md +107 -0
- ite_agent-0.0.23/docs/fix_idb.py +36 -0
- ite_agent-0.0.23/docs/fix_ios_simulator_mcp.md +74 -0
- ite_agent-0.0.23/docs/launch-real-life-eval.md +482 -0
- ite_agent-0.0.23/docs/live-memory-and-context-eval-pass.md +374 -0
- ite_agent-0.0.23/docs/manual-context-runtime-live-tests.md +214 -0
- ite_agent-0.0.23/docs/manual-git-tool-tests.md +290 -0
- ite_agent-0.0.23/docs/manual-skills-test-plan.md +618 -0
- ite_agent-0.0.23/docs/manual-verification-json-tool-tests.md +473 -0
- ite_agent-0.0.23/docs/poem.txt +21 -0
- ite_agent-0.0.23/docs/reup-inchat-reactive-cards.md +96 -0
- ite_agent-0.0.23/docs/reup-live-test-runbook.md +219 -0
- ite_agent-0.0.23/docs/reup-modal-click-note.md +54 -0
- ite_agent-0.0.23/docs/tool-implementation-plan.md +430 -0
- ite_agent-0.0.23/docs/tool-parallel-execution-plan.md +135 -0
- ite_agent-0.0.23/docs/tool-research.md +459 -0
- ite_agent-0.0.23/landing-site/README.md +30 -0
- ite_agent-0.0.23/landing-site/favicon.svg +5 -0
- ite_agent-0.0.23/landing-site/index.html +143 -0
- ite_agent-0.0.23/landing-site/ite-prev.png +0 -0
- ite_agent-0.0.23/landing-site/ite-preview.png +0 -0
- ite_agent-0.0.23/landing-site/ite.css +842 -0
- ite_agent-0.0.23/landing-site/ite.js +138 -0
- ite_agent-0.0.23/landing-site/netlify/functions/waitlist.mjs +56 -0
- ite_agent-0.0.23/landing-site/netlify.toml +6 -0
- ite_agent-0.0.23/landing-site/package.json +12 -0
- ite_agent-0.0.23/landing-site/server/waitlist-core.mjs +196 -0
- ite_agent-0.0.23/pyproject.toml +31 -0
- ite_agent-0.0.23/src/ite/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/agent/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/agent/agent.py +2345 -0
- ite_agent-0.0.23/src/ite/agent/change_history.py +315 -0
- ite_agent-0.0.23/src/ite/agent/events.py +202 -0
- ite_agent-0.0.23/src/ite/agent/session.py +753 -0
- ite_agent-0.0.23/src/ite/agent/session_manager.py +411 -0
- ite_agent-0.0.23/src/ite/agent/subagent_runtime.py +606 -0
- ite_agent-0.0.23/src/ite/attachment_refs.py +313 -0
- ite_agent-0.0.23/src/ite/attachments.py +241 -0
- ite_agent-0.0.23/src/ite/client/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/client/llm_client.py +780 -0
- ite_agent-0.0.23/src/ite/client/response.py +89 -0
- ite_agent-0.0.23/src/ite/cloud/__init__.py +11 -0
- ite_agent-0.0.23/src/ite/cloud/auth.py +437 -0
- ite_agent-0.0.23/src/ite/commands/__init__.py +107 -0
- ite_agent-0.0.23/src/ite/commands/aside.py +138 -0
- ite_agent-0.0.23/src/ite/commands/attach.py +182 -0
- ite_agent-0.0.23/src/ite/commands/branch.py +191 -0
- ite_agent-0.0.23/src/ite/commands/cloud.py +162 -0
- ite_agent-0.0.23/src/ite/commands/csv2json.py +139 -0
- ite_agent-0.0.23/src/ite/commands/general.py +220 -0
- ite_agent-0.0.23/src/ite/commands/history.py +133 -0
- ite_agent-0.0.23/src/ite/commands/info.py +1202 -0
- ite_agent-0.0.23/src/ite/commands/model.py +335 -0
- ite_agent-0.0.23/src/ite/commands/plan.py +146 -0
- ite_agent-0.0.23/src/ite/commands/publish.py +73 -0
- ite_agent-0.0.23/src/ite/commands/sandbox.py +160 -0
- ite_agent-0.0.23/src/ite/commands/session.py +675 -0
- ite_agent-0.0.23/src/ite/commands/skills.py +149 -0
- ite_agent-0.0.23/src/ite/commands/subagent.py +151 -0
- ite_agent-0.0.23/src/ite/commands/todos.py +112 -0
- ite_agent-0.0.23/src/ite/config/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/config/config.py +313 -0
- ite_agent-0.0.23/src/ite/config/loader.py +869 -0
- ite_agent-0.0.23/src/ite/config/setup.py +134 -0
- ite_agent-0.0.23/src/ite/context/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/context/compact_artifacts.py +47 -0
- ite_agent-0.0.23/src/ite/context/compaction.py +117 -0
- ite_agent-0.0.23/src/ite/context/loop_detector.py +103 -0
- ite_agent-0.0.23/src/ite/context/manager.py +768 -0
- ite_agent-0.0.23/src/ite/context/transcript.py +140 -0
- ite_agent-0.0.23/src/ite/git/__init__.py +2 -0
- ite_agent-0.0.23/src/ite/git/branches.py +116 -0
- ite_agent-0.0.23/src/ite/git/remotes.py +121 -0
- ite_agent-0.0.23/src/ite/git/working_tree.py +604 -0
- ite_agent-0.0.23/src/ite/hooks/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/hooks/hook_system.py +140 -0
- ite_agent-0.0.23/src/ite/main.py +1440 -0
- ite_agent-0.0.23/src/ite/memory/__init__.py +29 -0
- ite_agent-0.0.23/src/ite/memory/intent.py +261 -0
- ite_agent-0.0.23/src/ite/memory/manager.py +1030 -0
- ite_agent-0.0.23/src/ite/memory/response_intent.py +307 -0
- ite_agent-0.0.23/src/ite/memory/session_memory.py +258 -0
- ite_agent-0.0.23/src/ite/prompts/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/prompts/system.py +780 -0
- ite_agent-0.0.23/src/ite/safety/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/safety/approval.py +333 -0
- ite_agent-0.0.23/src/ite/safety/git_sandbox.py +310 -0
- ite_agent-0.0.23/src/ite/safety/sandbox.py +71 -0
- ite_agent-0.0.23/src/ite/skills/__init__.py +23 -0
- ite_agent-0.0.23/src/ite/skills/installer.py +144 -0
- ite_agent-0.0.23/src/ite/skills/manager.py +304 -0
- ite_agent-0.0.23/src/ite/skills/rendering.py +386 -0
- ite_agent-0.0.23/src/ite/skills/trust.py +47 -0
- ite_agent-0.0.23/src/ite/tools/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/tools/base.py +282 -0
- ite_agent-0.0.23/src/ite/tools/builtin/__init__.py +146 -0
- ite_agent-0.0.23/src/ite/tools/builtin/apply_patch.py +385 -0
- ite_agent-0.0.23/src/ite/tools/builtin/archive_tools.py +113 -0
- ite_agent-0.0.23/src/ite/tools/builtin/config_tools.py +783 -0
- ite_agent-0.0.23/src/ite/tools/builtin/edit_file.py +252 -0
- ite_agent-0.0.23/src/ite/tools/builtin/git_tools.py +793 -0
- ite_agent-0.0.23/src/ite/tools/builtin/glob.py +91 -0
- ite_agent-0.0.23/src/ite/tools/builtin/grep.py +146 -0
- ite_agent-0.0.23/src/ite/tools/builtin/http_tools.py +172 -0
- ite_agent-0.0.23/src/ite/tools/builtin/json_tools.py +302 -0
- ite_agent-0.0.23/src/ite/tools/builtin/list_dir.py +75 -0
- ite_agent-0.0.23/src/ite/tools/builtin/media_tools.py +407 -0
- ite_agent-0.0.23/src/ite/tools/builtin/memory.py +312 -0
- ite_agent-0.0.23/src/ite/tools/builtin/plan_question.py +79 -0
- ite_agent-0.0.23/src/ite/tools/builtin/read_file.py +144 -0
- ite_agent-0.0.23/src/ite/tools/builtin/shell.py +1055 -0
- ite_agent-0.0.23/src/ite/tools/builtin/skills.py +182 -0
- ite_agent-0.0.23/src/ite/tools/builtin/subagent_runtime_tools.py +366 -0
- ite_agent-0.0.23/src/ite/tools/builtin/todo.py +353 -0
- ite_agent-0.0.23/src/ite/tools/builtin/verification_tools.py +323 -0
- ite_agent-0.0.23/src/ite/tools/builtin/web_fetch.py +154 -0
- ite_agent-0.0.23/src/ite/tools/builtin/web_search.py +88 -0
- ite_agent-0.0.23/src/ite/tools/builtin/write_file.py +121 -0
- ite_agent-0.0.23/src/ite/tools/discovery.py +74 -0
- ite_agent-0.0.23/src/ite/tools/mcp/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/tools/mcp/client.py +382 -0
- ite_agent-0.0.23/src/ite/tools/mcp/mcp_manager.py +256 -0
- ite_agent-0.0.23/src/ite/tools/mcp/mcp_tool.py +364 -0
- ite_agent-0.0.23/src/ite/tools/mcp/oauth.py +206 -0
- ite_agent-0.0.23/src/ite/tools/policy.py +208 -0
- ite_agent-0.0.23/src/ite/tools/registry.py +753 -0
- ite_agent-0.0.23/src/ite/tools/subagent.py +822 -0
- ite_agent-0.0.23/src/ite/tools/subagent_loader.py +64 -0
- ite_agent-0.0.23/src/ite/ui/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/ui/assets/ite_image.png +0 -0
- ite_agent-0.0.23/src/ite/ui/gui/MIGRATION.md +53 -0
- ite_agent-0.0.23/src/ite/ui/gui/__init__.py +6 -0
- ite_agent-0.0.23/src/ite/ui/gui/adapters/registry.py +17 -0
- ite_agent-0.0.23/src/ite/ui/gui/app.py +2365 -0
- ite_agent-0.0.23/src/ite/ui/gui/builders/layout.py +933 -0
- ite_agent-0.0.23/src/ite/ui/gui/builders/messages.py +1467 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/agent_events.py +328 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/approval.py +204 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/branch.py +313 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/commands.py +474 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/scroll.py +51 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/sessions.py +407 -0
- ite_agent-0.0.23/src/ite/ui/gui/controllers/workspace.py +350 -0
- ite_agent-0.0.23/src/ite/ui/gui/state.py +279 -0
- ite_agent-0.0.23/src/ite/ui/gui/tokens.py +73 -0
- ite_agent-0.0.23/src/ite/ui/gui/types.py +7 -0
- ite_agent-0.0.23/src/ite/ui/gui.py +5 -0
- ite_agent-0.0.23/src/ite/ui/reup/__init__.py +15 -0
- ite_agent-0.0.23/src/ite/ui/reup/adapters/__init__.py +1 -0
- ite_agent-0.0.23/src/ite/ui/reup/adapters/registry.py +52 -0
- ite_agent-0.0.23/src/ite/ui/reup/app.py +8250 -0
- ite_agent-0.0.23/src/ite/ui/reup/change_tree.py +88 -0
- ite_agent-0.0.23/src/ite/ui/reup/change_views.py +67 -0
- ite_agent-0.0.23/src/ite/ui/reup/command_views.py +521 -0
- ite_agent-0.0.23/src/ite/ui/reup/composer_views.py +479 -0
- ite_agent-0.0.23/src/ite/ui/reup/markdown_widget.py +137 -0
- ite_agent-0.0.23/src/ite/ui/reup/modals.py +2353 -0
- ite_agent-0.0.23/src/ite/ui/reup/reup.tcss +2541 -0
- ite_agent-0.0.23/src/ite/ui/reup/tool_views.py +1688 -0
- ite_agent-0.0.23/src/ite/ui/tool_narrative.py +948 -0
- ite_agent-0.0.23/src/ite/ui/tui.py +1652 -0
- ite_agent-0.0.23/src/ite/utils/__init__.py +0 -0
- ite_agent-0.0.23/src/ite/utils/errors.py +236 -0
- ite_agent-0.0.23/src/ite/utils/paths.py +43 -0
- ite_agent-0.0.23/src/ite/utils/text.py +87 -0
- ite_agent-0.0.23/tests/test_agent_continuation.py +457 -0
- ite_agent-0.0.23/tests/test_agent_tool_recovery.py +820 -0
- ite_agent-0.0.23/tests/test_aside_command.py +164 -0
- ite_agent-0.0.23/tests/test_attachment_refs.py +101 -0
- ite_agent-0.0.23/tests/test_attachments.py +96 -0
- ite_agent-0.0.23/tests/test_bundled_docx_skill.py +106 -0
- ite_agent-0.0.23/tests/test_bundled_pdf_skill.py +98 -0
- ite_agent-0.0.23/tests/test_bundled_skill_creator.py +25 -0
- ite_agent-0.0.23/tests/test_change_history.py +108 -0
- ite_agent-0.0.23/tests/test_cli_modes.py +256 -0
- ite_agent-0.0.23/tests/test_cli_prompt.py +60 -0
- ite_agent-0.0.23/tests/test_cloud_auth.py +124 -0
- ite_agent-0.0.23/tests/test_compaction.py +77 -0
- ite_agent-0.0.23/tests/test_config_setup_requirements.py +32 -0
- ite_agent-0.0.23/tests/test_config_tools.py +141 -0
- ite_agent-0.0.23/tests/test_execution_todo_progress.py +66 -0
- ite_agent-0.0.23/tests/test_git_tools.py +347 -0
- ite_agent-0.0.23/tests/test_gui_state_store.py +52 -0
- ite_agent-0.0.23/tests/test_http_archive_tools.py +130 -0
- ite_agent-0.0.23/tests/test_info_commands.py +110 -0
- ite_agent-0.0.23/tests/test_json_tools.py +86 -0
- ite_agent-0.0.23/tests/test_llm_client.py +361 -0
- ite_agent-0.0.23/tests/test_loop_detector.py +52 -0
- ite_agent-0.0.23/tests/test_mcp.py +1165 -0
- ite_agent-0.0.23/tests/test_media_tools.py +233 -0
- ite_agent-0.0.23/tests/test_memory_behavior.py +414 -0
- ite_agent-0.0.23/tests/test_memory_eval_matrix.py +336 -0
- ite_agent-0.0.23/tests/test_memory_lifecycle.py +1047 -0
- ite_agent-0.0.23/tests/test_memory_manager.py +958 -0
- ite_agent-0.0.23/tests/test_provider_error_formatting.py +89 -0
- ite_agent-0.0.23/tests/test_publish_command.py +87 -0
- ite_agent-0.0.23/tests/test_release_bump_skill.py +41 -0
- ite_agent-0.0.23/tests/test_reup_command_palette.py +2331 -0
- ite_agent-0.0.23/tests/test_reup_modals.py +194 -0
- ite_agent-0.0.23/tests/test_reup_startup.py +452 -0
- ite_agent-0.0.23/tests/test_reup_theme_guards.py +67 -0
- ite_agent-0.0.23/tests/test_reup_tool_views.py +255 -0
- ite_agent-0.0.23/tests/test_saved_custom_provider.py +96 -0
- ite_agent-0.0.23/tests/test_session_commands.py +75 -0
- ite_agent-0.0.23/tests/test_session_manager.py +297 -0
- ite_agent-0.0.23/tests/test_session_naming.py +53 -0
- ite_agent-0.0.23/tests/test_setup_modal.py +515 -0
- ite_agent-0.0.23/tests/test_shell_web_capabilities.py +697 -0
- ite_agent-0.0.23/tests/test_skills.py +561 -0
- ite_agent-0.0.23/tests/test_subagent_runtime.py +777 -0
- ite_agent-0.0.23/tests/test_system_prompt.py +116 -0
- ite_agent-0.0.23/tests/test_text_utils.py +24 -0
- ite_agent-0.0.23/tests/test_todos_scoped.py +160 -0
- ite_agent-0.0.23/tests/test_tool_narrative.py +317 -0
- ite_agent-0.0.23/tests/test_tool_param_normalization.py +152 -0
- ite_agent-0.0.23/tests/test_tooling_v01.py +1091 -0
- ite_agent-0.0.23/tests/test_verification_tools.py +101 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: docx
|
|
3
|
+
description: Create, inspect, edit, and validate Microsoft Word .docx documents. Use for reading .docx content, generating reports, memos, or letters as Word files, editing existing documents, and converting Word documents into structured text or a new polished .docx.
|
|
4
|
+
tags:
|
|
5
|
+
- office
|
|
6
|
+
- documents
|
|
7
|
+
- word
|
|
8
|
+
author: ite
|
|
9
|
+
argument-hint: "[INPUT=<path>] [OUTPUT=<path>]"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# DOCX workflow
|
|
13
|
+
|
|
14
|
+
Use this skill whenever the user is working with a `.docx` file or explicitly wants a Word document as output.
|
|
15
|
+
|
|
16
|
+
## Core rules
|
|
17
|
+
|
|
18
|
+
- Do not use `read_file` directly on `.docx`; it is a packaged binary file.
|
|
19
|
+
- Resolve all script paths relative to this skill directory.
|
|
20
|
+
- Prefer a workspace-local scratch directory such as `./.ite/tmp/docx/<job-id>/` over `/tmp/`.
|
|
21
|
+
- Keep the source document intact unless the user explicitly asks to overwrite it.
|
|
22
|
+
- Validate any newly created or repacked `.docx` before presenting it as finished.
|
|
23
|
+
- If LibreOffice and Poppler are available, render pages for visual review before final delivery.
|
|
24
|
+
|
|
25
|
+
## Default workflows
|
|
26
|
+
|
|
27
|
+
### Inspect or extract text
|
|
28
|
+
|
|
29
|
+
Inspect first when the goal is to understand the file quickly:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
python <skill_dir>/scripts/inspect_docx.py input.docx
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Then use the extractor when you need readable content:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
python <skill_dir>/scripts/extract_docx.py input.docx --format markdown --output extracted.md
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Use `--format text` for a plain transcript or `--format json` for machine-readable sections.
|
|
42
|
+
|
|
43
|
+
### Unpack for structured editing
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
python <skill_dir>/scripts/unpack_docx.py input.docx ./.ite/tmp/docx/job-123/unpacked
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Edit the unpacked XML only when a simple text replacement is not enough.
|
|
50
|
+
|
|
51
|
+
### Simple text replacement
|
|
52
|
+
|
|
53
|
+
For straightforward content changes inside `word/document.xml`, use:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
python <skill_dir>/scripts/replace_text.py input.docx output.docx --replace "Old text" "New text"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
This is for direct text substitutions, not layout-heavy edits.
|
|
60
|
+
|
|
61
|
+
### Create a new document
|
|
62
|
+
|
|
63
|
+
For letters, memos, and simple reports, start from a markdown or text draft:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
python <skill_dir>/scripts/write_docx.py draft.md output.docx --title "Quarterly Update"
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Templates in `templates/` are starter content, not strict requirements.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
python <skill_dir>/scripts/write_docx.py output.docx --template memo --title "Quarterly Update"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Visual review
|
|
76
|
+
|
|
77
|
+
When layout matters, render the document to page images:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
python <skill_dir>/scripts/render_docx.py input.docx --output_dir ./.ite/tmp/docx/job-123/rendered
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
If the required system tools are unavailable, say so clearly and call out layout risk.
|
|
84
|
+
|
|
85
|
+
### Repack and validate
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
python <skill_dir>/scripts/pack_docx.py ./.ite/tmp/docx/job-123/unpacked output.docx
|
|
89
|
+
python <skill_dir>/scripts/validate_docx.py output.docx
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Validation is required before presenting the result as complete.
|
|
93
|
+
|
|
94
|
+
## Choosing the path
|
|
95
|
+
|
|
96
|
+
- Need to read or summarize a `.docx`: extract text.
|
|
97
|
+
- Need a polished new `.docx`: write a new document from structured content.
|
|
98
|
+
- Need a small wording change: replace text.
|
|
99
|
+
- Need deeper changes: unpack, edit carefully, repack, validate.
|
|
100
|
+
- Need to check pagination, tables, or spacing: render the pages and inspect them.
|
|
101
|
+
|
|
102
|
+
## Output expectations
|
|
103
|
+
|
|
104
|
+
- Tell the user what file was created or updated.
|
|
105
|
+
- Summarize the structural approach used.
|
|
106
|
+
- Mention validation status.
|
|
107
|
+
- If a limitation blocks the requested formatting, say so explicitly instead of silently improvising.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# DOCX recipes
|
|
2
|
+
|
|
3
|
+
Use the bundled scripts for these common jobs:
|
|
4
|
+
|
|
5
|
+
- Inspect a document: `extract_docx.py`
|
|
6
|
+
- Unpack for XML edits: `unpack_docx.py`
|
|
7
|
+
- Repack the edited package: `pack_docx.py`
|
|
8
|
+
- Validate a generated file: `validate_docx.py`
|
|
9
|
+
- Create a simple memo or report from a text draft: `write_docx.py`
|
|
10
|
+
- Apply direct wording swaps: `replace_text.py`
|
|
11
|
+
|
|
12
|
+
Prefer a workspace-local scratch directory under `./.ite/tmp/docx/` so shell commands remain inside the project sandbox.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# OOXML notes
|
|
2
|
+
|
|
3
|
+
A `.docx` file is a ZIP package. The most important members for this skill are:
|
|
4
|
+
|
|
5
|
+
- `[Content_Types].xml`
|
|
6
|
+
- `_rels/.rels`
|
|
7
|
+
- `word/document.xml`
|
|
8
|
+
|
|
9
|
+
The first-party `ite` docx skill only guarantees safe handling of plain paragraph content in `word/document.xml`.
|
|
10
|
+
If the user needs advanced layout, section, comment, or tracked-change behavior, say that the request exceeds the current bundled workflow and avoid fabricating support.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""Helper scripts for the bundled ite docx skill."""
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from zipfile import ZIP_DEFLATED
|
|
5
|
+
from zipfile import ZipFile
|
|
6
|
+
import shutil
|
|
7
|
+
import tempfile
|
|
8
|
+
import xml.etree.ElementTree as ET
|
|
9
|
+
import json
|
|
10
|
+
|
|
11
|
+
W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
12
|
+
NS = {"w": W_NS}
|
|
13
|
+
ET.register_namespace("w", W_NS)
|
|
14
|
+
|
|
15
|
+
REQUIRED_MEMBERS = {
|
|
16
|
+
"[Content_Types].xml",
|
|
17
|
+
"_rels/.rels",
|
|
18
|
+
"word/document.xml",
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def load_document_tree(docx_path: Path) -> ET.ElementTree:
|
|
23
|
+
with ZipFile(docx_path) as archive:
|
|
24
|
+
with archive.open("word/document.xml") as handle:
|
|
25
|
+
return ET.parse(handle)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def write_document_tree(source_docx: Path, output_docx: Path, tree: ET.ElementTree) -> None:
|
|
29
|
+
with tempfile.TemporaryDirectory(prefix="ite-docx-") as temp_dir:
|
|
30
|
+
temp_root = Path(temp_dir)
|
|
31
|
+
unpack_to_directory(source_docx, temp_root)
|
|
32
|
+
document_path = temp_root / "word" / "document.xml"
|
|
33
|
+
tree.write(document_path, encoding="utf-8", xml_declaration=True)
|
|
34
|
+
pack_directory(temp_root, output_docx)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def unpack_to_directory(docx_path: Path, output_dir: Path) -> None:
|
|
38
|
+
output_dir.mkdir(parents=True, exist_ok=True)
|
|
39
|
+
with ZipFile(docx_path) as archive:
|
|
40
|
+
archive.extractall(output_dir)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def pack_directory(source_dir: Path, output_docx: Path) -> None:
|
|
44
|
+
output_docx.parent.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
temp_output = output_docx.with_suffix(output_docx.suffix + ".tmp")
|
|
46
|
+
if temp_output.exists():
|
|
47
|
+
temp_output.unlink()
|
|
48
|
+
with ZipFile(temp_output, "w", compression=ZIP_DEFLATED) as archive:
|
|
49
|
+
for path in sorted(source_dir.rglob("*")):
|
|
50
|
+
if path.is_dir():
|
|
51
|
+
continue
|
|
52
|
+
archive.write(path, path.relative_to(source_dir).as_posix())
|
|
53
|
+
shutil.move(str(temp_output), str(output_docx))
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def paragraph_texts(tree: ET.ElementTree) -> list[str]:
|
|
57
|
+
paragraphs: list[str] = []
|
|
58
|
+
for paragraph in tree.findall(".//w:body/w:p", NS):
|
|
59
|
+
texts = [node.text or "" for node in paragraph.findall(".//w:t", NS)]
|
|
60
|
+
line = "".join(texts).strip()
|
|
61
|
+
if line:
|
|
62
|
+
paragraphs.append(line)
|
|
63
|
+
return paragraphs
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def inspect_docx(path: Path) -> dict[str, object]:
|
|
67
|
+
tree = load_document_tree(path)
|
|
68
|
+
paragraphs = paragraph_texts(tree)
|
|
69
|
+
preview = paragraphs[:5]
|
|
70
|
+
return {
|
|
71
|
+
"path": str(path.resolve()),
|
|
72
|
+
"size_bytes": path.stat().st_size,
|
|
73
|
+
"paragraph_count": len(paragraphs),
|
|
74
|
+
"preview": preview,
|
|
75
|
+
"validation_errors": validate_docx(path),
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def set_paragraphs(tree: ET.ElementTree, paragraphs: list[str]) -> ET.ElementTree:
|
|
80
|
+
root = tree.getroot()
|
|
81
|
+
body = root.find("w:body", NS)
|
|
82
|
+
if body is None:
|
|
83
|
+
raise ValueError("word/document.xml does not contain w:body")
|
|
84
|
+
|
|
85
|
+
sect_pr = body.find("w:sectPr", NS)
|
|
86
|
+
for child in list(body):
|
|
87
|
+
body.remove(child)
|
|
88
|
+
|
|
89
|
+
for text in paragraphs:
|
|
90
|
+
body.append(_paragraph_element(text))
|
|
91
|
+
|
|
92
|
+
if sect_pr is not None:
|
|
93
|
+
body.append(sect_pr)
|
|
94
|
+
return tree
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def replace_in_paragraphs(
|
|
98
|
+
paragraphs: list[str],
|
|
99
|
+
replacements: list[tuple[str, str]],
|
|
100
|
+
) -> tuple[list[str], int]:
|
|
101
|
+
updated = list(paragraphs)
|
|
102
|
+
total_replacements = 0
|
|
103
|
+
for old, new in replacements:
|
|
104
|
+
next_paragraphs: list[str] = []
|
|
105
|
+
for paragraph in updated:
|
|
106
|
+
occurrences = paragraph.count(old)
|
|
107
|
+
if occurrences:
|
|
108
|
+
total_replacements += occurrences
|
|
109
|
+
next_paragraphs.append(paragraph.replace(old, new))
|
|
110
|
+
updated = next_paragraphs
|
|
111
|
+
return updated, total_replacements
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def json_dump(payload: dict[str, object]) -> str:
|
|
115
|
+
return json.dumps(payload, indent=2)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def build_simple_document(paragraphs: list[str], title: str | None = None) -> bytes:
|
|
119
|
+
document = ET.Element(f"{{{W_NS}}}document")
|
|
120
|
+
body = ET.SubElement(document, f"{{{W_NS}}}body")
|
|
121
|
+
all_paragraphs = list(paragraphs)
|
|
122
|
+
if title:
|
|
123
|
+
all_paragraphs.insert(0, title.strip())
|
|
124
|
+
for paragraph in all_paragraphs:
|
|
125
|
+
body.append(_paragraph_element(paragraph))
|
|
126
|
+
sect_pr = ET.SubElement(body, f"{{{W_NS}}}sectPr")
|
|
127
|
+
pg_sz = ET.SubElement(sect_pr, f"{{{W_NS}}}pgSz")
|
|
128
|
+
pg_sz.set(f"{{{W_NS}}}w", "12240")
|
|
129
|
+
pg_sz.set(f"{{{W_NS}}}h", "15840")
|
|
130
|
+
pg_mar = ET.SubElement(sect_pr, f"{{{W_NS}}}pgMar")
|
|
131
|
+
for key, value in {
|
|
132
|
+
"top": "1440",
|
|
133
|
+
"right": "1440",
|
|
134
|
+
"bottom": "1440",
|
|
135
|
+
"left": "1440",
|
|
136
|
+
"header": "720",
|
|
137
|
+
"footer": "720",
|
|
138
|
+
"gutter": "0",
|
|
139
|
+
}.items():
|
|
140
|
+
pg_mar.set(f"{{{W_NS}}}{key}", value)
|
|
141
|
+
|
|
142
|
+
document_xml = ET.tostring(document, encoding="utf-8", xml_declaration=True)
|
|
143
|
+
return _package_document_xml(document_xml)
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _package_document_xml(document_xml: bytes) -> bytes:
|
|
147
|
+
with tempfile.TemporaryDirectory(prefix="ite-docx-build-") as temp_dir:
|
|
148
|
+
root = Path(temp_dir)
|
|
149
|
+
(root / "_rels").mkdir(parents=True, exist_ok=True)
|
|
150
|
+
(root / "word").mkdir(parents=True, exist_ok=True)
|
|
151
|
+
(root / "[Content_Types].xml").write_text(
|
|
152
|
+
"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
153
|
+
<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types">
|
|
154
|
+
<Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/>
|
|
155
|
+
<Default Extension="xml" ContentType="application/xml"/>
|
|
156
|
+
<Override PartName="/word/document.xml" ContentType="application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml"/>
|
|
157
|
+
<Override PartName="/docProps/core.xml" ContentType="application/vnd.openxmlformats-package.core-properties+xml"/>
|
|
158
|
+
<Override PartName="/docProps/app.xml" ContentType="application/vnd.openxmlformats-officedocument.extended-properties+xml"/>
|
|
159
|
+
</Types>
|
|
160
|
+
""",
|
|
161
|
+
encoding="utf-8",
|
|
162
|
+
)
|
|
163
|
+
(root / "_rels" / ".rels").write_text(
|
|
164
|
+
"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
165
|
+
<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships">
|
|
166
|
+
<Relationship Id="rId1" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument" Target="word/document.xml"/>
|
|
167
|
+
<Relationship Id="rId2" Type="http://schemas.openxmlformats.org/package/2006/relationships/metadata/core-properties" Target="docProps/core.xml"/>
|
|
168
|
+
<Relationship Id="rId3" Type="http://schemas.openxmlformats.org/officeDocument/2006/relationships/extended-properties" Target="docProps/app.xml"/>
|
|
169
|
+
</Relationships>
|
|
170
|
+
""",
|
|
171
|
+
encoding="utf-8",
|
|
172
|
+
)
|
|
173
|
+
(root / "word" / "document.xml").write_bytes(document_xml)
|
|
174
|
+
(root / "docProps").mkdir(parents=True, exist_ok=True)
|
|
175
|
+
(root / "docProps" / "core.xml").write_text(
|
|
176
|
+
"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
177
|
+
<cp:coreProperties xmlns:cp="http://schemas.openxmlformats.org/package/2006/metadata/core-properties" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:dcterms="http://purl.org/dc/terms/" xmlns:dcmitype="http://purl.org/dc/dcmitype/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
|
178
|
+
<dc:title>ite document</dc:title>
|
|
179
|
+
<dc:creator>ite</dc:creator>
|
|
180
|
+
</cp:coreProperties>
|
|
181
|
+
""",
|
|
182
|
+
encoding="utf-8",
|
|
183
|
+
)
|
|
184
|
+
(root / "docProps" / "app.xml").write_text(
|
|
185
|
+
"""<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
186
|
+
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/extended-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
|
|
187
|
+
<Application>ite</Application>
|
|
188
|
+
</Properties>
|
|
189
|
+
""",
|
|
190
|
+
encoding="utf-8",
|
|
191
|
+
)
|
|
192
|
+
output = root / "out.docx"
|
|
193
|
+
pack_directory(root, output)
|
|
194
|
+
return output.read_bytes()
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
def validate_docx(path: Path) -> list[str]:
|
|
198
|
+
errors: list[str] = []
|
|
199
|
+
try:
|
|
200
|
+
with ZipFile(path) as archive:
|
|
201
|
+
members = set(archive.namelist())
|
|
202
|
+
missing = REQUIRED_MEMBERS - members
|
|
203
|
+
if missing:
|
|
204
|
+
errors.append(f"missing required members: {', '.join(sorted(missing))}")
|
|
205
|
+
for member in sorted(REQUIRED_MEMBERS & members):
|
|
206
|
+
with archive.open(member) as handle:
|
|
207
|
+
try:
|
|
208
|
+
ET.parse(handle)
|
|
209
|
+
except ET.ParseError as exc:
|
|
210
|
+
errors.append(f"{member}: invalid XML ({exc})")
|
|
211
|
+
except Exception as exc:
|
|
212
|
+
errors.append(str(exc))
|
|
213
|
+
return errors
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _paragraph_element(text: str) -> ET.Element:
|
|
217
|
+
paragraph = ET.Element(f"{{{W_NS}}}p")
|
|
218
|
+
run = ET.SubElement(paragraph, f"{{{W_NS}}}r")
|
|
219
|
+
text_node = ET.SubElement(run, f"{{{W_NS}}}t")
|
|
220
|
+
if text.startswith(" ") or text.endswith(" "):
|
|
221
|
+
text_node.set("{http://www.w3.org/XML/1998/namespace}space", "preserve")
|
|
222
|
+
text_node.text = text
|
|
223
|
+
return paragraph
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
import json
|
|
5
|
+
from pathlib import Path
|
|
6
|
+
|
|
7
|
+
from _docx_core import load_document_tree
|
|
8
|
+
from _docx_core import paragraph_texts
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def main() -> int:
|
|
12
|
+
parser = argparse.ArgumentParser(description="Extract readable content from a .docx file.")
|
|
13
|
+
parser.add_argument("input", type=Path, help="Path to the source .docx file")
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--format",
|
|
16
|
+
choices=("text", "markdown", "json"),
|
|
17
|
+
default="text",
|
|
18
|
+
help="Output format",
|
|
19
|
+
)
|
|
20
|
+
parser.add_argument("--output", type=Path, help="Optional file to write extracted content to")
|
|
21
|
+
args = parser.parse_args()
|
|
22
|
+
|
|
23
|
+
tree = load_document_tree(args.input)
|
|
24
|
+
paragraphs = paragraph_texts(tree)
|
|
25
|
+
if args.format == "json":
|
|
26
|
+
content = json.dumps({"paragraphs": paragraphs}, indent=2)
|
|
27
|
+
elif args.format == "markdown":
|
|
28
|
+
content = "\n\n".join(paragraphs)
|
|
29
|
+
else:
|
|
30
|
+
content = "\n".join(paragraphs)
|
|
31
|
+
|
|
32
|
+
if args.output:
|
|
33
|
+
args.output.write_text(content, encoding="utf-8")
|
|
34
|
+
else:
|
|
35
|
+
print(content)
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from _docx_core import inspect_docx
|
|
7
|
+
from _docx_core import json_dump
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> int:
|
|
11
|
+
parser = argparse.ArgumentParser(description="Inspect a .docx file and report basic metadata.")
|
|
12
|
+
parser.add_argument("input", type=Path, help="Path to the source .docx file")
|
|
13
|
+
args = parser.parse_args()
|
|
14
|
+
|
|
15
|
+
print(json_dump(inspect_docx(args.input)))
|
|
16
|
+
return 0
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
if __name__ == "__main__":
|
|
20
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from _docx_core import pack_directory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> int:
|
|
10
|
+
parser = argparse.ArgumentParser(description="Pack an unpacked OOXML directory into a .docx file.")
|
|
11
|
+
parser.add_argument("input_dir", type=Path, help="Directory containing the unpacked document")
|
|
12
|
+
parser.add_argument("output", type=Path, help="Output .docx file")
|
|
13
|
+
args = parser.parse_args()
|
|
14
|
+
|
|
15
|
+
pack_directory(args.input_dir, args.output)
|
|
16
|
+
print(args.output.resolve())
|
|
17
|
+
return 0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
import shutil
|
|
6
|
+
import subprocess
|
|
7
|
+
import tempfile
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def main() -> int:
|
|
11
|
+
parser = argparse.ArgumentParser(description="Render a DOCX file to page PNGs using LibreOffice and Poppler.")
|
|
12
|
+
parser.add_argument("input", type=Path, help="Source .docx file")
|
|
13
|
+
parser.add_argument("--output_dir", type=Path, required=True, help="Directory for rendered page images")
|
|
14
|
+
parser.add_argument("--dpi", type=int, default=144, help="Rasterization DPI")
|
|
15
|
+
args = parser.parse_args()
|
|
16
|
+
|
|
17
|
+
for tool in ("soffice", "pdftoppm"):
|
|
18
|
+
if shutil.which(tool) is None:
|
|
19
|
+
raise SystemExit(f"missing required system tool: {tool}")
|
|
20
|
+
|
|
21
|
+
args.output_dir.mkdir(parents=True, exist_ok=True)
|
|
22
|
+
with tempfile.TemporaryDirectory(prefix="ite-docx-render-") as temp_dir:
|
|
23
|
+
temp_root = Path(temp_dir)
|
|
24
|
+
pdf_dir = temp_root / "pdf"
|
|
25
|
+
pdf_dir.mkdir(parents=True, exist_ok=True)
|
|
26
|
+
profile_dir = temp_root / "profile"
|
|
27
|
+
profile_dir.mkdir(parents=True, exist_ok=True)
|
|
28
|
+
|
|
29
|
+
subprocess.run(
|
|
30
|
+
[
|
|
31
|
+
"soffice",
|
|
32
|
+
f"-env:UserInstallation=file://{profile_dir}",
|
|
33
|
+
"--headless",
|
|
34
|
+
"--convert-to",
|
|
35
|
+
"pdf",
|
|
36
|
+
"--outdir",
|
|
37
|
+
str(pdf_dir),
|
|
38
|
+
str(args.input),
|
|
39
|
+
],
|
|
40
|
+
check=True,
|
|
41
|
+
capture_output=True,
|
|
42
|
+
text=True,
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
pdf_path = pdf_dir / f"{args.input.stem}.pdf"
|
|
46
|
+
if not pdf_path.is_file():
|
|
47
|
+
raise SystemExit("failed to produce an intermediate PDF")
|
|
48
|
+
|
|
49
|
+
prefix = args.output_dir / "page"
|
|
50
|
+
subprocess.run(
|
|
51
|
+
[
|
|
52
|
+
"pdftoppm",
|
|
53
|
+
"-png",
|
|
54
|
+
"-r",
|
|
55
|
+
str(args.dpi),
|
|
56
|
+
str(pdf_path),
|
|
57
|
+
str(prefix),
|
|
58
|
+
],
|
|
59
|
+
check=True,
|
|
60
|
+
capture_output=True,
|
|
61
|
+
text=True,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
for path in sorted(args.output_dir.glob("page-*.png")):
|
|
65
|
+
print(path.resolve())
|
|
66
|
+
return 0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
if __name__ == "__main__":
|
|
70
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from _docx_core import load_document_tree
|
|
7
|
+
from _docx_core import paragraph_texts
|
|
8
|
+
from _docx_core import replace_in_paragraphs
|
|
9
|
+
from _docx_core import set_paragraphs
|
|
10
|
+
from _docx_core import write_document_tree
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def main() -> int:
|
|
14
|
+
parser = argparse.ArgumentParser(description="Apply direct string replacements inside a .docx document.")
|
|
15
|
+
parser.add_argument("input", type=Path, help="Source .docx file")
|
|
16
|
+
parser.add_argument("output", type=Path, help="Output .docx file")
|
|
17
|
+
parser.add_argument(
|
|
18
|
+
"--replace",
|
|
19
|
+
nargs=2,
|
|
20
|
+
metavar=("OLD", "NEW"),
|
|
21
|
+
action="append",
|
|
22
|
+
required=True,
|
|
23
|
+
help="Pair of strings to replace in order",
|
|
24
|
+
)
|
|
25
|
+
args = parser.parse_args()
|
|
26
|
+
|
|
27
|
+
tree = load_document_tree(args.input)
|
|
28
|
+
paragraphs = paragraph_texts(tree)
|
|
29
|
+
updated, replacement_count = replace_in_paragraphs(paragraphs, args.replace)
|
|
30
|
+
if replacement_count == 0:
|
|
31
|
+
raise SystemExit("no matching text was found for replacement")
|
|
32
|
+
set_paragraphs(tree, updated)
|
|
33
|
+
write_document_tree(args.input, args.output, tree)
|
|
34
|
+
print(f"{args.output.resolve()} ({replacement_count} replacements)")
|
|
35
|
+
return 0
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
if __name__ == "__main__":
|
|
39
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from _docx_core import unpack_to_directory
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> int:
|
|
10
|
+
parser = argparse.ArgumentParser(description="Unpack a .docx file into a directory.")
|
|
11
|
+
parser.add_argument("input", type=Path, help="Path to the source .docx file")
|
|
12
|
+
parser.add_argument("output_dir", type=Path, help="Directory to receive the unpacked package")
|
|
13
|
+
args = parser.parse_args()
|
|
14
|
+
|
|
15
|
+
unpack_to_directory(args.input, args.output_dir)
|
|
16
|
+
print(args.output_dir.resolve())
|
|
17
|
+
return 0
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
if __name__ == "__main__":
|
|
21
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from _docx_core import validate_docx
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> int:
|
|
10
|
+
parser = argparse.ArgumentParser(description="Validate the basic shape of a .docx file.")
|
|
11
|
+
parser.add_argument("input", type=Path, help="Path to the .docx file")
|
|
12
|
+
args = parser.parse_args()
|
|
13
|
+
|
|
14
|
+
errors = validate_docx(args.input)
|
|
15
|
+
if errors:
|
|
16
|
+
for error in errors:
|
|
17
|
+
print(error)
|
|
18
|
+
return 1
|
|
19
|
+
print("ok")
|
|
20
|
+
return 0
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
if __name__ == "__main__":
|
|
24
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import argparse
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from _docx_core import build_simple_document
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> int:
|
|
10
|
+
parser = argparse.ArgumentParser(description="Create a simple .docx file from text or markdown.")
|
|
11
|
+
parser.add_argument("input", type=Path, nargs="?", help="Source text or markdown file")
|
|
12
|
+
parser.add_argument("output", type=Path, help="Output .docx path")
|
|
13
|
+
parser.add_argument("--title", help="Optional document title inserted as the first paragraph")
|
|
14
|
+
parser.add_argument(
|
|
15
|
+
"--template",
|
|
16
|
+
choices=("memo", "letter"),
|
|
17
|
+
help="Use a bundled starter template when no input file is provided",
|
|
18
|
+
)
|
|
19
|
+
args = parser.parse_args()
|
|
20
|
+
|
|
21
|
+
if args.input is None and args.template is None:
|
|
22
|
+
raise SystemExit("provide either an input file or --template")
|
|
23
|
+
|
|
24
|
+
source_path = args.input
|
|
25
|
+
if source_path is None:
|
|
26
|
+
source_path = Path(__file__).resolve().parent.parent / "templates" / f"{args.template}.md"
|
|
27
|
+
|
|
28
|
+
source = source_path.read_text(encoding="utf-8")
|
|
29
|
+
paragraphs = [line.strip() for line in source.splitlines() if line.strip()]
|
|
30
|
+
if not paragraphs:
|
|
31
|
+
raise SystemExit("input did not contain any non-empty lines")
|
|
32
|
+
payload = build_simple_document(paragraphs, title=args.title)
|
|
33
|
+
args.output.parent.mkdir(parents=True, exist_ok=True)
|
|
34
|
+
args.output.write_bytes(payload)
|
|
35
|
+
print(args.output.resolve())
|
|
36
|
+
return 0
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
if __name__ == "__main__":
|
|
40
|
+
raise SystemExit(main())
|