@fifine/aim-studio 0.0.8 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/cli/index.js +7 -7
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/commands/update.d.ts.map +1 -1
  4. package/dist/commands/update.js +3 -26
  5. package/dist/commands/update.js.map +1 -1
  6. package/dist/configurators/index.d.ts.map +1 -1
  7. package/dist/configurators/index.js +6 -2
  8. package/dist/configurators/index.js.map +1 -1
  9. package/dist/constants/paths.d.ts +1 -1
  10. package/dist/constants/paths.js +1 -1
  11. package/dist/index.d.ts +1 -1
  12. package/dist/index.js +1 -1
  13. package/dist/migrations/index.d.ts +1 -1
  14. package/dist/migrations/index.js +1 -1
  15. package/dist/templates/aim/scripts/common/cli_adapter.py +55 -223
  16. package/dist/templates/aim/scripts/create_bootstrap.py +3 -3
  17. package/dist/templates/aim/scripts/multi_agent/__init__.py +5 -0
  18. package/dist/templates/aim/scripts/multi_agent/cleanup.py +403 -0
  19. package/dist/templates/aim/scripts/multi_agent/create_pr.py +329 -0
  20. package/dist/templates/aim/scripts/multi_agent/plan.py +233 -0
  21. package/dist/templates/aim/scripts/multi_agent/start.py +461 -0
  22. package/dist/templates/aim/scripts/multi_agent/status.py +817 -0
  23. package/dist/templates/aim/worktree.yaml +1 -1
  24. package/dist/templates/claude/agents/director.md +66 -0
  25. package/dist/templates/claude/agents/prompt-engineer.md +96 -0
  26. package/dist/templates/claude/agents/storyboard-artist.md +62 -0
  27. package/dist/templates/claude/agents/writer.md +66 -0
  28. package/dist/templates/claude/commands/aim/onboard.md +3 -3
  29. package/dist/templates/claude/hooks/session-start.py +9 -12
  30. package/dist/templates/claude/index.d.ts +14 -1
  31. package/dist/templates/claude/index.d.ts.map +1 -1
  32. package/dist/templates/claude/index.js +21 -1
  33. package/dist/templates/claude/index.js.map +1 -1
  34. package/dist/templates/claude/skills/character-manager/SKILL.md +105 -0
  35. package/dist/templates/claude/skills/episode-manager/SKILL.md +89 -0
  36. package/dist/templates/claude/skills/image-prompt-optimizer/SKILL.md +103 -0
  37. package/dist/templates/claude/skills/script-parser/SKILL.md +115 -0
  38. package/dist/templates/claude/skills/storyboard-designer/SKILL.md +99 -0
  39. package/dist/templates/claude/skills/style-keeper/SKILL.md +77 -0
  40. package/dist/templates/claude/skills/video-prompt-optimizer/SKILL.md +109 -0
  41. package/dist/templates/markdown/gitignore.txt +1 -1
  42. package/dist/templates/markdown/index.d.ts +1 -23
  43. package/dist/templates/markdown/index.d.ts.map +1 -1
  44. package/dist/templates/markdown/index.js +2 -30
  45. package/dist/templates/markdown/index.js.map +1 -1
  46. package/dist/types/migration.d.ts +1 -1
  47. package/dist/types/migration.js +1 -1
  48. package/dist/utils/project-detector.d.ts.map +1 -1
  49. package/dist/utils/project-detector.js +0 -1
  50. package/dist/utils/project-detector.js.map +1 -1
  51. package/dist/utils/template-hash.d.ts +1 -1
  52. package/dist/utils/template-hash.js +1 -1
  53. package/package.json +11 -8
