@oh-story/dsh 0.1.9 → 0.1.10
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 +12 -12
- package/lib/client.js +204 -72
- package/lib/client.js.map +3 -3
- package/lib/drama/manifest.json +84 -74
- package/lib/drama/skills/short-drama/SKILL.md +2 -2
- package/lib/drama/skills/short-drama/scripts/creator_markdown_check.py +14 -0
- package/lib/drama/skills/short-drama-assets/references/character-and-look.md +10 -0
- package/lib/drama/skills/short-drama-assets/references/continuity-lock.md +6 -9
- package/lib/drama/skills/short-drama-assets/references/voice-direction.md +28 -0
- package/lib/drama/skills/short-drama-edit/SKILL.md +27 -29
- package/lib/drama/skills/short-drama-edit/assets//345/211/252/350/276/221/345/215/225.md +2 -2
- package/lib/drama/skills/short-drama-edit/references/cut-craft.md +24 -67
- package/lib/drama/skills/short-drama-edit/references/delivery-verify.md +27 -56
- package/lib/drama/skills/short-drama-edit/references/generated-footage.md +37 -79
- package/lib/drama/skills/short-drama-edit/references/sound-and-subtitles.md +39 -92
- package/lib/drama/skills/short-drama-edit/references/stage-contract.md +12 -16
- package/lib/drama/skills/short-drama-edit/scripts/edit_tool.py +126 -11
- package/lib/drama/skills/short-drama-edit/scripts/selftest.py +64 -0
- package/lib/drama/skills/short-drama-image-prompts/SKILL.md +2 -0
- package/lib/drama/skills/short-drama-image-prompts/references/common-recipe.md +11 -0
- package/lib/drama/skills/short-drama-produce/SKILL.md +5 -1
- package/lib/drama/skills/short-drama-produce/references/adapter-contract.md +14 -20
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-h3-video.md +8 -26
- package/lib/drama/skills/short-drama-produce/references/providers/minimax-speech.md +41 -0
- package/lib/drama/skills/short-drama-produce/scripts/provider_adapters.py +142 -1
- package/lib/drama/skills/short-drama-review/SKILL.md +2 -3
- package/lib/drama/skills/short-drama-review/references/production-quality-gates.md +6 -19
- package/lib/drama/skills/short-drama-review/references/rubric-visual-motion.md +9 -35
- package/lib/drama/skills/short-drama-storyboard/SKILL.md +10 -6
- package/lib/drama/skills/short-drama-storyboard/references/lighting-craft.md +39 -0
- package/lib/drama/skills/short-drama-storyboard/references/shot-craft.md +58 -21
- package/lib/drama/skills/short-drama-storyboard/references/stage-contract.md +1 -1
- package/lib/drama/skills/short-drama-video-prompts/SKILL.md +10 -9
- package/lib/drama/skills/short-drama-video-prompts/references/camera-audio-continuity.md +17 -0
- package/lib/drama/skills/short-drama-video-prompts/references/minimax-h3.md +12 -38
- package/lib/drama/skills/short-drama-video-prompts/references/motion-recipe.md +4 -7
- package/lib/drama/skills/short-drama-video-prompts/references/performance-action-timing.md +8 -22
- package/lib/drama/skills/short-drama-video-prompts/references/production-prompt-grammar.md +6 -19
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.0.md +2 -3
- package/lib/drama/skills/short-drama-video-prompts/references/seedance-2.5.md +34 -9
- package/lib/drama/skills/short-drama-video-prompts/references/stage-contract.md +2 -2
- package/lib/drama/skills/short-drama-write/SKILL.md +2 -4
- package/lib/index.js +289 -91
- package/lib/index.js.map +4 -4
- package/lib/novel-to-game/README.md +299 -59
- package/lib/novel-to-game/README_ZH.md +299 -56
- package/lib/novel-to-game/VERSION +1 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/BUILD_BRIEF.md +3 -1
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/RUN.md +11 -3
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/new-guofeng.css +397 -8
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/css/style.css +0 -31
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/data.js +71 -73
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/engine.js +78 -89
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/main.js +37 -14
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/js/text.js +5 -5
- package/lib/novel-to-game/examples/jin-ping-mei/build/app/package.json +2 -1
- package/lib/novel-to-game/examples/jin-ping-mei/qa/README.md +11 -16
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verification.json +11 -11
- package/lib/novel-to-game/examples/jin-ping-mei/qa/verify_playable_model.mjs +23 -0
- package/lib/novel-to-game/manifest.json +77 -87
- package/lib/novel-to-game/skills/game-art-direction/SKILL.md +7 -12
- package/lib/novel-to-game/skills/game-art-direction/references/art-direction-method.md +27 -106
- package/lib/novel-to-game/skills/game-build/SKILL.md +13 -14
- package/lib/novel-to-game/skills/game-build/references/blender-asset-workflow.md +24 -0
- package/lib/novel-to-game/skills/game-build/references/build-brief-contract.md +45 -82
- package/lib/novel-to-game/skills/game-build/references/generative-media-pipeline.md +14 -45
- package/lib/novel-to-game/skills/game-build/references/playable-model-contract.md +30 -65
- package/lib/novel-to-game/skills/game-build/references/tts-production-contract.md +13 -27
- package/lib/novel-to-game/skills/game-concept/SKILL.md +13 -15
- package/lib/novel-to-game/skills/game-concept/references/concept-method.md +28 -75
- package/lib/novel-to-game/skills/game-qa/SKILL.md +6 -7
- package/lib/novel-to-game/skills/game-qa/references/test-design-method.md +9 -5
- package/lib/novel-to-game/skills/game-world-design/SKILL.md +12 -16
- package/lib/novel-to-game/skills/game-world-design/references/narrative-design-method.md +23 -72
- package/lib/novel-to-game/skills/game-world-design/references/numeric-design-method.md +14 -48
- package/lib/novel-to-game/skills/game-world-design/references/world-design-method.md +36 -103
- package/lib/novel-to-game/skills/novel-game-analyze/SKILL.md +4 -10
- package/lib/novel-to-game/skills/novel-game-analyze/references/gameability-protocol.md +4 -4
- package/lib/novel-to-game/skills/novel-to-game/SKILL.md +9 -21
- package/lib/novel-to-game/skills/novel-to-game/references/intake-method.md +5 -4
- package/lib/novel-to-game/skills/novel-to-game/references/pipeline-contract.md +5 -8
- package/lib/oh-story/manifest.json +230 -185
- package/lib/oh-story/roles/chapter-extractor.md +177 -237
- package/lib/oh-story/roles/consistency-checker.md +4 -4
- package/lib/oh-story/roles/narrative-writer.md +24 -25
- package/lib/oh-story/roles/story-architect.md +5 -5
- package/lib/oh-story/roles/story-explorer.md +11 -11
- package/lib/oh-story/skills/story/SKILL.md +13 -7
- package/lib/oh-story/skills/story/VERSION +1 -1
- package/lib/oh-story/skills/story/references/author-memory.md +54 -24
- package/lib/oh-story/skills/story/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-cover/SKILL.md +10 -1
- package/lib/oh-story/skills/story-deslop/SKILL.md +40 -53
- package/lib/oh-story/skills/story-deslop/references/anti-ai-writing.md +11 -10
- package/lib/oh-story/skills/story-deslop/references/author-memory.md +54 -24
- package/lib/oh-story/skills/story-deslop/references/deslop-gates.md +1 -1
- package/lib/oh-story/skills/story-deslop/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-import/SKILL.md +77 -81
- package/lib/oh-story/skills/story-import/references/character-state-reverse.md +1 -1
- package/lib/oh-story/skills/story-import/references/state-tracking.md +4 -69
- package/lib/oh-story/skills/story-import/references/structure-mapping-long.md +10 -8
- package/lib/oh-story/skills/story-import/references/tracking-transaction.md +8 -6
- package/lib/oh-story/skills/story-import/scripts/check-outline-contract.js +34 -16
- package/lib/oh-story/skills/story-import/scripts/tracking_commit.py +210 -2
- package/lib/oh-story/skills/story-import/scripts/wordcount_core.py +3 -0
- package/lib/oh-story/skills/story-long-analyze/SKILL.md +91 -258
- package/lib/oh-story/skills/story-long-analyze/references/author-facing.md +245 -0
- package/lib/oh-story/skills/story-long-analyze/references/deconstruction-notes.md +2 -2
- package/lib/oh-story/skills/story-long-analyze/references/inspiration-library.md +105 -0
- package/lib/oh-story/skills/story-long-analyze/references/material-decomposition.md +113 -105
- package/lib/oh-story/skills/story-long-analyze/references/output-templates.md +169 -125
- package/lib/oh-story/skills/story-long-analyze/references/pipeline-ops.md +172 -64
- package/lib/oh-story/skills/story-long-analyze/references/semantic-acceptance-fixtures.md +84 -0
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-generator.md +42 -132
- package/lib/oh-story/skills/story-long-analyze/references/style-profile-protocol.md +5 -5
- package/lib/oh-story/skills/story-long-analyze/scripts/build_chapter_index.py +466 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspect_existing_assets.py +702 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/inspiration_index.py +864 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/manage_analysis_run.py +1106 -0
- package/lib/oh-story/skills/story-long-analyze/scripts/render_relation_chart.py +425 -0
- package/lib/oh-story/skills/story-long-scan/SKILL.md +4 -1
- package/lib/oh-story/skills/story-long-write/SKILL.md +38 -29
- package/lib/oh-story/skills/story-long-write/references/anti-ai-writing.md +11 -10
- package/lib/oh-story/skills/story-long-write/references/artifact-protocols.md +25 -20
- package/lib/oh-story/skills/story-long-write/references/author-memory.md +54 -24
- package/lib/oh-story/skills/story-long-write/references/benchmark-recall.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/cross-book-recall.md +26 -0
- package/lib/oh-story/skills/story-long-write/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-long-write/references/emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-long-write/references/long-chapter-quality.md +4 -13
- package/lib/oh-story/skills/story-long-write/references/outline-structure-theory.md +1 -1
- package/lib/oh-story/skills/story-long-write/references/project-files.md +9 -7
- package/lib/oh-story/skills/story-long-write/references/reader-contract-and-progression.md +12 -4
- package/lib/oh-story/skills/story-long-write/references/reference-index.md +1 -2
- package/lib/oh-story/skills/story-long-write/references/tracking-transaction.md +8 -6
- package/lib/oh-story/skills/story-long-write/references/workflow-chapter.md +86 -115
- package/lib/oh-story/skills/story-long-write/references/workflow-daily.md +55 -120
- package/lib/oh-story/skills/story-long-write/references/workflow-revision.md +15 -11
- package/lib/oh-story/skills/story-long-write/references/workflow-setup.md +66 -68
- package/lib/oh-story/skills/story-long-write/references/writer-prompt-fallback.md +3 -3
- package/lib/oh-story/skills/story-long-write/references/writing-craft.md +4 -12
- package/lib/oh-story/skills/story-long-write/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-long-write/scripts/build_writer_prompt.py +139 -18
- package/lib/oh-story/skills/story-long-write/scripts/check-outline-contract.js +34 -16
- package/lib/oh-story/skills/story-long-write/scripts/outline_view.py +5 -3
- package/lib/oh-story/skills/story-long-write/scripts/storyctl.py +58 -0
- package/lib/oh-story/skills/story-long-write/scripts/tracking_commit.py +210 -2
- package/lib/oh-story/skills/story-long-write/scripts/wordcount_core.py +3 -0
- package/lib/oh-story/skills/story-review/SKILL.md +68 -85
- package/lib/oh-story/skills/story-review/references/anti-ai-writing.md +11 -10
- package/lib/oh-story/skills/story-review/references/author-memory.md +54 -24
- package/lib/oh-story/skills/story-review/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-review/references/review-quality.md +4 -10
- package/lib/oh-story/skills/story-review/references/tracking-transaction.md +8 -6
- package/lib/oh-story/skills/story-review/scripts/author_memory_commit.py +934 -170
- package/lib/oh-story/skills/story-review/scripts/tracking_commit.py +210 -2
- package/lib/oh-story/skills/story-review/scripts/wordcount_core.py +3 -0
- package/lib/oh-story/skills/story-setup/SKILL.md +50 -42
- package/lib/oh-story/skills/story-setup/references/agent-references/anti-ai-writing.md +11 -10
- package/lib/oh-story/skills/story-setup/references/agent-references/deslop-gates.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-setup/references/agent-references/long-emotional-methods.md +2 -2
- package/lib/oh-story/skills/story-setup/references/agent-references/long-quality.md +1 -1
- package/lib/oh-story/skills/story-setup/references/agent-references/writing-craft.md +4 -12
- package/lib/oh-story/skills/story-setup/references/diagnostics.md +1 -1
- package/lib/oh-story/skills/story-short-analyze/SKILL.md +22 -3
- package/lib/oh-story/skills/story-short-analyze/references/material-decomposition.md +5 -1
- package/lib/oh-story/skills/story-short-scan/SKILL.md +5 -1
- package/lib/oh-story/skills/story-short-write/SKILL.md +6 -147
- package/lib/oh-story/skills/story-short-write/references/author-memory.md +54 -24
- package/lib/oh-story/skills/story-short-write/references/dialogue-mastery.md +4 -0
- package/lib/oh-story/skills/story-short-write/references/reference-index.md +2 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-design.md +3 -2
- package/lib/oh-story/skills/story-short-write/references/workflow-draft.md +126 -0
- package/lib/oh-story/skills/story-short-write/references/workflow-revision.md +32 -0
- package/lib/oh-story/skills/story-short-write/scripts/author_memory_commit.py +934 -170
- package/lib/types/client/drama-production-view.d.ts.map +1 -1
- package/lib/types/client/file-activity.d.ts +19 -1
- package/lib/types/client/file-activity.d.ts.map +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/production-prompts.d.ts +7 -0
- package/lib/types/client/production-prompts.d.ts.map +1 -1
- package/lib/types/client/production-runtime.d.ts +16 -0
- package/lib/types/client/production-runtime.d.ts.map +1 -1
- package/lib/types/drama-adapters.d.ts +5 -3
- package/lib/types/drama-adapters.d.ts.map +1 -1
- package/lib/types/native-hooks.d.ts +52 -0
- package/lib/types/native-hooks.d.ts.map +1 -1
- package/lib/types/role-identity.d.ts +17 -0
- package/lib/types/role-identity.d.ts.map +1 -0
- package/lib/types/role-tool.d.ts.map +1 -1
- package/lib/types/skill-provider.d.ts.map +1 -1
- package/package.json +45 -45
- package/lib/novel-to-game/skills/game-world-design/references/dialogue-design-method.md +0 -58
- package/lib/novel-to-game/skills/game-world-design/references/game-writing-craft.md +0 -53
- package/lib/novel-to-game/skills/novel-to-game/references/intake-benchmark-reference.md +0 -38
- package/lib/oh-story/skills/story-long-write/references/state-tracking.md +0 -108
package/lib/index.js
CHANGED
|
@@ -35,8 +35,9 @@ var DRAMA_ADAPTERS = [
|
|
|
35
35
|
name: "minimax-h3",
|
|
36
36
|
label: "MiniMax H3",
|
|
37
37
|
modality: "video",
|
|
38
|
-
|
|
39
|
-
|
|
38
|
+
// Every H3 job carries an integer duration, and the adapter refuses it without this range.
|
|
39
|
+
requiredEnv: ["MINIMAX_API_KEY", "MINIMAX_VIDEO_MODEL", "MINIMAX_VIDEO_RESOLUTIONS", "MINIMAX_VIDEO_MIN_DURATION", "MINIMAX_VIDEO_MAX_DURATION"],
|
|
40
|
+
optionalEnv: ["MINIMAX_VIDEO_BASE_URL", "MINIMAX_VIDEO_RATIOS"],
|
|
40
41
|
timeoutSeconds: 3600,
|
|
41
42
|
reference: "short-drama-produce/references/providers/minimax-h3-video.md"
|
|
42
43
|
},
|
|
@@ -48,6 +49,15 @@ var DRAMA_ADAPTERS = [
|
|
|
48
49
|
optionalEnv: ["MINIMAX_BASE_URL"],
|
|
49
50
|
timeoutSeconds: 600,
|
|
50
51
|
reference: "short-drama-produce/references/providers/minimax-music.md"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
name: "minimax-speech",
|
|
55
|
+
label: "MiniMax Speech",
|
|
56
|
+
modality: "tts",
|
|
57
|
+
requiredEnv: ["MINIMAX_API_KEY"],
|
|
58
|
+
optionalEnv: ["MINIMAX_BASE_URL"],
|
|
59
|
+
timeoutSeconds: 600,
|
|
60
|
+
reference: "short-drama-produce/references/providers/minimax-speech.md"
|
|
51
61
|
}
|
|
52
62
|
];
|
|
53
63
|
function generatedDirectory(temporaryRoot) {
|
|
@@ -118,17 +128,30 @@ var DSH_SKILL_BRIDGE = [
|
|
|
118
128
|
"DSH owns the workspace, model, preset, permissions, Session Log, tools, subagents, cancellation, resume, and Agent UI.",
|
|
119
129
|
"Never start another Agent runtime, session transport, Dashboard, SSE stream, polling loop, or model configuration.",
|
|
120
130
|
"All seven upstream Oh Story specialist Roles are bundled. Invoke one with oh_story_role and a self-contained prompt.",
|
|
121
|
-
"Never inspect .claude/agents, .codex/agents, .opencode/agents, .agents/agents, .zcode, or .story-deployed to decide whether a Role is available
|
|
131
|
+
"Never inspect .claude/agents, .codex/agents, .opencode/agents, .agents/agents, .zcode, or .story-deployed to decide whether a Role is available.",
|
|
132
|
+
"Oh Story Roles are reached only through oh_story_role. Never start one through a generic subagent or Task tool \u2014 not DSH's subagent or subagent_fork tools and not invoke_subagent \u2014 and never pass a Role name as subagent_type, agent_type, agent, or TypeName, even where the upstream text below describes that for another host.",
|
|
122
133
|
"Use only DSH-visible tools. DSH sandbox and permission policy remain authoritative.",
|
|
134
|
+
// Upstream deploys these 「与作者协作」 rules only in its CLAUDE.md/AGENTS.md
|
|
135
|
+
// templates, which DSH does not install.
|
|
136
|
+
"Working with the author: reply and report in the author's writing terms (book, chapter, outline, settings), never in script, field, or state names; attach a raw error only when something failed. A template block marked <!-- author-report --> is only a format reference: output its text directly, without that marker and without a code fence.",
|
|
137
|
+
"Record a writing habit or preference the author asks to remember, forget, or confirm only through the story Skill's author memory, whose scripts/author_memory_commit.py writes .story/\u4F5C\u8005\u8BB0\u5FC6/; never keep it in a host or DSH memory. Simply follow a one-off request without recording it.",
|
|
138
|
+
"Never edit the bundled Skill files (SKILL.md, references/, scripts/). When a Skill script fails, stop and show the author the command you ran and its error instead of patching or working around it.",
|
|
123
139
|
"</oh-story-dsh-integration>"
|
|
124
140
|
].join("\n");
|
|
141
|
+
var DSH_ANALYSIS_BATCHES = [
|
|
142
|
+
"Stage 2 batches in DSH: dispatch each planned batch with oh_story_role, role chapter-extractor, and a self-contained prompt carrying the batch ID, the input kind, the plan's source_files (per-chapter source_locator for a raw batch) and chapter_chars, the output file {\u62C6\u6587\u76EE\u5F55}/_analysis_cache/\u8F93\u5165-{\u6279\u6B21ID}.md, and the hand-off cache path when there is one.",
|
|
143
|
+
"The extractor reads the source itself, writes or edits only that batch input file (DSH denies any other write or edit by a chapter-extractor child), and replies with one receipt line; do not read the file yourself.",
|
|
144
|
+
"Commit it with manage_analysis_run.py commit, one commit at a time in this session; on rejection hand the error and the file path back to a chapter-extractor to fix in place with edit.",
|
|
145
|
+
"Run manage_analysis_run.py and the other bundled Python 3 scripts through the current DSH execution world, trying python3, python, then py -3. If none is available, stop and tell the author in plain words that this step needs Python 3 on the machine running DSH; never simulate a script's output or hand-write the files it owns.",
|
|
146
|
+
"Keep concurrent oh_story_role calls modest. The default is upstream's \u6709\u9650\u5E76\u884C tier: three batches per round, issued together, the next round only after all three are committed; an unasked, full, multi-book, or import run uses it. DSH runs calls from one step in parallel only up to the host's own limit and may run them one after another, so never promise more. Use \u4E32\u884C or \u4E0D\u9650\u6279\u6B21\u987A\u5E8F only when the author chooses it, and under \u4E0D\u9650\u6279\u6B21\u987A\u5E8F halve the count on any rate limit, timeout, or child error."
|
|
147
|
+
].join(" ");
|
|
125
148
|
var DSH_SKILL_OVERRIDES = {
|
|
126
149
|
story: "The \u5C0F\u8BF4 workspace is an official DSH conversation view. Never start or open a second web application.",
|
|
127
150
|
"story-setup": "Initialize or validate novel project data only. DSH already supplies Skills, Roles, hooks, tools, permissions, sessions, and UI; never deploy Claude/OpenCode/Codex/Antigravity/ZCode/OpenClaw/Reasonix files or a .story-deployed marker.",
|
|
128
|
-
"story-long-analyze":
|
|
129
|
-
"story-long-write": "All named Roles are provided through oh_story_role. Do not check platform agent files. Keep the upstream writing, Tracking, lint, outline, revision, and quality workflows.",
|
|
130
|
-
"story-review": "All named reviewer Roles are provided through oh_story_role. Do not check platform agent files; full/lean review may use the bundled Roles directly.",
|
|
131
|
-
"story-import":
|
|
151
|
+
"story-long-analyze": `Use oh_story_role for chapter extraction or specialist analysis. Never inspect platform agent directories or require a deployed external Agent definition. ${DSH_ANALYSIS_BATCHES}`,
|
|
152
|
+
"story-long-write": "All named Roles are provided through oh_story_role. Do not check platform agent files. Keep the upstream writing, Tracking, lint, outline, revision, and quality workflows. Create and assemble \u6B63\u6587/ chapter files only with the write and edit tools, never through shell redirection, cp, mv, tee, or a script, so that DSH's outline guard, its Tracking reminder, and the \u5C0F\u8BF4 view observe the write; upstream's own in-place chapter fixes such as storyctl.py chapter check --fix-punctuation stay as upstream describes.",
|
|
153
|
+
"story-review": "All named reviewer Roles are provided through oh_story_role. Do not check platform agent files; full/lean review may use the bundled Roles directly. When a review falls back to solo in DSH, the reason is that oh_story_role or DSH's spawn runtime is unavailable in this Session (Fallback agent tool unavailable -> solo) or a Role run failed (Fallback spawn failed -> solo); say that in one plain sentence. Never tell the author that the reviewers are not installed, missing, or outdated, and never tell them to run /story-setup.",
|
|
154
|
+
"story-import": `All named Roles are provided through oh_story_role. Do not require story-setup to deploy them, and never inspect platform agent directories. Phase 2 runs the story-long-analyze pipeline the DSH way below; an import counts as automatic continuation, so it uses the limited-parallel tier without asking. ${DSH_ANALYSIS_BATCHES} Run the Python 3 scripts this Skill needs (storyctl.py wordcount measure, tracking_commit.py init and check) the same way. Keep upstream's Phase 3-L order: Step 2 copies the manuscript into \u6B63\u6587/, Step 6 writes the \u7EC6\u7EB2, Step 7 initialises Tracking. DSH's prose guard mirrors the outline gate of upstream proseBlockReason for books with \u5927\u7EB2/ or \u8FFD\u8E2A/, so open an import window before Step 2 copies any chapter: create .story/work/\u5BFC\u5165\u4E2D.md in the book directory (the workspace root in DSH's usual single-book layout) with one line naming the book being imported, and delete it right after Step 7's tracking_commit.py init and check both succeed. The window only holds while \u8FFD\u8E2A/_tracking-state.json is absent.`,
|
|
132
155
|
"story-deslop": "Use the bundled narrative-writer Role through oh_story_role when specialist review is useful. Never inspect platform agent directories.",
|
|
133
156
|
"story-short-analyze": "Use oh_story_role for specialist analysis. Never inspect platform agent directories or require external Agent deployment.",
|
|
134
157
|
"story-short-write": "All named Roles are provided through oh_story_role. Do not inspect platform agent files; preserve the upstream short-fiction workflow and quality gates.",
|
|
@@ -141,7 +164,7 @@ var DSH_DRAMA_BRIDGE = [
|
|
|
141
164
|
"DSH owns the workspace, model, preset, permissions, Session Log, tools, approvals, cancellation, resume, and Agent UI.",
|
|
142
165
|
"The \u77ED\u5267 tab is the creator workspace. Never start dashboard_server.py, another web server, Dashboard, Agent runtime, session transport, or model configuration.",
|
|
143
166
|
"Drama Skills uses a creator-first contract: each episode keeps only the requested documents, up to five creator-facing sources at \u5267\u96C6/<EP>/\u5267\u672C.md, \u89C6\u89C9\u8BBE\u5B9A.md, \u5206\u955C.md, \u56FE\u7247\u63D0\u793A\u8BCD.md, and \u89C6\u9891\u63D0\u793A\u8BCD.md. Never precreate empty documents, backfill nominal stages, or start work the creator did not request. Persisted reviews use creator-readable Markdown under \u5BA1\u67E5/; an oral review writes nothing.",
|
|
144
|
-
"\u5267\u96C6/<EP>/\u526A\u8F91\u5355.md is the v0.7 assembly record, not a sixth creative truth. It is written after footage exists and records
|
|
167
|
+
"\u5267\u96C6/<EP>/\u526A\u8F91\u5355.md is the v0.7 assembly record, not a sixth creative truth: the five creator documents stay the creative truth. It is written after footage exists and records what reaches the cut \u2014 which footage is kept or left unused and why, each cut's in/out timing, post-processing such as shot-match correction or an external mix, and the delivery spec including optional \u9897\u7C92 grain. It never changes a line, a shot's job, or a declared duration; changing any of those means returning to the document that owns it.",
|
|
145
168
|
"For a v0.6 project, never create a parallel JSON/JSONL lifecycle truth, indexes, fingerprints, coverage tables, or QA records merely because legacy maintenance scripts and templates remain bundled.",
|
|
146
169
|
"Never upgrade a v0.5 structured project in place or mix both contracts in one project root. Keep legacy production/audit artifacts read-only and pinned to v0.5; migrate only into a new creator-first root with manual per-episode creator confirmation.",
|
|
147
170
|
"Use only tools visible in the current DSH preset and preserve the upstream project ownership, freshness, review, and explicit production-confirmation contracts.",
|
|
@@ -151,8 +174,16 @@ var DSH_DRAMA_BRIDGE = [
|
|
|
151
174
|
].join("\n");
|
|
152
175
|
var DSH_DRAMA_OVERRIDES = {
|
|
153
176
|
"short-drama": "A dashboard request means focus or use the native \u77ED\u5267 tab in this DSH Session. Do not run the bundled standalone Dashboard script. New projects follow only the v0.6 creator-first contract and create only documents required by the current request.",
|
|
154
|
-
"short-drama-produce": (skillRoot) => `Use an upstream adapter only after the creator explicitly confirms the exact current job. Its source must be the current creator-first Markdown and its output belongs under \u5267\u96C6/<EP>/\u5236\u4F5C\u6210\u679C/. DSH permissions and approval UI remain authoritative. When oh_story_production is visible, register
|
|
155
|
-
"short-drama-edit":
|
|
177
|
+
"short-drama-produce": (skillRoot) => `Use an upstream adapter only after the creator explicitly confirms the exact current job. Its source must be the current creator-first Markdown and its output belongs under \u5267\u96C6/<EP>/\u5236\u4F5C\u6210\u679C/. DSH permissions and approval UI remain authoritative. When oh_story_production is visible, register a previewed image or video job with track_job after the confirmation is valid and before run \u2014 never at prepare time \u2014 passing the same job ID, target, modality and count the creator just approved. track_job's jobKind is image, video or composition (the assembled cut), so from this Skill it registers image and video jobs only: speech (tts) and music jobs pass through the same prepare \u2192 explicit creator confirmation \u2192 run gate but are never registered with track_job or relabelled as another kind, and their audio results stay under \u5267\u96C6/<EP>/\u5236\u4F5C\u6210\u679C/. In this DSH integration audio is never bound as a video job's reference: upstream's creator-first path has no audio binding (\u8F93\u5165\u53C2\u8003\u56FE takes png/jpg/webp images only) and documents audio only as an external step, the edit stage's mix. DeepSeek generates no media: every image, video, speech, or music result comes from a provider adapter. The bundled adapters are ${dramaAdapterSummary()}; they are registered for this DSH host in ${dramaAdapterConfigPath(skillRoot).path} \u2014 pass that file as --adapter-config unless the creator names another config (${DRAMA_ADAPTER_CONFIG_ENV}). Credentials are read only from the DSH host process environment. Before preparing a paid job, name the adapter that will run it; if its variables are missing, tell the creator exactly which ones to export before starting DSH instead of failing inside run. Never read, print, or write credential values. A submitted video job is already billed, so an interrupted run is recovered, not resubmitted: when audit reports orphaned_provider_job, recover it with collect, passing --job-id \u2014 the drama job the finding names, not its provider_job_id, which collect reads from the run handle itself. collect spends nothing and does not need the confirmation gate \u2014 the gate exists to stop unintended spending \u2014 but re-running the same job does, and would charge the creator twice.`,
|
|
178
|
+
"short-drama-edit": [
|
|
179
|
+
"Assembly runs inside this DSH Session. Write \u5267\u96C6/<EP>/\u526A\u8F91\u5355.md first, then run edit_tool.py check and render through the current DSH execution world; the rendered cut belongs under \u5267\u96C6/<EP>/\u5236\u4F5C\u6210\u679C/\u6210\u7247/ and the \u77ED\u5267 tab shows it there. ffmpeg and ffprobe come from the DSH host \u2014 when one is missing, say so instead of approximating or reporting an untested measurement as a pass.",
|
|
180
|
+
// edit_tool.py 0.7.1 `_unaccounted_shots`: `- 未采用镜头:` is read only outside CUT blocks, split on `;`, and each item must fullmatch `MOTION-…(理由:<non-empty>)`.
|
|
181
|
+
"check blocks until every `## MOTION-*` heading in \u89C6\u9891\u63D0\u793A\u8BCD.md either backs a CUT's \u6765\u6E90 or is listed in the cut list's opening block, before the first `## CUT-` heading, on one line: `- \u672A\u91C7\u7528\u955C\u5934\uFF1AMOTION-\u2026\uFF08\u7406\u7531\uFF1A\u2026\uFF09\uFF1BMOTION-\u2026\uFF08\u7406\u7531\uFF1A\u2026\uFF09`. Join items with a full-width `\uFF1B`; each reason is non-empty, contains no `\uFF1B`, and names \u6587\u4EF6\u7F3A\u5931, \u8D28\u91CF\u4E0D\u53EF\u7528 or \u53D9\u4E8B\u53D6\u820D.",
|
|
182
|
+
"Every cut must share one width, height and frame rate: render hard-joins the encoded segments and never scales or retimes them. When check reports a mismatch, normalise the odd clips to the delivery spec with ffmpeg in the DSH execution world, under approval, preserving the composition. Write them under \u5267\u96C6/<EP>/\u5236\u4F5C\u6210\u679C/\u6210\u7247/\u89C4\u683C\u7EDF\u4E00/, an edit-owned intermediate, never beside the produce-stage originals and never over produced footage, and name them without the original's job-id token (the MOTION ID will do). Record the crop or pad in that CUT's block (not in its \u753B\u9762 line, which accepts only \u4EAE\u5EA6/\u9971\u548C/\u8272\u6E29), point \u6765\u6E90 at the new file, update \u5165\u70B9/\u51FA\u70B9 if the conversion moved them, and rerun check.",
|
|
183
|
+
"The default burned-subtitle route needs an ffmpeg built with libass; when the host's ffmpeg lacks it, say so instead of silently dropping subtitles. The Remotion route installs Node dependencies and renders every frame through a headless browser, so take it only after the creator approves that install, and never treat it as a second Agent runtime, dashboard, or session transport.",
|
|
184
|
+
"The \u58F0\u97F3 line in \u526A\u8F91\u5355.md is a record, not an instruction the built-in render executes: render only trims, hard-joins, burns subtitles, applies \u753B\u9762 corrections and optional \u9897\u7C92, and normalises loudness. A mix, crossfade, music bed, transition or format conversion is an external ffmpeg step, run under approval, that writes a temporary file, ends as render does with whole-film two-pass loudnorm to the declared \u4EA4\u4ED8\u54CD\u5EA6 (I=<\u4EA4\u4ED8\u54CD\u5EA6>:TP=-1.5:LRA=11, the second pass fed the first pass's measurements with linear=true, audio re-encoded as AAC 192k at 48 kHz), and only then replaces \u5267\u96C6/<EP>/\u5236\u4F5C\u6210\u679C/\u6210\u7247/\u6210\u7247.mp4; record its command in \u526A\u8F91\u5355.md and repeat it after any re-render, which overwrites that file. Run edit_tool.py verify last, on that delivered file, and report every \u672A\u6D4B item as untested, never as passed.",
|
|
185
|
+
"This stage never generates footage and never edits \u5267\u672C.md, \u5206\u955C.md, or \u89C6\u9891\u63D0\u793A\u8BCD.md \u2014 route those back to their owning Skill."
|
|
186
|
+
].join(" "),
|
|
156
187
|
"short-drama-assets": "Keep one stable `- ID\uFF1AVISUAL-*` line on every \u4EBA\u7269/\u9020\u578B/\u5730\u70B9/\u9053\u5177/\u72B6\u6001 entry in \u89C6\u89C9\u8BBE\u5B9A.md, and never change an existing ID when editing its heading or text. The \u751F\u4EA7 view identifies canvas nodes by that ID; entries without one still render, but the workbench reports their node identity as unstable."
|
|
157
188
|
};
|
|
158
189
|
var DSH_GAME_BRIDGE = [
|
|
@@ -184,29 +215,59 @@ var DSH_NATIVE_SKILLS = {
|
|
|
184
215
|
\u5728\u5F53\u524D DSH Session \u5185\u5224\u65AD\u7528\u6237\u610F\u56FE\uFF0C\u5E76\u52A0\u8F7D\u6700\u5339\u914D\u7684 Oh Story Skill\uFF1A
|
|
185
216
|
|
|
186
217
|
- \u65B0\u5EFA\u6216\u4FEE\u590D\u5C0F\u8BF4\u5DE5\u7A0B\uFF1Astory-setup
|
|
187
|
-
- \u957F\u7BC7\
|
|
218
|
+
- \u957F\u7BC7\u89C4\u5212\u4E0E\u5199\u4F5C\uFF08\u8BA8\u8BBA\u957F\u7BC7\u7ED3\u6784\u3001\u89C4\u5212\u5267\u60C5\u3001\u5F00\u4E66\u3001\u5199\u5927\u7EB2\u3001\u8865\u7EC6\u7EB2\u3001\u5199\u6B63\u6587\u3001\u65E5\u66F4\uFF09\uFF1A
|
|
219
|
+
story-long-write\u3002\u53EA\u8981\u7ED3\u6784\u3001\u5927\u7EB2\u3001\u5377\u7EB2\u6216\u7EC6\u7EB2\u65F6\u53EA\u4EA4\u4ED8\u6240\u8BF7\u6C42\u7684\u89C4\u5212\uFF0C\u4E0D\u5199\u6B63\u6587\uFF1B
|
|
220
|
+
\u7528\u6237\u660E\u786E\u8981\u6C42\u5199\u6B63\u6587\u624D\u8FDB\u5165\u6B63\u6587\u6D41\u7A0B
|
|
221
|
+
- \u957F\u7BC7\u9009\u9898/\u626B\u699C\uFF1Astory-long-scan\uFF1B\u957F\u7BC7\u62C6\u6587\uFF1Astory-long-analyze
|
|
222
|
+
- \u7075\u611F\u5E93\u3001\u63D0\u70BC\u7075\u611F\u3001\u8DE8\u4E66\u7075\u611F\u805A\u5408\u3001\u66F4\u65B0\u7075\u611F\u5E93\uFF1Astory-long-analyze \u7684\u53EF\u9009\u7075\u611F\u5E93\u7BA1\u9053
|
|
223
|
+
\uFF08\u5355\u4E66\u62C6\u6587\u4E0D\u81EA\u52A8\u5165\u5E93\uFF09
|
|
188
224
|
- \u77ED\u7BC7\u9009\u9898/\u62C6\u6587/\u5199\u4F5C\uFF1Astory-short-scan\u3001story-short-analyze\u3001story-short-write
|
|
189
225
|
- \u5BFC\u5165\u5DF2\u6709\u4F5C\u54C1\uFF1Astory-import
|
|
190
226
|
- \u5BA1\u7A3F\u4E0E\u53BB AI \u5473\uFF1Astory-review\u3001story-deslop
|
|
191
227
|
- \u5C01\u9762\uFF1Astory-cover
|
|
228
|
+
- \u67E5\u672C\u4E66\u7684\u89D2\u8272\u3001\u4F0F\u7B14\u3001\u8FDB\u5EA6\u6216\u8BBE\u5B9A\uFF1A\u7528 oh_story_role \u8C03\u7528 story-explorer\uFF1B\u67E5\u5916\u90E8\u8D44\u6599\uFF1A
|
|
229
|
+
story-researcher\u3002\u56DE\u7B54\u8BB2\u6545\u4E8B\u91CC\u7684\u4E8B\uFF0C\u7F16\u53F7\u53EA\u8DDF\u7740\u6545\u4E8B\u63CF\u8FF0\u51FA\u73B0
|
|
192
230
|
- \u7BA1\u7406\u4F5C\u8005\u4E60\u60EF\uFF08\u8BB0\u4F4F/\u67E5\u770B/\u786E\u8BA4/\u66FF\u6362/\u5FD8\u6389\u5199\u4F5C\u504F\u597D\uFF09\uFF1A\u52A0\u8F7D\u672C skill \u7684
|
|
193
231
|
references/author-memory.md\uFF0C\u53EA\u7528\u672C skill \u7684 scripts/author_memory_commit.py \u7BA1\u7406
|
|
194
|
-
\u5DE5\u4F5C\u533A
|
|
232
|
+
\u4E24\u7EA7\u4F5C\u8005\u8BB0\u5FC6\uFF1A\u5168\u5C40\u3001\u9898\u6750\u3001\u6D41\u7A0B\u6761\u76EE\u5728\u5DE5\u4F5C\u533A .story/\u4F5C\u8005\u8BB0\u5FC6/\uFF08AP \u7F16\u53F7\uFF09\uFF0C\u672C\u4E66\u6761\u76EE\u5728
|
|
233
|
+
\u4E66\u7EA7 store\uFF08BP \u7F16\u53F7\uFF09\u3002--workspace \u5FC5\u987B\u663E\u5F0F\u4F20\u3002DSH \u7684\u5C0F\u8BF4\u5DE5\u4F5C\u533A\u901A\u5E38\u5C31\u662F\u4E66\u6839
|
|
234
|
+
\uFF08\u6B63\u6587/\u3001\u8BBE\u5B9A/ \u76F4\u63A5\u5728\u5DE5\u4F5C\u533A\u6839\uFF09\uFF1A\u6B64\u65F6 --workspace \u4E0E --book-root \u90FD\u4F20\u5DE5\u4F5C\u533A\u672C\u8EAB\uFF0C
|
|
235
|
+
\u4E66\u7EA7 store \u5728 .story/\u4F5C\u8005\u8BB0\u5FC6/\u4E66\u7EA7/\uFF1B\u4E66\u5728\u5B50\u76EE\u5F55\u65F6 --book-root \u4F20\u90A3\u672C\u4E66\u7684\u76EE\u5F55\u3002
|
|
236
|
+
\u5904\u7406\u67D0\u672C\u4E66\u65F6\u6BCF\u6761\u547D\u4EE4\u90FD\u5E26 --book-root\uFF08\u4E66\u7EA7\u64CD\u4F5C\u7F3A\u5B83\u76F4\u63A5\u62A5\u9519\uFF09\uFF0Cquery \u5FC5\u987B\u5E26
|
|
237
|
+
--kind\uFF08\u53EF\u91CD\u590D\uFF09\u3002\u53EA\u8BB0\u4F5C\u8005\u660E\u786E\u8BF4\u51FA\u7684\u504F\u597D\uFF0C\u4E0D\u4ECE\u53CD\u590D\u4FEE\u6539\u6216\u6210\u7A3F\u63A8\u65AD\u3002\u5DE5\u5177\u672A\u8FD4\u56DE
|
|
238
|
+
Author Memory Receipt \u524D\u4E0D\u5F97\u58F0\u79F0\u5DF2\u8BB0\u4F4F\uFF1B\u544A\u8BC9\u4F5C\u8005\u65F6\u5148\u7528\u4E00\u53E5\u4EBA\u8BDD\u8BF4\u8BB0\u4F4F\u4E86\u4EC0\u4E48\uFF0C
|
|
239
|
+
\u56DE\u6267\u653E\u6700\u540E\u4E00\u884C\u3002\u5DE5\u4F5C\u533A\u753B\u50CF\u91CC\u8FD8\u6709\u300C\u672C\u4E66\uFF1A\u300D\u6761\u76EE\uFF08\u5347\u7EA7\u524D\u5199\u5165\u7684\u672C\u4E66\u504F\u597D\uFF0C\u5DF2\u4E0D\u53C2\u4E0E
|
|
240
|
+
\u67E5\u8BE2\uFF09\u65F6\uFF0C\u63D0\u8BAE\u8FD0\u884C\u4E00\u6B21 migrate --workspace {\u5DE5\u4F5C\u533A} --book-root {\u4E66\u76EE\u5F55} \u628A\u5B83\u4EEC\u642C\u8FDB
|
|
241
|
+
\u4E66\u7EA7 store\u3002
|
|
195
242
|
|
|
196
|
-
\
|
|
197
|
-
|
|
198
|
-
\
|
|
243
|
+
\u53EA\u8BF4 /story\u3001\u770B\u4E0D\u51FA\u610F\u56FE\u65F6\uFF0C\u4E0D\u8D34\u8DEF\u7531\u8868\uFF0C\u7ED9\u56DB\u4E2A\u767D\u8BDD\u9009\u9879\uFF1A\u300C\u5F00\u4E00\u672C\u957F\u7BC7\u6216\u63A5\u7740\u5199\u300D\u2192
|
|
244
|
+
story-long-write\uFF1B\u300C\u5199\u4E00\u7BC7\u77ED\u7BC7\u300D\u2192 story-short-write\uFF1B\u300C\u628A\u4E00\u7AE0\u6539\u5F97\u4E0D\u90A3\u4E48 AI\u300D\u2192
|
|
245
|
+
story-deslop\uFF1B\u300C\u66F4\u591A\uFF08\u62C6\u4E66\u3001\u626B\u699C\u3001\u5BFC\u5165\u65E7\u7A3F\u3001\u5BA1\u7A3F\u3001\u5C01\u9762\uFF09\u300D\u2192 \u518D\u5217\u8FDB\u9636\u9879\u3002\u610F\u56FE\u660E\u786E\u65F6
|
|
246
|
+
\u76F4\u63A5\u8FDB\u5165\u5BF9\u5E94 Skill\uFF1B\u53EA\u5DEE\u4E00\u4E2A\u4F1A\u6539\u53D8\u6D41\u7A0B\u7684\u9009\u62E9\u65F6\u53EA\u95EE\u8FD9\u4E00\u4E2A\u95EE\u9898\u3002\u9879\u76EE\u6587\u4EF6\u3001Agent\u3001
|
|
247
|
+
\u6A21\u578B\u3001\u6743\u9650\u3001Session Log \u548C UI \u5747\u7531\u5F53\u524D DSH \u4F1A\u8BDD\u7BA1\u7406\u3002\u5C0F\u8BF4\u6587\u4EF6\u901A\u8FC7\u201C\u5C0F\u8BF4\u201D\u89C6\u56FE\u67E5\u770B\uFF0C
|
|
248
|
+
Agent \u8FC7\u7A0B\u901A\u8FC7\u53F3\u4FA7\u52A8\u6001\u680F\u6216\u5B98\u65B9 Chat \u67E5\u770B\u3002\u7981\u6B62\u542F\u52A8\u72EC\u7ACB Dashboard\u3002`,
|
|
199
249
|
"story-setup": `# story-setup \u2014 DSH \u539F\u751F\u5C0F\u8BF4\u5DE5\u7A0B\u521D\u59CB\u5316
|
|
200
250
|
|
|
201
251
|
\u53EA\u521D\u59CB\u5316\u6216\u6821\u9A8C\u5F53\u524D DSH workspace \u4E2D\u7684\u5C0F\u8BF4\u6570\u636E\uFF0C\u4E0D\u90E8\u7F72\u4EFB\u4F55 Agent \u5E73\u53F0\u6587\u4EF6\u3002
|
|
202
252
|
|
|
203
253
|
1. \u68C0\u67E5\u73B0\u6709\u6B63\u6587\u3001\u8BBE\u5B9A\u3001\u5927\u7EB2\u548C\u8FFD\u8E2A\u6587\u4EF6\uFF0C\u5DF2\u6709\u5185\u5BB9\u7EDD\u4E0D\u8986\u76D6\u3002
|
|
204
254
|
2. \u6839\u636E\u7528\u6237\u58F0\u660E\u4E0E\u73B0\u6709\u7ED3\u6784\u5224\u65AD\u957F\u7BC7/\u77ED\u7BC7\uFF1B\u65E0\u6CD5\u53EF\u9760\u5224\u65AD\u65F6\u8BF7\u6C42\u786E\u8BA4\u3002
|
|
205
|
-
3. \u957F\u7BC7\u6309\u9700\u5EFA\u7ACB \u6B63\u6587/\u3001\u8BBE\u5B9A/\u3001\u5927\u7EB2/\u3001\
|
|
255
|
+
3. \u957F\u7BC7\u6309\u9700\u5EFA\u7ACB \u6B63\u6587/\u3001\u8BBE\u5B9A/\u3001\u5927\u7EB2/\u3002\u521D\u59CB\u5316\u3001\u5F00\u4E66\u3001\u5927\u7EB2\u548C\u7EC6\u7EB2\u9636\u6BB5\u90FD\u4E0D\u5EFA \u8FFD\u8E2A/\uFF0C
|
|
256
|
+
\u4E5F\u4E0D\u5199 \u8FFD\u8E2A/_tracking-state.json\uFF1A\u8FFD\u8E2A\u5728\u7B2C\u4E00\u7AE0\u6B63\u6587\u52A8\u7B14\u524D\uFF0C\u7531 story-long-write
|
|
257
|
+
\u5355\u7AE0\u6D41\u7A0B\u7528\u5B83\u7684 scripts/tracking_commit.py init \u521D\u59CB\u5316\uFF08\u4E8B\u52A1 JSON \u653E\u4E66\u76EE\u5F55
|
|
258
|
+
.story/work/\uFF0Ccheck \u901A\u8FC7\u540E\u5220\u6389\uFF09\uFF0C\u4E4B\u540E\u6BCF\u7AE0\u7531\u811A\u672C\u63D0\u4EA4\u3002_tracking-state.json \u4E0E
|
|
259
|
+
\u4E0A\u4E0B\u6587.md \u7B49\u6D3E\u751F Tracking \u89C6\u56FE\u53EA\u7531\u811A\u672C\u751F\u6210\uFF0C\u7EDD\u4E0D\u624B\u6539\u3002
|
|
206
260
|
\u7B2C\u4E00\u7AE0\u6B63\u6587\u843D\u76D8\u524D\u5FC5\u987B\u6709\u5BF9\u5E94\u7EC6\u7EB2\u3002\u77ED\u7BC7\u4FDD\u6301\u8F7B\u91CF\u7ED3\u6784\uFF0C\u4E0D\u5F3A\u52A0\u957F\u7BC7 Tracking\u3002
|
|
207
|
-
4. \
|
|
261
|
+
4. \u5DF2\u6709\u6B63\u6587\u5374\u6CA1\u6709 \u8FFD\u8E2A/_tracking-state.json \u65F6\uFF0C\u4E0D\u81EA\u884C\u8865\u5EFA\u8FFD\u8E2A\u6587\u4EF6\uFF1A\u5DF2\u6709\u65E7 \u8FFD\u8E2A/
|
|
262
|
+
\u7684\u662F\u65E7\u8FFD\u8E2A\u7ED3\u6784\uFF0C\u5EFA\u8BAE\u8D70 story-import \u7684\u300C\u65E7\u8FFD\u8E2A\u9879\u76EE\u8FC1\u79FB\u300D\uFF1B\u6CA1\u6709 \u8FFD\u8E2A/ \u7684\u65E2\u6709\u4E66\u7A3F
|
|
263
|
+
\u5EFA\u8BAE\u7528 story-import \u5BFC\u5165\u3002
|
|
264
|
+
5. \u9700\u8981\u67B6\u6784\u3001\u89D2\u8272\u6216\u7814\u7A76\u5DE5\u4F5C\u65F6\uFF0C\u901A\u8FC7 oh_story_role \u8C03\u7528\u5DF2\u6253\u5305 Role\uFF1B\u4E0D\u8981\u68C0\u67E5\u6216
|
|
208
265
|
\u751F\u6210 .claude\u3001.codex\u3001.opencode\u3001.agents\u3001.zcode\u3001AGENTS.md \u6216 .story-deployed\u3002
|
|
209
|
-
|
|
266
|
+
6. \u7ED9\u4F5C\u8005\u7684\u62A5\u544A\u6309\u8FD9\u4E2A\u987A\u5E8F\u5199\uFF1A\u5148\u5199\u300C\u73B0\u5728\u53EF\u4EE5\u505A\u4EC0\u4E48\u300D\uFF0C\u7528\u5199\u4E66\u7684\u8BDD\u5217\u771F\u6B63\u53EF\u7528\u7684\u4E8B
|
|
267
|
+
\uFF08\u5982\u300C\u53EF\u4EE5\u5F00\u65B0\u4E66\u3001\u7EED\u5199\uFF1A\u8BF4 /story-long-write\u300D\uFF09\uFF1B\u518D\u5199\u300C\u4F60\u8FD8\u9700\u8981\u505A\u7684\u4E8B\u300D\uFF0C\u9010\u6761
|
|
268
|
+
\u53EF\u7167\u505A\uFF08\u542B\u9700\u8981\u4F5C\u8005\u786E\u8BA4\u7684\u957F\u7BC7/\u77ED\u7BC7\u5224\u65AD\uFF09\uFF0C\u6CA1\u6709\u5C31\u5199\u300C\u65E0\u9700\u5176\u4ED6\u64CD\u4F5C\u300D\u3002\u8FD9\u4E24\u6BB5\u4E0D\u51FA\u73B0
|
|
269
|
+
\u811A\u672C\u540D\u3001\u5B57\u6BB5\u540D\u3001\u72B6\u6001\u540D\u6216\u6587\u4EF6\u8DEF\u5F84\uFF1B\u6700\u540E\u624D\u7B80\u77ED\u5217\u51FA\u521B\u5EFA\u548C\u4FDD\u7559\u4E86\u54EA\u4E9B\u6587\u4EF6\u3002
|
|
270
|
+
\u4E0D\u8981\u914D\u7F6E\u6A21\u578B\u3001\u6743\u9650\u3001Hooks \u6216 Session\u3002
|
|
210
271
|
|
|
211
272
|
\u9898\u6750\u3001\u89D2\u8272\u3001\u8282\u594F\u3001\u51B2\u7A81\u3001\u5F00\u7BC7\u548C\u5199\u4F5C\u65B9\u6CD5\u8D44\u6599\u4F4D\u4E8E references/agent-references/\uFF1B
|
|
212
273
|
\u53EA\u52A0\u8F7D\u5F53\u524D\u4EFB\u52A1\u9700\u8981\u7684\u6587\u4EF6\u3002`,
|
|
@@ -233,6 +294,8 @@ Agent\u3001\u6A21\u578B\u3001\u6743\u9650\u3001Session Log \u548C UI \u5747\u753
|
|
|
233
294
|
4. \u6309\u9898\u6750\u5206\u5E03\u3001\u65B0\u9898\u6750\u4FE1\u53F7\u3001\u7ECF\u5178\u9898\u6750\u53D8\u5316\u3001\u7BC7\u5E45\u4E0E\u66F4\u65B0\u3001\u4E66\u540D\u6A21\u5F0F\u3001\u5F00\u5934\u5356\u70B9\u548C
|
|
234
295
|
\u5DEE\u5F02\u5316\u5143\u7D20\u5206\u6790\u3002\u9700\u8981\u51B3\u7B56\u95E8\u7981\u65F6\u4F7F\u7528 references/topic-decision.md\u3002
|
|
235
296
|
5. \u8F93\u51FA\u5E02\u573A\u6982\u51B5\u3001\u9898\u6750\u70ED\u5EA6\u3001\u8BC1\u636E\u4E0E\u53EF\u4FE1\u5EA6\u3001\u98CE\u9669\u3001\u4E09\u9879\u53EF\u6267\u884C\u65B9\u5411\u548C\u4E0B\u6B21\u590D\u626B\u65F6\u95F4\u3002
|
|
297
|
+
6. \u62A5\u544A\u5199\u7ED9\u4F5C\u8005\uFF1A\u8BB2\u5E02\u573A\u7ED3\u8BBA\u548C\u80FD\u5199\u7684\u65B9\u5411\uFF1B\u811A\u672C\u540D\u3001\u547D\u4EE4\u548C\u91C7\u96C6\u72B6\u6001\u7801\u4E0D\u8FDB\u62A5\u544A\u3002
|
|
298
|
+
\u67D0\u4E2A\u699C\u6CA1\u91C7\u5230\uFF0C\u5C31\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u300CXX \u699C\u8FD9\u6B21\u6CA1\u62FF\u5230\uFF08\u539F\u56E0\uFF09\uFF0C\u7ED3\u8BBA\u4E0D\u542B\u5B83\u300D\u3002
|
|
236
299
|
|
|
237
300
|
\u5F53\u524D Preset \u6CA1\u6709\u7F51\u9875\u80FD\u529B\u4E14\u7528\u6237\u4E5F\u672A\u63D0\u4F9B\u6837\u672C\u65F6\uFF0C\u4F7F\u7528\u5185\u7F6E\u53C2\u8003\u5B8C\u6210\u65B9\u6CD5\u8BBA\u5206\u6790\uFF0C
|
|
238
301
|
\u5E76\u5217\u51FA\u4ECD\u9700\u6838\u9A8C\u7684\u699C\u5355\uFF1B\u4E0D\u8981\u542F\u52A8 CDP\u3001\u72EC\u7ACB\u6D4F\u89C8\u5668\u6216\u5E76\u884C\u8FD0\u884C\u65F6\u3002`,
|
|
@@ -247,7 +310,10 @@ Agent\u3001\u6A21\u578B\u3001\u6743\u9650\u3001Session Log \u548C UI \u5747\u753
|
|
|
247
310
|
3. \u8BB0\u5F55\u6765\u6E90 URL\u3001\u91C7\u96C6\u65E5\u671F\u3001\u699C\u5355\u53E3\u5F84\u3001\u6709\u6548\u6837\u672C\u6570\u3001\u7F3A\u5931\u5B57\u6BB5\u548C\u5F02\u5E38\u9879\u3002
|
|
248
311
|
4. \u5206\u6790\u60C5\u7EEA\u7C7B\u578B\u3001\u9898\u6750\u70ED\u70B9\u3001\u7BC7\u5E45\u3001\u5F00\u5934\u3001\u7ED3\u5C3E\u3001\u6807\u9898\u3001\u4EBA\u8BBE\u4E0E\u4F20\u64AD\u89E6\u53D1\u70B9\uFF1B
|
|
249
312
|
\u7ED9\u6BCF\u4E2A\u8D8B\u52BF\u6807\u6CE8\u8BC1\u636E\u5F3A\u5EA6\u3001\u9971\u548C\u98CE\u9669\u548C\u6709\u6548\u671F\u3002
|
|
250
|
-
5. \u8F93\u51FA\u5E02\u573A\u6982\u51B5\
|
|
313
|
+
5. \u8F93\u51FA\u5E02\u573A\u6982\u51B5\uFF08\u626B\u699C\u65F6\u95F4\u3001\u6838\u5FC3\u53D1\u73B0\u548C\u53EF\u4FE1\u5EA6\uFF1A\u6837\u672C\u591A\u5C11\u7BC7\u3001\u6765\u81EA\u54EA\u51E0\u4E2A\u699C\uFF0C\u5E76\u5EFA\u8BAE
|
|
314
|
+
\u54EA\u5929\u524D\u540E\u518D\u626B\u4E00\u6B21\uFF09\u3001\u60C5\u7EEA\u70ED\u5EA6\u3001\u9898\u6750\u70ED\u70B9\u3001\u5173\u952E\u6570\u636E\u3001\u98CE\u53E3\u9884\u8B66\u3001\u4E09\u9879\u53EF\u5199\u65B9\u5411\u3002
|
|
315
|
+
6. \u62A5\u544A\u5199\u7ED9\u4F5C\u8005\uFF1A\u8BB2\u5E02\u573A\u7ED3\u8BBA\u548C\u80FD\u5199\u7684\u65B9\u5411\uFF1B\u811A\u672C\u540D\u3001\u547D\u4EE4\u548C SKIP \u8FD9\u7C7B\u91C7\u96C6\u72B6\u6001\u4E0D\u8FDB\u62A5\u544A\u3002
|
|
316
|
+
\u67D0\u4E2A\u5E73\u53F0\u6CA1\u91C7\u5230\uFF0C\u5C31\u7528\u4E00\u53E5\u8BDD\u8BF4\u660E\u300CXX \u8FD9\u6B21\u6CA1\u62FF\u5230\uFF08\u539F\u56E0\uFF09\uFF0C\u7ED3\u8BBA\u4E0D\u542B\u5B83\u300D\u3002
|
|
251
317
|
|
|
252
318
|
\u5F53\u524D Preset \u6CA1\u6709\u7F51\u9875\u80FD\u529B\u4E14\u7528\u6237\u4E5F\u672A\u63D0\u4F9B\u6837\u672C\u65F6\uFF0C\u53EA\u505A\u5386\u53F2\u8D44\u6599\u5206\u6790\u5E76\u5217\u51FA\u9A8C\u8BC1\u52A8\u4F5C\uFF1B
|
|
253
319
|
\u4E0D\u8981\u542F\u52A8 CDP\u3001\u72EC\u7ACB\u6D4F\u89C8\u5668\u6216\u5E76\u884C\u8FD0\u884C\u65F6\u3002`
|
|
@@ -419,11 +485,82 @@ async function hostPython(probe = commandOutput) {
|
|
|
419
485
|
}
|
|
420
486
|
|
|
421
487
|
// src/native-hooks.ts
|
|
422
|
-
import { createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
488
|
+
import { boundContextSummary, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
489
|
+
|
|
490
|
+
// src/role-provider.ts
|
|
491
|
+
import { readFile as readFile2 } from "node:fs/promises";
|
|
492
|
+
import { basename as basename2, dirname as dirname3, join as join3, resolve as resolve3 } from "node:path";
|
|
493
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
494
|
+
var OH_STORY_ROLE_NAMES = [
|
|
495
|
+
"chapter-extractor",
|
|
496
|
+
"character-designer",
|
|
497
|
+
"consistency-checker",
|
|
498
|
+
"narrative-writer",
|
|
499
|
+
"story-architect",
|
|
500
|
+
"story-explorer",
|
|
501
|
+
"story-researcher"
|
|
502
|
+
];
|
|
503
|
+
function roleBody(source) {
|
|
504
|
+
const frontmatter = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/u.exec(source);
|
|
505
|
+
return source.slice(frontmatter?.[0].length ?? 0).trim();
|
|
506
|
+
}
|
|
507
|
+
function defaultBundledRoleRoot() {
|
|
508
|
+
const current = dirname3(fileURLToPath2(import.meta.url));
|
|
509
|
+
return basename2(current) === "src" ? resolve3(current, "../../knowledge/oh-story/roles") : resolve3(current, "oh-story/roles");
|
|
510
|
+
}
|
|
511
|
+
async function loadBundledRole(name2, roleRoot = defaultBundledRoleRoot(), execution = "tool-free") {
|
|
512
|
+
const body = roleBody(await readFile2(join3(resolve3(roleRoot), `${name2}.md`), "utf8"));
|
|
513
|
+
if (body.length === 0) throw new Error(`Bundled role "${name2}" is empty.`);
|
|
514
|
+
const integration = execution === "tool-free" ? [
|
|
515
|
+
"You are running inside an Oh Story review-required DSH collaboration. The caller supplies every permitted input in the prompt.",
|
|
516
|
+
"Do not read or write project files, do not call tools, and do not follow legacy .claude/.codex path or deployment instructions in the upstream role text.",
|
|
517
|
+
"Return only the output contract requested by the caller; never claim to have changed the project."
|
|
518
|
+
] : [
|
|
519
|
+
"You are running as a native oh-story-dsh specialist. The current DSH workspace and visible tool set are your complete authority boundary.",
|
|
520
|
+
"Never inspect or require legacy .claude/.opencode/.codex agent deployment files; this exact pinned Role is already active.",
|
|
521
|
+
"Bundled story-setup references are pinned plugin resources, not project files or project Skills. When the upstream Role marks one mandatory, call oh_story_bundled_reference with the exact story-setup/references/agent-references path, then use only the returned content.",
|
|
522
|
+
"If oh_story_bundled_reference or a required bundled reference is unavailable, report the missing reference to the caller. Never call the generic skill tool, fall back to a legacy platform path, or claim that an unread reference was used.",
|
|
523
|
+
"Use only the tools actually visible to you. Mutate files only when the caller explicitly requests it and your visible DSH tools permit it; otherwise return findings to the caller."
|
|
524
|
+
];
|
|
525
|
+
return [
|
|
526
|
+
`OH_STORY_DSH_ROLE:${name2}`,
|
|
527
|
+
...integration,
|
|
528
|
+
"",
|
|
529
|
+
body
|
|
530
|
+
].join("\n");
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
// src/role-identity.ts
|
|
534
|
+
var ROLE_LABEL_PREFIX = "oh-story:";
|
|
535
|
+
function ohStoryRoleLabel(role) {
|
|
536
|
+
return `${ROLE_LABEL_PREFIX}${role}`;
|
|
537
|
+
}
|
|
538
|
+
function roleFromLabel(label) {
|
|
539
|
+
if (typeof label !== "string" || !label.startsWith(ROLE_LABEL_PREFIX)) return void 0;
|
|
540
|
+
const name2 = label.slice(ROLE_LABEL_PREFIX.length);
|
|
541
|
+
return OH_STORY_ROLE_NAMES.find((role) => role === name2);
|
|
542
|
+
}
|
|
543
|
+
var roleSessions = /* @__PURE__ */ new WeakMap();
|
|
544
|
+
function observeOhStoryRoleDescriptor(session, event) {
|
|
545
|
+
if (event.type !== "subagent/descriptor" || roleSessions.has(session)) return;
|
|
546
|
+
const role = roleFromLabel(event.data.label);
|
|
547
|
+
if (role !== void 0) roleSessions.set(session, role);
|
|
548
|
+
}
|
|
549
|
+
function markOhStoryRoleSession(session, role) {
|
|
550
|
+
if (!roleSessions.has(session)) roleSessions.set(session, role);
|
|
551
|
+
}
|
|
552
|
+
function ohStoryRoleOfSession(session) {
|
|
553
|
+
return session === void 0 ? void 0 : roleSessions.get(session);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
// src/native-hooks.ts
|
|
423
557
|
var MUTATION_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "str_replace_editor"]);
|
|
558
|
+
function argumentRecord(args) {
|
|
559
|
+
return typeof args === "object" && args !== null && !Array.isArray(args) ? args : void 0;
|
|
560
|
+
}
|
|
424
561
|
function mutationPath(name2, args) {
|
|
425
|
-
|
|
426
|
-
|
|
562
|
+
const record2 = argumentRecord(args);
|
|
563
|
+
if (!MUTATION_TOOLS.has(name2) || record2 === void 0) return void 0;
|
|
427
564
|
if (name2 === "str_replace_editor") {
|
|
428
565
|
if (!(/* @__PURE__ */ new Set(["create", "str_replace", "insert"])).has(String(record2.command))) return void 0;
|
|
429
566
|
return typeof record2.path === "string" && record2.path.trim() !== "" ? record2.path : void 0;
|
|
@@ -441,6 +578,19 @@ function normalizedRelativePath(path) {
|
|
|
441
578
|
}
|
|
442
579
|
return segments.join("/");
|
|
443
580
|
}
|
|
581
|
+
function proseLocation(path) {
|
|
582
|
+
const segments = path.split("/");
|
|
583
|
+
if (segments.length < 2) return void 0;
|
|
584
|
+
const index = segments.lastIndexOf("\u6B63\u6587", segments.length - 2);
|
|
585
|
+
if (index < 0) return void 0;
|
|
586
|
+
return { book: segments.slice(0, index).join("/"), name: segments.at(-1) ?? "" };
|
|
587
|
+
}
|
|
588
|
+
function inBook(book, path) {
|
|
589
|
+
return book === "" ? path : `${book}/${path}`;
|
|
590
|
+
}
|
|
591
|
+
function lastSegment(path) {
|
|
592
|
+
return path.replaceAll("\\", "/").split("/").filter((part) => part !== "").at(-1) ?? "";
|
|
593
|
+
}
|
|
444
594
|
function detectStoryMutation(name2, args, cwd) {
|
|
445
595
|
const rawPath = mutationPath(name2, args);
|
|
446
596
|
if (cwd === void 0 || rawPath === void 0) return void 0;
|
|
@@ -451,8 +601,9 @@ function detectStoryMutation(name2, args, cwd) {
|
|
|
451
601
|
if (absolute && !insideRoot) return void 0;
|
|
452
602
|
const normalized = normalizedRelativePath(absolute ? candidate.slice(root.length + 1) : candidate);
|
|
453
603
|
if (normalized === void 0) return void 0;
|
|
454
|
-
|
|
455
|
-
|
|
604
|
+
const location = proseLocation(normalized);
|
|
605
|
+
if (location === void 0) return void 0;
|
|
606
|
+
const chapterText = /^第0*(\d+)章/u.exec(location.name)?.[1];
|
|
456
607
|
return { root, path: normalized, ...chapterText === void 0 ? {} : { chapter: Number(chapterText) } };
|
|
457
608
|
}
|
|
458
609
|
async function storyMutation(exec, fs) {
|
|
@@ -476,20 +627,35 @@ async function target(fs, root, path, signal) {
|
|
|
476
627
|
async function exists(fs, root, path, signal) {
|
|
477
628
|
return target(fs, root, path, signal).then((value) => fs.stat(value, signal)).then((info) => info !== void 0, () => false);
|
|
478
629
|
}
|
|
479
|
-
async function
|
|
480
|
-
|
|
630
|
+
async function isLongFormBook(fs, root, book, signal) {
|
|
631
|
+
return await exists(fs, root, inBook(book, "\u5927\u7EB2"), signal) || await exists(fs, root, inBook(book, "\u8FFD\u8E2A"), signal);
|
|
632
|
+
}
|
|
633
|
+
async function hasChapterOutline(fs, root, book, chapter, signal) {
|
|
634
|
+
const entries = await target(fs, root, inBook(book, "\u5927\u7EB2"), signal).then((directory) => fs.listDir(directory, signal)).catch(() => []);
|
|
481
635
|
return entries.some((entry) => entry.type === "file" && Number(/^细纲_第0*(\d+)章.*\.md$/u.exec(entry.name)?.[1]) === chapter);
|
|
482
636
|
}
|
|
637
|
+
var TRACKING_STATE = "\u8FFD\u8E2A/_tracking-state.json";
|
|
638
|
+
var IMPORT_MARKER = ".story/work/\u5BFC\u5165\u4E2D.md";
|
|
639
|
+
async function hasTrackingState(fs, root, book, signal) {
|
|
640
|
+
return exists(fs, root, inBook(book, TRACKING_STATE), signal);
|
|
641
|
+
}
|
|
642
|
+
async function hasImportSignal(fs, root, book, signal) {
|
|
643
|
+
if (await exists(fs, root, inBook(book, IMPORT_MARKER), signal)) return true;
|
|
644
|
+
const bookName = book === "" ? lastSegment(root) : lastSegment(book);
|
|
645
|
+
return bookName !== "" && await exists(fs, root, `\u62C6\u6587\u5E93/${bookName}`, signal);
|
|
646
|
+
}
|
|
483
647
|
async function validateStoryMutation(fs, mutation, signal) {
|
|
484
|
-
const
|
|
485
|
-
if (
|
|
486
|
-
|
|
648
|
+
const location = proseLocation(mutation.path);
|
|
649
|
+
if (mutation.chapter === void 0 || location === void 0) return void 0;
|
|
650
|
+
const { book } = location;
|
|
651
|
+
if (!await isLongFormBook(fs, mutation.root, book, signal)) return void 0;
|
|
652
|
+
if (await exists(fs, mutation.root, mutation.path, signal)) return void 0;
|
|
653
|
+
if (!await hasTrackingState(fs, mutation.root, book, signal) && await hasImportSignal(fs, mutation.root, book, signal)) {
|
|
487
654
|
return void 0;
|
|
488
655
|
}
|
|
489
|
-
if (
|
|
490
|
-
|
|
491
|
-
}
|
|
492
|
-
return void 0;
|
|
656
|
+
if (await hasChapterOutline(fs, mutation.root, book, mutation.chapter, signal)) return void 0;
|
|
657
|
+
const padded = String(mutation.chapter).padStart(3, "0");
|
|
658
|
+
return `Oh Story \u963B\u6B62\u5199\u5165\u7B2C ${String(mutation.chapter)} \u7AE0\uFF1A\u672A\u627E\u5230\u5BF9\u5E94\u7684 ${inBook(book, "\u5927\u7EB2")}/\u7EC6\u7EB2_\u7B2C${padded}\u7AE0*.md\u3002\u5148\u6309 story-long-write \u5355\u7AE0\u6D41\u7A0B\u8865\u5EFA\u7EC6\u7EB2\u518D\u5199\u6B63\u6587\u3002`;
|
|
493
659
|
}
|
|
494
660
|
async function decideStoryMutation(exec, next) {
|
|
495
661
|
const fs = exec.agent?.ctx.get("fs");
|
|
@@ -500,20 +666,90 @@ async function decideStoryMutation(exec, next) {
|
|
|
500
666
|
if (reason !== void 0) return { kind: "deny", reason };
|
|
501
667
|
return next();
|
|
502
668
|
}
|
|
669
|
+
var ANALYSIS_INPUT = /^输入-(RAW|REUSE)-(\d+)-(\d+)\.md$/u;
|
|
670
|
+
var ANALYSIS_INPUT_RULE = "\u53EA\u5141\u8BB8\u5199\u8C03\u7528\u65B9\u7ED9\u7684 {\u62C6\u6587\u76EE\u5F55}/_analysis_cache/\u8F93\u5165-{\u6279\u6B21ID}.md\uFF08\u6279\u6B21 ID \u5982 RAW-4-6\uFF09\uFF0C\u4E0D\u5199\u5176\u4ED6\u6587\u4EF6\u3002";
|
|
671
|
+
function isFileMutationCall(name2, args) {
|
|
672
|
+
if (name2 === "write" || name2 === "edit") return true;
|
|
673
|
+
return name2 === "str_replace_editor" && argumentRecord(args)?.command !== "view";
|
|
674
|
+
}
|
|
675
|
+
function displayParent(path) {
|
|
676
|
+
const index = Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\"));
|
|
677
|
+
return index <= 0 ? path.slice(0, index + 1) : path.slice(0, index);
|
|
678
|
+
}
|
|
679
|
+
function displayName(path) {
|
|
680
|
+
return path.slice(Math.max(path.lastIndexOf("/"), path.lastIndexOf("\\")) + 1);
|
|
681
|
+
}
|
|
682
|
+
async function isAnalysisDirectory(fs, directory, signal) {
|
|
683
|
+
return await exists(fs, directory, "chapter_index.csv", signal) || await exists(fs, directory, "_progress.md", signal);
|
|
684
|
+
}
|
|
685
|
+
async function validateAnalysisInputWrite(fs, cwd, path, signal) {
|
|
686
|
+
const deny = (shown, reasons2) => `Oh Story \u963B\u6B62 chapter-extractor \u5199\u5165 ${shown}\uFF1A${reasons2.join("\uFF1B")}\u3002${ANALYSIS_INPUT_RULE}`;
|
|
687
|
+
if (path === void 0) return deny("\uFF08\u672A\u7ED9\u51FA\u8DEF\u5F84\uFF09", ["\u5199\u5165\u6CA1\u6709\u7ED9\u51FA\u6587\u4EF6\u8DEF\u5F84"]);
|
|
688
|
+
let rootTarget;
|
|
689
|
+
let fileTarget;
|
|
690
|
+
try {
|
|
691
|
+
[rootTarget, fileTarget] = await Promise.all([
|
|
692
|
+
fs.resolve(cwd, signal === void 0 ? {} : { signal }),
|
|
693
|
+
fs.resolve(path, { cwd, ...signal === void 0 ? {} : { signal } })
|
|
694
|
+
]);
|
|
695
|
+
} catch {
|
|
696
|
+
return deny(path, ["\u8DEF\u5F84\u65E0\u6CD5\u89E3\u6790"]);
|
|
697
|
+
}
|
|
698
|
+
const reasons = [];
|
|
699
|
+
const match = ANALYSIS_INPUT.exec(displayName(fileTarget.displayPath));
|
|
700
|
+
if (match === null) reasons.push("\u6587\u4EF6\u540D\u5FC5\u987B\u662F \u8F93\u5165-{RAW|REUSE}-{\u8D77\u7AE0}-{\u6B62\u7AE0}.md");
|
|
701
|
+
else if (Number(match[2]) < 1 || Number(match[3]) < Number(match[2])) reasons.push("\u6279\u6B21\u7AE0\u53F7\u8303\u56F4\u65E0\u6548");
|
|
702
|
+
const cacheDirectory = displayParent(fileTarget.displayPath);
|
|
703
|
+
if (displayName(cacheDirectory) !== "_analysis_cache") reasons.push("\u53EA\u80FD\u5199\u5728\u62C6\u6587\u76EE\u5F55\u7684 _analysis_cache/ \u4E0B");
|
|
704
|
+
else if (!await isAnalysisDirectory(fs, displayParent(cacheDirectory), signal)) {
|
|
705
|
+
reasons.push("\u4E0A\u7EA7\u76EE\u5F55\u4E0D\u662F\u62C6\u6587\u76EE\u5F55\uFF08\u7F3A chapter_index.csv \u4E0E _progress.md\uFF09");
|
|
706
|
+
}
|
|
707
|
+
if (!fs.contains(rootTarget, fileTarget)) reasons.push("\u8DEF\u5F84\u5728\u9879\u76EE\u76EE\u5F55\u4E4B\u5916");
|
|
708
|
+
return reasons.length === 0 ? void 0 : deny(path, reasons);
|
|
709
|
+
}
|
|
710
|
+
async function decideChapterExtractorWrite(exec, next) {
|
|
711
|
+
if (ohStoryRoleOfSession(exec.agent?.session) !== "chapter-extractor" || !isFileMutationCall(exec.name, exec.arguments)) {
|
|
712
|
+
return next();
|
|
713
|
+
}
|
|
714
|
+
const fs = exec.agent?.ctx.get("fs");
|
|
715
|
+
const cwd = exec.agent?.session.header.cwd;
|
|
716
|
+
const reason = fs === void 0 || cwd === void 0 ? `Oh Story \u963B\u6B62 chapter-extractor \u5199\u5165\uFF1A\u65E0\u6CD5\u786E\u8BA4\u9879\u76EE\u76EE\u5F55\u3002${ANALYSIS_INPUT_RULE}` : await validateAnalysisInputWrite(fs, cwd, mutationPath(exec.name, exec.arguments), exec.signal);
|
|
717
|
+
return reason === void 0 ? next() : { kind: "deny", reason };
|
|
718
|
+
}
|
|
719
|
+
function postWriteReminderText(path, options = {}) {
|
|
720
|
+
return [
|
|
721
|
+
`<oh-story-post-write>\u6B63\u6587 ${path} \u5DF2\u53D8\u66F4\u3002`,
|
|
722
|
+
"\u6BCF\u6B21\u5199\u5165\u6B63\u6587\u90FD\u4F1A\u51FA\u73B0\u8FD9\u6761\u63D0\u9192\uFF0C\u4E00\u7AE0\u5199\u5230\u4E00\u534A\u65F6\u4E5F\u4F1A\uFF1B\u5B83\u4E0D\u8868\u793A\u672C\u7AE0\u5DF2\u7ECF\u5B8C\u6210\uFF0C\u4E5F\u4E0D\u662F\u4F5C\u8005\u7684\u65B0\u5199\u4F5C\u8981\u6C42\uFF0C\u7EE7\u7EED\u5F53\u524D\u6B65\u9AA4\u5373\u53EF\u3002",
|
|
723
|
+
...options.trackingUninitialized === true ? [
|
|
724
|
+
"\u672C\u4E66\u8FD8\u6CA1\u6709 \u8FFD\u8E2A/_tracking-state.json\u3002\u65B0\u4E66\u5199\u7B2C\u4E00\u7AE0\u65F6\uFF0Cdraft \u4E4B\u524D\u5148\u6309 story-long-write workflow-daily \u7684\u300C\u9996\u6B21\u521D\u59CB\u5316\u300D\u5B8C\u6574\u8BFB\u53D6 references/tracking-initialization.md\uFF0C\u6784\u9020 last_chapter=0 \u7684\u521D\u59CB\u5316\u4E8B\u52A1\u5B58\u4E66\u76EE\u5F55 .story/work/init.json\uFF0C\u8FD0\u884C scripts/tracking_commit.py init\uFF0C\u518D\u8FD0\u884C tracking_commit.py check\uFF0C\u901A\u8FC7\u540E\u5220\u6389 init.json\uFF1B",
|
|
725
|
+
"\u4E66\u91CC\u5728\u672C\u7AE0\u4E4B\u524D\u5DF2\u6709\u6B63\u6587\u65F6\u4E0D\u8981\u521D\u59CB\u5316\uFF0C\u505C\u4E0B\u6765\u6539\u8D70 story-import \u7684\u300C\u65E7\u8FFD\u8E2A\u9879\u76EE\u8FC1\u79FB\u300D\u3002"
|
|
726
|
+
] : [],
|
|
727
|
+
"\u7B49\u672C\u7AE0\u5199\u5B8C\u6536\u5C3E\u65F6\u518D\u63D0\u4EA4\u8FFD\u8E2A\uFF1A\u65B0\u7AE0\u5148\u7528 story-long-write \u7684 scripts/tracking_commit.py draft \u751F\u6210\u672C\u7AE0\u8349\u7A3F\uFF0C\u586B\u597D\u540E\u6267\u884C scripts/storyctl.py chapter commit\uFF08\u4F5C\u8005\u63A5\u53D7\u5F53\u524D\u957F\u5EA6\u65F6\u7528 chapter accept-current-length\uFF09\uFF1B",
|
|
728
|
+
"\u4FEE\u6539\u5DF2\u63D0\u4EA4\u7684\u7AE0\u8282\u6309 workflow-revision \u63D0\u4EA4\u4E00\u6B21 mode=revision \u4E8B\u52A1\uFF1B\u5BFC\u5165\u65E2\u6709\u6B63\u6587\u65F6\u7531 story-import \u5728\u8FC1\u79FB\u540E\u7528 tracking_commit.py init \u7EDF\u4E00\u521D\u59CB\u5316\u3002",
|
|
729
|
+
"\u8FFD\u8E2A/_tracking-state.json \u4EE5\u53CA \u4E0A\u4E0B\u6587.md\u3001\u89D2\u8272\u72B6\u6001/\u3001\u4F0F\u7B14.md\u3001\u65F6\u95F4\u7EBF/\u3001\u9010\u7AE0\u8BB0\u5F55/ \u7B49\u6D3E\u751F Tracking \u89C6\u56FE\u53EA\u7531\u8FD9\u4E9B\u811A\u672C\u5199\u5165\uFF0C\u7EDD\u4E0D\u624B\u6539\u3002</oh-story-post-write>"
|
|
730
|
+
].join("");
|
|
731
|
+
}
|
|
503
732
|
function registerOhStoryHooks(context) {
|
|
733
|
+
context.on("session/event", (session, event) => {
|
|
734
|
+
observeOhStoryRoleDescriptor(session, event);
|
|
735
|
+
});
|
|
736
|
+
context.on("tools/pre-execute", decideChapterExtractorWrite);
|
|
504
737
|
context.on("tools/pre-execute", decideStoryMutation);
|
|
505
738
|
context.on("tools/post-execute", async (exec, result, next) => {
|
|
506
739
|
const downstream = await next();
|
|
507
740
|
if (result.isError || downstream.kind !== "accept") return downstream;
|
|
741
|
+
if (ohStoryRoleOfSession(exec.agent?.session) !== void 0) return downstream;
|
|
508
742
|
const fs = exec.agent?.ctx.get("fs");
|
|
509
743
|
const mutation = fs === void 0 ? void 0 : await storyMutation(exec, fs);
|
|
510
|
-
|
|
744
|
+
const location = mutation === void 0 ? void 0 : proseLocation(mutation.path);
|
|
745
|
+
if (fs === void 0 || mutation === void 0 || location === void 0) return downstream;
|
|
746
|
+
const { root } = mutation;
|
|
747
|
+
const { book } = location;
|
|
748
|
+
if (!await isLongFormBook(fs, root, book, exec.signal)) return downstream;
|
|
749
|
+
const trackingUninitialized = !await hasTrackingState(fs, root, book, exec.signal) && !await hasImportSignal(fs, root, book, exec.signal);
|
|
511
750
|
const reminder = createUserMessage({
|
|
512
|
-
source: { kind: "
|
|
513
|
-
content: [{
|
|
514
|
-
type: "text",
|
|
515
|
-
text: `<oh-story-post-write>\u6B63\u6587 ${mutation.path} \u5DF2\u53D8\u66F4\u3002\u7EE7\u7EED\u5F53\u524D\u6B65\u9AA4\u524D\u6838\u5BF9\u5E76\u66F4\u65B0 _tracking-state.json \u53CA\u5BF9\u5E94\u6D3E\u751F Tracking \u89C6\u56FE\uFF1B\u4E0D\u8981\u628A\u8FD9\u6761\u63D0\u9192\u5F53\u4F5C\u7528\u6237\u7684\u65B0\u5199\u4F5C\u8981\u6C42\u3002</oh-story-post-write>`
|
|
516
|
-
}]
|
|
751
|
+
source: { kind: "oh-story-post-write", form: "notice", summary: boundContextSummary(`\u6B63\u6587 ${mutation.path} \u5DF2\u53D8\u66F4`) },
|
|
752
|
+
content: [{ type: "text", text: postWriteReminderText(mutation.path, { trackingUninitialized }) }]
|
|
517
753
|
});
|
|
518
754
|
return {
|
|
519
755
|
...downstream,
|
|
@@ -526,22 +762,22 @@ function registerOhStoryHooks(context) {
|
|
|
526
762
|
import { defineTool as defineTool2 } from "@deepseek-ai/dsh-tools";
|
|
527
763
|
|
|
528
764
|
// src/reference-tool.ts
|
|
529
|
-
import { readdir as readdir2, readFile as
|
|
530
|
-
import { isAbsolute as isAbsolute2, join as
|
|
765
|
+
import { readdir as readdir2, readFile as readFile3, realpath } from "node:fs/promises";
|
|
766
|
+
import { isAbsolute as isAbsolute2, join as join4, relative as relative2, resolve as resolve4 } from "node:path";
|
|
531
767
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
532
768
|
var OH_STORY_REFERENCE_TOOL_NAME = "oh_story_bundled_reference";
|
|
533
769
|
async function collectMarkdown(root, directory = root) {
|
|
534
770
|
const entries = await readdir2(directory, { withFileTypes: true });
|
|
535
771
|
const nested = await Promise.all(entries.map(async (entry) => {
|
|
536
|
-
const path =
|
|
772
|
+
const path = join4(directory, entry.name);
|
|
537
773
|
if (entry.isDirectory()) return collectMarkdown(root, path);
|
|
538
774
|
return entry.isFile() && entry.name.endsWith(".md") ? [path] : [];
|
|
539
775
|
}));
|
|
540
776
|
return nested.flat();
|
|
541
777
|
}
|
|
542
778
|
async function bundledReferences(storySetupRoot) {
|
|
543
|
-
const root = await realpath(
|
|
544
|
-
const referenceRoot = await realpath(
|
|
779
|
+
const root = await realpath(resolve4(storySetupRoot));
|
|
780
|
+
const referenceRoot = await realpath(join4(root, "references", "agent-references"));
|
|
545
781
|
const referenceDirectory = relative2(root, referenceRoot);
|
|
546
782
|
if (referenceDirectory === "" || referenceDirectory.startsWith("..") || isAbsolute2(referenceDirectory)) {
|
|
547
783
|
throw new Error("Bundled Oh Story reference directory escaped its package root.");
|
|
@@ -559,7 +795,7 @@ async function bundledReferences(storySetupRoot) {
|
|
|
559
795
|
}));
|
|
560
796
|
return values.sort((left, right) => left.name.localeCompare(right.name));
|
|
561
797
|
}
|
|
562
|
-
async function createOhStoryReferenceTool(storySetupRoot =
|
|
798
|
+
async function createOhStoryReferenceTool(storySetupRoot = join4(defaultBundledSkillRoot(), "story-setup")) {
|
|
563
799
|
const references = await bundledReferences(storySetupRoot);
|
|
564
800
|
if (references.length === 0) throw new Error("No bundled Oh Story references were found.");
|
|
565
801
|
const paths = references.map((reference) => reference.name);
|
|
@@ -595,7 +831,7 @@ ${value.content}`
|
|
|
595
831
|
async execute(args) {
|
|
596
832
|
const path = byName.get(args.reference);
|
|
597
833
|
if (path === void 0) throw new Error(`Oh Story reference is not bundled: ${args.reference}`);
|
|
598
|
-
return { reference: args.reference, content: await
|
|
834
|
+
return { reference: args.reference, content: await readFile3(path, "utf8") };
|
|
599
835
|
}
|
|
600
836
|
});
|
|
601
837
|
}
|
|
@@ -603,53 +839,12 @@ function bundledReferenceGuard(definition, tools) {
|
|
|
603
839
|
return (execution) => execution.name === OH_STORY_REFERENCE_TOOL_NAME && tools.get(OH_STORY_REFERENCE_TOOL_NAME, execution.agent) !== definition ? "The pinned Oh Story reference tool was shadowed in this Agent scope." : void 0;
|
|
604
840
|
}
|
|
605
841
|
|
|
606
|
-
// src/role-provider.ts
|
|
607
|
-
import { readFile as readFile3 } from "node:fs/promises";
|
|
608
|
-
import { basename as basename2, dirname as dirname3, join as join4, resolve as resolve4 } from "node:path";
|
|
609
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
610
|
-
var OH_STORY_ROLE_NAMES = [
|
|
611
|
-
"chapter-extractor",
|
|
612
|
-
"character-designer",
|
|
613
|
-
"consistency-checker",
|
|
614
|
-
"narrative-writer",
|
|
615
|
-
"story-architect",
|
|
616
|
-
"story-explorer",
|
|
617
|
-
"story-researcher"
|
|
618
|
-
];
|
|
619
|
-
function roleBody(source) {
|
|
620
|
-
const frontmatter = /^---\r?\n[\s\S]*?\r?\n---\r?\n?/u.exec(source);
|
|
621
|
-
return source.slice(frontmatter?.[0].length ?? 0).trim();
|
|
622
|
-
}
|
|
623
|
-
function defaultBundledRoleRoot() {
|
|
624
|
-
const current = dirname3(fileURLToPath2(import.meta.url));
|
|
625
|
-
return basename2(current) === "src" ? resolve4(current, "../../knowledge/oh-story/roles") : resolve4(current, "oh-story/roles");
|
|
626
|
-
}
|
|
627
|
-
async function loadBundledRole(name2, roleRoot = defaultBundledRoleRoot(), execution = "tool-free") {
|
|
628
|
-
const body = roleBody(await readFile3(join4(resolve4(roleRoot), `${name2}.md`), "utf8"));
|
|
629
|
-
if (body.length === 0) throw new Error(`Bundled role "${name2}" is empty.`);
|
|
630
|
-
const integration = execution === "tool-free" ? [
|
|
631
|
-
"You are running inside an Oh Story review-required DSH collaboration. The caller supplies every permitted input in the prompt.",
|
|
632
|
-
"Do not read or write project files, do not call tools, and do not follow legacy .claude/.codex path or deployment instructions in the upstream role text.",
|
|
633
|
-
"Return only the output contract requested by the caller; never claim to have changed the project."
|
|
634
|
-
] : [
|
|
635
|
-
"You are running as a native oh-story-dsh specialist. The current DSH workspace and visible tool set are your complete authority boundary.",
|
|
636
|
-
"Never inspect or require legacy .claude/.opencode/.codex agent deployment files; this exact pinned Role is already active.",
|
|
637
|
-
"Bundled story-setup references are pinned plugin resources, not project files or project Skills. When the upstream Role marks one mandatory, call oh_story_bundled_reference with the exact story-setup/references/agent-references path, then use only the returned content.",
|
|
638
|
-
"If oh_story_bundled_reference or a required bundled reference is unavailable, report the missing reference to the caller. Never call the generic skill tool, fall back to a legacy platform path, or claim that an unread reference was used.",
|
|
639
|
-
"Use only the tools actually visible to you. Mutate files only when the caller explicitly requests it and your visible DSH tools permit it; otherwise return findings to the caller."
|
|
640
|
-
];
|
|
641
|
-
return [
|
|
642
|
-
`OH_STORY_DSH_ROLE:${name2}`,
|
|
643
|
-
...integration,
|
|
644
|
-
"",
|
|
645
|
-
body
|
|
646
|
-
].join("\n");
|
|
647
|
-
}
|
|
648
|
-
|
|
649
842
|
// src/role-tool.ts
|
|
650
843
|
var OH_STORY_ROLE_TOOL_NAME = "oh_story_role";
|
|
651
844
|
var roleTools = {
|
|
652
|
-
|
|
845
|
+
// Oh Story 0.8.0 lets the extractor write its own batch input file (never
|
|
846
|
+
// bash). native-hooks fences write/edit to {拆文目录}/_analysis_cache/输入-*.md.
|
|
847
|
+
"chapter-extractor": ["read", "glob", "grep", "write", "edit"],
|
|
653
848
|
"character-designer": [OH_STORY_REFERENCE_TOOL_NAME, "read", "glob", "grep", "write", "edit"],
|
|
654
849
|
"consistency-checker": [OH_STORY_REFERENCE_TOOL_NAME, "read", "glob", "grep"],
|
|
655
850
|
"narrative-writer": [OH_STORY_REFERENCE_TOOL_NAME, "read", "glob", "grep", "write", "edit", "bash"],
|
|
@@ -705,7 +900,9 @@ async function createOhStoryRoleTool(subagents) {
|
|
|
705
900
|
const runtime = subagents ?? exec.agent.ctx.get("subagents");
|
|
706
901
|
if (runtime === void 0) throw new Error("oh_story_role requires the DSH subagent runtime.");
|
|
707
902
|
const run = await runtime.start("spawn", {
|
|
708
|
-
label
|
|
903
|
+
// DSH persists this label in the child's subagent/descriptor; the
|
|
904
|
+
// native hooks read the Role back from it before the child's first tool call.
|
|
905
|
+
label: ohStoryRoleLabel(args.role),
|
|
709
906
|
prompt: [{ type: "text", text: args.prompt }],
|
|
710
907
|
parent: exec.agent,
|
|
711
908
|
persona,
|
|
@@ -713,6 +910,7 @@ async function createOhStoryRoleTool(subagents) {
|
|
|
713
910
|
maxDepth: 1,
|
|
714
911
|
signal: exec.signal
|
|
715
912
|
});
|
|
913
|
+
if (run.localAgent !== void 0) markOhStoryRoleSession(run.localAgent.session, args.role);
|
|
716
914
|
try {
|
|
717
915
|
const result = await run.result;
|
|
718
916
|
if (result.stopReason !== "completed") {
|