ppt-template-reuse 0.4.0-rc.2

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.
Files changed (39) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE.md +8 -0
  3. package/README.md +233 -0
  4. package/adapters/README.md +13 -0
  5. package/adapters/claude/.claude-plugin/plugin.json +16 -0
  6. package/adapters/claude/.mcp.json +8 -0
  7. package/adapters/claude/skills/learn-ppt-template/SKILL.md +105 -0
  8. package/adapters/kimi/kimi.plugin.json +12 -0
  9. package/adapters/kimi/skills/learn-ppt-template/SKILL.md +105 -0
  10. package/adapters/kimi-work/mcp.json +8 -0
  11. package/adapters/kimi-work/skills/learn-ppt-template/SKILL.md +105 -0
  12. package/adapters/workbuddy/mcp.json +8 -0
  13. package/adapters/workbuddy/skills/learn-ppt-template/SKILL.md +105 -0
  14. package/package.json +53 -0
  15. package/plugins/ppt-template-reuse/.codex-plugin/plugin.json +38 -0
  16. package/plugins/ppt-template-reuse/.mcp.json +9 -0
  17. package/plugins/ppt-template-reuse/skills/learn-ppt-template/SKILL.md +105 -0
  18. package/plugins/ppt-template-reuse/skills/learn-ppt-template/agents/openai.yaml +6 -0
  19. package/skills/ppt-template-reuse/SKILL.md +105 -0
  20. package/skills/ppt-template-reuse/agents/openai.yaml +6 -0
  21. package/src/cli/ppt-reuse.mjs +203 -0
  22. package/src/core/adaptive-planner.mjs +1192 -0
  23. package/src/core/content-audit.mjs +573 -0
  24. package/src/core/frame-map.mjs +91 -0
  25. package/src/core/index.mjs +38 -0
  26. package/src/core/io.mjs +81 -0
  27. package/src/core/legacy-capsule-import.mjs +382 -0
  28. package/src/core/ooxml-editor.mjs +753 -0
  29. package/src/core/paths.mjs +60 -0
  30. package/src/core/planner-lib.mjs +59 -0
  31. package/src/core/pptx-package.mjs +227 -0
  32. package/src/core/renderer.mjs +402 -0
  33. package/src/core/source-extractor.mjs +412 -0
  34. package/src/core/template-inspector.mjs +489 -0
  35. package/src/core/template-intelligence.mjs +675 -0
  36. package/src/core/universal-engine.mjs +1075 -0
  37. package/src/index.mjs +2 -0
  38. package/src/install/installer.mjs +293 -0
  39. package/src/mcp/server.mjs +377 -0
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: learn-ppt-template
3
+ description: Create a content-complete, editable PowerPoint from a user-provided PPTX/POTX template and DOCX, PDF, PPTX, Markdown, or text material. Use whenever the user asks to make, imitate, learn, follow, or reuse a presentation template. Identify what every template page is for, adapt page count to source volume, preserve reasoning in native speaker notes, request missing visuals, expose real page previews, and stop on unresolved layouts, unsafe object edits, incomplete source coverage, or failed rendering. Works through the host-neutral ppt-template-reuse MCP or ppt-reuse CLI.
4
+ ---
5
+
6
+ # Learn PPT Template
7
+
8
+ Use the `ppt-template-reuse` MCP server as the normal execution path. Do not ask
9
+ the user to prepare JSON, choose a page count, or run commands.
10
+
11
+ ## Default behavior
12
+
13
+ For a request such as “参考这个模板和这些材料做一份内容完整、可编辑、带讲稿的
14
+ PPT”:
15
+
16
+ 1. Call `ppt_create_session` with the template, material, output path, current
17
+ workspace roots, and stated audience/duration.
18
+ 2. Call `ppt_inspect`.
19
+ 3. Read every extracted source unit. Submit every original `id` and `anchor`
20
+ through `ppt_submit_semantics`; label facts, chronology, issues, rules,
21
+ evidence, analysis, counterarguments, rebuttals, conclusions, and
22
+ recommendations. Preserve `parentAnchor`, `segmentIndex`, and `segmentCount`
23
+ when a long paragraph, table, slide, or PDF page was deterministically
24
+ segmented. Never silently merge away or omit a unit.
25
+ For structured layouts, also supply short `displayFragments` with roles such
26
+ as `date`, `label`, `step`, `metric-label`, `body`, or `description`. These
27
+ are source-grounded slot values, not invented decoration. A timeline needs
28
+ dates and node labels; a multi-item grid needs one label/body pair per item.
29
+ 4. Review only template slides returned as unresolved. Classify each page's
30
+ purpose, safe slots, capacity, required assets, protected elements, and
31
+ editability. Never approve uncertain SmartArt, chart, media, animation, icon,
32
+ or flattened-object edits. If several opening or closing variants are
33
+ returned, approve exactly one for each role and block the alternatives; do
34
+ not choose by page order.
35
+ 5. Call `ppt_plan`. Let source volume, argument structure, slot capacity, and
36
+ requested speaking time determine page count. If planning returns
37
+ `template-selection`, show the returned full-size candidates, approve one
38
+ source slide for that role, block the competing candidates in the frozen
39
+ template profile, resubmit semantics, and plan again. Never break a tie by
40
+ page number.
41
+ 6. If assets are required, use the host's image search or generation capability
42
+ when authorized; otherwise ask once for all missing assets. Register them
43
+ with `ppt_supply_assets`. Never permit gray blocks or temporary placeholders.
44
+ 7. Call `ppt_generate`, then poll `ppt_status`. Show new real page previews in
45
+ order. Stop when the status contains `actionRequired`.
46
+ 8. Call `ppt_validate` when generation did not already validate. Deliver only
47
+ when state is `completed`.
48
+
49
+ ## Content rules
50
+
51
+ - Preserve 100% of `critical` units and at least 95% weighted relevant content.
52
+ - Require every included source unit to have a verified destination trace.
53
+ Preserve 100% of original source text in speaker notes, visibly represent
54
+ 100% of critical units, and visibly represent at least 90% of weighted
55
+ relevant units. A `sourceUnitId` without matching field or notes text is not
56
+ coverage.
57
+ - Put source-verification text in a clearly marked
58
+ `[来源追踪,不逐字朗读]` notes block. Estimate duration from the spoken
59
+ explanation, not from that reference block. Do not add filler slides merely
60
+ because a short source finishes below the requested minimum time.
61
+ - Each analysis slide needs a claim, evidence, why-it-matters explanation, and
62
+ native speaker notes.
63
+ - Pair each represented counterargument with a response.
64
+ - Split overloaded material across more approved layouts; never shrink inherited
65
+ typography or delete reasoning to force a target page count.
66
+ - Use `core`, `appendix`, and `exclude` deliberately. An explicit duration
67
+ conflict requires a user choice between more time, narrower scope, or multiple
68
+ decks.
69
+ - A prior 19-page deck is only a regression example, never a fixed length.
70
+ - Do not infer length from unit count alone. Use original character volume,
71
+ argument nodes, counterargument chains, requested time, and the approved
72
+ template slot capacity. Very long material may legitimately produce dozens
73
+ or more than one hundred pages; stop for a duration/scope decision instead of
74
+ compressing it to a familiar deck length.
75
+
76
+ ## Template and object rules
77
+
78
+ - Exact native mode supports `.pptx` and `.potx`; `.pptm` is rejected and macros
79
+ are never executed.
80
+ - Treat flattened templates as `hybrid-locked`. Do not call them fully editable.
81
+ - Edit only declared slots. Preserve masters, layouts, themes, logos, page
82
+ chrome, animations, SmartArt, media, and all untargeted objects.
83
+ - Replace inherited images in place. Update tables only when dimensions match.
84
+ Update charts only when the embedded workbook and rendered result can both be
85
+ validated; otherwise stop.
86
+ - “插入图片”, “相关图片”, “ICON”, sample copy, and gray image blocks are
87
+ requirements, not audience-facing content.
88
+ - If an older reviewed template capsule is available, import it only when its
89
+ source SHA256 matches. Keep deny-by-default safety, preserve the old
90
+ preview/review bindings, approve exactly one opening and closing, and never
91
+ re-guess already reviewed slots.
92
+
93
+ ## Completion gate
94
+
95
+ Completion requires a frozen content plan and frame map, native speaker notes,
96
+ no residual examples or placeholders, matching slide/render counts, real
97
+ LibreOffice rendering, rendered-text fidelity, and a passing QA report. If
98
+ LibreOffice is missing or its PDF contains missing/substituted glyphs, analysis
99
+ and planning may complete but the PPTX must remain `validation-failed` until a
100
+ manual WPS or PowerPoint review confirms text, line breaks, images, notes, and
101
+ masters.
102
+
103
+ The engine is local-first: it only reads declared roots/current workspace and
104
+ temporary files, does not upload templates or material, and does not use the
105
+ network by itself.
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "ppt-template-reuse",
3
+ "version": "0.4.0-rc.2",
4
+ "description": "A host-neutral PPTX template learning, content planning, native editing, validation, CLI, and MCP engine.",
5
+ "license": "MIT",
6
+ "author": "NEU-ZHA",
7
+ "private": false,
8
+ "type": "module",
9
+ "engines": {
10
+ "node": ">=20"
11
+ },
12
+ "bin": {
13
+ "ppt-reuse": "./src/cli/ppt-reuse.mjs",
14
+ "ppt-template-reuse-mcp": "./src/mcp/server.mjs"
15
+ },
16
+ "exports": {
17
+ ".": "./src/index.mjs",
18
+ "./core": "./src/core/index.mjs",
19
+ "./mcp": "./src/mcp/server.mjs"
20
+ },
21
+ "files": [
22
+ "src/",
23
+ "skills/",
24
+ "adapters/",
25
+ "plugins/ppt-template-reuse/.codex-plugin/",
26
+ "plugins/ppt-template-reuse/.mcp.json",
27
+ "plugins/ppt-template-reuse/skills/learn-ppt-template/SKILL.md",
28
+ "plugins/ppt-template-reuse/skills/learn-ppt-template/agents/",
29
+ "LICENSE",
30
+ "NOTICE.md",
31
+ "README.md"
32
+ ],
33
+ "scripts": {
34
+ "check": "node tests/run-tests.mjs",
35
+ "check:universal": "node tests/run-universal-tests.mjs",
36
+ "check:all": "npm run check && npm run check:universal",
37
+ "doctor": "node src/cli/ppt-reuse.mjs doctor --host all",
38
+ "privacy": "node scripts/privacy-scan.mjs",
39
+ "release:build": "node scripts/build-release.mjs"
40
+ },
41
+ "dependencies": {
42
+ "@modelcontextprotocol/sdk": "^1.30.0",
43
+ "@xmldom/xmldom": "^0.8.11",
44
+ "jszip": "^3.10.1",
45
+ "pdfjs-dist": "^5.4.296",
46
+ "pixelmatch": "^7.1.0",
47
+ "pngjs": "^7.0.0",
48
+ "zod": "^4.1.11"
49
+ },
50
+ "optionalDependencies": {
51
+ "@napi-rs/canvas": "^0.1.80"
52
+ }
53
+ }
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "ppt-template-reuse",
3
+ "version": "0.4.0-rc.2",
4
+ "description": "Cross-host PPTX template learning, adaptive content planning, native OOXML authoring, previews, and validation through a local MCP engine.",
5
+ "author": {
6
+ "name": "NEU-ZHA"
7
+ },
8
+ "license": "MIT",
9
+ "keywords": [
10
+ "powerpoint",
11
+ "pptx",
12
+ "template",
13
+ "speaker-notes",
14
+ "content-audit",
15
+ "adaptive-planning",
16
+ "template-intelligence",
17
+ "deterministic",
18
+ "visual-qa"
19
+ ],
20
+ "skills": "./skills/",
21
+ "mcpServers": "./.mcp.json",
22
+ "interface": {
23
+ "displayName": "PPT Template Reuse",
24
+ "shortDescription": "Purpose-aware, content-adaptive reuse of native PPTX templates.",
25
+ "longDescription": "Uses the same local engine as Claude Code, Kimi Code, Kimi Work, and WorkBuddy. It identifies template purposes and safe native slots, anchors every source unit, adapts page count to content volume and speaking time, preserves reasoning in notes, blocks missing visuals and unsafe edits, emits real previews, and requires rendered QA before delivery.",
26
+ "developerName": "NEU-ZHA",
27
+ "category": "Productivity",
28
+ "capabilities": [
29
+ "Read",
30
+ "Write"
31
+ ],
32
+ "defaultPrompt": [
33
+ "Use this PPTX template and my source material to make a content-complete editable presentation.",
34
+ "Identify what every page in this template is for, then plan the deck from my material.",
35
+ "Choose the best template from my local library and create a detailed presentation with speaker notes."
36
+ ]
37
+ }
38
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "mcpServers": {
3
+ "ppt-template-reuse": {
4
+ "command": "ppt-template-reuse-mcp",
5
+ "args": [],
6
+ "note": "Local host-neutral PPTX template analysis, content planning, native OOXML authoring, previews, and validation."
7
+ }
8
+ }
9
+ }
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: learn-ppt-template
3
+ description: Create a content-complete, editable PowerPoint from a user-provided PPTX/POTX template and DOCX, PDF, PPTX, Markdown, or text material. Use whenever the user asks to make, imitate, learn, follow, or reuse a presentation template. Identify what every template page is for, adapt page count to source volume, preserve reasoning in native speaker notes, request missing visuals, expose real page previews, and stop on unresolved layouts, unsafe object edits, incomplete source coverage, or failed rendering. Works through the host-neutral ppt-template-reuse MCP or ppt-reuse CLI.
4
+ ---
5
+
6
+ # Learn PPT Template
7
+
8
+ Use the `ppt-template-reuse` MCP server as the normal execution path. Do not ask
9
+ the user to prepare JSON, choose a page count, or run commands.
10
+
11
+ ## Default behavior
12
+
13
+ For a request such as “参考这个模板和这些材料做一份内容完整、可编辑、带讲稿的
14
+ PPT”:
15
+
16
+ 1. Call `ppt_create_session` with the template, material, output path, current
17
+ workspace roots, and stated audience/duration.
18
+ 2. Call `ppt_inspect`.
19
+ 3. Read every extracted source unit. Submit every original `id` and `anchor`
20
+ through `ppt_submit_semantics`; label facts, chronology, issues, rules,
21
+ evidence, analysis, counterarguments, rebuttals, conclusions, and
22
+ recommendations. Preserve `parentAnchor`, `segmentIndex`, and `segmentCount`
23
+ when a long paragraph, table, slide, or PDF page was deterministically
24
+ segmented. Never silently merge away or omit a unit.
25
+ For structured layouts, also supply short `displayFragments` with roles such
26
+ as `date`, `label`, `step`, `metric-label`, `body`, or `description`. These
27
+ are source-grounded slot values, not invented decoration. A timeline needs
28
+ dates and node labels; a multi-item grid needs one label/body pair per item.
29
+ 4. Review only template slides returned as unresolved. Classify each page's
30
+ purpose, safe slots, capacity, required assets, protected elements, and
31
+ editability. Never approve uncertain SmartArt, chart, media, animation, icon,
32
+ or flattened-object edits. If several opening or closing variants are
33
+ returned, approve exactly one for each role and block the alternatives; do
34
+ not choose by page order.
35
+ 5. Call `ppt_plan`. Let source volume, argument structure, slot capacity, and
36
+ requested speaking time determine page count. If planning returns
37
+ `template-selection`, show the returned full-size candidates, approve one
38
+ source slide for that role, block the competing candidates in the frozen
39
+ template profile, resubmit semantics, and plan again. Never break a tie by
40
+ page number.
41
+ 6. If assets are required, use the host's image search or generation capability
42
+ when authorized; otherwise ask once for all missing assets. Register them
43
+ with `ppt_supply_assets`. Never permit gray blocks or temporary placeholders.
44
+ 7. Call `ppt_generate`, then poll `ppt_status`. Show new real page previews in
45
+ order. Stop when the status contains `actionRequired`.
46
+ 8. Call `ppt_validate` when generation did not already validate. Deliver only
47
+ when state is `completed`.
48
+
49
+ ## Content rules
50
+
51
+ - Preserve 100% of `critical` units and at least 95% weighted relevant content.
52
+ - Require every included source unit to have a verified destination trace.
53
+ Preserve 100% of original source text in speaker notes, visibly represent
54
+ 100% of critical units, and visibly represent at least 90% of weighted
55
+ relevant units. A `sourceUnitId` without matching field or notes text is not
56
+ coverage.
57
+ - Put source-verification text in a clearly marked
58
+ `[来源追踪,不逐字朗读]` notes block. Estimate duration from the spoken
59
+ explanation, not from that reference block. Do not add filler slides merely
60
+ because a short source finishes below the requested minimum time.
61
+ - Each analysis slide needs a claim, evidence, why-it-matters explanation, and
62
+ native speaker notes.
63
+ - Pair each represented counterargument with a response.
64
+ - Split overloaded material across more approved layouts; never shrink inherited
65
+ typography or delete reasoning to force a target page count.
66
+ - Use `core`, `appendix`, and `exclude` deliberately. An explicit duration
67
+ conflict requires a user choice between more time, narrower scope, or multiple
68
+ decks.
69
+ - A prior 19-page deck is only a regression example, never a fixed length.
70
+ - Do not infer length from unit count alone. Use original character volume,
71
+ argument nodes, counterargument chains, requested time, and the approved
72
+ template slot capacity. Very long material may legitimately produce dozens
73
+ or more than one hundred pages; stop for a duration/scope decision instead of
74
+ compressing it to a familiar deck length.
75
+
76
+ ## Template and object rules
77
+
78
+ - Exact native mode supports `.pptx` and `.potx`; `.pptm` is rejected and macros
79
+ are never executed.
80
+ - Treat flattened templates as `hybrid-locked`. Do not call them fully editable.
81
+ - Edit only declared slots. Preserve masters, layouts, themes, logos, page
82
+ chrome, animations, SmartArt, media, and all untargeted objects.
83
+ - Replace inherited images in place. Update tables only when dimensions match.
84
+ Update charts only when the embedded workbook and rendered result can both be
85
+ validated; otherwise stop.
86
+ - “插入图片”, “相关图片”, “ICON”, sample copy, and gray image blocks are
87
+ requirements, not audience-facing content.
88
+ - If an older reviewed template capsule is available, import it only when its
89
+ source SHA256 matches. Keep deny-by-default safety, preserve the old
90
+ preview/review bindings, approve exactly one opening and closing, and never
91
+ re-guess already reviewed slots.
92
+
93
+ ## Completion gate
94
+
95
+ Completion requires a frozen content plan and frame map, native speaker notes,
96
+ no residual examples or placeholders, matching slide/render counts, real
97
+ LibreOffice rendering, rendered-text fidelity, and a passing QA report. If
98
+ LibreOffice is missing or its PDF contains missing/substituted glyphs, analysis
99
+ and planning may complete but the PPTX must remain `validation-failed` until a
100
+ manual WPS or PowerPoint review confirms text, line breaks, images, notes, and
101
+ masters.
102
+
103
+ The engine is local-first: it only reads declared roots/current workspace and
104
+ temporary files, does not upload templates or material, and does not use the
105
+ network by itself.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "PPT模板智能复用"
3
+ short_description: "识别模板用途并按材料完整度生成内容充实、可编辑、带讲稿的PPT"
4
+ default_prompt: "使用 $learn-ppt-template 参考我的PPTX模板和材料,自动规划页数并生成内容完整、可编辑、带讲稿和真实预览的演示文稿。"
5
+ policy:
6
+ allow_implicit_invocation: true
@@ -0,0 +1,105 @@
1
+ ---
2
+ name: learn-ppt-template
3
+ description: Create a content-complete, editable PowerPoint from a user-provided PPTX/POTX template and DOCX, PDF, PPTX, Markdown, or text material. Use whenever the user asks to make, imitate, learn, follow, or reuse a presentation template. Identify what every template page is for, adapt page count to source volume, preserve reasoning in native speaker notes, request missing visuals, expose real page previews, and stop on unresolved layouts, unsafe object edits, incomplete source coverage, or failed rendering. Works through the host-neutral ppt-template-reuse MCP or ppt-reuse CLI.
4
+ ---
5
+
6
+ # Learn PPT Template
7
+
8
+ Use the `ppt-template-reuse` MCP server as the normal execution path. Do not ask
9
+ the user to prepare JSON, choose a page count, or run commands.
10
+
11
+ ## Default behavior
12
+
13
+ For a request such as “参考这个模板和这些材料做一份内容完整、可编辑、带讲稿的
14
+ PPT”:
15
+
16
+ 1. Call `ppt_create_session` with the template, material, output path, current
17
+ workspace roots, and stated audience/duration.
18
+ 2. Call `ppt_inspect`.
19
+ 3. Read every extracted source unit. Submit every original `id` and `anchor`
20
+ through `ppt_submit_semantics`; label facts, chronology, issues, rules,
21
+ evidence, analysis, counterarguments, rebuttals, conclusions, and
22
+ recommendations. Preserve `parentAnchor`, `segmentIndex`, and `segmentCount`
23
+ when a long paragraph, table, slide, or PDF page was deterministically
24
+ segmented. Never silently merge away or omit a unit.
25
+ For structured layouts, also supply short `displayFragments` with roles such
26
+ as `date`, `label`, `step`, `metric-label`, `body`, or `description`. These
27
+ are source-grounded slot values, not invented decoration. A timeline needs
28
+ dates and node labels; a multi-item grid needs one label/body pair per item.
29
+ 4. Review only template slides returned as unresolved. Classify each page's
30
+ purpose, safe slots, capacity, required assets, protected elements, and
31
+ editability. Never approve uncertain SmartArt, chart, media, animation, icon,
32
+ or flattened-object edits. If several opening or closing variants are
33
+ returned, approve exactly one for each role and block the alternatives; do
34
+ not choose by page order.
35
+ 5. Call `ppt_plan`. Let source volume, argument structure, slot capacity, and
36
+ requested speaking time determine page count. If planning returns
37
+ `template-selection`, show the returned full-size candidates, approve one
38
+ source slide for that role, block the competing candidates in the frozen
39
+ template profile, resubmit semantics, and plan again. Never break a tie by
40
+ page number.
41
+ 6. If assets are required, use the host's image search or generation capability
42
+ when authorized; otherwise ask once for all missing assets. Register them
43
+ with `ppt_supply_assets`. Never permit gray blocks or temporary placeholders.
44
+ 7. Call `ppt_generate`, then poll `ppt_status`. Show new real page previews in
45
+ order. Stop when the status contains `actionRequired`.
46
+ 8. Call `ppt_validate` when generation did not already validate. Deliver only
47
+ when state is `completed`.
48
+
49
+ ## Content rules
50
+
51
+ - Preserve 100% of `critical` units and at least 95% weighted relevant content.
52
+ - Require every included source unit to have a verified destination trace.
53
+ Preserve 100% of original source text in speaker notes, visibly represent
54
+ 100% of critical units, and visibly represent at least 90% of weighted
55
+ relevant units. A `sourceUnitId` without matching field or notes text is not
56
+ coverage.
57
+ - Put source-verification text in a clearly marked
58
+ `[来源追踪,不逐字朗读]` notes block. Estimate duration from the spoken
59
+ explanation, not from that reference block. Do not add filler slides merely
60
+ because a short source finishes below the requested minimum time.
61
+ - Each analysis slide needs a claim, evidence, why-it-matters explanation, and
62
+ native speaker notes.
63
+ - Pair each represented counterargument with a response.
64
+ - Split overloaded material across more approved layouts; never shrink inherited
65
+ typography or delete reasoning to force a target page count.
66
+ - Use `core`, `appendix`, and `exclude` deliberately. An explicit duration
67
+ conflict requires a user choice between more time, narrower scope, or multiple
68
+ decks.
69
+ - A prior 19-page deck is only a regression example, never a fixed length.
70
+ - Do not infer length from unit count alone. Use original character volume,
71
+ argument nodes, counterargument chains, requested time, and the approved
72
+ template slot capacity. Very long material may legitimately produce dozens
73
+ or more than one hundred pages; stop for a duration/scope decision instead of
74
+ compressing it to a familiar deck length.
75
+
76
+ ## Template and object rules
77
+
78
+ - Exact native mode supports `.pptx` and `.potx`; `.pptm` is rejected and macros
79
+ are never executed.
80
+ - Treat flattened templates as `hybrid-locked`. Do not call them fully editable.
81
+ - Edit only declared slots. Preserve masters, layouts, themes, logos, page
82
+ chrome, animations, SmartArt, media, and all untargeted objects.
83
+ - Replace inherited images in place. Update tables only when dimensions match.
84
+ Update charts only when the embedded workbook and rendered result can both be
85
+ validated; otherwise stop.
86
+ - “插入图片”, “相关图片”, “ICON”, sample copy, and gray image blocks are
87
+ requirements, not audience-facing content.
88
+ - If an older reviewed template capsule is available, import it only when its
89
+ source SHA256 matches. Keep deny-by-default safety, preserve the old
90
+ preview/review bindings, approve exactly one opening and closing, and never
91
+ re-guess already reviewed slots.
92
+
93
+ ## Completion gate
94
+
95
+ Completion requires a frozen content plan and frame map, native speaker notes,
96
+ no residual examples or placeholders, matching slide/render counts, real
97
+ LibreOffice rendering, rendered-text fidelity, and a passing QA report. If
98
+ LibreOffice is missing or its PDF contains missing/substituted glyphs, analysis
99
+ and planning may complete but the PPTX must remain `validation-failed` until a
100
+ manual WPS or PowerPoint review confirms text, line breaks, images, notes, and
101
+ masters.
102
+
103
+ The engine is local-first: it only reads declared roots/current workspace and
104
+ temporary files, does not upload templates or material, and does not use the
105
+ network by itself.
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "PPT模板智能复用"
3
+ short_description: "识别模板用途并按材料完整度生成内容充实、可编辑、带讲稿的PPT"
4
+ default_prompt: "使用 $learn-ppt-template 参考我的PPTX模板和材料,自动规划页数并生成内容完整、可编辑、带讲稿和真实预览的演示文稿。"
5
+ policy:
6
+ allow_implicit_invocation: true
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { pathToFileURL } from "node:url";
6
+
7
+ import {
8
+ createSession,
9
+ engineDoctor,
10
+ generateSession,
11
+ getSessionStatus,
12
+ validateSession,
13
+ } from "../core/universal-engine.mjs";
14
+ import { parseCliArgs } from "../core/io.mjs";
15
+ import { importLegacyCapsule } from "../core/legacy-capsule-import.mjs";
16
+ import { installHosts, uninstallHost } from "../install/installer.mjs";
17
+ import { startHttp, startStdio } from "../mcp/server.mjs";
18
+
19
+ const packageJson = JSON.parse(
20
+ fs.readFileSync(new URL("../../package.json", import.meta.url), "utf8"),
21
+ );
22
+
23
+ function usage() {
24
+ return `ppt-reuse ${packageJson.version}
25
+
26
+ Commands:
27
+ ppt-reuse install --auto [--dry-run] [--home-root <path>]
28
+ ppt-reuse doctor --host all
29
+ ppt-reuse import-capsule --capsule <dir> --template <file> --opening <n> --closing <n> [--approve <1,2,3>]
30
+ ppt-reuse create --template <file> --source <file> --output <file> --root <dir>
31
+ ppt-reuse status <session-id> [--after-event <n>]
32
+ ppt-reuse resume <session-id>
33
+ ppt-reuse validate <session-id>
34
+ ppt-reuse mcp --stdio
35
+ ppt-reuse serve --host 127.0.0.1 [--port 8765]
36
+ ppt-reuse uninstall --host <name> [--dry-run]`;
37
+ }
38
+
39
+ function required(args, key) {
40
+ const value = args[key];
41
+ if (!value || value === true) throw new Error(`Missing --${key}`);
42
+ return String(value);
43
+ }
44
+
45
+ function output(value) {
46
+ process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
47
+ }
48
+
49
+ function requirements(args) {
50
+ return {
51
+ title: args.title,
52
+ subtitle: args.subtitle,
53
+ audience: args.audience,
54
+ communicationJob: args["communication-job"],
55
+ useCase: args["use-case"],
56
+ density: args.density,
57
+ scope: args.scope,
58
+ duration: args.duration,
59
+ };
60
+ }
61
+
62
+ export async function runCli(argv = process.argv.slice(2)) {
63
+ const [command, ...rest] = argv;
64
+ const args = parseCliArgs(rest);
65
+ if (command === "--version" || command === "-v") {
66
+ process.stdout.write(`${packageJson.version}\n`);
67
+ return;
68
+ }
69
+ if (!command || command === "--help" || command === "-h" || args.help) {
70
+ process.stdout.write(`${usage()}\n`);
71
+ return;
72
+ }
73
+ if (command === "install") {
74
+ const installOptions = {
75
+ host: args.host || (args.auto === true ? "auto" : "auto"),
76
+ homeRoot: args["home-root"],
77
+ };
78
+ const plan = await installHosts({ ...installOptions, dryRun: true });
79
+ output(
80
+ args["dry-run"] === true
81
+ ? { plan }
82
+ : {
83
+ plan,
84
+ result: await installHosts({
85
+ ...installOptions,
86
+ dryRun: false,
87
+ }),
88
+ },
89
+ );
90
+ return;
91
+ }
92
+ if (command === "doctor") {
93
+ const engine = await engineDoctor({ home: args.home });
94
+ const hosts = await installHosts({
95
+ host: args.host || "all",
96
+ homeRoot: args["home-root"],
97
+ dryRun: true,
98
+ });
99
+ output({ engine, hosts });
100
+ return;
101
+ }
102
+ if (command === "import-capsule") {
103
+ const approvedSourceSlides = String(args.approve || "")
104
+ .split(",")
105
+ .map((value) => Number(value.trim()))
106
+ .filter(Number.isFinite);
107
+ output(
108
+ await importLegacyCapsule({
109
+ capsuleDir: required(args, "capsule"),
110
+ templatePath: required(args, "template"),
111
+ openingSourceSlide: Number(required(args, "opening")),
112
+ closingSourceSlide: Number(required(args, "closing")),
113
+ approvedSourceSlides,
114
+ home: args.home,
115
+ persist: args["dry-run"] !== true,
116
+ }),
117
+ );
118
+ return;
119
+ }
120
+ if (command === "create") {
121
+ const rootValues = rest
122
+ .flatMap((token, index) =>
123
+ token === "--root" && rest[index + 1] ? [rest[index + 1]] : [],
124
+ );
125
+ output(
126
+ await createSession(
127
+ {
128
+ template: required(args, "template"),
129
+ source: required(args, "source"),
130
+ output: required(args, "output"),
131
+ roots: rootValues,
132
+ requirements: requirements(args),
133
+ },
134
+ { home: args.home },
135
+ ),
136
+ );
137
+ return;
138
+ }
139
+ if (command === "status") {
140
+ const id = args._[0];
141
+ if (!id) throw new Error("Missing session id");
142
+ output(
143
+ await getSessionStatus(id, {
144
+ home: args.home,
145
+ afterEvent: Number(args["after-event"] || 0),
146
+ }),
147
+ );
148
+ return;
149
+ }
150
+ if (command === "resume") {
151
+ const id = args._[0];
152
+ if (!id) throw new Error("Missing session id");
153
+ output(
154
+ await generateSession(id, {
155
+ home: args.home,
156
+ validate: true,
157
+ livePreview: args["no-live-preview"] !== true,
158
+ }),
159
+ );
160
+ return;
161
+ }
162
+ if (command === "validate") {
163
+ const id = args._[0];
164
+ if (!id) throw new Error("Missing session id");
165
+ output(await validateSession(id, { home: args.home }));
166
+ return;
167
+ }
168
+ if (command === "mcp") {
169
+ await startStdio({ home: args.home });
170
+ return;
171
+ }
172
+ if (command === "serve") {
173
+ const host = String(args.host || "127.0.0.1");
174
+ const port = Number(args.port || 8765);
175
+ await startHttp({ host, port, home: args.home });
176
+ process.stdout.write(
177
+ `ppt-template-reuse MCP listening at http://${host}:${port}/mcp\n`,
178
+ );
179
+ return;
180
+ }
181
+ if (command === "uninstall") {
182
+ output(
183
+ await uninstallHost({
184
+ host: required(args, "host"),
185
+ homeRoot: args["home-root"],
186
+ dryRun: args["dry-run"] === true,
187
+ }),
188
+ );
189
+ return;
190
+ }
191
+ throw new Error(`Unknown command: ${command}\n${usage()}`);
192
+ }
193
+
194
+ const isMain =
195
+ process.argv[1] &&
196
+ pathToFileURL(fs.realpathSync(path.resolve(process.argv[1]))).href ===
197
+ import.meta.url;
198
+ if (isMain) {
199
+ runCli().catch((error) => {
200
+ process.stderr.write(`${error.stack || error.message}\n`);
201
+ process.exit(1);
202
+ });
203
+ }