@@ -1,19 +1,12 @@
1
1
  """
2
- CLI Adapter for Multi-Platform Support.
2
+ CLI Adapter for Claude Code.
3
3
 
4
- Abstracts differences between Claude Code, OpenCode, Cursor, iFlow, and Codex interfaces.
5
-
6
- Supported platforms:
7
- - claude: Claude Code (default)
8
- - opencode: OpenCode
9
- - cursor: Cursor IDE
10
- - iflow: iFlow CLI
11
- - codex: Codex CLI (skills-based)
4
+ Simplified adapter for Claude Code only. All other platforms have been removed.
12
5
 
13
6
  Usage:
14
7
  from common.cli_adapter import CLIAdapter
15
8
 
16
- adapter = CLIAdapter("opencode")
9
+ adapter = CLIAdapter()
17
10
  cmd = adapter.build_run_command(
18
11
  agent="dispatch",
19
12
  session_id="abc123",
@@ -23,44 +16,33 @@ Usage:
23
16
 
24
17
  from __future__ import annotations
25
18
 
26
- from dataclasses import dataclass
19
+ from dataclasses import dataclass, field
27
20
  from pathlib import Path
28
- from typing import ClassVar, Literal
21
+ from typing import Literal
29
22
 
30
- Platform = Literal["claude", "opencode", "cursor", "iflow", "codex"]
23
+ Platform = Literal["claude"]
31
24
 
32
25
 
33
26
  @dataclass
34
27
  class CLIAdapter:
35
- """Adapter for different AI coding CLI tools."""
28
+ """Adapter for Claude Code CLI tool."""
36
29
 
37
- platform: Platform
30
+ platform: Platform = "claude"
38
31
 
39
32
  # =========================================================================
40
33
  # Agent Name Mapping
41
34
  # =========================================================================
42
35
 
43
- # OpenCode has built-in agents that cannot be overridden
44
- # See: https://github.com/sst/opencode/issues/4271
45
- # Note: Class-level constant, not a dataclass field
46
- _AGENT_NAME_MAP: ClassVar[dict[Platform, dict[str, str]]] = {
47
- "claude": {}, # No mapping needed
48
- "opencode": {
49
- "plan": "trellis-plan", # 'plan' is built-in in OpenCode
50
- },
51
- }
52
-
53
36
  def get_agent_name(self, agent: str) -> str:
54
- """Get platform-specific agent name.
37
+ """Get agent name (no mapping needed for Claude Code).
55
38
 
56
39
  Args:
57
40
  agent: Original agent name (e.g., 'plan', 'dispatch')
58
41
 
59
42
  Returns:
60
- Platform-specific agent name (e.g., 'trellis-plan' for OpenCode)
43
+ Agent name (unchanged for Claude Code)
61
44
  """
62
- mapping = self._AGENT_NAME_MAP.get(self.platform, {})
63
- return mapping.get(agent, agent)
45
+ return agent
64
46
 
65
47
  # =========================================================================
66
48
  # Agent Path
@@ -68,30 +50,21 @@ class CLIAdapter:
68
50
 
69
51
  @property
70
52
  def config_dir_name(self) -> str:
71
- """Get platform-specific config directory name.
53
+ """Get config directory name.
72
54
 
73
55
  Returns:
74
- Directory name ('.claude', '.opencode', '.cursor', '.iflow', or '.agents')
56
+ Directory name '.claude'
75
57
  """
76
- if self.platform == "opencode":
77
- return ".opencode"
78
- elif self.platform == "cursor":
79
- return ".cursor"
80
- elif self.platform == "iflow":
81
- return ".iflow"
82
- elif self.platform == "codex":
83
- return ".agents"
84
- else:
85
- return ".claude"
58
+ return ".claude"
86
59
 
87
60
  def get_config_dir(self, project_root: Path) -> Path:
88
- """Get platform-specific config directory.
61
+ """Get config directory path.
89
62
 
90
63
  Args:
91
64
  project_root: Project root directory
92
65
 
93
66
  Returns:
94
- Path to config directory (.claude, .opencode, .cursor, .iflow, or .agents)
67
+ Path to .claude config directory
95
68
  """
96
69
  return project_root / self.config_dir_name
97
70
 
@@ -99,60 +72,39 @@ class CLIAdapter:
99
72
  """Get path to agent definition file.
100
73
 
101
74
  Args:
102
- agent: Agent name (original, before mapping)
75
+ agent: Agent name
103
76
  project_root: Project root directory
104
77
 
105
78
  Returns:
106
79
  Path to agent .md file
107
80
  """
108
- mapped_name = self.get_agent_name(agent)
109
- return self.get_config_dir(project_root) / "agents" / f"{mapped_name}.md"
81
+ return self.get_config_dir(project_root) / "agents" / f"{agent}.md"
110
82
 
111
83
  def get_commands_path(self, project_root: Path, *parts: str) -> Path:
112
84
  """Get path to commands directory or specific command file.
113
85
 
114
86
  Args:
115
87
  project_root: Project root directory
116
- *parts: Additional path parts (e.g., 'trellis', 'finish-work.md')
88
+ *parts: Additional path parts (e.g., 'aim', 'finish-work.md')
117
89
 
118
90
  Returns:
119
91
  Path to commands directory or file
120
-
121
- Note:
122
- Cursor uses prefix naming: .cursor/commands/trellis-<name>.md
123
- Claude/OpenCode use subdirectory: .claude/commands/trellis/<name>.md
124
92
  """
