cowork-flow 0.0.1
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 +193 -0
- package/bin/cowork-flow.js +9 -0
- package/package.json +28 -0
- package/src/cli.js +61 -0
- package/src/commands/init.js +88 -0
- package/src/commands/sync.js +33 -0
- package/src/commands/update.js +60 -0
- package/src/lib/copy-template.js +189 -0
- package/src/lib/package-info.js +49 -0
- package/src/lib/paths.js +6 -0
- package/template/.agent/skills/break-loop/SKILL.md +131 -0
- package/template/.agent/skills/check-cross-layer/SKILL.md +158 -0
- package/template/.agent/skills/finish-work/SKILL.md +163 -0
- package/template/.agent/skills/record-session/SKILL.md +78 -0
- package/template/.agent/skills/start/SKILL.md +390 -0
- package/template/.agent/skills/update-spec/SKILL.md +335 -0
- package/template/.cowork-flow/.version +1 -0
- package/template/.cowork-flow/changes/archive/.gitkeep +1 -0
- package/template/.cowork-flow/config.yaml +49 -0
- package/template/.cowork-flow/plans/.gitkeep +1 -0
- package/template/.cowork-flow/scripts/add_session.py +441 -0
- package/template/.cowork-flow/scripts/change.py +314 -0
- package/template/.cowork-flow/scripts/common/__init__.py +84 -0
- package/template/.cowork-flow/scripts/common/config.py +128 -0
- package/template/.cowork-flow/scripts/common/developer.py +205 -0
- package/template/.cowork-flow/scripts/common/files.py +24 -0
- package/template/.cowork-flow/scripts/common/git_context.py +628 -0
- package/template/.cowork-flow/scripts/common/paths.py +350 -0
- package/template/.cowork-flow/scripts/common/task_utils.py +200 -0
- package/template/.cowork-flow/scripts/get_context.py +16 -0
- package/template/.cowork-flow/scripts/get_developer.py +26 -0
- package/template/.cowork-flow/scripts/init_developer.py +51 -0
- package/template/.cowork-flow/scripts/task.py +1378 -0
- package/template/.cowork-flow/spec/backend/database-guidelines.md +26 -0
- package/template/.cowork-flow/spec/backend/directory-structure.md +27 -0
- package/template/.cowork-flow/spec/backend/error-handling.md +20 -0
- package/template/.cowork-flow/spec/backend/index.md +36 -0
- package/template/.cowork-flow/spec/backend/logging-guidelines.md +20 -0
- package/template/.cowork-flow/spec/backend/quality-guidelines.md +24 -0
- package/template/.cowork-flow/spec/frontend/component-guidelines.md +19 -0
- package/template/.cowork-flow/spec/frontend/directory-structure.md +18 -0
- package/template/.cowork-flow/spec/frontend/hook-guidelines.md +17 -0
- package/template/.cowork-flow/spec/frontend/index.md +28 -0
- package/template/.cowork-flow/spec/frontend/quality-guidelines.md +23 -0
- package/template/.cowork-flow/spec/frontend/state-management.md +18 -0
- package/template/.cowork-flow/spec/frontend/type-safety.md +18 -0
- package/template/.cowork-flow/spec/guides/code-reuse-thinking-guide.md +26 -0
- package/template/.cowork-flow/spec/guides/cross-layer-thinking-guide.md +21 -0
- package/template/.cowork-flow/spec/guides/index.md +31 -0
- package/template/.cowork-flow/spec/guides/pre-implementation-checklist.md +15 -0
- package/template/.cowork-flow/tasks/.gitkeep +1 -0
- package/template/.cowork-flow/tasks/archive/.gitkeep +1 -0
- package/template/.cowork-flow/workflow.md +411 -0
- package/template/.cowork-flow/workspace/index.md +95 -0
- package/template/.superpowers/brainstorming/SKILL.md +164 -0
- package/template/.superpowers/brainstorming/scripts/frame-template.html +214 -0
- package/template/.superpowers/brainstorming/scripts/helper.js +88 -0
- package/template/.superpowers/brainstorming/scripts/server.cjs +354 -0
- package/template/.superpowers/brainstorming/scripts/start-server.sh +148 -0
- package/template/.superpowers/brainstorming/scripts/stop-server.sh +56 -0
- package/template/.superpowers/brainstorming/spec-document-reviewer-prompt.md +49 -0
- package/template/.superpowers/brainstorming/visual-companion.md +287 -0
- package/template/.superpowers/dispatching-parallel-agents/SKILL.md +182 -0
- package/template/.superpowers/executing-plans/SKILL.md +70 -0
- package/template/.superpowers/finishing-a-development-branch/SKILL.md +251 -0
- package/template/.superpowers/receiving-code-review/SKILL.md +213 -0
- package/template/.superpowers/requesting-code-review/SKILL.md +103 -0
- package/template/.superpowers/requesting-code-review/code-reviewer.md +168 -0
- package/template/.superpowers/subagent-driven-development/SKILL.md +279 -0
- package/template/.superpowers/subagent-driven-development/code-quality-reviewer-prompt.md +25 -0
- package/template/.superpowers/subagent-driven-development/implementer-prompt.md +113 -0
- package/template/.superpowers/subagent-driven-development/spec-reviewer-prompt.md +61 -0
- package/template/.superpowers/systematic-debugging/CREATION-LOG.md +119 -0
- package/template/.superpowers/systematic-debugging/SKILL.md +296 -0
- package/template/.superpowers/systematic-debugging/condition-based-waiting-example.ts +158 -0
- package/template/.superpowers/systematic-debugging/condition-based-waiting.md +115 -0
- package/template/.superpowers/systematic-debugging/defense-in-depth.md +122 -0
- package/template/.superpowers/systematic-debugging/find-polluter.sh +63 -0
- package/template/.superpowers/systematic-debugging/root-cause-tracing.md +169 -0
- package/template/.superpowers/systematic-debugging/test-academic.md +14 -0
- package/template/.superpowers/systematic-debugging/test-pressure-1.md +58 -0
- package/template/.superpowers/systematic-debugging/test-pressure-2.md +68 -0
- package/template/.superpowers/systematic-debugging/test-pressure-3.md +69 -0
- package/template/.superpowers/test-driven-development/SKILL.md +371 -0
- package/template/.superpowers/test-driven-development/testing-anti-patterns.md +299 -0
- package/template/.superpowers/using-git-worktrees/SKILL.md +215 -0
- package/template/.superpowers/using-superpowers/SKILL.md +117 -0
- package/template/.superpowers/using-superpowers/references/codex-tools.md +59 -0
- package/template/.superpowers/using-superpowers/references/copilot-tools.md +42 -0
- package/template/.superpowers/using-superpowers/references/gemini-tools.md +51 -0
- package/template/.superpowers/verification-before-completion/SKILL.md +139 -0
- package/template/.superpowers/writing-plans/SKILL.md +152 -0
- package/template/.superpowers/writing-plans/plan-document-reviewer-prompt.md +49 -0
- package/template/.superpowers/writing-skills/SKILL.md +655 -0
- package/template/.superpowers/writing-skills/anthropic-best-practices.md +1150 -0
- package/template/.superpowers/writing-skills/examples/CLAUDE_MD_TESTING.md +189 -0
- package/template/.superpowers/writing-skills/graphviz-conventions.dot +172 -0
- package/template/.superpowers/writing-skills/persuasion-principles.md +187 -0
- package/template/.superpowers/writing-skills/render-graphs.js +168 -0
- package/template/.superpowers/writing-skills/testing-skills-with-subagents.md +384 -0
- package/template/AGENTS.md +116 -0
|
@@ -0,0 +1,441 @@
|
|
|
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 argparse
|
|
14
|
+
import re
|
|
15
|
+
import subprocess
|
|
16
|
+
import sys
|
|
17
|
+
from datetime import datetime
|
|
18
|
+
from pathlib import Path
|
|
19
|
+
|
|
20
|
+
from common.paths import (
|
|
21
|
+
FILE_JOURNAL_PREFIX,
|
|
22
|
+
count_lines,
|
|
23
|
+
get_repo_root,
|
|
24
|
+
get_developer,
|
|
25
|
+
get_workspace_dir,
|
|
26
|
+
)
|
|
27
|
+
from common.developer import ensure_developer
|
|
28
|
+
from common.config import get_session_commit_message, get_max_journal_lines
|
|
29
|
+
|
|
30
|
+
MARKER_CURRENT_STATUS_START = "@@@auto:current-status"
|
|
31
|
+
MARKER_CURRENT_STATUS_END = "@@@/auto:current-status"
|
|
32
|
+
MARKER_ACTIVE_DOCUMENTS_START = "@@@auto:active-documents"
|
|
33
|
+
MARKER_ACTIVE_DOCUMENTS_END = "@@@/auto:active-documents"
|
|
34
|
+
MARKER_SESSION_HISTORY_START = "@@@auto:session-history"
|
|
35
|
+
MARKER_SESSION_HISTORY_END = "@@@/auto:session-history"
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# =============================================================================
|
|
39
|
+
# Helper Functions
|
|
40
|
+
# =============================================================================
|
|
41
|
+
|
|
42
|
+
def get_latest_journal_info(dev_dir: Path) -> tuple[Path | None, int, int]:
|
|
43
|
+
"""Get latest journal file info.
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Tuple of (file_path, file_number, line_count).
|
|
47
|
+
"""
|
|
48
|
+
latest_file: Path | None = None
|
|
49
|
+
latest_num = -1
|
|
50
|
+
|
|
51
|
+
for f in dev_dir.glob(f"{FILE_JOURNAL_PREFIX}*.md"):
|
|
52
|
+
if not f.is_file():
|
|
53
|
+
continue
|
|
54
|
+
|
|
55
|
+
match = re.search(r"(\d+)$", f.stem)
|
|
56
|
+
if match:
|
|
57
|
+
num = int(match.group(1))
|
|
58
|
+
if num > latest_num:
|
|
59
|
+
latest_num = num
|
|
60
|
+
latest_file = f
|
|
61
|
+
|
|
62
|
+
if latest_file:
|
|
63
|
+
lines = count_lines(latest_file)
|
|
64
|
+
return latest_file, latest_num, lines
|
|
65
|
+
|
|
66
|
+
return None, 0, 0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def get_current_session(index_file: Path) -> int:
|
|
70
|
+
"""Get current session number from index.md."""
|
|
71
|
+
if not index_file.is_file():
|
|
72
|
+
return 0
|
|
73
|
+
|
|
74
|
+
content = index_file.read_text(encoding="utf-8")
|
|
75
|
+
for line in content.splitlines():
|
|
76
|
+
if "Total Sessions" in line or "\u603b\u4f1a\u8bdd\u6570" in line:
|
|
77
|
+
match = re.search(r"[:\uFF1A]\s*(\d+)", line)
|
|
78
|
+
if match:
|
|
79
|
+
return int(match.group(1))
|
|
80
|
+
return 0
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def _extract_journal_num(filename: str) -> int:
|
|
84
|
+
"""Extract journal number from filename for sorting."""
|
|
85
|
+
match = re.search(r"(\d+)", filename)
|
|
86
|
+
return int(match.group(1)) if match else 0
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _format_commit_display(commit: str) -> str:
|
|
90
|
+
"""Format commit hashes for display in index.md."""
|
|
91
|
+
if not commit or commit == "-":
|
|
92
|
+
return "-"
|
|
93
|
+
return re.sub(r"([a-f0-9]{7,})", r"`\1`", commit.replace(",", ", "))
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _format_commit_table(commit: str) -> str:
|
|
97
|
+
"""Format commit hashes for a session entry."""
|
|
98
|
+
if not commit or commit == "-":
|
|
99
|
+
return "(no code commit; planning or sync session)"
|
|
100
|
+
|
|
101
|
+
commit_table = """| Hash | Note |
|
|
102
|
+
|------|------|"""
|
|
103
|
+
for c in commit.split(","):
|
|
104
|
+
c = c.strip()
|
|
105
|
+
commit_table += f"\n| `{c}` | See git log |"
|
|
106
|
+
return commit_table
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def count_journal_files(dev_dir: Path, active_num: int) -> str:
|
|
110
|
+
"""Count journal files and return table rows."""
|
|
111
|
+
active_file = f"{FILE_JOURNAL_PREFIX}{active_num}.md"
|
|
112
|
+
result_lines = []
|
|
113
|
+
|
|
114
|
+
files = sorted(
|
|
115
|
+
[f for f in dev_dir.glob(f"{FILE_JOURNAL_PREFIX}*.md") if f.is_file()],
|
|
116
|
+
key=lambda f: _extract_journal_num(f.stem),
|
|
117
|
+
reverse=True
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
for f in files:
|
|
121
|
+
filename = f.name
|
|
122
|
+
lines = count_lines(f)
|
|
123
|
+
status = "Current" if filename == active_file else "Archived"
|
|
124
|
+
result_lines.append(f"| `{filename}` | ~{lines} | {status} |")
|
|
125
|
+
|
|
126
|
+
return "\n".join(result_lines)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def create_new_journal_file(
|
|
130
|
+
dev_dir: Path, num: int, developer: str, today: str, max_lines: int = 2000,
|
|
131
|
+
) -> Path:
|
|
132
|
+
"""Create a new journal file."""
|
|
133
|
+
prev_num = num - 1
|
|
134
|
+
new_file = dev_dir / f"{FILE_JOURNAL_PREFIX}{num}.md"
|
|
135
|
+
|
|
136
|
+
content = f"""# Development Journal - {developer} (Part {num})
|
|
137
|
+
|
|
138
|
+
> Continued from `{FILE_JOURNAL_PREFIX}{prev_num}.md` (archived after about {max_lines} lines)
|
|
139
|
+
> Start date: {today}
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
"""
|
|
144
|
+
new_file.write_text(content, encoding="utf-8")
|
|
145
|
+
return new_file
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def generate_session_content(
|
|
149
|
+
session_num: int,
|
|
150
|
+
title: str,
|
|
151
|
+
commit: str,
|
|
152
|
+
summary: str,
|
|
153
|
+
extra_content: str,
|
|
154
|
+
today: str
|
|
155
|
+
) -> str:
|
|
156
|
+
"""Generate session content."""
|
|
157
|
+
commit_table = _format_commit_table(commit)
|
|
158
|
+
|
|
159
|
+
return f"""
|
|
160
|
+
|
|
161
|
+
## Session {session_num}: {title}
|
|
162
|
+
|
|
163
|
+
**Date**: {today}
|
|
164
|
+
**Task**: {title}
|
|
165
|
+
|
|
166
|
+
### Summary
|
|
167
|
+
|
|
168
|
+
{summary}
|
|
169
|
+
|
|
170
|
+
### Main Changes
|
|
171
|
+
|
|
172
|
+
{extra_content}
|
|
173
|
+
|
|
174
|
+
### Git Commit
|
|
175
|
+
|
|
176
|
+
{commit_table}
|
|
177
|
+
|
|
178
|
+
### Verification
|
|
179
|
+
|
|
180
|
+
- [OK] (add verification results)
|
|
181
|
+
|
|
182
|
+
### Status
|
|
183
|
+
|
|
184
|
+
[OK] **Completed**
|
|
185
|
+
|
|
186
|
+
### Follow-up Actions
|
|
187
|
+
|
|
188
|
+
- None, current task is complete
|
|
189
|
+
"""
|
|
190
|
+
|
|
191
|
+
|
|
192
|
+
def update_index(
|
|
193
|
+
index_file: Path,
|
|
194
|
+
dev_dir: Path,
|
|
195
|
+
title: str,
|
|
196
|
+
commit: str,
|
|
197
|
+
new_session: int,
|
|
198
|
+
active_file: str,
|
|
199
|
+
today: str
|
|
200
|
+
) -> bool:
|
|
201
|
+
"""Update index.md with new session info."""
|
|
202
|
+
commit_display = _format_commit_display(commit)
|
|
203
|
+
|
|
204
|
+
# Get file number from active_file name
|
|
205
|
+
match = re.search(r"(\d+)", active_file)
|
|
206
|
+
active_num = int(match.group(1)) if match else 0
|
|
207
|
+
files_table = count_journal_files(dev_dir, active_num)
|
|
208
|
+
|
|
209
|
+
print(f"Updating index.md (session {new_session})...", file=sys.stderr)
|
|
210
|
+
print(f" Title: {title}", file=sys.stderr)
|
|
211
|
+
print(f" Commit: {commit_display}", file=sys.stderr)
|
|
212
|
+
print(f" Current file: {active_file}", file=sys.stderr)
|
|
213
|
+
print("", file=sys.stderr)
|
|
214
|
+
|
|
215
|
+
content = index_file.read_text(encoding="utf-8")
|
|
216
|
+
|
|
217
|
+
if MARKER_CURRENT_STATUS_START not in content:
|
|
218
|
+
print("Error: index.md is missing auto-update markers. Please restore the template first.", file=sys.stderr)
|
|
219
|
+
return False
|
|
220
|
+
|
|
221
|
+
# Process sections
|
|
222
|
+
lines = content.splitlines()
|
|
223
|
+
new_lines = []
|
|
224
|
+
|
|
225
|
+
in_current_status = False
|
|
226
|
+
in_active_documents = False
|
|
227
|
+
in_session_history = False
|
|
228
|
+
header_written = False
|
|
229
|
+
|
|
230
|
+
for line in lines:
|
|
231
|
+
if MARKER_CURRENT_STATUS_START in line:
|
|
232
|
+
new_lines.append(line)
|
|
233
|
+
in_current_status = True
|
|
234
|
+
new_lines.append(f"- **Current file**: `{active_file}`")
|
|
235
|
+
new_lines.append(f"- **Total Sessions**: {new_session}")
|
|
236
|
+
new_lines.append(f"- **Last Active**: {today}")
|
|
237
|
+
continue
|
|
238
|
+
|
|
239
|
+
if MARKER_CURRENT_STATUS_END in line:
|
|
240
|
+
in_current_status = False
|
|
241
|
+
new_lines.append(line)
|
|
242
|
+
continue
|
|
243
|
+
|
|
244
|
+
if MARKER_ACTIVE_DOCUMENTS_START in line:
|
|
245
|
+
new_lines.append(line)
|
|
246
|
+
in_active_documents = True
|
|
247
|
+
new_lines.append("| File | Lines | Status |")
|
|
248
|
+
new_lines.append("|------|------|------|")
|
|
249
|
+
new_lines.append(files_table)
|
|
250
|
+
continue
|
|
251
|
+
|
|
252
|
+
if MARKER_ACTIVE_DOCUMENTS_END in line:
|
|
253
|
+
in_active_documents = False
|
|
254
|
+
new_lines.append(line)
|
|
255
|
+
continue
|
|
256
|
+
|
|
257
|
+
if MARKER_SESSION_HISTORY_START in line:
|
|
258
|
+
new_lines.append(line)
|
|
259
|
+
in_session_history = True
|
|
260
|
+
header_written = False
|
|
261
|
+
continue
|
|
262
|
+
|
|
263
|
+
if MARKER_SESSION_HISTORY_END in line:
|
|
264
|
+
in_session_history = False
|
|
265
|
+
new_lines.append(line)
|
|
266
|
+
continue
|
|
267
|
+
|
|
268
|
+
if in_current_status:
|
|
269
|
+
continue
|
|
270
|
+
|
|
271
|
+
if in_active_documents:
|
|
272
|
+
continue
|
|
273
|
+
|
|
274
|
+
if in_session_history:
|
|
275
|
+
new_lines.append(line)
|
|
276
|
+
if re.match(r"^\|\s*-", line) and not header_written:
|
|
277
|
+
new_lines.append(f"| {new_session} | {today} | {title} | {commit_display} |")
|
|
278
|
+
header_written = True
|
|
279
|
+
continue
|
|
280
|
+
|
|
281
|
+
new_lines.append(line)
|
|
282
|
+
|
|
283
|
+
index_file.write_text("\n".join(new_lines), encoding="utf-8")
|
|
284
|
+
print("[OK] index.md updated.", file=sys.stderr)
|
|
285
|
+
return True
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# =============================================================================
|
|
289
|
+
# Main Function
|
|
290
|
+
# =============================================================================
|
|
291
|
+
|
|
292
|
+
def _auto_commit_workspace(repo_root: Path) -> None:
|
|
293
|
+
"""Stage .cowork-flow/workspace and .cowork-flow/tasks, then commit with a configured message."""
|
|
294
|
+
commit_msg = get_session_commit_message(repo_root)
|
|
295
|
+
subprocess.run(
|
|
296
|
+
["git", "add", "-A", ".cowork-flow/workspace", ".cowork-flow/tasks"],
|
|
297
|
+
cwd=repo_root,
|
|
298
|
+
capture_output=True,
|
|
299
|
+
)
|
|
300
|
+
# Check if there are staged changes
|
|
301
|
+
result = subprocess.run(
|
|
302
|
+
["git", "diff", "--cached", "--quiet", "--", ".cowork-flow/workspace", ".cowork-flow/tasks"],
|
|
303
|
+
cwd=repo_root,
|
|
304
|
+
)
|
|
305
|
+
if result.returncode == 0:
|
|
306
|
+
print("[OK] No workspace metadata changes to commit.", file=sys.stderr)
|
|
307
|
+
return
|
|
308
|
+
commit_result = subprocess.run(
|
|
309
|
+
["git", "commit", "-m", commit_msg],
|
|
310
|
+
cwd=repo_root,
|
|
311
|
+
capture_output=True,
|
|
312
|
+
text=True,
|
|
313
|
+
)
|
|
314
|
+
if commit_result.returncode == 0:
|
|
315
|
+
print(f"[OK] Metadata auto-committed: {commit_msg}", file=sys.stderr)
|
|
316
|
+
else:
|
|
317
|
+
print(f"[WARN] Metadata auto-commit failed: {commit_result.stderr.strip()}", file=sys.stderr)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def add_session(
|
|
321
|
+
title: str,
|
|
322
|
+
commit: str = "-",
|
|
323
|
+
summary: str = "(add summary)",
|
|
324
|
+
extra_content: str = "(add details)",
|
|
325
|
+
auto_commit: bool = True,
|
|
326
|
+
) -> int:
|
|
327
|
+
"""Add a new session."""
|
|
328
|
+
repo_root = get_repo_root()
|
|
329
|
+
ensure_developer(repo_root)
|
|
330
|
+
|
|
331
|
+
developer = get_developer(repo_root)
|
|
332
|
+
if not developer:
|
|
333
|
+
print("Error: developer identity has not been initialized.", file=sys.stderr)
|
|
334
|
+
return 1
|
|
335
|
+
|
|
336
|
+
dev_dir = get_workspace_dir(repo_root)
|
|
337
|
+
if not dev_dir:
|
|
338
|
+
print("Error: workspace directory not found.", file=sys.stderr)
|
|
339
|
+
return 1
|
|
340
|
+
|
|
341
|
+
max_lines = get_max_journal_lines(repo_root)
|
|
342
|
+
|
|
343
|
+
index_file = dev_dir / "index.md"
|
|
344
|
+
today = datetime.now().strftime("%Y-%m-%d")
|
|
345
|
+
|
|
346
|
+
journal_file, current_num, current_lines = get_latest_journal_info(dev_dir)
|
|
347
|
+
current_session = get_current_session(index_file)
|
|
348
|
+
new_session = current_session + 1
|
|
349
|
+
|
|
350
|
+
session_content = generate_session_content(
|
|
351
|
+
new_session, title, commit, summary, extra_content, today
|
|
352
|
+
)
|
|
353
|
+
content_lines = len(session_content.splitlines())
|
|
354
|
+
|
|
355
|
+
print("========================================", file=sys.stderr)
|
|
356
|
+
print("Add Session Record", file=sys.stderr)
|
|
357
|
+
print("========================================", file=sys.stderr)
|
|
358
|
+
print("", file=sys.stderr)
|
|
359
|
+
print(f"Session: {new_session}", file=sys.stderr)
|
|
360
|
+
print(f"Title: {title}", file=sys.stderr)
|
|
361
|
+
print(f"Commit: {commit}", file=sys.stderr)
|
|
362
|
+
print("", file=sys.stderr)
|
|
363
|
+
print(f"Current journal file: {FILE_JOURNAL_PREFIX}{current_num}.md", file=sys.stderr)
|
|
364
|
+
print(f"Current lines: {current_lines}", file=sys.stderr)
|
|
365
|
+
print(f"New content lines: {content_lines}", file=sys.stderr)
|
|
366
|
+
print(f"Total lines after append: {current_lines + content_lines}", file=sys.stderr)
|
|
367
|
+
print("", file=sys.stderr)
|
|
368
|
+
|
|
369
|
+
target_file = journal_file
|
|
370
|
+
target_num = current_num
|
|
371
|
+
|
|
372
|
+
if current_lines + content_lines > max_lines:
|
|
373
|
+
target_num = current_num + 1
|
|
374
|
+
print(f"[!] Over {max_lines} lines, creating {FILE_JOURNAL_PREFIX}{target_num}.md", file=sys.stderr)
|
|
375
|
+
target_file = create_new_journal_file(dev_dir, target_num, developer, today, max_lines)
|
|
376
|
+
print(f"Created: {target_file}", file=sys.stderr)
|
|
377
|
+
|
|
378
|
+
# Append session content
|
|
379
|
+
if target_file:
|
|
380
|
+
with target_file.open("a", encoding="utf-8") as f:
|
|
381
|
+
f.write(session_content)
|
|
382
|
+
print(f"[OK] Appended session to {target_file.name}", file=sys.stderr)
|
|
383
|
+
|
|
384
|
+
print("", file=sys.stderr)
|
|
385
|
+
|
|
386
|
+
# Update index.md
|
|
387
|
+
active_file = f"{FILE_JOURNAL_PREFIX}{target_num}.md"
|
|
388
|
+
if not update_index(index_file, dev_dir, title, commit, new_session, active_file, today):
|
|
389
|
+
return 1
|
|
390
|
+
|
|
391
|
+
print("", file=sys.stderr)
|
|
392
|
+
print("========================================", file=sys.stderr)
|
|
393
|
+
print(f"[OK] Session {new_session} recorded successfully.", file=sys.stderr)
|
|
394
|
+
print("========================================", file=sys.stderr)
|
|
395
|
+
print("", file=sys.stderr)
|
|
396
|
+
print("Updated files:", file=sys.stderr)
|
|
397
|
+
print(f" - {target_file.name if target_file else 'journal'}", file=sys.stderr)
|
|
398
|
+
print(" - index.md", file=sys.stderr)
|
|
399
|
+
|
|
400
|
+
# Auto-commit workspace changes
|
|
401
|
+
if auto_commit:
|
|
402
|
+
print("", file=sys.stderr)
|
|
403
|
+
_auto_commit_workspace(repo_root)
|
|
404
|
+
|
|
405
|
+
return 0
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
# =============================================================================
|
|
409
|
+
# Main Entry
|
|
410
|
+
# =============================================================================
|
|
411
|
+
|
|
412
|
+
def main() -> int:
|
|
413
|
+
"""CLI entry point."""
|
|
414
|
+
parser = argparse.ArgumentParser(
|
|
415
|
+
description="Append a session record to the journal and update index.md"
|
|
416
|
+
)
|
|
417
|
+
parser.add_argument("--title", required=True, help="Session title")
|
|
418
|
+
parser.add_argument("--commit", default="-", help="Comma-separated commit hashes")
|
|
419
|
+
parser.add_argument("--summary", default="(add summary)", help="Brief summary")
|
|
420
|
+
parser.add_argument("--content-file", help="Path to a file containing detailed content")
|
|
421
|
+
parser.add_argument("--no-commit", action="store_true",
|
|
422
|
+
help="Skip automatic .cowork-flow metadata commit")
|
|
423
|
+
|
|
424
|
+
args = parser.parse_args()
|
|
425
|
+
|
|
426
|
+
extra_content = "(add details)"
|
|
427
|
+
if args.content_file:
|
|
428
|
+
content_path = Path(args.content_file)
|
|
429
|
+
if content_path.is_file():
|
|
430
|
+
extra_content = content_path.read_text(encoding="utf-8")
|
|
431
|
+
elif not sys.stdin.isatty():
|
|
432
|
+
extra_content = sys.stdin.read()
|
|
433
|
+
|
|
434
|
+
return add_session(
|
|
435
|
+
args.title, args.commit, args.summary, extra_content,
|
|
436
|
+
auto_commit=not args.no_commit,
|
|
437
|
+
)
|
|
438
|
+
|
|
439
|
+
|
|
440
|
+
if __name__ == "__main__":
|
|
441
|
+
sys.exit(main())
|