@fifine/aim-studio 0.0.3 → 0.0.4
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 +65 -99
- package/dist/constants/paths.d.ts +5 -5
- package/dist/constants/paths.d.ts.map +1 -1
- package/dist/constants/paths.js +5 -5
- package/dist/constants/paths.js.map +1 -1
- package/dist/templates/aim/index.d.ts +1 -0
- package/dist/templates/aim/index.d.ts.map +1 -1
- package/dist/templates/aim/index.js +2 -0
- package/dist/templates/aim/index.js.map +1 -1
- package/dist/templates/aim/scripts/common/developer.py +2 -1
- package/dist/templates/aim/scripts/export.py +304 -13
- package/dist/templates/aim/workflow.md +12 -13
- package/dist/templates/claude/commands/aim/export.md +208 -32
- package/dist/templates/claude/commands/aim/finish-work.md +23 -0
- package/dist/templates/claude/commands/aim/story.md +275 -67
- package/package.json +1 -1
|
@@ -10,11 +10,14 @@ Usage:
|
|
|
10
10
|
python export.py --scene 1 # Export scene 1 of current episode
|
|
11
11
|
python export.py --format seedance # Seedance format (default)
|
|
12
12
|
python export.py --format simple # Simple format for quick copy
|
|
13
|
+
python export.py --duration 10 # Set video duration (5/10/15/30/45/60 seconds)
|
|
14
|
+
python export.py --check # Check for violations before export
|
|
13
15
|
"""
|
|
14
16
|
|
|
15
17
|
import argparse
|
|
16
18
|
import json
|
|
17
19
|
import os
|
|
20
|
+
import re
|
|
18
21
|
import sys
|
|
19
22
|
from pathlib import Path
|
|
20
23
|
from datetime import datetime
|
|
@@ -24,6 +27,222 @@ sys.path.insert(0, str(Path(__file__).parent))
|
|
|
24
27
|
from common.paths import get_project_root, get_tasks_dir
|
|
25
28
|
|
|
26
29
|
|
|
30
|
+
# =============================================================================
|
|
31
|
+
# Duration Options
|
|
32
|
+
# =============================================================================
|
|
33
|
+
|
|
34
|
+
DURATION_OPTIONS = ["5", "10", "15", "30", "45", "60"]
|
|
35
|
+
|
|
36
|
+
# =============================================================================
|
|
37
|
+
# Violation Detection
|
|
38
|
+
# =============================================================================
|
|
39
|
+
|
|
40
|
+
# Keywords that may indicate real person references (celebrities, public figures)
|
|
41
|
+
REAL_PERSON_KEYWORDS = [
|
|
42
|
+
# Chinese celebrities
|
|
43
|
+
"明星", "演员", "歌手", "主持人", "网红", "名人", "偶像",
|
|
44
|
+
"刘德华", "周杰伦", "成龙", "李连杰", "甄子丹", "吴京",
|
|
45
|
+
"赵丽颖", "杨幂", "范冰冰", "李冰冰", "Angelababy", "迪丽热巴",
|
|
46
|
+
"肖战", "王一博", "蔡徐坤", "李易峰", "吴亦凡", "鹿晗",
|
|
47
|
+
"特朗普", "拜登", "奥巴马", "普京", "马斯克", "乔布斯",
|
|
48
|
+
# International celebrities
|
|
49
|
+
"Leonardo DiCaprio", "Brad Pitt", "Tom Cruise", "Jennifer Lawrence",
|
|
50
|
+
"Taylor Swift", "Beyonce", "K-pop", "BTS", "Blackpink",
|
|
51
|
+
"周杰伦", "王菲", "张学友", "刘德华", "郭富城", "黎明",
|
|
52
|
+
# General real person terms
|
|
53
|
+
"真人", "照片", "写真", "肖像", "脸", "颜值",
|
|
54
|
+
"真实照片", "本人", "本人照片", "自拍照",
|
|
55
|
+
]
|
|
56
|
+
|
|
57
|
+
# Keywords that may indicate copyright issues
|
|
58
|
+
COPYRIGHT_KEYWORDS = [
|
|
59
|
+
# Copyright protected terms
|
|
60
|
+
"版权", "侵权", "盗版", "抄袭", "原创",
|
|
61
|
+
"小说改编", "影视改编", "动漫改编", "游戏改编",
|
|
62
|
+
"IP", "知识产权", "授权", "许可",
|
|
63
|
+
"哈利波特", "漫威", "DC", "迪士尼", "皮克斯",
|
|
64
|
+
" Batman", "Spider-Man", "Superman", "Iron Man",
|
|
65
|
+
"金庸", "古龙", "琼瑶", "JK罗琳", "马丁",
|
|
66
|
+
"腾讯", "网易", "字节", "B站",
|
|
67
|
+
]
|
|
68
|
+
|
|
69
|
+
# Keywords that may indicate sensitive content
|
|
70
|
+
SENSITIVE_KEYWORDS = [
|
|
71
|
+
"暴力", "血腥", "色情", "裸露", "赌博", "毒品",
|
|
72
|
+
"政治", "宗教", "邪教", "恐怖", "自杀", "自残",
|
|
73
|
+
"未成年人", "儿童", "小孩", "幼儿",
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def detect_violations(content: str) -> dict:
|
|
78
|
+
"""
|
|
79
|
+
Detect potential violations in content.
|
|
80
|
+
|
|
81
|
+
Returns:
|
|
82
|
+
dict with 'has_violation', 'real_person', 'copyright', 'sensitive' flags
|
|
83
|
+
"""
|
|
84
|
+
result = {
|
|
85
|
+
"has_violation": False,
|
|
86
|
+
"real_person": [],
|
|
87
|
+
"copyright": [],
|
|
88
|
+
"sensitive": [],
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
# Check for real person references
|
|
92
|
+
for keyword in REAL_PERSON_KEYWORDS:
|
|
93
|
+
if keyword.lower() in content.lower():
|
|
94
|
+
result["real_person"].append(keyword)
|
|
95
|
+
result["has_violation"] = True
|
|
96
|
+
|
|
97
|
+
# Check for copyright issues
|
|
98
|
+
for keyword in COPYRIGHT_KEYWORDS:
|
|
99
|
+
if keyword in content:
|
|
100
|
+
result["copyright"].append(keyword)
|
|
101
|
+
result["has_violation"] = True
|
|
102
|
+
|
|
103
|
+
# Check for sensitive content
|
|
104
|
+
for keyword in SENSITIVE_KEYWORDS:
|
|
105
|
+
if keyword in content:
|
|
106
|
+
result["sensitive"].append(keyword)
|
|
107
|
+
result["has_violation"] = True
|
|
108
|
+
|
|
109
|
+
return result
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def check_violations_in_project(project_root: Path) -> dict:
|
|
113
|
+
"""Check all project files for potential violations."""
|
|
114
|
+
spec_dir = project_root / ".aim-studio" / "spec"
|
|
115
|
+
|
|
116
|
+
all_violations = {
|
|
117
|
+
"characters": [],
|
|
118
|
+
"world": [],
|
|
119
|
+
"scenes": [],
|
|
120
|
+
"has_violation": False,
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
# Check character file
|
|
124
|
+
char_file = spec_dir / "story" / "character.md"
|
|
125
|
+
if char_file.exists():
|
|
126
|
+
content = read_file(char_file)
|
|
127
|
+
violations = detect_violations(content)
|
|
128
|
+
if violations["has_violation"]:
|
|
129
|
+
all_violations["characters"] = violations
|
|
130
|
+
all_violations["has_violation"] = True
|
|
131
|
+
|
|
132
|
+
# Check world file
|
|
133
|
+
world_file = spec_dir / "story" / "world.md"
|
|
134
|
+
if world_file.exists():
|
|
135
|
+
content = read_file(world_file)
|
|
136
|
+
violations = detect_violations(content)
|
|
137
|
+
if violations["has_violation"]:
|
|
138
|
+
all_violations["world"] = violations
|
|
139
|
+
all_violations["has_violation"] = True
|
|
140
|
+
|
|
141
|
+
# Check scenes
|
|
142
|
+
tasks_dir = get_tasks_dir(project_root)
|
|
143
|
+
if tasks_dir.exists():
|
|
144
|
+
for ep_dir in tasks_dir.iterdir():
|
|
145
|
+
if ep_dir.is_dir() and "EP" in ep_dir.name.upper():
|
|
146
|
+
for scene_file in ep_dir.glob("*.md"):
|
|
147
|
+
content = read_file(scene_file)
|
|
148
|
+
violations = detect_violations(content)
|
|
149
|
+
if violations["has_violation"]:
|
|
150
|
+
all_violations["scenes"].append({
|
|
151
|
+
"file": str(scene_file.relative_to(project_root)),
|
|
152
|
+
"violations": violations,
|
|
153
|
+
})
|
|
154
|
+
all_violations["has_violation"] = True
|
|
155
|
+
|
|
156
|
+
return all_violations
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def print_violation_report(violations: dict) -> None:
|
|
160
|
+
"""Print a detailed violation report."""
|
|
161
|
+
print("\n" + "=" * 60)
|
|
162
|
+
print("🚨 违规检测报告")
|
|
163
|
+
print("=" * 60)
|
|
164
|
+
|
|
165
|
+
if not violations["has_violation"]:
|
|
166
|
+
print("\n✅ 未检测到违规内容,可以继续导出!")
|
|
167
|
+
return
|
|
168
|
+
|
|
169
|
+
print("\n⚠️ 检测到以下潜在违规内容:\n")
|
|
170
|
+
|
|
171
|
+
# Real person violations
|
|
172
|
+
if violations.get("characters") and violations["characters"].get("real_person"):
|
|
173
|
+
print("【真人素材风险】")
|
|
174
|
+
for keyword in violations["characters"]["real_person"]:
|
|
175
|
+
print(f" - 关键词: {keyword}")
|
|
176
|
+
print()
|
|
177
|
+
|
|
178
|
+
if violations.get("world") and violations["world"].get("real_person"):
|
|
179
|
+
print("【世界观真人素材风险】")
|
|
180
|
+
for keyword in violations["world"]["real_person"]:
|
|
181
|
+
print(f" - 关键词: {keyword}")
|
|
182
|
+
print()
|
|
183
|
+
|
|
184
|
+
# Copyright violations
|
|
185
|
+
if violations.get("characters") and violations["characters"].get("copyright"):
|
|
186
|
+
print("【版权风险】")
|
|
187
|
+
for keyword in violations["characters"]["copyright"]:
|
|
188
|
+
print(f" - 关键词: {keyword}")
|
|
189
|
+
print()
|
|
190
|
+
|
|
191
|
+
if violations.get("world") and violations["world"].get("copyright"):
|
|
192
|
+
print("【世界观版权风险】")
|
|
193
|
+
for keyword in violations["world"]["copyright"]:
|
|
194
|
+
print(f" - 关键词: {keyword}")
|
|
195
|
+
print()
|
|
196
|
+
|
|
197
|
+
# Sensitive content
|
|
198
|
+
if violations.get("characters") and violations["characters"].get("sensitive"):
|
|
199
|
+
print("【敏感内容风险】")
|
|
200
|
+
for keyword in violations["characters"]["sensitive"]:
|
|
201
|
+
print(f" - 关键词: {keyword}")
|
|
202
|
+
print()
|
|
203
|
+
|
|
204
|
+
# Scene violations
|
|
205
|
+
if violations.get("scenes"):
|
|
206
|
+
print("【场景违规详情】")
|
|
207
|
+
for scene in violations["scenes"][:5]: # Show first 5
|
|
208
|
+
print(f" - {scene['file']}")
|
|
209
|
+
if scene["violations"].get("real_person"):
|
|
210
|
+
print(f" 真人: {', '.join(scene['violations']['real_person'][:3])}")
|
|
211
|
+
if scene["violations"].get("copyright"):
|
|
212
|
+
print(f" 版权: {', '.join(scene['violations']['copyright'][:3])}")
|
|
213
|
+
if len(violations["scenes"]) > 5:
|
|
214
|
+
print(f" ... 还有 {len(violations['scenes']) - 5} 个场景")
|
|
215
|
+
print()
|
|
216
|
+
|
|
217
|
+
print("=" * 60)
|
|
218
|
+
print("💡 建议处理方式:")
|
|
219
|
+
print(" 1. 修改角色设定 - 使用虚构角色替代真人")
|
|
220
|
+
print(" 2. 替换版权内容 - 使用原创元素替代受版权保护的内容")
|
|
221
|
+
print(" 3. 简化敏感描述 - 移除可能引发争议的描述")
|
|
222
|
+
print(" 4. 强制导出 - 仍要导出(风险自负)")
|
|
223
|
+
print("=" * 60)
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
def get_user_choice() -> str:
|
|
227
|
+
"""Get user's choice for handling violations."""
|
|
228
|
+
print("\n请选择处理方式(输入数字):")
|
|
229
|
+
print(" 1. 修改角色设定 - 手动修改角色文件")
|
|
230
|
+
print(" 2. 替换版权内容 - 手动修改版权相关内容")
|
|
231
|
+
print(" 3. 简化敏感描述 - 移除敏感描述后重试")
|
|
232
|
+
print(" 4. 强制导出 - 仍要导出(风险自负)")
|
|
233
|
+
print(" 5. 退出 - 不导出")
|
|
234
|
+
|
|
235
|
+
while True:
|
|
236
|
+
choice = input("\n请输入选项 (1-5): ").strip()
|
|
237
|
+
if choice in ["1", "2", "3", "4", "5"]:
|
|
238
|
+
return choice
|
|
239
|
+
print("无效选择,请重新输入。")
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
# =============================================================================
|
|
243
|
+
# File Operations
|
|
244
|
+
# =============================================================================
|
|
245
|
+
|
|
27
246
|
def read_file(file_path: Path) -> str:
|
|
28
247
|
"""Read file content."""
|
|
29
248
|
try:
|
|
@@ -118,6 +337,7 @@ def generate_seedance_prompt(
|
|
|
118
337
|
world_prompt: str,
|
|
119
338
|
scene_name: str,
|
|
120
339
|
previous_context: str = "",
|
|
340
|
+
duration: str = "10",
|
|
121
341
|
) -> str:
|
|
122
342
|
"""
|
|
123
343
|
Generate a clean, copy-paste ready prompt for Seedance.
|
|
@@ -134,6 +354,10 @@ def generate_seedance_prompt(
|
|
|
134
354
|
output.append(f"=== {scene_name} ===")
|
|
135
355
|
output.append("")
|
|
136
356
|
|
|
357
|
+
# Duration
|
|
358
|
+
output.append(f"[DURATION: {duration}s]")
|
|
359
|
+
output.append("")
|
|
360
|
+
|
|
137
361
|
# Character prompts
|
|
138
362
|
if character_prompts:
|
|
139
363
|
output.append("[CHARACTERS]")
|
|
@@ -197,7 +421,7 @@ def generate_seedance_prompt(
|
|
|
197
421
|
return "\n".join(output)
|
|
198
422
|
|
|
199
423
|
|
|
200
|
-
def generate_simple_prompt(scene_content: str, scene_name: str) -> str:
|
|
424
|
+
def generate_simple_prompt(scene_content: str, scene_name: str, duration: str = "10") -> str:
|
|
201
425
|
"""
|
|
202
426
|
Generate a simple, minimal prompt for quick copy.
|
|
203
427
|
Just the essential scene description.
|
|
@@ -207,6 +431,7 @@ def generate_simple_prompt(scene_content: str, scene_name: str) -> str:
|
|
|
207
431
|
output = []
|
|
208
432
|
|
|
209
433
|
output.append(f"Scene: {scene_name}")
|
|
434
|
+
output.append(f"Duration: {duration}s")
|
|
210
435
|
output.append("")
|
|
211
436
|
|
|
212
437
|
# Extract just the dialogue and action
|
|
@@ -266,6 +491,8 @@ def export_episode(
|
|
|
266
491
|
spec_dir: Path,
|
|
267
492
|
output_dir: Path,
|
|
268
493
|
format_type: str = "seedance",
|
|
494
|
+
duration: str = "10",
|
|
495
|
+
skip_violations: bool = False,
|
|
269
496
|
) -> list:
|
|
270
497
|
"""Export a single episode."""
|
|
271
498
|
|
|
@@ -281,6 +508,12 @@ def export_episode(
|
|
|
281
508
|
scene_name = scene_file.stem
|
|
282
509
|
content = read_file(scene_file)
|
|
283
510
|
|
|
511
|
+
# Check violations if not skipped
|
|
512
|
+
if not skip_violations:
|
|
513
|
+
violations = detect_violations(content)
|
|
514
|
+
if violations["has_violation"]:
|
|
515
|
+
print(f" ⚠️ 场景 {scene_name} 包含潜在违规内容")
|
|
516
|
+
|
|
284
517
|
# Get character prompts for this scene (simplified - use all characters)
|
|
285
518
|
character_prompts = {}
|
|
286
519
|
character_file = spec_dir / "story" / "character.md"
|
|
@@ -297,13 +530,14 @@ def export_episode(
|
|
|
297
530
|
|
|
298
531
|
# Generate prompt based on format
|
|
299
532
|
if format_type == "simple":
|
|
300
|
-
prompt = generate_simple_prompt(content, scene_name)
|
|
533
|
+
prompt = generate_simple_prompt(content, scene_name, duration)
|
|
301
534
|
else:
|
|
302
535
|
prompt = generate_seedance_prompt(
|
|
303
536
|
content,
|
|
304
537
|
character_prompts,
|
|
305
538
|
world_prompt,
|
|
306
539
|
scene_name,
|
|
540
|
+
duration=duration,
|
|
307
541
|
)
|
|
308
542
|
|
|
309
543
|
# Write to output directory
|
|
@@ -335,6 +569,13 @@ def main():
|
|
|
335
569
|
choices=["seedance", "simple"],
|
|
336
570
|
help="Export format: seedance (full) or simple (minimal)",
|
|
337
571
|
)
|
|
572
|
+
parser.add_argument(
|
|
573
|
+
"--duration",
|
|
574
|
+
type=str,
|
|
575
|
+
default="10",
|
|
576
|
+
choices=DURATION_OPTIONS,
|
|
577
|
+
help="Video duration in seconds (5/10/15/30/45/60)",
|
|
578
|
+
)
|
|
338
579
|
parser.add_argument(
|
|
339
580
|
"--output",
|
|
340
581
|
type=str,
|
|
@@ -346,6 +587,16 @@ def main():
|
|
|
346
587
|
action="store_true",
|
|
347
588
|
help="Open output directory after export",
|
|
348
589
|
)
|
|
590
|
+
parser.add_argument(
|
|
591
|
+
"--check",
|
|
592
|
+
action="store_true",
|
|
593
|
+
help="Only check for violations, do not export",
|
|
594
|
+
)
|
|
595
|
+
parser.add_argument(
|
|
596
|
+
"--force",
|
|
597
|
+
action="store_true",
|
|
598
|
+
help="Force export even with violations",
|
|
599
|
+
)
|
|
349
600
|
|
|
350
601
|
args = parser.parse_args()
|
|
351
602
|
|
|
@@ -359,12 +610,43 @@ def main():
|
|
|
359
610
|
print("Error: This is not a story project. No spec/story/ directory found.")
|
|
360
611
|
sys.exit(1)
|
|
361
612
|
|
|
613
|
+
# Check violations first
|
|
614
|
+
print("🔍 正在检查违规内容...")
|
|
615
|
+
violations = check_violations_in_project(project_root)
|
|
616
|
+
print_violation_report(violations)
|
|
617
|
+
|
|
618
|
+
# If --check only, exit here
|
|
619
|
+
if args.check:
|
|
620
|
+
sys.exit(0 if not violations["has_violation"] else 1)
|
|
621
|
+
|
|
622
|
+
# Handle violations
|
|
623
|
+
if violations["has_violation"] and not args.force:
|
|
624
|
+
choice = get_user_choice()
|
|
625
|
+
|
|
626
|
+
if choice == "1":
|
|
627
|
+
print("\n请手动修改角色设定文件后重试:")
|
|
628
|
+
print(f" {spec_dir / 'story' / 'character.md'}")
|
|
629
|
+
sys.exit(1)
|
|
630
|
+
elif choice == "2":
|
|
631
|
+
print("\n请手动修改版权相关内容后重试。")
|
|
632
|
+
sys.exit(1)
|
|
633
|
+
elif choice == "3":
|
|
634
|
+
print("\n请移除敏感描述后重试。")
|
|
635
|
+
sys.exit(1)
|
|
636
|
+
elif choice == "4":
|
|
637
|
+
print("\n⚠️ 强制导出已启用,风险自负!")
|
|
638
|
+
elif choice == "5":
|
|
639
|
+
print("\n已退出导出。")
|
|
640
|
+
sys.exit(0)
|
|
641
|
+
|
|
362
642
|
# Create output directory
|
|
363
643
|
output_dir = project_root / args.output
|
|
364
644
|
output_dir.mkdir(exist_ok=True)
|
|
365
645
|
|
|
366
|
-
print(f"
|
|
367
|
-
print(f"
|
|
646
|
+
print(f"\n导出设置:")
|
|
647
|
+
print(f" - 格式: {args.format}")
|
|
648
|
+
print(f" - 时长: {args.duration}秒")
|
|
649
|
+
print(f" - 输出: {output_dir}")
|
|
368
650
|
print("")
|
|
369
651
|
|
|
370
652
|
exported_files = []
|
|
@@ -373,8 +655,11 @@ def main():
|
|
|
373
655
|
if args.ep == "all":
|
|
374
656
|
episodes = find_episodes(tasks_dir)
|
|
375
657
|
for ep_name, ep_dir in episodes:
|
|
376
|
-
print(f"
|
|
377
|
-
files = export_episode(
|
|
658
|
+
print(f"导出 {ep_name}...")
|
|
659
|
+
files = export_episode(
|
|
660
|
+
ep_name, ep_dir, spec_dir, output_dir,
|
|
661
|
+
args.format, args.duration, args.force
|
|
662
|
+
)
|
|
378
663
|
exported_files.extend(files)
|
|
379
664
|
elif "-" in str(args.ep):
|
|
380
665
|
# Range like 1-3
|
|
@@ -383,8 +668,11 @@ def main():
|
|
|
383
668
|
ep_name = f"EP{ep_num:02d}"
|
|
384
669
|
ep_dir = tasks_dir / ep_name
|
|
385
670
|
if ep_dir.exists():
|
|
386
|
-
print(f"
|
|
387
|
-
files = export_episode(
|
|
671
|
+
print(f"导出 {ep_name}...")
|
|
672
|
+
files = export_episode(
|
|
673
|
+
ep_name, ep_dir, spec_dir, output_dir,
|
|
674
|
+
args.format, args.duration, args.force
|
|
675
|
+
)
|
|
388
676
|
exported_files.extend(files)
|
|
389
677
|
elif args.ep:
|
|
390
678
|
# Single episode
|
|
@@ -393,18 +681,21 @@ def main():
|
|
|
393
681
|
ep_dir = tasks_dir / ep_name
|
|
394
682
|
|
|
395
683
|
if ep_dir.exists():
|
|
396
|
-
print(f"
|
|
397
|
-
exported_files = export_episode(
|
|
684
|
+
print(f"导出 {ep_name}...")
|
|
685
|
+
exported_files = export_episode(
|
|
686
|
+
ep_name, ep_dir, spec_dir, output_dir,
|
|
687
|
+
args.format, args.duration, args.force
|
|
688
|
+
)
|
|
398
689
|
else:
|
|
399
690
|
print(f"Error: Episode {ep_name} not found in {tasks_dir}")
|
|
400
691
|
sys.exit(1)
|
|
401
692
|
else:
|
|
402
|
-
print("Error:
|
|
693
|
+
print("Error: 请指定 --ep 或 --all")
|
|
403
694
|
parser.print_help()
|
|
404
695
|
sys.exit(1)
|
|
405
696
|
|
|
406
697
|
print("")
|
|
407
|
-
print(f"
|
|
698
|
+
print(f"✅ 已导出 {len(exported_files)} 个文件:")
|
|
408
699
|
for f in exported_files:
|
|
409
700
|
print(f" - {f.relative_to(project_root)}")
|
|
410
701
|
|
|
@@ -420,7 +711,7 @@ def main():
|
|
|
420
711
|
subprocess.run(["xdg-open", str(output_dir)])
|
|
421
712
|
|
|
422
713
|
print("")
|
|
423
|
-
print("
|
|
714
|
+
print("完成!文件已准备好用于 Seedance。")
|
|
424
715
|
|
|
425
716
|
|
|
426
717
|
if __name__ == "__main__":
|
|
@@ -67,11 +67,10 @@ aim init
|
|
|
67
67
|
│ ├── script.md # 剧本与分镜规范
|
|
68
68
|
│ ├── character.md # 角色设定规范
|
|
69
69
|
│ └── world.md # 世界观设定规范
|
|
70
|
-
├── workspace/
|
|
71
|
-
│ └── {
|
|
72
|
-
│ ├──
|
|
73
|
-
│
|
|
74
|
-
│ └── 第1集_*/ # 剧集目录
|
|
70
|
+
├── aim-workspace/ # 工作区(项目根目录下)
|
|
71
|
+
│ └── {开发者名}/ # 每个开发者一个目录
|
|
72
|
+
│ ├── journal-1.md # 工作日志
|
|
73
|
+
│ └── index.md # 工作索引
|
|
75
74
|
├── materials/ # 小说原文
|
|
76
75
|
└── tasks/ # 任务追踪
|
|
77
76
|
```
|
|
@@ -168,18 +167,18 @@ spec/story/
|
|
|
168
167
|
- 修复问题后需要补充规范
|
|
169
168
|
- 建立新的约定
|
|
170
169
|
|
|
171
|
-
### 2. workspace/ - 工作区
|
|
170
|
+
### 2. aim-workspace/ - 工作区
|
|
172
171
|
|
|
173
|
-
|
|
172
|
+
**用途**:存放开发者的工作日志和会话记录
|
|
173
|
+
|
|
174
|
+
**位置**:直接在项目根目录下(不在 .aim-studio/ 中)
|
|
174
175
|
|
|
175
176
|
**结构**:
|
|
176
177
|
```
|
|
177
|
-
workspace/{
|
|
178
|
-
├──
|
|
179
|
-
├──
|
|
180
|
-
|
|
181
|
-
├── 第1集_*/ # 剧集目录
|
|
182
|
-
└── ...
|
|
178
|
+
aim-workspace/{开发者名}/
|
|
179
|
+
├── journal-1.md # 工作日志(第1次会话)
|
|
180
|
+
├── journal-2.md # 工作日志(第2次会话)
|
|
181
|
+
└── index.md # 工作索引
|
|
183
182
|
```
|
|
184
183
|
|
|
185
184
|
### 3. materials/ - 小说原文
|