125
93
  if not parts:
126
94
  return self.get_config_dir(project_root) / "commands"
127
95
 
128
- # Cursor uses prefix naming instead of subdirectory
129
- if self.platform == "cursor" and len(parts) >= 2 and parts[0] == "aim":
130
- # Convert aim/<name>.md to aim-<name>.md
131
- filename = parts[-1]
132
- return self.get_config_dir(project_root) / "commands" / f"aim-{filename}"
133
-
134
96
  return self.get_config_dir(project_root) / "commands" / Path(*parts)
135
97
 
136
98
  def get_aim_command_path(self, name: str) -> str:
137
- """Get relative path to a aim command file.
99
+ """Get relative path to an aim command file.
138
100
 
139
101
  Args:
140
102
  name: Command name without extension (e.g., 'finish-work', 'check-backend')
141
103
 
142
104
  Returns:
143
105
  Relative path string for use in JSONL entries
144
-
145
- Note:
146
- Cursor: .cursor/commands/aim-{name}.md
147
- Codex: .agents/skills/{name}/SKILL.md
148
- Others: .{platform}/commands/aim/{name}.md
149
106
  """
150
- if self.platform == "cursor":
151
- return f".cursor/commands/aim-{name}.md"
152
- elif self.platform == "codex":
153
- return f".agents/skills/{name}/SKILL.md"
154
- else:
155
- return f"{self.config_dir_name}/commands/aim/{name}.md"
107
+ return f"{self.config_dir_name}/commands/aim/{name}.md"
156
108
 
157
109
  # =========================================================================
158
110
  # Environment Variables
@@ -164,12 +116,7 @@ class CLIAdapter:
164
116
  Returns:
165
117
  Dict of environment variables to set
166
118
  """
167
- if self.platform == "opencode":
168
- return {"OPENCODE_NON_INTERACTIVE": "1"}
169
- elif self.platform == "codex":
170
- return {"CODEX_NON_INTERACTIVE": "1"}
171
- else:
172
- return {"CLAUDE_NON_INTERACTIVE": "1"}
119
+ return {"CLAUDE_NON_INTERACTIVE": "1"}
173
120
 
174
121
  # =========================================================================
175
122
  # CLI Command Building
@@ -187,9 +134,9 @@ class CLIAdapter:
187
134
  """Build CLI command for running an agent.
188
135
 
189
136
  Args:
190
- agent: Agent name (will be mapped if needed)
137
+ agent: Agent name
191
138
  prompt: Prompt to send to the agent
192
- session_id: Optional session ID (Claude Code only for creation)
139
+ session_id: Optional session ID
193
140
  skip_permissions: Whether to skip permission prompts
194
141
  verbose: Whether to enable verbose output
195
142
  json_output: Whether to use JSON output format
@@ -197,48 +144,22 @@ class CLIAdapter:
197
144
  Returns:
198
145
  List of command arguments
199
146
  """
200
- mapped_agent = self.get_agent_name(agent)
201
-
202
- if self.platform == "opencode":
203
- cmd = ["opencode", "run"]
204
- cmd.extend(["--agent", mapped_agent])
205
-
206
- # Note: OpenCode 'run' mode is non-interactive by default
207
- # No equivalent to Claude Code's --dangerously-skip-permissions
208
- # See: https://github.com/anomalyco/opencode/issues/9070
209
-
210
- if json_output:
211
- cmd.extend(["--format", "json"])
212
-
213
- if verbose:
214
- cmd.extend(["--log-level", "DEBUG", "--print-logs"])
147
+ cmd = ["claude", "-p"]
148
+ cmd.extend(["--agent", agent])
215
149
 
216
- # Note: OpenCode doesn't support --session-id on creation
217
- # Session ID must be extracted from logs after startup
150
+ if session_id:
151
+ cmd.extend(["--session-id", session_id])
218
152
 
219
- cmd.append(prompt)
153
+ if skip_permissions:
154
+ cmd.append("--dangerously-skip-permissions")
220
155
 
221
- elif self.platform == "codex":
222
- cmd = ["codex", "exec"]
223
- cmd.append(prompt)
156
+ if json_output:
157
+ cmd.extend(["--output-format", "stream-json"])
224
158
 
225
- else: # claude
226
- cmd = ["claude", "-p"]
227
- cmd.extend(["--agent", mapped_agent])
159
+ if verbose:
160
+ cmd.append("--verbose")
228
161
 
