@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,92 +0,0 @@
1
- /**
2
- * Trellis workflow templates
3
- *
4
- * These are GENERIC templates for user projects.
5
- * Do NOT use Trellis project's own .trellis/ directory (which may be customized).
6
- *
7
- * Directory structure:
8
- * trellis/
9
- * ├── scripts/
10
- * │ ├── __init__.py
11
- * │ ├── common/ # Shared utilities (Python)
12
- * │ ├── multi_agent/ # Multi-agent pipeline scripts (Python)
13
- * │ └── *.py # Main scripts (Python)
14
- * ├── scripts-shell-archive/ # Archived shell scripts (for reference)
15
- * ├── workflow.md # Workflow guide
16
- * ├── worktree.yaml # Worktree configuration
17
- * └── gitignore.txt # .gitignore content
18
- */
19
- import { readFileSync } from "node:fs";
20
- import { dirname, join } from "node:path";
21
- import { fileURLToPath } from "node:url";
22
- const __filename = fileURLToPath(import.meta.url);
23
- const __dirname = dirname(__filename);
24
- function readTemplate(relativePath) {
25
- return readFileSync(join(__dirname, relativePath), "utf-8");
26
- }
27
- // Python scripts - package init
28
- export const scriptsInit = readTemplate("scripts/__init__.py");
29
- // Python scripts - common
30
- export const commonInit = readTemplate("scripts/common/__init__.py");
31
- export const commonPaths = readTemplate("scripts/common/paths.py");
32
- export const commonDeveloper = readTemplate("scripts/common/developer.py");
33
- export const commonGitContext = readTemplate("scripts/common/git_context.py");
34
- export const commonWorktree = readTemplate("scripts/common/worktree.py");
35
- export const commonTaskQueue = readTemplate("scripts/common/task_queue.py");
36
- export const commonTaskUtils = readTemplate("scripts/common/task_utils.py");
37
- export const commonPhase = readTemplate("scripts/common/phase.py");
38
- export const commonRegistry = readTemplate("scripts/common/registry.py");
39
- export const commonCliAdapter = readTemplate("scripts/common/cli_adapter.py");
40
- // Python scripts - multi_agent
41
- export const multiAgentInit = readTemplate("scripts/multi_agent/__init__.py");
42
- export const multiAgentStart = readTemplate("scripts/multi_agent/start.py");
43
- export const multiAgentCleanup = readTemplate("scripts/multi_agent/cleanup.py");
44
- export const multiAgentStatus = readTemplate("scripts/multi_agent/status.py");
45
- export const multiAgentCreatePr = readTemplate("scripts/multi_agent/create_pr.py");
46
- export const multiAgentPlan = readTemplate("scripts/multi_agent/plan.py");
47
- // Python scripts - main
48
- export const getDeveloperScript = readTemplate("scripts/get_developer.py");
49
- export const initDeveloperScript = readTemplate("scripts/init_developer.py");
50
- export const taskScript = readTemplate("scripts/task.py");
51
- export const getContextScript = readTemplate("scripts/get_context.py");
52
- export const addSessionScript = readTemplate("scripts/add_session.py");
53
- export const createBootstrapScript = readTemplate("scripts/create_bootstrap.py");
54
- // Configuration files
55
- export const workflowMdTemplate = readTemplate("workflow.md");
56
- export const worktreeYamlTemplate = readTemplate("worktree.yaml");
57
- export const gitignoreTemplate = readTemplate("gitignore.txt");
58
- /**
59
- * Get all script templates as a map of relative path to content
60
- */
61
- export function getAllScripts() {
62
- const scripts = new Map();
63
- // Package init
64
- scripts.set("__init__.py", scriptsInit);
65
- // Common
66
- scripts.set("common/__init__.py", commonInit);
67
- scripts.set("common/paths.py", commonPaths);
68
- scripts.set("common/developer.py", commonDeveloper);
69
- scripts.set("common/git_context.py", commonGitContext);
70
- scripts.set("common/worktree.py", commonWorktree);
71
- scripts.set("common/task_queue.py", commonTaskQueue);
72
- scripts.set("common/task_utils.py", commonTaskUtils);
73
- scripts.set("common/phase.py", commonPhase);
74
- scripts.set("common/registry.py", commonRegistry);
75
- scripts.set("common/cli_adapter.py", commonCliAdapter);
76
- // Multi-agent
77
- scripts.set("multi_agent/__init__.py", multiAgentInit);
78
- scripts.set("multi_agent/start.py", multiAgentStart);
79
- scripts.set("multi_agent/cleanup.py", multiAgentCleanup);
80
- scripts.set("multi_agent/status.py", multiAgentStatus);
81
- scripts.set("multi_agent/create_pr.py", multiAgentCreatePr);
82
- scripts.set("multi_agent/plan.py", multiAgentPlan);
83
- // Main
84
- scripts.set("get_developer.py", getDeveloperScript);
85
- scripts.set("init_developer.py", initDeveloperScript);
86
- scripts.set("task.py", taskScript);
87
- scripts.set("get_context.py", getContextScript);
88
- scripts.set("add_session.py", addSessionScript);
89
- scripts.set("create_bootstrap.py", createBootstrapScript);
90
- return scripts;
91
- }
92
- //# sourceMappingURL=index.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/templates/trellis/index.ts"],"names":[],"mappings":"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"}
@@ -1,5 +0,0 @@
1
- """
2
- Trellis Python Scripts
3
-
4
- This module provides Python implementations of Trellis workflow scripts.
5
- """
@@ -1,392 +0,0 @@
1
- #!/usr/bin/env python3
2
- # -*- coding: utf-8 -*-
3
- """
4
- Add a new session to journal file and update index.md.
5
-
6
- Usage:
7
- python3 add_session.py --title "Title" --commit "hash" --summary "Summary"
8
- echo "content" | python3 add_session.py --title "Title" --commit "hash"
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 argparse
25
- import re
26
- import sys
27
- from datetime import datetime
28
- from pathlib import Path
29
-
30
- from common.paths import (
31
- FILE_JOURNAL_PREFIX,
32
- get_repo_root,
33
- get_developer,
34
- get_workspace_dir,
35
- )
36
- from common.developer import ensure_developer
37
-
38
-
39
- MAX_LINES = 2000
40
-
41
-
42
- # =============================================================================
43
- # Helper Functions
44
- # =============================================================================
45
-
46
- def get_latest_journal_info(dev_dir: Path) -> tuple[Path | None, int, int]:
47
- """Get latest journal file info.
48
-
49
- Returns:
50
- Tuple of (file_path, file_number, line_count).
51
- """
52
- latest_file: Path | None = None
53
- latest_num = -1
54
-
55
- for f in dev_dir.glob(f"{FILE_JOURNAL_PREFIX}*.md"):
56
- if not f.is_file():
57
- continue
58
-
59
- match = re.search(r"(\d+)$", f.stem)
60
- if match:
61
- num = int(match.group(1))
62
- if num > latest_num:
63
- latest_num = num
64
- latest_file = f
65
-
66
- if latest_file:
67
- lines = len(latest_file.read_text(encoding="utf-8").splitlines())
68
- return latest_file, latest_num, lines
69
-
70
- return None, 0, 0
71
-
72
-
73
- def get_current_session(index_file: Path) -> int:
74
- """Get current session number from index.md."""
75
- if not index_file.is_file():
76
- return 0
77
-
78
- content = index_file.read_text(encoding="utf-8")
79
- for line in content.splitlines():
80
- if "Total Sessions" in line:
81
- match = re.search(r":\s*(\d+)", line)
82
- if match:
83
- return int(match.group(1))
84
- return 0
85
-
86
-
87
- def _extract_journal_num(filename: str) -> int:
88
- """Extract journal number from filename for sorting."""
89
- match = re.search(r"(\d+)", filename)
90
- return int(match.group(1)) if match else 0
91
-
92
-
93
- def count_journal_files(dev_dir: Path, active_num: int) -> str:
94
- """Count journal files and return table rows."""
95
- active_file = f"{FILE_JOURNAL_PREFIX}{active_num}.md"
96
- result_lines = []
97
-
98
- files = sorted(
99
- [f for f in dev_dir.glob(f"{FILE_JOURNAL_PREFIX}*.md") if f.is_file()],
100
- key=lambda f: _extract_journal_num(f.stem),
101
- reverse=True
102
- )
103
-
104
- for f in files:
105
- filename = f.name
106
- lines = len(f.read_text(encoding="utf-8").splitlines())
107
- status = "Active" if filename == active_file else "Archived"
108
- result_lines.append(f"| `{filename}` | ~{lines} | {status} |")
109
-
110
- return "\n".join(result_lines)
111
-
112
-
113
- def create_new_journal_file(dev_dir: Path, num: int, developer: str, today: str) -> Path:
114
- """Create a new journal file."""
115
- prev_num = num - 1
116
- new_file = dev_dir / f"{FILE_JOURNAL_PREFIX}{num}.md"
117
-
118
- content = f"""# Journal - {developer} (Part {num})
119
-
120
- > Continuation from `{FILE_JOURNAL_PREFIX}{prev_num}.md` (archived at ~{MAX_LINES} lines)
121
- > Started: {today}
122
-
123
- ---
124
-
125
- """
126
- new_file.write_text(content, encoding="utf-8")
127
- return new_file
128
-
129
-
130
- def generate_session_content(
131
- session_num: int,
132
- title: str,
133
- commit: str,
134
- summary: str,
135
- extra_content: str,
136
- today: str
137
- ) -> str:
138
- """Generate session content."""
139
- if commit and commit != "-":
140
- commit_table = """| Hash | Message |
141
- |------|---------|"""
142
- for c in commit.split(","):
143
- c = c.strip()
144
- commit_table += f"\n| `{c}` | (see git log) |"
145
- else:
146
- commit_table = "(No commits - planning session)"
147
-
148
- return f"""
149
-
150
- ## Session {session_num}: {title}
151
-
152
- **Date**: {today}
153
- **Task**: {title}
154
-
155
- ### Summary
156
-
157
- {summary}
158
-
159
- ### Main Changes
160
-
161
- {extra_content}
162
-
163
- ### Git Commits
164
-
165
- {commit_table}
166
-
167
- ### Testing
168
-
169
- - [OK] (Add test results)
170
-
171
- ### Status
172
-
173
- [OK] **Completed**
174
-
175
- ### Next Steps
176
-
177
- - None - task complete
178
- """
179
-
180
-
181
- def update_index(
182
- index_file: Path,
183
- dev_dir: Path,
184
- title: str,
185
- commit: str,
186
- new_session: int,
187
- active_file: str,
188
- today: str
189
- ) -> bool:
190
- """Update index.md with new session info."""
191
- # Format commit for display
192
- commit_display = "-"
193
- if commit and commit != "-":
194
- commit_display = re.sub(r"([a-f0-9]{7,})", r"`\1`", commit.replace(",", ", "))
195
-
196
- # Get file number from active_file name
197
- match = re.search(r"(\d+)", active_file)
198
- active_num = int(match.group(1)) if match else 0
199
- files_table = count_journal_files(dev_dir, active_num)
200
-
201
- print(f"Updating index.md for session {new_session}...")
202
- print(f" Title: {title}")
203
- print(f" Commit: {commit_display}")
204
- print(f" Active File: {active_file}")
205
- print()
206
-
207
- content = index_file.read_text(encoding="utf-8")
208
-
209
- if "@@@auto:current-status" not in content:
210
- print("Error: Markers not found in index.md. Please ensure markers exist.", file=sys.stderr)
211
- return False
212
-
213
- # Process sections
214
- lines = content.splitlines()
215
- new_lines = []
216
-
217
- in_current_status = False
218
- in_active_documents = False
219
- in_session_history = False
220
- header_written = False
221
-
222
- for line in lines:
223
- if "@@@auto:current-status" in line:
224
- new_lines.append(line)
225
- in_current_status = True
226
- new_lines.append(f"- **Active File**: `{active_file}`")
227
- new_lines.append(f"- **Total Sessions**: {new_session}")
228
- new_lines.append(f"- **Last Active**: {today}")
229
- continue
230
-
231
- if "@@@/auto:current-status" in line:
232
- in_current_status = False
233
- new_lines.append(line)
234
- continue
235
-
236
- if "@@@auto:active-documents" in line:
237
- new_lines.append(line)
238
- in_active_documents = True
239
- new_lines.append("| File | Lines | Status |")
240
- new_lines.append("|------|-------|--------|")
241
- new_lines.append(files_table)
242
- continue
243
-
244
- if "@@@/auto:active-documents" in line:
245
- in_active_documents = False
246
- new_lines.append(line)
247
- continue
248
-
249
- if "@@@auto:session-history" in line:
250
- new_lines.append(line)
251
- in_session_history = True
252
- header_written = False
253
- continue
254
-
255
- if "@@@/auto:session-history" in line:
256
- in_session_history = False
257
- new_lines.append(line)
258
- continue
259
-
260
- if in_current_status:
261
- continue
262
-
263
- if in_active_documents:
264
- continue
265
-
266
- if in_session_history:
267
- new_lines.append(line)
268
- if line.startswith("|---") and not header_written:
269
- new_lines.append(f"| {new_session} | {today} | {title} | {commit_display} |")
270
- header_written = True
271
- continue
272
-
273
- new_lines.append(line)
274
-
275
- index_file.write_text("\n".join(new_lines), encoding="utf-8")
276
- print("[OK] Updated index.md successfully!")
277
- return True
278
-
279
-
280
- # =============================================================================
281
- # Main Function
282
- # =============================================================================
283
-
284
- def add_session(
285
- title: str,
286
- commit: str = "-",
287
- summary: str = "(Add summary)",
288
- extra_content: str = "(Add details)"
289
- ) -> int:
290
- """Add a new session."""
291
- repo_root = get_repo_root()
292
- ensure_developer(repo_root)
293
-
294
- developer = get_developer(repo_root)
295
- if not developer:
296
- print("Error: Developer not initialized", file=sys.stderr)
297
- return 1
298
-
299
- dev_dir = get_workspace_dir(repo_root)
300
- if not dev_dir:
301
- print("Error: Workspace directory not found", file=sys.stderr)
302
- return 1
303
-
304
- index_file = dev_dir / "index.md"
305
- today = datetime.now().strftime("%Y-%m-%d")
306
-
307
- journal_file, current_num, current_lines = get_latest_journal_info(dev_dir)
308
- current_session = get_current_session(index_file)
309
- new_session = current_session + 1
310
-
311
- session_content = generate_session_content(
312
- new_session, title, commit, summary, extra_content, today
313
- )
314
- content_lines = len(session_content.splitlines())
315
-
316
- print("========================================", file=sys.stderr)
317
- print("ADD SESSION", file=sys.stderr)
318
- print("========================================", file=sys.stderr)
319
- print("", file=sys.stderr)
320
- print(f"Session: {new_session}", file=sys.stderr)
321
- print(f"Title: {title}", file=sys.stderr)
322
- print(f"Commit: {commit}", file=sys.stderr)
323
- print("", file=sys.stderr)
324
- print(f"Current journal file: {FILE_JOURNAL_PREFIX}{current_num}.md", file=sys.stderr)
325
- print(f"Current lines: {current_lines}", file=sys.stderr)
326
- print(f"New content lines: {content_lines}", file=sys.stderr)
327
- print(f"Total after append: {current_lines + content_lines}", file=sys.stderr)
328
- print("", file=sys.stderr)
329
-
330
- target_file = journal_file
331
- target_num = current_num
332
-
333
- if current_lines + content_lines > MAX_LINES:
334
- target_num = current_num + 1
335
- print(f"[!] Exceeds {MAX_LINES} lines, creating {FILE_JOURNAL_PREFIX}{target_num}.md", file=sys.stderr)
336
- target_file = create_new_journal_file(dev_dir, target_num, developer, today)
337
- print(f"Created: {target_file}", file=sys.stderr)
338
-
339
- # Append session content
340
- if target_file:
341
- with target_file.open("a", encoding="utf-8") as f:
342
- f.write(session_content)
343
- print(f"[OK] Appended session to {target_file.name}", file=sys.stderr)
344
-
345
- print("", file=sys.stderr)
346
-
347
- # Update index.md
348
- active_file = f"{FILE_JOURNAL_PREFIX}{target_num}.md"
349
- if not update_index(index_file, dev_dir, title, commit, new_session, active_file, today):
350
- return 1
351
-
352
- print("", file=sys.stderr)
353
- print("========================================", file=sys.stderr)
354
- print(f"[OK] Session {new_session} added successfully!", file=sys.stderr)
355
- print("========================================", file=sys.stderr)
356
- print("", file=sys.stderr)
357
- print("Files updated:", file=sys.stderr)
358
- print(f" - {target_file.name if target_file else 'journal'}", file=sys.stderr)
359
- print(" - index.md", file=sys.stderr)
360
-
361
- return 0
362
-
363
-
364
- # =============================================================================
365
- # Main Entry
366
- # =============================================================================
367
-
368
- def main() -> int:
369
- """CLI entry point."""
370
- parser = argparse.ArgumentParser(
371
- description="Add a new session to journal file and update index.md"
372
- )
373
- parser.add_argument("--title", required=True, help="Session title")
374
- parser.add_argument("--commit", default="-", help="Comma-separated commit hashes")
375
- parser.add_argument("--summary", default="(Add summary)", help="Brief summary")
376
- parser.add_argument("--content-file", help="Path to file with detailed content")
377
-
378
- args = parser.parse_args()
379
-
380
- extra_content = "(Add details)"
381
- if args.content_file:
382
- content_path = Path(args.content_file)
383
- if content_path.is_file():
384
- extra_content = content_path.read_text(encoding="utf-8")
385
- elif not sys.stdin.isatty():
386
- extra_content = sys.stdin.read()
387
-
388
- return add_session(args.title, args.commit, args.summary, extra_content)
389
-
390
-
391
- if __name__ == "__main__":
392
- sys.exit(main())
@@ -1,80 +0,0 @@
1
- """
2
- Common utilities for Trellis workflow scripts.
3
-
4
- This module provides shared functionality used by other Trellis scripts.
5
- """
6
-
7
- import io
8
- import sys
9
-
10
- # =============================================================================
11
- # Windows Encoding Fix (MUST be at top, before any other output)
12
- # =============================================================================
13
- # On Windows, stdout defaults to the system code page (often GBK/CP936).
14
- # This causes UnicodeEncodeError when printing non-ASCII characters.
15
- #
16
- # Any script that imports from common will automatically get this fix.
17
- # =============================================================================
18
-
19
-
20
- def _configure_stream(stream: object) -> object:
21
- """Configure a stream for UTF-8 encoding on Windows."""
22
- # Try reconfigure() first (Python 3.7+, more reliable)
23
- if hasattr(stream, "reconfigure"):
24
- stream.reconfigure(encoding="utf-8", errors="replace") # type: ignore[union-attr]
25
- return stream
26
- # Fallback: detach and rewrap with TextIOWrapper
27
- elif hasattr(stream, "detach"):
28
- return io.TextIOWrapper(
29
- stream.detach(), # type: ignore[union-attr]
30
- encoding="utf-8",
31
- errors="replace",
32
- )
33
- return stream
34
-
35
-
36
- if sys.platform == "win32":
37
- sys.stdout = _configure_stream(sys.stdout) # type: ignore[assignment]
38
- sys.stderr = _configure_stream(sys.stderr) # type: ignore[assignment]
39
-
40
-
41
- def configure_encoding() -> None:
42
- """
43
- Configure stdout/stderr for UTF-8 encoding on Windows.
44
-
45
- This is automatically called when importing from common,
46
- but can be called manually for scripts that don't import common.
47
-
48
- Safe to call multiple times.
49
- """
50
- global sys
51
- if sys.platform == "win32":
52
- sys.stdout = _configure_stream(sys.stdout) # type: ignore[assignment]
53
- sys.stderr = _configure_stream(sys.stderr) # type: ignore[assignment]
54
-
55
-
56
- from .paths import (
57
- DIR_WORKFLOW,
58
- DIR_WORKSPACE,
59
- DIR_TASKS,
60
- DIR_ARCHIVE,
61
- DIR_SPEC,
62
- DIR_SCRIPTS,
63
- FILE_DEVELOPER,
64
- FILE_CURRENT_TASK,
65
- FILE_TASK_JSON,
66
- FILE_JOURNAL_PREFIX,
67
- get_repo_root,
68
- get_developer,
69
- check_developer,
70
- get_tasks_dir,
71
- get_workspace_dir,
72
- get_active_journal_file,
73
- count_lines,
74
- get_current_task,
75
- get_current_task_abs,
76
- set_current_task,
77
- clear_current_task,
78
- has_current_task,
79
- generate_task_date_prefix,
80
- )