chore: migrate from opencode to trellis 0.5.0-rc.6

- Remove legacy .opencode/ directory and configuration
- Update .trellis/ to v0.5.0-rc.6 structure
- Refactor scripts: modularize common/, remove multi_agent/
- Add new common modules: git.py, io.py, log.py, types.py, etc.
- Update workflow.md and AGENTS.md
- Archive completed migration tasks
This commit is contained in:
zl-q
2026-05-06 14:29:25 +08:00
parent 4e234be647
commit 04b493ed09
102 changed files with 8377 additions and 9922 deletions
+111 -13
View File
@@ -4,8 +4,19 @@
Add a new session to journal file and update index.md.
Usage:
python3 add_session.py --title "Title" --commit "hash" --summary "Summary"
echo "content" | python3 add_session.py --title "Title" --commit "hash"
python3 add_session.py --title "Title" --commit "hash" --summary "Summary" [--package cli]
python3 add_session.py --title "Title" --branch "feat/my-branch"
# Pipe detailed content via stdin (use --stdin to opt in):
cat << 'EOF' | python3 add_session.py --stdin --title "Title" --summary "Summary"
<session content here>
EOF
Branch resolution order:
1. --branch CLI arg (explicit)
2. task.json branch field (from active task)
3. git branch --show-current (auto-detect)
4. None (omitted gracefully)
"""
from __future__ import annotations
@@ -20,11 +31,21 @@ from pathlib import Path
from common.paths import (
FILE_JOURNAL_PREFIX,
get_repo_root,
get_current_task,
get_developer,
get_workspace_dir,
)
from common.developer import ensure_developer
from common.config import get_session_commit_message, get_max_journal_lines
from common.git import run_git
from common.tasks import load_task
from common.config import (
get_packages,
get_session_commit_message,
get_max_journal_lines,
is_monorepo,
resolve_package,
validate_package,
)
# =============================================================================
@@ -123,7 +144,9 @@ def generate_session_content(
commit: str,
summary: str,
extra_content: str,
today: str
today: str,
package: str | None = None,
branch: str | None = None,
) -> str:
"""Generate session content."""
if commit and commit != "-":
@@ -135,12 +158,15 @@ def generate_session_content(
else:
commit_table = "(No commits - planning session)"
package_line = f"\n**Package**: {package}" if package else ""
branch_line = f"\n**Branch**: `{branch}`" if branch else ""
return f"""
## Session {session_num}: {title}
**Date**: {today}
**Task**: {title}
**Task**: {title}{package_line}{branch_line}
### Summary
@@ -175,7 +201,8 @@ def update_index(
commit: str,
new_session: int,
active_file: str,
today: str
today: str,
branch: str | None = None,
) -> bool:
"""Update index.md with new session info."""
# Format commit for display
@@ -254,10 +281,25 @@ def update_index(
continue
if in_session_history:
new_lines.append(line)
if re.match(r"^\|\s*-", line) and not header_written:
new_lines.append(f"| {new_session} | {today} | {title} | {commit_display} |")
# Migrate old 4/6-column headers to 5-column Branch-only history.
if re.match(
r"^\|\s*#\s*\|\s*Date\s*\|\s*Title\s*\|\s*Commits\s*\|\s*Branch\s*\|\s*Base Branch\s*\|\s*$",
line,
):
new_lines.append("| # | Date | Title | Commits | Branch |")
continue
if re.match(r"^\|\s*#\s*\|\s*Date\s*\|\s*Title\s*\|\s*Commits\s*\|\s*Branch\s*\|\s*$", line):
new_lines.append("| # | Date | Title | Commits | Branch |")
continue
if re.match(r"^\|\s*#\s*\|\s*Date\s*\|\s*Title\s*\|\s*Commits\s*\|\s*$", line):
new_lines.append("| # | Date | Title | Commits | Branch |")
continue
if re.match(r"^\|[-| ]+\|\s*$", line) and not header_written:
new_lines.append("|---|------|-------|---------|--------|")
new_lines.append(f"| {new_session} | {today} | {title} | {commit_display} | `{branch or '-'}` |")
header_written = True
continue
new_lines.append(line)
continue
new_lines.append(line)
@@ -274,11 +316,16 @@ def update_index(
def _auto_commit_workspace(repo_root: Path) -> None:
"""Stage .trellis/workspace and .trellis/tasks, then commit with a configured message."""
commit_msg = get_session_commit_message(repo_root)
subprocess.run(
add_result = subprocess.run(
["git", "add", "-A", ".trellis/workspace", ".trellis/tasks"],
cwd=repo_root,
capture_output=True,
text=True,
)
if add_result.returncode != 0:
print(f"[WARN] git add failed (exit {add_result.returncode}): {add_result.stderr.strip()}", file=sys.stderr)
print("[WARN] Please commit .trellis/ changes manually: git add .trellis && git commit", file=sys.stderr)
return
# Check if there are staged changes
result = subprocess.run(
["git", "diff", "--cached", "--quiet", "--", ".trellis/workspace", ".trellis/tasks"],
@@ -305,6 +352,8 @@ def add_session(
summary: str = "(Add summary)",
extra_content: str = "(Add details)",
auto_commit: bool = True,
package: str | None = None,
branch: str | None = None,
) -> int:
"""Add a new session."""
repo_root = get_repo_root()
@@ -330,7 +379,8 @@ def add_session(
new_session = current_session + 1
session_content = generate_session_content(
new_session, title, commit, summary, extra_content, today
new_session, title, commit, summary, extra_content, today, package,
branch,
)
content_lines = len(session_content.splitlines())
@@ -367,7 +417,16 @@ def add_session(
# Update index.md
active_file = f"{FILE_JOURNAL_PREFIX}{target_num}.md"
if not update_index(index_file, dev_dir, title, commit, new_session, active_file, today):
if not update_index(
index_file,
dev_dir,
title,
commit,
new_session,
active_file,
today,
branch,
):
return 1
print("", file=sys.stderr)
@@ -400,8 +459,12 @@ def main() -> int:
parser.add_argument("--commit", default="-", help="Comma-separated commit hashes")
parser.add_argument("--summary", default="(Add summary)", help="Brief summary")
parser.add_argument("--content-file", help="Path to file with detailed content")
parser.add_argument("--package", help="Package name tag (e.g., cli, docs-site)")
parser.add_argument("--branch", help="Branch name (auto-detected if omitted)")
parser.add_argument("--no-commit", action="store_true",
help="Skip auto-commit of workspace changes")
parser.add_argument("--stdin", action="store_true",
help="Read extra content from stdin (explicit opt-in)")
args = parser.parse_args()
@@ -410,12 +473,47 @@ def main() -> int:
content_path = Path(args.content_file)
if content_path.is_file():
extra_content = content_path.read_text(encoding="utf-8")
elif not sys.stdin.isatty():
elif args.stdin:
extra_content = sys.stdin.read()
# Load active task once — shared by package and branch resolution
repo_root = get_repo_root()
current = get_current_task(repo_root)
task_data = load_task(repo_root / current) if current else None
package = args.package
if package:
# CLI source: fail-fast in monorepo, ignore in single-repo
if not is_monorepo(repo_root):
print("Warning: --package ignored in single-repo project", file=sys.stderr)
package = None
elif not validate_package(package, repo_root):
packages = get_packages(repo_root)
available = ", ".join(sorted(packages.keys())) if packages else "(none)"
print(f"Error: unknown package '{package}'. Available: {available}", file=sys.stderr)
return 1
else:
# Inferred: active task's task.json.package → default_package → None
task_package = task_data.package if task_data else None
package = resolve_package(task_package, repo_root)
# Resolve branch: CLI → task.json → git auto-detect → None
branch = args.branch
if not branch:
if task_data and task_data.raw.get("branch"):
branch = task_data.raw["branch"]
else:
_, branch_out, _ = run_git(["branch", "--show-current"], cwd=repo_root)
detected = branch_out.strip()
if detected:
branch = detected
return add_session(
args.title, args.commit, args.summary, extra_content,
auto_commit=not args.no_commit,
package=package,
branch=branch,
)