229
- if session_id:
230
- cmd.extend(["--session-id", session_id])
231
-
232
- if skip_permissions:
233
- cmd.append("--dangerously-skip-permissions")
234
-
235
- if json_output:
236
- cmd.extend(["--output-format", "stream-json"])
237
-
238
- if verbose:
239
- cmd.append("--verbose")
240
-
241
- cmd.append(prompt)
162
+ cmd.append(prompt)
242
163
 
243
164
  return cmd
244
165
 
@@ -251,12 +172,7 @@ class CLIAdapter:
251
172
  Returns:
252
173
  List of command arguments
253
174
  """
254
- if self.platform == "opencode":
255
- return ["opencode", "run", "--session", session_id]
256
- elif self.platform == "codex":
257
- return ["codex", "resume", session_id]
258
- else:
259
- return ["claude", "--resume", session_id]
175
+ return ["claude", "--resume", session_id]
260
176
 
261
177
  def get_resume_command_str(self, session_id: str, cwd: str | None = None) -> str:
262
178
  """Get human-readable resume command string.
@@ -279,42 +195,20 @@ class CLIAdapter:
279
195
  # Platform Detection Helpers
280
196
  # =========================================================================
281
197
 
282
- @property
283
- def is_opencode(self) -> bool:
284
- """Check if platform is OpenCode."""
285
- return self.platform == "opencode"
286
-
287
198
  @property
288
199
  def is_claude(self) -> bool:
289
- """Check if platform is Claude Code."""
290
- return self.platform == "claude"
291
-
292
- @property
293
- def is_cursor(self) -> bool:
294
- """Check if platform is Cursor."""
295
- return self.platform == "cursor"
200
+ """Check if platform is Claude Code (always True)."""
201
+ return True
296
202
 
297
203
  @property
298
204
  def cli_name(self) -> str:
299
- """Get CLI executable name.
300
-
301
- Note: Cursor doesn't have a CLI tool, returns None-like value.
302
- """
303
- if self.is_opencode:
304
- return "opencode"
305
- elif self.is_cursor:
306
- return "cursor" # Note: Cursor is IDE-only, no CLI
307
- else:
308
- return "claude"
205
+ """Get CLI executable name."""
206
+ return "claude"
309
207
 
310
208
  @property
311
209
  def supports_cli_agents(self) -> bool:
312
- """Check if platform supports running agents via CLI.
313
-
314
- Claude Code and OpenCode support CLI agent execution.
315
- Cursor is IDE-only and doesn't support CLI agents.
316
- """
317
- return self.platform in ("claude", "opencode")
210
+ """Check if platform supports running agents via CLI (always True)."""
211
+ return True
318
212
 
319
213
  # =========================================================================
320
214
  # Session ID Handling
@@ -322,31 +216,8 @@ class CLIAdapter:
322
216
 
323
217
  @property
324
218
  def supports_session_id_on_create(self) -> bool:
325
- """Check if platform supports specifying session ID on creation.
326
-
327
- Claude Code: Yes (--session-id)
328
- OpenCode: No (auto-generated, extract from logs)
329
- """
330
- return self.platform == "claude"
331
-
332
- def extract_session_id_from_log(self, log_content: str) -> str | None:
333
- """Extract session ID from log output (OpenCode only).
334
-
335
- OpenCode generates session IDs in format: ses_xxx
336
-
337
- Args:
338
- log_content: Log file content
339
-
340
- Returns:
341
- Session ID if found, None otherwise
342
- """
343
- import re
344
-
345
- # OpenCode session ID pattern
346
- match = re.search(r"ses_[a-zA-Z0-9]+", log_content)
347
- if match:
348
- return match.group(0)
349
- return None
219
+ """Check if platform supports specifying session ID on creation (always True)."""
220
+ return True
350
221
 
351
222
 
352
223
  # =============================================================================
@@ -355,81 +226,42 @@ class CLIAdapter:
355
226
 
356
227
 
357
228
  def get_cli_adapter(platform: str = "claude") -> CLIAdapter:
358
- """Get CLI adapter for the specified platform.
229
+ """Get CLI adapter for Claude Code.
359
230
 
360
231
  Args:
361
- platform: Platform name ('claude', 'opencode', 'cursor', 'iflow', or 'codex')
232
+ platform: Platform name (only 'claude' is supported)
362
233
 
363
234
  Returns:
364
235
  CLIAdapter instance
365
236
 
366
237
  Raises:
