@fifine/aim-studio 0.0.2 → 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 (91) hide show
  1. package/dist/commands/init.d.ts.map +1 -1
  2. package/dist/commands/init.js +123 -101
  3. package/dist/commands/init.js.map +1 -1
  4. package/dist/configurators/workflow.d.ts.map +1 -1
  5. package/dist/configurators/workflow.js +91 -65
  6. package/dist/configurators/workflow.js.map +1 -1
  7. package/dist/templates/aim/scripts/common/paths.py +3 -2
  8. package/dist/templates/aim/scripts/export.py +427 -0
  9. package/dist/templates/claude/commands/aim/export.md +89 -115
  10. package/dist/templates/claude/commands/aim/onboard.md +10 -8
  11. package/dist/templates/claude/commands/aim/start.md +104 -37
  12. package/dist/templates/claude/hooks/inject-subagent-context.py +6 -10
  13. package/dist/templates/claude/hooks/session-start.py +134 -24
  14. package/dist/templates/markdown/index.d.ts +5 -0
  15. package/dist/templates/markdown/index.d.ts.map +1 -1
  16. package/dist/templates/markdown/index.js +6 -0
  17. package/dist/templates/markdown/index.js.map +1 -1
  18. package/dist/templates/markdown/spec/cli/directory-structure.md.txt +71 -0
  19. package/dist/templates/markdown/spec/cli/error-handling.md.txt +91 -0
  20. package/dist/templates/markdown/spec/cli/index.md.txt +37 -0
  21. package/dist/templates/markdown/spec/cli/options-flags.md.txt +71 -0
  22. package/dist/templates/markdown/spec/cli/output-formatting.md.txt +93 -0
  23. package/dist/utils/project-detector.d.ts +1 -1
  24. package/dist/utils/project-detector.d.ts.map +1 -1
  25. package/dist/utils/project-detector.js +20 -0
  26. package/dist/utils/project-detector.js.map +1 -1
  27. package/package.json +1 -1
  28. package/dist/templates/claude/commands/trellis/before-backend-dev.md +0 -13
  29. package/dist/templates/claude/commands/trellis/before-frontend-dev.md +0 -13
  30. package/dist/templates/claude/commands/trellis/break-loop.md +0 -125
  31. package/dist/templates/claude/commands/trellis/check-backend.md +0 -13
  32. package/dist/templates/claude/commands/trellis/check-cross-layer.md +0 -153
  33. package/dist/templates/claude/commands/trellis/check-frontend.md +0 -13
  34. package/dist/templates/claude/commands/trellis/create-command.md +0 -154
  35. package/dist/templates/claude/commands/trellis/finish-work.md +0 -129
  36. package/dist/templates/claude/commands/trellis/integrate-skill.md +0 -219
  37. package/dist/templates/claude/commands/trellis/onboard.md +0 -358
  38. package/dist/templates/claude/commands/trellis/parallel.md +0 -193
  39. package/dist/templates/claude/commands/trellis/record-session.md +0 -62
  40. package/dist/templates/claude/commands/trellis/start.md +0 -280
  41. package/dist/templates/claude/commands/trellis/update-spec.md +0 -285
  42. package/dist/templates/trellis/gitignore.txt +0 -29
  43. package/dist/templates/trellis/index.d.ts +0 -49
  44. package/dist/templates/trellis/index.d.ts.map +0 -1
  45. package/dist/templates/trellis/index.js +0 -92
  46. package/dist/templates/trellis/index.js.map +0 -1
  47. package/dist/templates/trellis/scripts/__init__.py +0 -5
  48. package/dist/templates/trellis/scripts/add_session.py +0 -392
  49. package/dist/templates/trellis/scripts/common/__init__.py +0 -80
  50. package/dist/templates/trellis/scripts/common/cli_adapter.py +0 -435
  51. package/dist/templates/trellis/scripts/common/developer.py +0 -190
  52. package/dist/templates/trellis/scripts/common/git_context.py +0 -383
  53. package/dist/templates/trellis/scripts/common/paths.py +0 -347
  54. package/dist/templates/trellis/scripts/common/phase.py +0 -253
  55. package/dist/templates/trellis/scripts/common/registry.py +0 -366
  56. package/dist/templates/trellis/scripts/common/task_queue.py +0 -255
  57. package/dist/templates/trellis/scripts/common/task_utils.py +0 -178
  58. package/dist/templates/trellis/scripts/common/worktree.py +0 -219
  59. package/dist/templates/trellis/scripts/create_bootstrap.py +0 -290
  60. package/dist/templates/trellis/scripts/get_context.py +0 -16
  61. package/dist/templates/trellis/scripts/get_developer.py +0 -26
  62. package/dist/templates/trellis/scripts/init_developer.py +0 -51
  63. package/dist/templates/trellis/scripts/multi_agent/__init__.py +0 -5
  64. package/dist/templates/trellis/scripts/multi_agent/cleanup.py +0 -403
  65. package/dist/templates/trellis/scripts/multi_agent/create_pr.py +0 -329
  66. package/dist/templates/trellis/scripts/multi_agent/plan.py +0 -233
  67. package/dist/templates/trellis/scripts/multi_agent/start.py +0 -461
  68. package/dist/templates/trellis/scripts/multi_agent/status.py +0 -817
  69. package/dist/templates/trellis/scripts/task.py +0 -1056
  70. package/dist/templates/trellis/scripts-shell-archive/add-session.sh +0 -384
  71. package/dist/templates/trellis/scripts-shell-archive/common/developer.sh +0 -129
  72. package/dist/templates/trellis/scripts-shell-archive/common/git-context.sh +0 -263
  73. package/dist/templates/trellis/scripts-shell-archive/common/paths.sh +0 -208
  74. package/dist/templates/trellis/scripts-shell-archive/common/phase.sh +0 -150
  75. package/dist/templates/trellis/scripts-shell-archive/common/registry.sh +0 -247
  76. package/dist/templates/trellis/scripts-shell-archive/common/task-queue.sh +0 -142
  77. package/dist/templates/trellis/scripts-shell-archive/common/task-utils.sh +0 -151
  78. package/dist/templates/trellis/scripts-shell-archive/common/worktree.sh +0 -128
  79. package/dist/templates/trellis/scripts-shell-archive/create-bootstrap.sh +0 -299
  80. package/dist/templates/trellis/scripts-shell-archive/get-context.sh +0 -7
  81. package/dist/templates/trellis/scripts-shell-archive/get-developer.sh +0 -15
  82. package/dist/templates/trellis/scripts-shell-archive/init-developer.sh +0 -34
  83. package/dist/templates/trellis/scripts-shell-archive/multi-agent/cleanup.sh +0 -396
  84. package/dist/templates/trellis/scripts-shell-archive/multi-agent/create-pr.sh +0 -241
  85. package/dist/templates/trellis/scripts-shell-archive/multi-agent/plan.sh +0 -207
  86. package/dist/templates/trellis/scripts-shell-archive/multi-agent/start.sh +0 -317
  87. package/dist/templates/trellis/scripts-shell-archive/multi-agent/status.sh +0 -828
  88. package/dist/templates/trellis/scripts-shell-archive/task.sh +0 -1204
  89. package/dist/templates/trellis/tasks/.gitkeep +0 -0
  90. package/dist/templates/trellis/workflow.md +0 -416
  91. 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()