@fifine/aim-studio 0.0.1 → 0.0.3

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 (95) hide show
  1. package/README.md +3 -3
  2. package/dist/commands/init.d.ts.map +1 -1
  3. package/dist/commands/init.js +123 -101
  4. package/dist/commands/init.js.map +1 -1
  5. package/dist/configurators/workflow.d.ts.map +1 -1
  6. package/dist/configurators/workflow.js +91 -65
  7. package/dist/configurators/workflow.js.map +1 -1
  8. package/dist/templates/aim/scripts/common/paths.py +3 -2
  9. package/dist/templates/aim/scripts/export.py +427 -0
  10. package/dist/templates/claude/commands/aim/export.md +89 -115
  11. package/dist/templates/claude/commands/aim/onboard.md +10 -8
  12. package/dist/templates/claude/commands/aim/start.md +104 -37
  13. package/dist/templates/claude/hooks/inject-subagent-context.py +6 -10
  14. package/dist/templates/claude/hooks/session-start.py +134 -24
  15. package/dist/templates/markdown/index.d.ts +5 -0
  16. package/dist/templates/markdown/index.d.ts.map +1 -1
  17. package/dist/templates/markdown/index.js +6 -0
  18. package/dist/templates/markdown/index.js.map +1 -1
  19. package/dist/templates/markdown/spec/cli/directory-structure.md.txt +71 -0
  20. package/dist/templates/markdown/spec/cli/error-handling.md.txt +91 -0
  21. package/dist/templates/markdown/spec/cli/index.md.txt +37 -0
  22. package/dist/templates/markdown/spec/cli/options-flags.md.txt +71 -0
  23. package/dist/templates/markdown/spec/cli/output-formatting.md.txt +93 -0
  24. package/dist/utils/project-detector.d.ts +1 -1
  25. package/dist/utils/project-detector.d.ts.map +1 -1
  26. package/dist/utils/project-detector.js +20 -0
  27. package/dist/utils/project-detector.js.map +1 -1
  28. package/dist/utils/template-fetcher.d.ts +1 -1
  29. package/dist/utils/template-fetcher.js +3 -3
  30. package/dist/utils/template-fetcher.js.map +1 -1
  31. package/package.json +4 -4
  32. package/dist/templates/claude/commands/trellis/before-backend-dev.md +0 -13
  33. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +0 -13
  34. package/dist/templates/claude/commands/trellis/break-loop.md +0 -125
  35. package/dist/templates/claude/commands/trellis/check-backend.md +0 -13
  36. package/dist/templates/claude/commands/trellis/check-cross-layer.md +0 -153
  37. package/dist/templates/claude/commands/trellis/check-frontend.md +0 -13
  38. package/dist/templates/claude/commands/trellis/create-command.md +0 -154
  39. package/dist/templates/claude/commands/trellis/finish-work.md +0 -129
  40. package/dist/templates/claude/commands/trellis/integrate-skill.md +0 -219
  41. package/dist/templates/claude/commands/trellis/onboard.md +0 -358
  42. package/dist/templates/claude/commands/trellis/parallel.md +0 -193
  43. package/dist/templates/claude/commands/trellis/record-session.md +0 -62
  44. package/dist/templates/claude/commands/trellis/start.md +0 -280
  45. package/dist/templates/claude/commands/trellis/update-spec.md +0 -285
  46. package/dist/templates/trellis/gitignore.txt +0 -29
  47. package/dist/templates/trellis/index.d.ts +0 -49
  48. package/dist/templates/trellis/index.d.ts.map +0 -1
  49. package/dist/templates/trellis/index.js +0 -92
  50. package/dist/templates/trellis/index.js.map +0 -1
  51. package/dist/templates/trellis/scripts/__init__.py +0 -5
  52. package/dist/templates/trellis/scripts/add_session.py +0 -392
  53. package/dist/templates/trellis/scripts/common/__init__.py +0 -80
  54. package/dist/templates/trellis/scripts/common/cli_adapter.py +0 -435
  55. package/dist/templates/trellis/scripts/common/developer.py +0 -190
  56. package/dist/templates/trellis/scripts/common/git_context.py +0 -383
  57. package/dist/templates/trellis/scripts/common/paths.py +0 -347
  58. package/dist/templates/trellis/scripts/common/phase.py +0 -253
  59. package/dist/templates/trellis/scripts/common/registry.py +0 -366
  60. package/dist/templates/trellis/scripts/common/task_queue.py +0 -255
  61. package/dist/templates/trellis/scripts/common/task_utils.py +0 -178
  62. package/dist/templates/trellis/scripts/common/worktree.py +0 -219
  63. package/dist/templates/trellis/scripts/create_bootstrap.py +0 -290
  64. package/dist/templates/trellis/scripts/get_context.py +0 -16
  65. package/dist/templates/trellis/scripts/get_developer.py +0 -26
  66. package/dist/templates/trellis/scripts/init_developer.py +0 -51
  67. package/dist/templates/trellis/scripts/multi_agent/__init__.py +0 -5
  68. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +0 -403
  69. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +0 -329
  70. package/dist/templates/trellis/scripts/multi_agent/plan.py +0 -233
  71. package/dist/templates/trellis/scripts/multi_agent/start.py +0 -461
  72. package/dist/templates/trellis/scripts/multi_agent/status.py +0 -817
  73. package/dist/templates/trellis/scripts/task.py +0 -1056
  74. package/dist/templates/trellis/scripts-shell-archive/add-session.sh +0 -384
  75. package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +0 -129
  76. package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +0 -263
  77. package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +0 -208
  78. package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +0 -150
  79. package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +0 -247
  80. package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +0 -142
  81. package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +0 -151
  82. package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +0 -128
  83. package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +0 -299
  84. package/dist/templates/trellis/scripts-shell-archive/get-context.sh +0 -7
  85. package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +0 -15
  86. package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +0 -34
  87. package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +0 -396
  88. package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +0 -241
  89. package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +0 -207
  90. package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +0 -317
  91. package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +0 -828
  92. package/dist/templates/trellis/scripts-shell-archive/task.sh +0 -1204
  93. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  94. package/dist/templates/trellis/workflow.md +0 -416
  95. package/dist/templates/trellis/worktree.yaml +0 -47