367
- ValueError: If platform is not supported
238
+ ValueError: If platform is not 'claude'
368
239
  """
369
- if platform not in ("claude", "opencode", "cursor", "iflow", "codex"):
370
- raise ValueError(f"Unsupported platform: {platform} (must be 'claude', 'opencode', 'cursor', 'iflow', or 'codex')")
240
+ if platform != "claude":
241
+ raise ValueError(f"Unsupported platform: {platform} (only 'claude' is supported)")
371
242
 
372
- return CLIAdapter(platform=platform) # type: ignore
243
+ return CLIAdapter()
373
244
 
374
245
 
375
246
  def detect_platform(project_root: Path) -> Platform:
376
- """Auto-detect platform based on existing config directories.
377
-
378
- Detection order:
379
- 1. TRELLIS_PLATFORM environment variable (if set)
380
- 2. .opencode directory exists → opencode
381
- 3. .iflow directory exists → iflow
382
- 4. .cursor directory exists (without .claude) → cursor
383
- 5. .agents/skills exists and no other platform dirs → codex
384
- 6. Default → claude
247
+ """Detect platform (always returns 'claude').
385
248
 
386
249
  Args:
387
250
  project_root: Project root directory
388
251
 
389
252
  Returns:
390
- Detected platform ('claude', 'opencode', 'cursor', 'iflow', or 'codex')
253
+ 'claude'
391
254
  """
392
- import os
393
-
394
- # Check environment variable first
395
- env_platform = os.environ.get("TRELLIS_PLATFORM", "").lower()
396
- if env_platform in ("claude", "opencode", "cursor", "iflow", "codex"):
397
- return env_platform # type: ignore
398
-
399
- # Check for .opencode directory (OpenCode-specific)
400
- # Note: .claude might exist in both platforms during migration
401
- if (project_root / ".opencode").is_dir():
402
- return "opencode"
403
-
404
- # Check for .iflow directory (iFlow-specific)
405
- # Note: .claude might exist in both platforms during migration
406
- if (project_root / ".iflow").is_dir():
407
- return "iflow"
408
-
409
- # Check for .cursor directory (Cursor-specific)
410
- # Only detect as cursor if .claude doesn't exist (to avoid confusion)
411
- if (project_root / ".cursor").is_dir() and not (project_root / ".claude").is_dir():
412
- return "cursor"
413
-
414
- # Check for Codex skills directory only when no other platform config exists
415
- other_platform_dirs = (".claude", ".cursor", ".iflow", ".opencode")
416
- has_other_platform_config = any(
417
- (project_root / directory).is_dir() for directory in other_platform_dirs
418
- )
419
- if (project_root / ".agents" / "skills").is_dir() and not has_other_platform_config:
420
- return "codex"
421
-
422
255
  return "claude"
423
256
 
424
257
 
425
258
  def get_cli_adapter_auto(project_root: Path) -> CLIAdapter:
426
- """Get CLI adapter with auto-detected platform.
259
+ """Get CLI adapter (always returns Claude Code adapter).
427
260
 
428
261
  Args:
429
262
  project_root: Project root directory
430
263
 
431
264
  Returns:
432
- CLIAdapter instance for detected platform
265
+ CLIAdapter instance for Claude Code
433
266
  """
434
- platform = detect_platform(project_root)
435
- return CLIAdapter(platform=platform)
267
+ return CLIAdapter()
@@ -162,8 +162,8 @@ python3 ./.aim-studio/scripts/task.py archive 00-bootstrap-guidelines
162
162
  After completing this task:
163
163
 
164
164
  1. AI will write code that matches your project style
165
- 2. Relevant `/trellis:before-*-dev` commands will inject real context
166
- 3. `/trellis:check-*` commands will validate against your actual standards
165
+ 2. Relevant `/aim:before-*-dev` commands will inject real context
166
+ 3. `/aim:check-*` commands will validate against your actual standards
167
167
  4. Future developers (human or AI) will onboard faster
168
168
  """
169
169
 
@@ -229,7 +229,7 @@ def write_task_json(task_dir: Path, developer: str, project_type: str) -> None:
229
229
  "commit": None,
230
230
  "subtasks": subtasks,
231
231
  "relatedFiles": related_files,
232
- "notes": f"First-time setup task created by trellis init ({project_type} project)",
232
+ "notes": f"First-time setup task created by aim init ({project_type} project)",
233
233
  }
234
234
 
235
235
  task_json = task_dir / "task.json"
@@ -0,0 +1,5 @@
1
+ """
2
+ Multi-Agent Pipeline Scripts.
3
+
4
+ This module provides orchestration for multi-agent workflows.
5
+ """