@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
|
@@ -117,11 +117,26 @@ def require_known_keys(mapping: dict[str, Any], allowed: set[str], label: str) -
|
|
|
117
117
|
require(not unknown, f"{label} contains unsupported fields: {', '.join(sorted(unknown))}")
|
|
118
118
|
|
|
119
119
|
|
|
120
|
+
# 事务校验期间收集全部超长字段,一次报完;None 表示逐条立即报错(初始化、状态读取等路径)。
|
|
121
|
+
_LENGTH_ISSUES: list[str] | None = None
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def length_hint(label: str, text: str, max_bytes: int) -> str:
|
|
125
|
+
"""把字节上限换算成字数:模型写的是中文,只看得懂「要删几个字」。"""
|
|
126
|
+
size = len(text.encode("utf-8"))
|
|
127
|
+
allowed = max(1, max_bytes * len(text) // size)
|
|
128
|
+
return (f"{label} exceeds {max_bytes} bytes(现 {len(text)} 字,上限约 {allowed} 字,"
|
|
129
|
+
f"至少删 {len(text) - allowed} 字)")
|
|
130
|
+
|
|
131
|
+
|
|
120
132
|
def clean_text(value: object, label: str, *, allow_empty: bool = False, max_bytes: int = 768) -> str:
|
|
121
133
|
require(isinstance(value, str), f"{label} must be a string")
|
|
122
134
|
cleaned = " ".join(value.replace("|", "|").split())
|
|
123
135
|
require(allow_empty or bool(cleaned), f"{label} must not be empty")
|
|
124
|
-
|
|
136
|
+
if len(cleaned.encode("utf-8")) > max_bytes:
|
|
137
|
+
if _LENGTH_ISSUES is None:
|
|
138
|
+
raise TrackingError(length_hint(label, cleaned, max_bytes))
|
|
139
|
+
_LENGTH_ISSUES.append(length_hint(label, cleaned, max_bytes))
|
|
125
140
|
return cleaned
|
|
126
141
|
|
|
127
142
|
|
|
@@ -277,9 +292,15 @@ def archive_retired_tracking_paths(tracking: Path) -> list[str]:
|
|
|
277
292
|
return retired
|
|
278
293
|
|
|
279
294
|
|
|
295
|
+
def _both_blank(position: dict[str, Any]) -> bool:
|
|
296
|
+
return all(isinstance(position.get(key), str) and not position[key].strip() for key in ("story_time", "scene"))
|
|
297
|
+
|
|
298
|
+
|
|
280
299
|
def validate_position(value: object, label: str = "context.position") -> dict[str, Any]:
|
|
281
300
|
position = as_mapping(value, label)
|
|
282
301
|
require_known_keys(position, {"volume", "volume_start_chapter", "story_time", "scene"}, label)
|
|
302
|
+
require(not _both_blank(position),
|
|
303
|
+
f"{label}.story_time and {label}.scene must not be empty: fill where this chapter ends")
|
|
283
304
|
return {
|
|
284
305
|
"volume": safe_file_component(position.get("volume"), f"{label}.volume"),
|
|
285
306
|
"volume_start_chapter": as_int(
|
|
@@ -781,7 +802,13 @@ def render_delta(chapter: int, title: str, delta: dict[str, Any], core_names: se
|
|
|
781
802
|
lines.extend(f"- {item}" for item in retired)
|
|
782
803
|
payload = "\n".join(lines) + "\n"
|
|
783
804
|
size = byte_size(payload)
|
|
784
|
-
|
|
805
|
+
if size > DELTA_MAX_BYTES:
|
|
806
|
+
longest = sorted((line for line in lines if line.startswith("- ")), key=len, reverse=True)[:3]
|
|
807
|
+
allowed = DELTA_MAX_BYTES * len(payload) // size
|
|
808
|
+
raise TrackingError(
|
|
809
|
+
f"chapter delta is {size} bytes; hard cap is {DELTA_MAX_BYTES}(逐章记录现 {len(payload)} 字,"
|
|
810
|
+
f"上限约 {allowed} 字,至少压缩 {len(payload) - allowed} 字;最长几项:"
|
|
811
|
+
+ ";".join(f"{line[2:18]}…({len(line) - 2} 字)" for line in longest) + ")")
|
|
785
812
|
return payload
|
|
786
813
|
|
|
787
814
|
|
|
@@ -901,6 +928,17 @@ def normalize_initial_document(document: object) -> dict[str, Any]:
|
|
|
901
928
|
|
|
902
929
|
|
|
903
930
|
def normalize_transaction(project: Path, state: dict[str, Any], document: object) -> dict[str, Any]:
|
|
931
|
+
global _LENGTH_ISSUES
|
|
932
|
+
_LENGTH_ISSUES = []
|
|
933
|
+
try:
|
|
934
|
+
transaction = _normalize_transaction(project, state, document)
|
|
935
|
+
finally:
|
|
936
|
+
issues, _LENGTH_ISSUES = _LENGTH_ISSUES, None
|
|
937
|
+
require(not issues, f"{len(issues)} 个字段超长,一次改完再提交:" + ";".join(issues))
|
|
938
|
+
return transaction
|
|
939
|
+
|
|
940
|
+
|
|
941
|
+
def _normalize_transaction(project: Path, state: dict[str, Any], document: object) -> dict[str, Any]:
|
|
904
942
|
root = as_mapping(document, "transaction")
|
|
905
943
|
require_known_keys(
|
|
906
944
|
root,
|
|
@@ -999,6 +1037,13 @@ def merge_transaction(state: dict[str, Any], transaction: dict[str, Any]) -> dic
|
|
|
999
1037
|
"a revision must resubmit every current context item; retire them in an append transaction instead: "
|
|
1000
1038
|
+ ";".join(sorted(dropped)),
|
|
1001
1039
|
)
|
|
1040
|
+
kept = set(next_context["long_term_constraints"]) | set(next_context["continuity_risks"])
|
|
1041
|
+
still_listed = sorted(set(transaction["delta"]["retired_context_items"]) & kept)
|
|
1042
|
+
require(
|
|
1043
|
+
not still_listed,
|
|
1044
|
+
"delta.retired_context_items lists items that are still in context; remove them from context: "
|
|
1045
|
+
+ ";".join(still_listed),
|
|
1046
|
+
)
|
|
1002
1047
|
undeclared = sorted(dropped - set(transaction["delta"]["retired_context_items"]))
|
|
1003
1048
|
require(
|
|
1004
1049
|
not undeclared,
|
|
@@ -1188,6 +1233,126 @@ def check_project(project: Path) -> dict[str, Any]:
|
|
|
1188
1233
|
return state
|
|
1189
1234
|
|
|
1190
1235
|
|
|
1236
|
+
# 事务草稿里各文本字段的字数上限(按全中文估:UTF-8 每字 3 字节),写进提示,免得模型反复试错。
|
|
1237
|
+
DRAFT_LIMITS = {
|
|
1238
|
+
"delta.result": 160, "character_changes[].change": 120, "foreshadow_changes[].summary": 120,
|
|
1239
|
+
"timeline_events[].objective_fact / reader_knowledge": 160, "context.position.story_time / scene": 80,
|
|
1240
|
+
"character_snapshots.*.goal / state": 100, "逐章记录总量": 1024,
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
|
|
1244
|
+
def draft_transaction(project: Path, chapter: int) -> tuple[dict[str, Any], dict[str, Any], dict[str, Any]]:
|
|
1245
|
+
"""按当前 state 预填一份逐章事务:修订号、模式、章名和整份提交的上下文当前值都填好,
|
|
1246
|
+
调用方只写本章变化。另返回在场核心角色的当前快照,供有变化时整份改写后放进事务。
|
|
1247
|
+
新章(append)的故事时间与场景留空:它们必须是本章结束时的位置,沿用上一章的值能静默提交,
|
|
1248
|
+
所以不预填,上一章的值另行返回作参考。"""
|
|
1249
|
+
state = load_state(project)
|
|
1250
|
+
last = state["last_committed_chapter"]
|
|
1251
|
+
require(1 <= chapter <= last + 1, f"draft chapter must be between 1 and {last + 1}")
|
|
1252
|
+
mode = "append" if chapter == last + 1 else "revision"
|
|
1253
|
+
title = ""
|
|
1254
|
+
for outline in sorted((project / "大纲").glob(f"细纲_第{chapter:03d}章*.md")):
|
|
1255
|
+
match = re.search(rf"^#{{1,4}}\s*第\s*0*{chapter}\s*章\s*[::]\s*(.+?)\s*$",
|
|
1256
|
+
outline.read_text(encoding="utf-8"), re.M)
|
|
1257
|
+
if match:
|
|
1258
|
+
title = match.group(1).strip()
|
|
1259
|
+
break
|
|
1260
|
+
context = json.loads(json.dumps(state["context"], ensure_ascii=False))
|
|
1261
|
+
previous_position = {key: context["position"][key] for key in ("story_time", "scene")}
|
|
1262
|
+
delta: dict[str, Any] = {
|
|
1263
|
+
"result": "", "character_changes": [], "foreshadow_changes": [], "timeline_events": [],
|
|
1264
|
+
"constraints": [], "next_chapter_commitments": [],
|
|
1265
|
+
}
|
|
1266
|
+
if mode == "append":
|
|
1267
|
+
delta.update({"retired_context_items": [], "retired_characters": []})
|
|
1268
|
+
context["position"].update({"story_time": "", "scene": ""})
|
|
1269
|
+
document = {
|
|
1270
|
+
"schema_version": INPUT_SCHEMA_VERSION,
|
|
1271
|
+
"mode": mode,
|
|
1272
|
+
"chapter": chapter,
|
|
1273
|
+
"chapter_title": title,
|
|
1274
|
+
"expected_state_revision": state["state_revision"],
|
|
1275
|
+
"delta": delta,
|
|
1276
|
+
"context": {key: context[key] for key in
|
|
1277
|
+
("position", "long_term_constraints", "active_character_names", "continuity_risks")},
|
|
1278
|
+
"character_snapshots": {},
|
|
1279
|
+
}
|
|
1280
|
+
snapshots = {name: state["characters"][name] for name in context["active_character_names"]
|
|
1281
|
+
if name in state["characters"]}
|
|
1282
|
+
return document, snapshots, previous_position if mode == "append" else {}
|
|
1283
|
+
|
|
1284
|
+
|
|
1285
|
+
def rebuild_stale_draft(document: dict[str, Any], existing: dict[str, Any], append: bool,
|
|
1286
|
+
current_characters: Any = ()) -> str:
|
|
1287
|
+
"""修订号变了:中间提交过别的事务。以当前状态为底,只自动合并确定安全的部分,其余列给调用方核对。
|
|
1288
|
+
|
|
1289
|
+
新章草稿之间只可能插进修订事务,修订不能删约束与风险条目,所以当前约束与风险覆盖旧底稿:
|
|
1290
|
+
旧草稿多出来的就是本章新增,delta 里声明退役的就是本章删除,可以精确合并。
|
|
1291
|
+
修订草稿之间可能插进新章(新章能退役条目),在场角色名修订也能删,都无法判断谁新:
|
|
1292
|
+
取当前状态,差异列出来;只有本章带了快照的新角色名自动加回。"""
|
|
1293
|
+
def items(value: object) -> list[str]:
|
|
1294
|
+
return [item for item in value if isinstance(item, str)] if isinstance(value, list) else []
|
|
1295
|
+
|
|
1296
|
+
def norm(item: str) -> str:
|
|
1297
|
+
return " ".join(item.replace("|", "|").split())
|
|
1298
|
+
|
|
1299
|
+
notes = ["修订号已变:context 按当前状态重建,本章已填的 delta" + ("、新增与退役的约束、结尾位置" if append else "")
|
|
1300
|
+
+ "已合并"]
|
|
1301
|
+
for key in ("delta", "character_snapshots"):
|
|
1302
|
+
if isinstance(existing.get(key), dict):
|
|
1303
|
+
document[key] = existing[key]
|
|
1304
|
+
if existing.get("chapter_title"):
|
|
1305
|
+
document["chapter_title"] = existing["chapter_title"]
|
|
1306
|
+
old_context = existing.get("context") if isinstance(existing.get("context"), dict) else {}
|
|
1307
|
+
delta = document["delta"] if isinstance(document["delta"], dict) else {}
|
|
1308
|
+
context = document["context"]
|
|
1309
|
+
retired = {norm(item) for item in items(delta.get("retired_context_items"))}
|
|
1310
|
+
left_out: list[str] = []
|
|
1311
|
+
for key in ("long_term_constraints", "continuity_risks"):
|
|
1312
|
+
current = {norm(item) for item in context[key]}
|
|
1313
|
+
extra = []
|
|
1314
|
+
for item in items(old_context.get(key)):
|
|
1315
|
+
if norm(item) not in current:
|
|
1316
|
+
current.add(norm(item))
|
|
1317
|
+
extra.append(item)
|
|
1318
|
+
if append:
|
|
1319
|
+
context[key] = [item for item in context[key] if norm(item) not in retired] + extra
|
|
1320
|
+
else:
|
|
1321
|
+
left_out += extra
|
|
1322
|
+
snapshots = document["character_snapshots"] if isinstance(document["character_snapshots"], dict) else {}
|
|
1323
|
+
names = context["active_character_names"]
|
|
1324
|
+
for name in items(old_context.get("active_character_names")):
|
|
1325
|
+
if name in names:
|
|
1326
|
+
continue
|
|
1327
|
+
if append and name in snapshots:
|
|
1328
|
+
names.append(name)
|
|
1329
|
+
else:
|
|
1330
|
+
left_out.append(f"在场角色 {name}")
|
|
1331
|
+
names[:] = [name for name in names if name not in set(items(delta.get("retired_characters")))]
|
|
1332
|
+
known = set(current_characters)
|
|
1333
|
+
for name in list(snapshots):
|
|
1334
|
+
if name not in known and name not in names:
|
|
1335
|
+
snapshots.pop(name)
|
|
1336
|
+
left_out.append(f"角色快照 {name}")
|
|
1337
|
+
if left_out:
|
|
1338
|
+
notes.append("草稿里有、当前状态没有的条目没有自动带回(可能已被别的提交退役),本章确实需要就加回:"
|
|
1339
|
+
+ ";".join(left_out))
|
|
1340
|
+
old_position = old_context.get("position") if isinstance(old_context.get("position"), dict) else {}
|
|
1341
|
+
position = context["position"]
|
|
1342
|
+
if append:
|
|
1343
|
+
for key in ("story_time", "scene"):
|
|
1344
|
+
if isinstance(old_position.get(key), str) and old_position[key].strip():
|
|
1345
|
+
position[key] = old_position[key]
|
|
1346
|
+
differs = [f"{key}:草稿写的是「{old_position[key]}」,当前状态是「{position[key]}」"
|
|
1347
|
+
for key in ("volume", "volume_start_chapter")
|
|
1348
|
+
if key in old_position and old_position[key] != position[key]]
|
|
1349
|
+
if differs:
|
|
1350
|
+
notes.append("卷信息与当前状态不同,已取当前状态,本章确实换卷就改回(" + ";".join(differs) + ")")
|
|
1351
|
+
if snapshots:
|
|
1352
|
+
notes.append("带过来的角色快照(" + "、".join(snapshots) + ")可能被中间的提交改过,逐个对照 current_snapshots 重核")
|
|
1353
|
+
return ";".join(notes)
|
|
1354
|
+
|
|
1355
|
+
|
|
1191
1356
|
def build_parser() -> argparse.ArgumentParser:
|
|
1192
1357
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
1193
1358
|
subparsers = parser.add_subparsers(dest="command", required=True)
|
|
@@ -1197,12 +1362,55 @@ def build_parser() -> argparse.ArgumentParser:
|
|
|
1197
1362
|
subparser.add_argument("--input", type=Path, required=True, help="UTF-8 JSON input document")
|
|
1198
1363
|
check_parser = subparsers.add_parser("check")
|
|
1199
1364
|
check_parser.add_argument("--project", type=Path, required=True, help="book project root containing 追踪/")
|
|
1365
|
+
draft_parser = subparsers.add_parser("draft", help="write a pre-filled chapter transaction to fill in")
|
|
1366
|
+
draft_parser.add_argument("--project", type=Path, required=True, help="book project root containing 追踪/")
|
|
1367
|
+
draft_parser.add_argument("--chapter", type=int, required=True)
|
|
1368
|
+
draft_parser.add_argument("--out", type=Path, help="default: <project>/.story/work/第NNN章/tracking.json")
|
|
1369
|
+
draft_parser.add_argument("--force", action="store_true",
|
|
1370
|
+
help="discard an existing draft instead of only refreshing its revision")
|
|
1200
1371
|
return parser
|
|
1201
1372
|
|
|
1202
1373
|
|
|
1203
1374
|
def main() -> int:
|
|
1204
1375
|
args = build_parser().parse_args()
|
|
1205
1376
|
try:
|
|
1377
|
+
if args.command == "draft":
|
|
1378
|
+
document, snapshots, previous_position = draft_transaction(args.project, args.chapter)
|
|
1379
|
+
out = args.out or args.project / ".story" / "work" / f"第{args.chapter:03d}章" / "tracking.json"
|
|
1380
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
1381
|
+
refreshed = ""
|
|
1382
|
+
if out.exists() and not args.force:
|
|
1383
|
+
# 重跑 draft 不冲掉已经填好的变化。修订号变了说明中间提交过别的事务:context 按当前
|
|
1384
|
+
# 状态重建(沿用旧 context 会把那次变更整份覆盖回去),只带过本章自己填的部分。
|
|
1385
|
+
existing = read_json(out)
|
|
1386
|
+
require(isinstance(existing, dict) and existing.get("chapter") == document["chapter"]
|
|
1387
|
+
and existing.get("mode") == document["mode"],
|
|
1388
|
+
f"{out} already holds a draft for a different chapter or mode; pass --force to replace it")
|
|
1389
|
+
if existing.get("expected_state_revision") == document["expected_state_revision"]:
|
|
1390
|
+
document, refreshed = existing, "已有草稿且修订号未变,原样保留"
|
|
1391
|
+
else:
|
|
1392
|
+
refreshed = rebuild_stale_draft(document, existing, bool(previous_position),
|
|
1393
|
+
load_state(args.project)["characters"])
|
|
1394
|
+
out.write_text(json.dumps(document, ensure_ascii=False, indent=1) + "\n", encoding="utf-8")
|
|
1395
|
+
fill = ("只填 delta 里本章的变化;context 其余字段已是当前值,要撤下的长期约束或连贯性风险从 context 删掉并把原文放进 "
|
|
1396
|
+
"delta.retired_context_items(仅 append);本章有变化的核心角色把下面的当前快照整份改好放进 character_snapshots,"
|
|
1397
|
+
"并在 character_changes 写一句变化。不要从脚本源码或 state 文件里另找格式。")
|
|
1398
|
+
blank = not (document["context"]["position"].get("story_time") or document["context"]["position"].get("scene"))
|
|
1399
|
+
if previous_position and blank:
|
|
1400
|
+
fill = ("context.position 的 story_time 与 scene 留空,填本章结束时的故事时间与场景(上一章结束时见 "
|
|
1401
|
+
"previous_position;换卷时连同 volume 与 volume_start_chapter 一起改)。") + fill
|
|
1402
|
+
payload = {
|
|
1403
|
+
"draft": str(out),
|
|
1404
|
+
"mode": document["mode"],
|
|
1405
|
+
"expected_state_revision": document["expected_state_revision"],
|
|
1406
|
+
"fill": (f"{refreshed};要从头生成加 --force。" if refreshed else "") + fill,
|
|
1407
|
+
"limits_chars": DRAFT_LIMITS,
|
|
1408
|
+
"current_snapshots": snapshots,
|
|
1409
|
+
}
|
|
1410
|
+
if previous_position:
|
|
1411
|
+
payload["previous_position"] = previous_position
|
|
1412
|
+
emit(json.dumps(payload, ensure_ascii=False))
|
|
1413
|
+
return 0
|
|
1206
1414
|
if args.command == "init":
|
|
1207
1415
|
result = initialize(args.project, read_json(args.input))
|
|
1208
1416
|
elif args.command == "commit":
|
|
@@ -172,6 +172,9 @@ def target_from_outline(value: str) -> int:
|
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
def _chapter_number_from_name(name: str, *, outline: bool) -> int | None:
|
|
175
|
+
# 改稿流程把原文备份为 第N章_章名_原稿_{日期}.md,与 hooks 一样不把它当本章正文。
|
|
176
|
+
if not outline and "_原稿_" in name:
|
|
177
|
+
return None
|
|
175
178
|
pattern = r"^细纲_第0*(\d+)章.*\.md$" if outline else r"^第0*(\d+)章(?:[_\- ].*)?\.md$"
|
|
176
179
|
match = re.match(pattern, name)
|
|
177
180
|
return int(match.group(1)) if match else None
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: story-setup
|
|
3
|
-
version: 1.
|
|
3
|
+
version: 1.3.0
|
|
4
4
|
description: "网文写作工具集基础设施部署与检查。为 Claude Code / OpenCode / Codex / Google Antigravity / ZCode / OpenClaw / Reasonix 提供内置适配;Web AI / 通用 Agent 可走 skills + AGENTS.md 文件模式。触发方式:/story-setup、$story-setup、「准备写书」「帮我搭一下环境」「配置写作项目」「检查写作环境」。"
|
|
5
5
|
metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudecode"}}
|
|
6
6
|
---
|
|
@@ -24,10 +24,11 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
24
24
|
> 判据是「有没有 `SKILL.md`」:只看正在执行的 `SKILL.md` 同级的 `references/`。项目内 `.claude/skills/story-setup/`、`.codex/skills/story-setup/` 和 OpenCode 的 `skills/story-setup/` 只有 `references/agent-references/`、不含 `SKILL.md`,不会是执行目录,也不要拿它们核对。Antigravity / ZCode / OpenClaw / Reasonix / generic 的项目副本是整份 skill 拷贝、自带 `SKILL.md`,9 个子目录本就齐全,照常核对即可。
|
|
25
25
|
|
|
26
26
|
1. 检查当前目录是否已部署过(存在 `.story-deployed`)
|
|
27
|
-
- `agents_version` 缺失、非整数或小于 `
|
|
28
|
-
- `agents_version:
|
|
29
|
-
- `agents_version` 大于 `
|
|
27
|
+
- `agents_version` 缺失、非整数或小于 `32` → 标记为待更新,继续执行当前部署
|
|
28
|
+
- `agents_version: 32` → 使用 AskUserQuestion 确认是否重新部署;提示里写明重新部署只用**当前本地 skill 包**刷新项目文件,要拿 skill 本身的新版本得先更新 oh-story-claudecode(`npx skills add` 或 marketplace),再回来重跑
|
|
29
|
+
- `agents_version` 大于 `32` → 当前 story-setup 比项目部署旧;停止以避免降级覆盖,提示先更新 oh-story-claudecode,不写任何部署文件
|
|
30
30
|
- 同时读 `target_cli` 字段。**已部署项目以 sentinel 里的值为准**:非空时(逗号分隔的多端组合原样保留)跳过下面第 5-12 步的环境探测与选择,直接按这些端重新部署。只有字段缺失或为空,才回落到探测。用户明确要求增删目标端时,用 AskUserQuestion 在现有值基础上改,改完的值写回 sentinel。
|
|
31
|
+
- `target_cli` 不含 opencode、但项目里有 `.opencode/plugins/story-hooks.ts` 或 `.opencode/agents/`(多端部署时 OpenCode 曾被版本门拦下)→ 用 AskUserQuestion 问是否把 OpenCode 加回来;选加回则先过「OpenCode 部署前置」,通过后写回 `target_cli`
|
|
31
32
|
2. 检查是否有书名目录(包含 `追踪/` 子目录的目录,或用户自定义结构)
|
|
32
33
|
- 有 → 识别为长篇项目,显示当前项目信息
|
|
33
34
|
- 无 → 识别为新项目或短篇项目
|
|
@@ -37,7 +38,7 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
37
38
|
4. 检查 `.active-book` 文件是否存在
|
|
38
39
|
- 存在 → 显示当前活跃书目
|
|
39
40
|
- 不存在 → 跳过
|
|
40
|
-
5. 检查 `opencode.json` 或 `.opencode/` 是否存在
|
|
41
|
+
5. 检查 `opencode.json`、`opencode.jsonc` 或 `.opencode/` 是否存在
|
|
41
42
|
- 存在 → 识别为 opencode 项目,`target_cli = opencode`
|
|
42
43
|
- 不存在 → 跳过
|
|
43
44
|
6. 检查 `.codex/`、`.codex/config.toml`、`.codex/agents/`、`.codex/hooks.json`、`AGENTS.md` 中的 Codex 段
|
|
@@ -63,7 +64,7 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
63
64
|
|
|
64
65
|
12. 如 `.claude/` 或 `CLAUDE.md`、OpenCode、Codex、Antigravity、ZCode、OpenClaw、Reasonix、generic 标记同时存在 → 使用 AskUserQuestion 让用户选择目标环境(选项:Claude Code / OpenCode / Codex / Google Antigravity / ZCode / OpenClaw / Reasonix / 通用 Web AI 或其他 Agent / 任意组合)
|
|
65
66
|
13. 如八类标记都不存在(全新项目)→ 使用 AskUserQuestion 让用户选择目标环境
|
|
66
|
-
- 用户选择 opencode → `target_cli = opencode`,部署时创建
|
|
67
|
+
- 用户选择 opencode → `target_cli = opencode`,部署时创建 `.opencode/`
|
|
67
68
|
- 用户选择 claude-code → 按现有逻辑处理
|
|
68
69
|
- 用户选择 codex → `target_cli = codex`,部署时创建 `.codex/`
|
|
69
70
|
- 用户选择 antigravity → `target_cli = antigravity`,部署时创建 `.agents/skills`、`.agents/agents`、`.agents/rules`、`.agents/hooks` 并合并 `.agents/hooks.json`
|
|
@@ -101,7 +102,6 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
101
102
|
| `skills/story-setup/references/opencode/plugin.ts` | `.opencode/plugins/story-hooks.ts` | story-setup managed | replace | TypeScript plugin file exists | target_cli 含 opencode |
|
|
102
103
|
| `skills/story-setup/references/opencode/story_hook_core.js` | `.opencode/plugins/lib/story_hook_core.js` | story-setup managed | replace | Node syntax valid;与 ZCode 副本字节一致;被 story-hooks.ts import | target_cli 含 opencode |
|
|
103
104
|
| `skills/story-setup/references/opencode/commands/` | `.opencode/commands/` | story-setup managed | replace | 13 command files exist | target_cli 含 opencode |
|
|
104
|
-
| `skills/story-setup/references/opencode/opencode.json.patch` | merge into `opencode.json` | user+managed | merge by plugin/permission key | plugin entry registered | target_cli 含 opencode |
|
|
105
105
|
| repository `skills/story-setup/references/agent-references/` | `skills/story-setup/references/agent-references/` | story-setup managed | replace | every reference resolves | target_cli 含 opencode |
|
|
106
106
|
| `skills/story-setup/references/opencode/pre-commit.sh` | `.git/hooks/pre-commit` | user+managed | append or create | file exists and is executable;含 marker 块则替换块内容,不含则检测 exit 0 位置智能插入 | target_cli 含 opencode |
|
|
107
107
|
| `skills/story-setup/references/codex/AGENTS.md.tmpl` | `AGENTS.md` | user+managed | marker/section merge | contains Codex story skill routing sections | target_cli 含 codex |
|
|
@@ -128,14 +128,16 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
128
128
|
| repository `skills/{browser-cdp,story*}/` | `skills/{browser-cdp,story*}/` | story-setup managed for known skill names | replace known skill dirs only | 13 `SKILL.md` files exist; OpenClaw-compatible frontmatter | target_cli 含 openclaw 或 generic 或 reasonix |
|
|
129
129
|
| repository `skills/story-setup/references/agent-references/` | 随上一行整份 skill 拷贝落地,本行 no-op | story-setup managed | 不单独复制 | every reference resolves | target_cli 含 openclaw 或 generic 或 reasonix |
|
|
130
130
|
|
|
131
|
-
###
|
|
131
|
+
### OpenCode 部署前置(先于上表任何 OpenCode 行执行)
|
|
132
132
|
|
|
133
|
-
|
|
133
|
+
只适配 OpenCode 2.x:1.x 的插件 loader 读不了 `story-hooks.ts`,只记一行日志后照常运行,写正文守卫整场缺席;1.x 也不认 agents 的 `permissions:` 规则,三个只读 agent 会拿到写文件与 shell 权限。版本门 fail-closed。
|
|
134
134
|
|
|
135
|
-
1.
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
1. 运行 `opencode --version`,取第一个 `主.次.修` 版本号:
|
|
136
|
+
- 主版本 ≥ 2 → 继续
|
|
137
|
+
- 主版本 < 2 → 停止 OpenCode 部署(其它 target 照常),告诉用户先升级到 2.x(先 `npm rm -g opencode-ai`,再 `npm i -g @opencode/cli` 或 `curl -fsSL https://opencode.ai/v2/install | bash`),装好后重跑 story-setup
|
|
138
|
+
- 命令不可用或解析不出版本 → 同样停止 OpenCode 部署,请用户在自己的终端运行 `opencode --version`:用户在对话里确认显示 2.x 后才继续;是 1.x 按上一条处理
|
|
139
|
+
- 停止 OpenCode 部署时:target 只有 opencode 则不写、不更新 `.story-deployed`(已有的原样保留,不抬 `agents_version`),也不写任何 OpenCode 文件;多 target 时其它端照常部署,写入的 `target_cli` 不含 opencode,报告首行写明 OpenCode 未部署及原因,并告诉作者升级后重跑 story-setup、选择把 OpenCode 加回来
|
|
140
|
+
2. 插件由 OpenCode 自动发现 `.opencode/plugins/*.ts` 加载,不写 `opencode.json`。项目根已有 `opencode.json` / `opencode.jsonc` 时,从其 `plugin`、`plugins` 数组删掉指向 `.opencode/plugins/story-hooks.ts` 的项(旧版部署留下;2.x 丢弃单文件路径并告警),数组删空就删掉该键,其余内容原样保留。
|
|
139
141
|
|
|
140
142
|
### Step 2:部署 CLAUDE.md
|
|
141
143
|
|
|
@@ -181,8 +183,8 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
181
183
|
- 读取 `skills/story-setup/references/codex/agents/` 下所有 `.toml` 文件,复制到用户项目 `.codex/agents/`
|
|
182
184
|
- Agent 文件属于 story-setup 管理文件,可安全覆盖;`references/codex/agents/` 里的 TOML 由仓库根的 `scripts/generate-codex-agents.py` 从 Claude agent 模板确定性生成后提交入库,部署只做复制
|
|
183
185
|
- 校验每个 TOML 都能解析,且包含 Codex 必需字段:`name`、`description`、`developer_instructions`
|
|
184
|
-
- 只读职责 agent(`
|
|
185
|
-
- **部署后必须 trust + 新开 Codex 会话**(报告文案与 fallback 规则见「验证 Codex
|
|
186
|
+
- 只读职责 agent(`consistency-checker`、`story-explorer`)必须保留 `sandbox_mode = "read-only"`;`chapter-extractor` 要写批次输入文件,不设只读沙箱
|
|
187
|
+
- **部署后必须 trust + 新开 Codex 会话**(报告文案与 fallback 规则见「验证 Codex 部署」);hooks 未在 `/hooks` 信任前会被 Codex 静默跳过,包括写正文前的大纲守卫;若运行时返回 `unknown agent_type`,调用方必须降级 solo/direct 并报告 fallback。
|
|
186
188
|
- 将 `skills/story-setup/references/agent-references/` 同步复制到 `.codex/skills/story-setup/references/agent-references/`,作为 Codex agent 的项目内参考资料主路径
|
|
187
189
|
|
|
188
190
|
#### 部署 Antigravity Agents(target_cli 含 antigravity 时)
|
|
@@ -190,7 +192,7 @@ metadata: {"openclaw":{"source":"https://github.com/zenstory-ai/oh-story-claudec
|
|
|
190
192
|
- 先确认 `node` 在 PATH;Antigravity agent 生成与项目 hooks 都依赖 Node。缺失时停止 Antigravity 这一目标的部署,不留下半成品,并提示安装 Node 后重跑。
|
|
191
193
|
- 执行 `node "{story-setup skill目录}/scripts/generate-antigravity-agents.mjs" --source "{story-setup skill目录}/references/templates/agents" --dest "{项目}/.agents/agents"`。生成器先渲染全部 7 个 agent,再原子替换这 7 个已知 `.agents/agents/agent-name/agent.md` 定义(`agent-name` 为实际名称),并清理旧版同名扁平 `.md`;保留其他用户 agent,任一源 frontmatter 异常时不得留下半更新目录,也不得沿 managed agent symlink 写出项目外。
|
|
192
194
|
- 校验 7 个 `.md`:`name` 与文件名一致;`mainAgent: false`、`subagent: true`;模型只使用 `flash` / `pro`;工具只来自 Antigravity 官方名称 `view_file`、`find_by_name`、`grep_search`、`write_to_file`、`replace_file_content`、`multi_replace_file_content`、`run_command`;不得残留 Claude 的 `Read/Glob/Grep/Write/Edit/Bash` 工具名或 `.claude/skills/` reference 前缀。
|
|
193
|
-
- 只读 agent(`
|
|
195
|
+
- 只读 agent(`consistency-checker`、`story-explorer`)不得包含写文件或命令工具;`chapter-extractor` 只有读取与写文件、无命令工具;其他 agent 按 Claude 真源的能力边界映射。
|
|
194
196
|
- Antigravity 通过 `invoke_subagent` 的 `TypeName` 调用这些 agent。部署后新开 Antigravity conversation,再用 `story-review` 验证 full/lean;运行时无法解析某个 custom agent 时按 skill 的 solo/direct fallback 执行。
|
|
195
197
|
|
|
196
198
|
#### 配置 OpenCode Agent 模型
|
|
@@ -203,7 +205,7 @@ OpenCode agents 部署是 `replace`,会覆盖上次写入的 `model:`。所以
|
|
|
203
205
|
|
|
204
206
|
##### Step 2:获取模型列表
|
|
205
207
|
|
|
206
|
-
|
|
208
|
+
优先在项目根执行 `opencode api model.list -H "x-opencode-directory:<项目根绝对路径>"`,输出 JSON:`data[]` 每项的 `providerID/id` 即模型 ID,`cost[]` 为每百万 token 的 input/output 单价(空数组即无成本数据),`limit.context` 为上下文长度;不可用或解析失败时回退到 `opencode models` 纯文本(每行 `provider/model`)。两者都用 60000ms(60 秒)超时,因为首次运行需加载 models.dev 缓存。
|
|
207
209
|
|
|
208
210
|
- 成功 → 进入「模型分级」
|
|
209
211
|
- 超时 → 重试一次(缓存可能未预热);仍然超时则按「保留已有模型配置」缓存回填已有 `model:`、跳过自动配置,在安装报告中输出手动配置指南
|
|
@@ -211,9 +213,9 @@ OpenCode agents 部署是 `replace`,会覆盖上次写入的 `model:`。所以
|
|
|
211
213
|
|
|
212
214
|
##### Step 3:模型分级
|
|
213
215
|
|
|
214
|
-
**优先按成本分级(有
|
|
216
|
+
**优先按成本分级(有 `model.list` 成本数据时)**:按每模型实际 cost 从低到高分档——低端取最便宜/免费档、中端取中价档、高端取最贵或上下文/能力最强档。免费模型按真实 cost=0 归低端,**不按名字里的营销词**(如 `nemotron-3-ultra-free` 名含 `ultra` 但 cost=0,应归低端)。无 cost 数据的模型也据此进入候选,不被丢弃。
|
|
215
217
|
|
|
216
|
-
|
|
218
|
+
**回退按关键词分级(只有 `opencode models` 或无 cost 时)**:按模型 ID 中最后一个 `/` 之后的模型名按 `-`、`.`、`_` 分割为段,逐段精确匹配关键词(不区分大小写)。例如 `minimax-m3` 拆为 `[minimax, m3]`,不匹配 `mini` 也不匹配 `max`;`claude-haiku-4.5` 拆为 `[claude, haiku, 4, 5]`,匹配 `haiku`。关键词分级是启发式,安装报告中标注 `分级依据:关键词(heuristic)`。
|
|
217
219
|
|
|
218
220
|
| 等级 | 匹配关键词 | 对应 Agent |
|
|
219
221
|
|------|-----------|-----------|
|
|
@@ -274,15 +276,19 @@ OpenCode agents 部署是 `replace`,会覆盖上次写入的 `model:`。所以
|
|
|
274
276
|
|
|
275
277
|
##### Step 5:写入 model 字段
|
|
276
278
|
|
|
277
|
-
对应用户选择的 agent 文件(`.opencode/agents/*.md`,由部署清单中 OpenCode agents 部署步骤在此步骤之前已部署),在 frontmatter 末尾、closing `---` 之前,以**零缩进的顶层字段**插入 `model:`(不要插进 `
|
|
279
|
+
对应用户选择的 agent 文件(`.opencode/agents/*.md`,由部署清单中 OpenCode agents 部署步骤在此步骤之前已部署),在 frontmatter 末尾、closing `---` 之前,以**零缩进的顶层字段**插入 `model:`(不要插进 `permissions:` 规则列表等多行块的缩进内部)。值含 YAML 特殊字符时加引号,确保不破坏 frontmatter:
|
|
278
280
|
|
|
279
281
|
```yaml
|
|
280
282
|
---
|
|
281
283
|
description: ...
|
|
282
284
|
mode: subagent
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
285
|
+
permissions:
|
|
286
|
+
- action: "*"
|
|
287
|
+
resource: "*"
|
|
288
|
+
effect: deny
|
|
289
|
+
- action: read
|
|
290
|
+
resource: "*"
|
|
291
|
+
effect: allow
|
|
286
292
|
steps: 12
|
|
287
293
|
model: provider/model-id
|
|
288
294
|
---
|
|
@@ -377,15 +383,15 @@ Reasonix(DeepSeek-Reasonix CLI)当前只部署 skills 与 `AGENTS.md`,不
|
|
|
377
383
|
- 写入以下字段(YAML `key: value` 格式,hook 用 `references/templates/hooks/lib/sentinel.sh` 读取):
|
|
378
384
|
```
|
|
379
385
|
deployed_at: <date -u +"%Y-%m-%dT%H:%M:%SZ">
|
|
380
|
-
agents_version:
|
|
381
|
-
setup_skill_version: 1.
|
|
386
|
+
agents_version: 32
|
|
387
|
+
setup_skill_version: 1.3.0
|
|
382
388
|
target_cli: claude-code(或 opencode、codex、antigravity、zcode、openclaw、reasonix、generic,或其任意组合)
|
|
383
389
|
resolver_strategy: project-local-skill-reference
|
|
384
390
|
references_dir: .claude/skills/story-setup/references/agent-references(Codex 写 .codex/skills/...;Antigravity 写 .agents/skills/...;ZCode 写 .zcode/skills/...;OpenClaw / Reasonix / generic 写 skills/...;多端用逗号分隔)
|
|
385
391
|
```
|
|
386
392
|
- 此文件供 session-start.sh 和写作 skill 检测部署状态,避免重复提示
|
|
387
393
|
- target_cli 含 claude-code 时,同时创建一次性标记文件 `.claude/.agents-pending-restart`(空文件即可)。session-start.sh 在下一个会话启动时据此确认 agents 已随新会话注册,并自动删除该标记——用来向用户确认「重启已生效」。ZCode 不创建该标记,因为它不部署项目 agents。
|
|
388
|
-
- 如果 `.story-deployed` 已存在但 `agents_version` 缺失、非整数或小于 `
|
|
394
|
+
- 如果 `.story-deployed` 已存在但 `agents_version` 缺失、非整数或小于 `32`,按本次流程更新 hooks/agents/rules/reference bundle(具体变更见 `UPGRADING.md`);大于 `32` 时已在 Phase 1 停止,不得降级覆盖
|
|
389
395
|
|
|
390
396
|
## Phase 3:验证安装
|
|
391
397
|
|
|
@@ -403,11 +409,12 @@ Reasonix(DeepSeek-Reasonix CLI)当前只部署 skills 与 `AGENTS.md`,不
|
|
|
403
409
|
- 检查 `.claude/skills/story-setup/references/agent-references/` 下 reference 文件完整
|
|
404
410
|
- 检查所有 `story-setup/references/agent-references/<file>.md` 都能解析到 deployed bundle
|
|
405
411
|
5. 验证部署标记:
|
|
406
|
-
- 检查 `.story-deployed` 是否存在且包含时间戳、`agents_version:
|
|
407
|
-
6.
|
|
408
|
-
-
|
|
409
|
-
-
|
|
410
|
-
|
|
412
|
+
- 检查 `.story-deployed` 是否存在且包含时间戳、`agents_version: 32`、`setup_skill_version: 1.3.0`、`target_cli`、`resolver_strategy`、`references_dir`
|
|
413
|
+
6. 输出安装报告。读者是不懂编程的作者,按这个顺序写:
|
|
414
|
+
- **先写「现在可以做什么」**:用写书的话列本次部署后真正可用的事(如「可以开新书、续写:说 /story-long-write」「可以拆一本对标书」),端的限制如实翻译(如「这个工具里审稿由我一个人完成,没有分工助手」)。
|
|
415
|
+
- **再写「你还需要做的事」**:逐条可照做,如「新开一个会话」「在 Codex 里打开 /hooks,把 oh-story 的几条信任一下」「先安装 Node」;没有就写「无需其他操作」。这两段不出现脚本名、字段名、状态名或文件路径;各端「安装报告必须提示」的内容先翻译进这两段。
|
|
416
|
+
- **最后是简短的「部署明细」**:已部署文件、已合并的配置、删掉的残留路径、下面的模型配置摘要和技术原因,放在报告末尾。
|
|
417
|
+
- **⚠️ 重启提示(必须醒目,放进「你还需要做的事」)**:本次部署写入了 `.claude/agents/`,但这些 custom agent 只在「会话启动」时才会被 Claude Code 注册成 `subagent_type`。**请新开一个 Claude Code 会话再开始写作**,否则当前会话里 story-review / story-long-write 等想 spawn `story-architect`、`narrative-writer` 等时会拿到「subagent_type 不可用」并降级 solo(单视角,失去多 agent 协作)。判断是否生效:新会话里跑 `/story-review`,报告开头「这次怎么审的」写着几个视角(完整审或精简审)即注册成功;若写着「我一个人审」说明还在旧会话或未注册。
|
|
411
418
|
- 重启后即可使用 `/story-long-write` 或 `/story-short-write`
|
|
412
419
|
- 如果执行了「配置 OpenCode Agent 模型」,输出 Agent 模型配置摘要:
|
|
413
420
|
```
|
|
@@ -430,16 +437,17 @@ Reasonix(DeepSeek-Reasonix CLI)当前只部署 skills 与 `AGENTS.md`,不
|
|
|
430
437
|
手动配置方法:编辑 .opencode/agents/{agent名}.md,在 frontmatter 中添加:
|
|
431
438
|
model: provider/model-id
|
|
432
439
|
|
|
433
|
-
|
|
440
|
+
可用模型列表可通过 opencode models 查看;成本与上下文长度见 opencode api model.list 的 cost/limit 字段。
|
|
434
441
|
模型库与定价见 OpenCode 官方模型源 https://models.dev/。
|
|
435
442
|
```
|
|
436
443
|
7. 验证 opencode 部署(仅当 target_cli 含 opencode 时):
|
|
437
|
-
- 检查 `.opencode/agents/` 下的 7 个 agent 定义文件是否存在,且 frontmatter 包含 `mode: subagent` 和 `
|
|
444
|
+
- 检查 `.opencode/agents/` 下的 7 个 agent 定义文件是否存在,且 frontmatter 包含 `mode: subagent` 和 `permissions` 规则列表
|
|
438
445
|
- 检查 `.opencode/plugins/story-hooks.ts` 是否存在
|
|
439
|
-
- 检查 `.opencode/plugins/lib/story_hook_core.js` 存在且 `node --check` 通过(story-hooks.ts import 之,与 `.zcode` 副本字节一致的共享写正文守卫核;置于 `lib/` 子目录以避开 OpenCode
|
|
446
|
+
- 检查 `.opencode/plugins/lib/story_hook_core.js` 存在且 `node --check` 通过(story-hooks.ts import 之,与 `.zcode` 副本字节一致的共享写正文守卫核;置于 `lib/` 子目录以避开 OpenCode 对 `.opencode/plugins/*.js` 的插件自动发现,`lib/` 里不得放 `index.*` / `server.*`)
|
|
440
447
|
- 检查 `.opencode/commands/` 下的 13 个 command 文件是否存在
|
|
441
448
|
- 检查 `skills/story-setup/references/agent-references/` 下 reference 文件完整且数量与源目录一致
|
|
442
|
-
- 检查 `opencode.json`
|
|
449
|
+
- 检查 `opencode.json` / `opencode.jsonc`(如有)的 `plugin`、`plugins` 数组不再含指向 story-hooks.ts 的项
|
|
450
|
+
- `opencode` 可用时在项目根执行 `opencode api plugin.list -H "x-opencode-directory:<项目根绝对路径>"`,确认 `id` 为 `oh-story.story-hooks` 的条目 `state.status` 为 `active`(首次请求可能返回空列表,隔几秒重试)
|
|
443
451
|
- 检查 `.git/hooks/pre-commit` 是否存在且有执行权限(Windows 上跳过执行权限检查)
|
|
444
452
|
- 检查 `.opencode/agents/` 下 agent 文件 frontmatter 可被 YAML 解析、`model:`(如有配置)是合法顶层标量,而非仅 grep 到 `model:` 子串
|
|
445
453
|
8. 验证 Codex 部署(仅当 target_cli 含 codex 时):
|
|
@@ -448,14 +456,14 @@ Reasonix(DeepSeek-Reasonix CLI)当前只部署 skills 与 `AGENTS.md`,不
|
|
|
448
456
|
- 检查 `.codex/hooks.json` 存在且 JSON 有效,Unix `command` 仅通过 `run-story-hook.sh` 启动,Windows `commandWindows` 仅通过 `run-story-hook.cmd` 启动;不存在直调 `story_codex_hook.py` 的注册
|
|
449
457
|
- 检查 `.codex/hooks/story_codex_hook.py`、`run-story-hook.sh`、`run-story-hook.cmd` 存在,Python 语法有效,POSIX/Windows launcher 能从嵌套 cwd 定位项目根
|
|
450
458
|
- 检查 `.codex/skills/story-setup/references/agent-references/` 下 reference 文件完整且数量与源目录一致
|
|
451
|
-
- 安装报告必须提示:Codex 需要 trust 项目 `.codex/` 配置层,并在 `/hooks` review/trust 非 managed hooks
|
|
459
|
+
- 安装报告必须提示:Codex 需要 trust 项目 `.codex/` 配置层,并在 `/hooks` review/trust 非 managed hooks——**未信任前 Codex 静默跳过这些 hooks,写正文前的大纲守卫也不生效,且不报错**;自动化里的 `codex exec` 可加 `--dangerously-bypass-hook-trust`;部署后新开 Codex 会话让 custom agents 生效;若当前运行时仍返回 `unknown agent_type`,按各 skill 的 fallback 规则降级 solo/direct
|
|
452
460
|
9. 验证 Antigravity 部署(仅当 target_cli 含 antigravity 时):
|
|
453
461
|
- 检查 `.agents/skills/` 下 13 个 story skills 为真实目录且 `SKILL.md` 可读;`.agents/skills/story-setup/references/agent-references/` 完整
|
|
454
462
|
- 检查 `.agents/agents/` 下 7 个 Markdown agent 可解析,名称、模型档、官方工具白名单、只读边界与 `.agents/skills/` reference 前缀正确
|
|
455
463
|
- 检查 `.agents/rules/oh-story.md` 为 `trigger: always_on` 且未超过 12,000 字符
|
|
456
464
|
- 检查 `.agents/hooks.json` 有效、顶层 `oh-story` group 恰有 PreToolUse/PostToolUse/PreInvocation/Stop,用户 hook groups 保留;检查 `.agents/hooks/story_antigravity_hook.js` 与 `story_hook_core.js` 语法有效
|
|
457
465
|
- 用 fixture 验证:PreToolUse 缺纲/追踪时 deny、普通写入 allow、commit advisory;PostToolUse stdout 恒为 `{}` 且把正文 findings 写进 session artifact;下一次 PreInvocation 注入 findings;Stop 对未处理 findings 最多 continue 一次;干净正文清除 pending state
|
|
458
|
-
- 安装报告必须提示:新开 Antigravity conversation 刷新 customization;Hooks 依赖 PATH 中的 `node`;外部 hook API 没有 PreCompact/PostCompact,compact 恢复由 Always-On Rule 读取 `追踪/上下文.md`;IDE 与交互式 `agy` 仍建议分别实机 smoke test
|
|
466
|
+
- 安装报告必须提示:新开 Antigravity conversation 刷新 customization;Hooks 依赖 PATH 中的 `node`;外部 hook API 没有 PreCompact/PostCompact,compact 恢复由 Always-On Rule 读取 `追踪/上下文.md`;IDE 与交互式 `agy` 仍建议分别实机 smoke test;命令行写作从项目目录进入交互式 `agy`,确认 `/skills`、`/agents`、`/hooks` 已发现 oh-story 后再发任务;print 模式 `agy -p` 必须带 `--add-dir "$PWD"`(实测 agy 1.2.10 带上才加载工作区 `.agents/` 的 13 个 skills、7 个 agents 与 hooks,不带则都不加载,可能回退写入 `~/.gemini/antigravity-cli/scratch/`),测试后检查 scratch 无意外小说产物
|
|
459
467
|
10. 验证 ZCode 部署(仅当 target_cli 含 zcode 时):
|
|
460
468
|
- 检查根 `AGENTS.md` 含 ZCode `$story-*` 路由、大纲守卫和 solo/direct fallback
|
|
461
469
|
- 检查 `.zcode/skills/` 下 13 个 Skills 与 `.zcode/commands/` 下 13 个 Commands,验证 frontmatter 和命名
|
|
@@ -500,7 +508,7 @@ Reasonix(DeepSeek-Reasonix CLI)当前只部署 skills 与 `AGENTS.md`,不
|
|
|
500
508
|
1. 优先识别 story-setup 管理块标记(如果旧项目已有标记,只替换标记内内容)
|
|
501
509
|
2. 无标记时,读取用户现有 CLAUDE.md,按 `##` 标题切分为 section map
|
|
502
510
|
3. 读取模板 CLAUDE.md.tmpl,同样切分
|
|
503
|
-
4. 模板中的标准 section(Skill
|
|
511
|
+
4. 模板中的标准 section(Skill 路由表、文件结构、协作规则、与作者协作、Compact 后恢复上下文;模板有而用户文件没有的 section 直接补入)**覆盖**用户同名 section
|
|
504
512
|
5. 用户独有的 section(自定义内容)**保留**不动
|
|
505
513
|
6. 未知冲突用 AskUserQuestion 让用户选择保留哪个版本
|
|
506
514
|
|
|
@@ -510,15 +518,15 @@ Reasonix(DeepSeek-Reasonix CLI)当前只部署 skills 与 `AGENTS.md`,不
|
|
|
510
518
|
1. 优先识别 story-setup 管理块标记(如果旧项目已有标记,只替换标记内内容)
|
|
511
519
|
2. 无标记时,读取用户现有 AGENTS.md,按 `##` 标题切分为 section map
|
|
512
520
|
3. OpenCode 使用 `skills/story-setup/references/opencode/AGENTS.md.tmpl`;Codex 使用 `skills/story-setup/references/codex/AGENTS.md.tmpl`;ZCode 使用 `skills/story-setup/references/zcode/AGENTS.md.tmpl`;OpenClaw 使用 `skills/story-setup/references/openclaw/AGENTS.md.tmpl`;Reasonix 使用 `skills/story-setup/references/reasonix/AGENTS.md.tmpl`;通用 Web AI / 其他 Agent 使用 `skills/story-setup/references/generic/AGENTS.md.tmpl`
|
|
513
|
-
4. 模板中的标准 section(Skill
|
|
521
|
+
4. 模板中的标准 section(Skill 路由表、文件结构、协作规则、与作者协作、Compact 后恢复上下文;模板有而用户文件没有的 section 直接补入)覆盖同名 section;用户独有 section 保留
|
|
514
522
|
5. 多端同时部署时,Codex/OpenCode/ZCode/OpenClaw/Reasonix/generic 共同可用的通用段落只保留一份;工具特有说明以小节区分,避免互相覆盖
|
|
515
523
|
|
|
516
524
|
## 重新部署
|
|
517
525
|
|
|
518
526
|
- `.story-deployed` 不存在 → 全新安装,Phase 2 全部执行
|
|
519
|
-
- `.story-deployed` 存在且 `agents_version:
|
|
520
|
-
- `.story-deployed` 存在但 `agents_version` 缺失、非整数或小于 `
|
|
521
|
-
- `.story-deployed` 存在且 `agents_version` 大于 `
|
|
527
|
+
- `.story-deployed` 存在且 `agents_version: 32` → 提示已部署,AskUserQuestion 确认是否重新部署;提示里写明重新部署只用当前本地 skill 包刷新项目文件,skill 本身的更新走 `npx skills add` 或 marketplace
|
|
528
|
+
- `.story-deployed` 存在但 `agents_version` 缺失、非整数或小于 `32` → 提示需要更新,重新执行 Phase 2 覆盖 agents/hooks/rules/reference bundle,CLAUDE.md / AGENTS.md / settings.local.json / .codex/hooks.json / `.agents/hooks.json` / .zcode/config.json 走合并策略
|
|
529
|
+
- `.story-deployed` 存在且 `agents_version` 大于 `32` → 当前 skill 版本过旧,停止并提示先更新 oh-story-claudecode;不覆盖项目中的更新部署
|
|
522
530
|
|
|
523
531
|
---
|
|
524
532
|
|
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> 本文件的句长、视角、标点、修辞与禁用词是默认写法,服从 [style-resolution.md](style-resolution.md) 的逐维裁决。所选 Gate 检查表达效果,不因作者有意选择某写法就机械删除;获准命中按书级 `.deslop-whitelist` 处理。
|
|
4
4
|
|
|
5
|
-
<!-- 同名副本×
|
|
5
|
+
<!-- 同名副本×4 字节同步,改动后跑 scripts/check-shared-files.sh -->
|
|
6
6
|
|
|
7
|
-
> 识别AI
|
|
7
|
+
> 识别AI写作指纹、改写顺序、禁用词约束、改写范例库。用于正文写作后做去AI味自检和改写时查阅。
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
| 写完正文后做去AI自检 | 核心规则 -> AI写作模式检测 -> 质量维度检查 |
|
|
16
16
|
| 改写某段AI味重的文字 | 改写范例库 + 冲突对话改写范例 |
|
|
17
17
|
| 检查是否用了禁用词 | 禁用词与句式速查 -> AI高频词(模式1) |
|
|
18
|
-
| 系统性去除整章AI味 |
|
|
18
|
+
| 系统性去除整章AI味 | 改写顺序 |
|
|
19
19
|
| 检查章尾是否有总结升华 | AI写作指纹 -> 章末总结体 |
|
|
20
20
|
| 判断情绪描写是否告知式 | Show Don't Tell原则 + 去AI味补充技法 |
|
|
21
21
|
| 快速扫描全章质量 | 快速自检口诀 + 质量维度检查 |
|
|
@@ -372,9 +372,11 @@ AI 喜欢把事情凑成三个以显"完整"。-> 砍到只剩最有力的一条
|
|
|
372
372
|
|
|
373
373
|
---
|
|
374
374
|
|
|
375
|
-
##
|
|
375
|
+
## 改写顺序(只排所选 Gate 的先后)
|
|
376
376
|
|
|
377
|
-
|
|
377
|
+
下面三步只决定所选 Gate 内问题的处理先后,不另起一轮全篇去味;某一步没有对应的所选 Gate 就跳过。
|
|
378
|
+
|
|
379
|
+
### 第一步:去泛化(Strip Generic)
|
|
378
380
|
- 抽象情绪总结句 -> 按规则 2 判断:删重复说明,保留准确直写,需要时用原文已有信息落地
|
|
379
381
|
- 假深度句 -> 删
|
|
380
382
|
- 意义膨胀 -> 缩小到具体影响
|
|
@@ -384,9 +386,9 @@ AI 喜欢把事情凑成三个以显"完整"。-> 砍到只剩最有力的一条
|
|
|
384
386
|
- 过度使用"于是""然而""此刻" -> 删掉一半
|
|
385
387
|
- 所有角色说话一样"高级" -> 区分语气
|
|
386
388
|
|
|
387
|
-
|
|
389
|
+
**原则**:能删就删,不能删就用具体细节替换。
|
|
388
390
|
|
|
389
|
-
###
|
|
391
|
+
### 第二步:去书面化(Cut Professional Diction)
|
|
390
392
|
- 分析性用词("机制""结构""逻辑""体系"出现在小说中)-> 换成日常表达
|
|
391
393
|
- 抽象名词滥用 -> 直接说事
|
|
392
394
|
- 体制内用语("进一步""深入""推进""落实")-> 删
|
|
@@ -394,7 +396,7 @@ AI 喜欢把事情凑成三个以显"完整"。-> 砍到只剩最有力的一条
|
|
|
394
396
|
|
|
395
397
|
**例外**:保留专业感的场景(历史题材正式用语、文学向刻意密度、喜剧夸张修辞)。
|
|
396
398
|
|
|
397
|
-
###
|
|
399
|
+
### 第三步:回自然感(Restore Natural Presence)
|
|
398
400
|
- 具体的感官细节(气味、温度、触感)
|
|
399
401
|
- 角色说话方式的区分(不同人不同语气)
|
|
400
402
|
- 句首变化:连续 3+ 句用同一主语或同一词性开头时换开法(动作、场景、对话引入)
|
|
@@ -407,7 +409,7 @@ AI 喜欢把事情凑成三个以显"完整"。-> 砍到只剩最有力的一条
|
|
|
407
409
|
|
|
408
410
|
### 执行范围
|
|
409
411
|
|
|
410
|
-
调用方指定 Gate 时,只处理选定 Gate
|
|
412
|
+
调用方指定 Gate 时,只处理选定 Gate;改写顺序只排先后,不重新分级或扩大范围。未指定范围时,按实际问题选择适用检查。
|
|
411
413
|
|
|
412
414
|
### 自检清单
|
|
413
415
|
- 对话自然度检查:对话是否使用口语化表达,是否避免了书面语/正式腔调
|
|
@@ -551,5 +553,4 @@ AI 喜欢把事情凑成三个以显"完整"。-> 砍到只剩最有力的一条
|
|
|
551
553
|
- [ ] **detector advisory 逐条复核**:`micro-action-tic` / `stock-reaction-tic` / `abstract-summary-tic` / `cliche-density-tic` / `metaphor-density-tic` / `reasoning-chain-tic` / `system-notice-formality-tic` / `overcompressed-prose-tic` / `low-connective-density-tic` / `action-list-tic` 命中时按脚本给出的修法处理:先通读判断是不是机械复现,确属再改;功能性写法保留或标 `[需复核]`,不做同义词轮换、不机械注水
|
|
552
554
|
- [ ] **不做硬指标投机**:不为反检测强制每句换行、50-60 字一行、对话 50%-60%、英文点省略号,或把 `地/得` 全改成 `的`
|
|
553
555
|
- [ ] **任务卡点服从原文边界**:抽象总结若改成角色办事被卡住,必须来自原文已有任务/证据/手续/物件缺口;不新增原文没有的事件链
|
|
554
|
-
- [ ] **去AI三遍法执行**:轻度只做Pass1,中度做Pass1+2,重度完整三遍
|
|
555
556
|
- [ ] **对话自然度测试**:无书面语痕迹 = 通过
|