@@ -1,383 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Git and Session Context utilities.
5
-
6
- Provides:
7
- output_json - Output context in JSON format
8
- output_text - Output context in text format
9
- """
10
-
11
- from __future__ import annotations
12
-
13
- import sys
14
-
15
- # IMPORTANT: Force stdout to use UTF-8 on Windows
16
- # This fixes UnicodeEncodeError when outputting non-ASCII characters
17
- if sys.platform == "win32":
18
- import io as _io
19
- if hasattr(sys.stdout, "reconfigure"):
20
- sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
21
- elif hasattr(sys.stdout, "detach"):
22
- sys.stdout = _io.TextIOWrapper(sys.stdout.detach(), encoding="utf-8", errors="replace") # type: ignore[union-attr]
23
-
24
- import json
25
- import subprocess
26
- from pathlib import Path
27
-
28
- from .paths import (
29
- DIR_SCRIPTS,
30
- DIR_SPEC,
31
- DIR_TASKS,
32
- DIR_WORKFLOW,
33
- DIR_WORKSPACE,
34
- FILE_TASK_JSON,
35
- count_lines,
36
- get_active_journal_file,
37
- get_current_task,
38
- get_developer,
39
- get_repo_root,
40
- get_tasks_dir,
41
- )
42
-
43
- # =============================================================================
44
- # Helper Functions
45
- # =============================================================================
46
-
47
-
48
- def _run_git_command(args: list[str], cwd: Path | None = None) -> tuple[int, str, str]:
49
- """Run a git command and return (returncode, stdout, stderr).
50
-
51
- Uses UTF-8 encoding with -c i18n.logOutputEncoding=UTF-8 to ensure
52
- consistent output across all platforms (Windows, macOS, Linux).
53
- """
54
- try:
55
- # Force git to output UTF-8 for consistent cross-platform behavior
56
- git_args = ["git", "-c", "i18n.logOutputEncoding=UTF-8"] + args
57
- result = subprocess.run(
58
- git_args,
59
- cwd=cwd,
60
- capture_output=True,
61
- text=True,
62
- encoding="utf-8",
63
- errors="replace",
64
- )
65
- return result.returncode, result.stdout, result.stderr
66
- except Exception as e:
67
- return 1, "", str(e)
68
-
69
-
70
- def _read_json_file(path: Path) -> dict | None:
71
- """Read and parse a JSON file."""
72
- try:
73
- return json.loads(path.read_text(encoding="utf-8"))
74
- except (FileNotFoundError, json.JSONDecodeError, OSError):
75
- return None
76
-
77
-
78
- # =============================================================================
79
- # JSON Output
80
- # =============================================================================
81
-
82
-
83
- def get_context_json(repo_root: Path | None = None) -> dict:
84
- """Get context as a dictionary.
85
-
86
- Args:
87
- repo_root: Repository root path. Defaults to auto-detected.
88
-
89
- Returns:
90
- Context dictionary.
91
- """
92
- if repo_root is None:
93
- repo_root = get_repo_root()
94
-
95
- developer = get_developer(repo_root)
96
- tasks_dir = get_tasks_dir(repo_root)
97
- journal_file = get_active_journal_file(repo_root)
98
-
99
- journal_lines = 0
100
- journal_relative = ""
101
- if journal_file and developer:
102
- journal_lines = count_lines(journal_file)
103
- journal_relative = (
104
- f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
105
- )
106
-
107
- # Git info
108
- _, branch_out, _ = _run_git_command(["branch", "--show-current"], cwd=repo_root)
109
- branch = branch_out.strip() or "unknown"
110
-
111
- _, status_out, _ = _run_git_command(["status", "--porcelain"], cwd=repo_root)
112
- git_status_count = len([line for line in status_out.splitlines() if line.strip()])
113
- is_clean = git_status_count == 0
114
-
115
- # Recent commits
116
- _, log_out, _ = _run_git_command(["log", "--oneline", "-5"], cwd=repo_root)
117
- commits = []
118
- for line in log_out.splitlines():
119
- if line.strip():
120
- parts = line.split(" ", 1)
121
- if len(parts) >= 2:
122
- commits.append({"hash": parts[0], "message": parts[1]})
123
- elif len(parts) == 1:
124
- commits.append({"hash": parts[0], "message": ""})
125
-
126
- # Tasks
127
- tasks = []
128
- if tasks_dir.is_dir():
129
- for d in tasks_dir.iterdir():
130
- if d.is_dir() and d.name != "archive":
131
- task_json_path = d / FILE_TASK_JSON
132
- if task_json_path.is_file():
133
- data = _read_json_file(task_json_path)
134
- if data:
135
- tasks.append(
136
- {
137
- "dir": d.name,
138
- "name": data.get("name") or data.get("id") or "unknown",
139
- "status": data.get("status", "unknown"),
140
- }
141
- )
142
-
143
- return {
144
- "developer": developer or "",
145
- "git": {
146
- "branch": branch,
147
- "isClean": is_clean,
148
- "uncommittedChanges": git_status_count,
149
- "recentCommits": commits,
150
- },
151
- "tasks": {
152
- "active": tasks,
153
- "directory": f"{DIR_WORKFLOW}/{DIR_TASKS}",
154
- },
155
- "journal": {
156
- "file": journal_relative,
157
- "lines": journal_lines,
158
- "nearLimit": journal_lines > 1800,
159
- },
160
- }
161
-
162
-
163
- def output_json(repo_root: Path | None = None) -> None:
164
- """Output context in JSON format.
165
-
166
- Args:
167
- repo_root: Repository root path. Defaults to auto-detected.
168
- """
169
- context = get_context_json(repo_root)
170
- print(json.dumps(context, indent=2, ensure_ascii=False))
171
-
172
-
173
- # =============================================================================
174
- # Text Output
175
- # =============================================================================
176
-
177
-
178
- def get_context_text(repo_root: Path | None = None) -> str:
179
- """Get context as formatted text.
180
-
181
- Args:
182
- repo_root: Repository root path. Defaults to auto-detected.
183
-
184
- Returns:
185
- Formatted text output.
186
- """
187
- if repo_root is None:
188
- repo_root = get_repo_root()
189
-
190
- lines = []
191
- lines.append("========================================")
192
- lines.append("SESSION CONTEXT")
193
- lines.append("========================================")
194
- lines.append("")
195
-
196
- developer = get_developer(repo_root)
197
-
198
- # Developer section
199
- lines.append("## DEVELOPER")
200
- if not developer:
201
- lines.append(
202
- f"ERROR: Not initialized. Run: python3 ./{DIR_WORKFLOW}/{DIR_SCRIPTS}/init_developer.py <name>"
203
- )
204
- return "\n".join(lines)
205
-
206
- lines.append(f"Name: {developer}")
207
- lines.append("")
208
-
209
- # Git status
210
- lines.append("## GIT STATUS")
211
- _, branch_out, _ = _run_git_command(["branch", "--show-current"], cwd=repo_root)
212
- branch = branch_out.strip() or "unknown"
213
- lines.append(f"Branch: {branch}")
214
-
215
- _, status_out, _ = _run_git_command(["status", "--porcelain"], cwd=repo_root)
216
- status_lines = [line for line in status_out.splitlines() if line.strip()]
217
- status_count = len(status_lines)
218
-
219
- if status_count == 0:
220
- lines.append("Working directory: Clean")
221
- else:
222
- lines.append(f"Working directory: {status_count} uncommitted change(s)")
223
- lines.append("")
224
- lines.append("Changes:")
225
- _, short_out, _ = _run_git_command(["status", "--short"], cwd=repo_root)
226
- for line in short_out.splitlines()[:10]:
227
- lines.append(line)
228
- lines.append("")
229
-
230
- # Recent commits
231
- lines.append("## RECENT COMMITS")
232
- _, log_out, _ = _run_git_command(["log", "--oneline", "-5"], cwd=repo_root)
233
- if log_out.strip():
234
- for line in log_out.splitlines():
235
- lines.append(line)
236
- else:
237
- lines.append("(no commits)")
238
- lines.append("")
239
-
240
- # Current task
241
- lines.append("## CURRENT TASK")
242
- current_task = get_current_task(repo_root)
243
- if current_task:
244
- current_task_dir = repo_root / current_task
245
- task_json_path = current_task_dir / FILE_TASK_JSON
246
- lines.append(f"Path: {current_task}")
247
-
248
- if task_json_path.is_file():
249
- data = _read_json_file(task_json_path)
250
- if data:
251
- t_name = data.get("name") or data.get("id") or "unknown"
252
- t_status = data.get("status", "unknown")
253
- t_created = data.get("createdAt", "unknown")
254
- t_desc = data.get("description", "")
255
-
256
- lines.append(f"Name: {t_name}")
257
- lines.append(f"Status: {t_status}")
258
- lines.append(f"Created: {t_created}")
259
- if t_desc:
260
- lines.append(f"Description: {t_desc}")
261
-
262
- # Check for prd.md
263
- prd_file = current_task_dir / "prd.md"
264
- if prd_file.is_file():
265
- lines.append("")
266
- lines.append("[!] This task has prd.md - read it for task details")
267
- else:
268
- lines.append("(none)")
269
- lines.append("")
270
-
271
- # Active tasks
272
- lines.append("## ACTIVE TASKS")
273
- tasks_dir = get_tasks_dir(repo_root)
274
- task_count = 0
275
-
276
- if tasks_dir.is_dir():
277
- for d in sorted(tasks_dir.iterdir()):
278
- if d.is_dir() and d.name != "archive":
279
- dir_name = d.name
280
- t_json = d / FILE_TASK_JSON
281
- status = "unknown"
282
- assignee = "-"
283
-
284
- if t_json.is_file():
285
- data = _read_json_file(t_json)
286
- if data:
287
- status = data.get("status", "unknown")
288
- assignee = data.get("assignee", "-")
289
-
290
- lines.append(f"- {dir_name}/ ({status}) @{assignee}")
291
- task_count += 1
292
-
293
- if task_count == 0:
294
- lines.append("(no active tasks)")
295
- lines.append(f"Total: {task_count} active task(s)")
296
- lines.append("")
297
-
298
- # My tasks
299
- lines.append("## MY TASKS (Assigned to me)")
300
- my_task_count = 0
301
-
302
- if tasks_dir.is_dir():
303
- for d in sorted(tasks_dir.iterdir()):
304
- if d.is_dir() and d.name != "archive":
305
- t_json = d / FILE_TASK_JSON
306
- if t_json.is_file():
307
- data = _read_json_file(t_json)
308
- if data:
309
- assignee = data.get("assignee", "")
310
- status = data.get("status", "planning")
311
-
312
- if assignee == developer and status != "done":
313
- title = data.get("title") or data.get("name") or "unknown"
314
- priority = data.get("priority", "P2")
315
- lines.append(f"- [{priority}] {title} ({status})")
316
- my_task_count += 1
317
-
318
- if my_task_count == 0:
319
- lines.append("(no tasks assigned to you)")
320
- lines.append("")
321
-
322
- # Journal file
323
- lines.append("## JOURNAL FILE")
324
- journal_file = get_active_journal_file(repo_root)
325
- if journal_file:
326
- journal_lines = count_lines(journal_file)
327
- relative = f"{DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/{journal_file.name}"
328
- lines.append(f"Active file: {relative}")
329
- lines.append(f"Line count: {journal_lines} / 2000")
330
- if journal_lines > 1800:
331
- lines.append("[!] WARNING: Approaching 2000 line limit!")
332
- else:
333
- lines.append("No journal file found")
334
- lines.append("")
335
-
336
- # Paths
337
- lines.append("## PATHS")
338
- lines.append(f"Workspace: {DIR_WORKFLOW}/{DIR_WORKSPACE}/{developer}/")
339
- lines.append(f"Tasks: {DIR_WORKFLOW}/{DIR_TASKS}/")
340
- lines.append(f"Spec: {DIR_WORKFLOW}/{DIR_SPEC}/")
341
- lines.append("")
342
-
343
- lines.append("========================================")
344
-
345
- return "\n".join(lines)
346
-
347
-
348
- def output_text(repo_root: Path | None = None) -> None:
349
- """Output context in text format.
350
-
351
- Args:
352
- repo_root: Repository root path. Defaults to auto-detected.
353
- """
354
- print(get_context_text(repo_root))
355
-
356
-
357
- # =============================================================================
358
- # Main Entry
359
- # =============================================================================
360
-
361
-
362
- def main() -> None:
363
- """CLI entry point."""
364
- import argparse
365
-
366
- parser = argparse.ArgumentParser(description="Get Session Context for AI Agent")
367
- parser.add_argument(
368
- "--json",
369
- "-j",
370
- action="store_true",
371
- help="Output context in JSON format",
372
- )
373
-
374
- args = parser.parse_args()
375
-
376
- if args.json:
377
- output_json()
378
- else:
379
- output_text()
380
-
381
-
382
- if __name__ == "__main__":
383
- main()